xref: /reactos/ntoskrnl/mm/ARM3/virtual.c (revision fc3ccb39)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/virtual.c
5  * PURPOSE:         ARM Memory Manager Virtual Memory Management
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
17 
18 #define MI_MAPPED_COPY_PAGES  14
19 #define MI_POOL_COPY_BYTES    512
20 #define MI_MAX_TRANSFER_SIZE  64 * 1024
21 
22 NTSTATUS NTAPI
23 MiProtectVirtualMemory(IN PEPROCESS Process,
24                        IN OUT PVOID *BaseAddress,
25                        IN OUT PSIZE_T NumberOfBytesToProtect,
26                        IN ULONG NewAccessProtection,
27                        OUT PULONG OldAccessProtection  OPTIONAL);
28 
29 VOID
30 NTAPI
31 MiFlushTbAndCapture(IN PMMVAD FoundVad,
32                     IN PMMPTE PointerPte,
33                     IN ULONG ProtectionMask,
34                     IN PMMPFN Pfn1,
35                     IN BOOLEAN CaptureDirtyBit);
36 
37 
38 /* PRIVATE FUNCTIONS **********************************************************/
39 
40 ULONG
41 NTAPI
42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
43                           IN ULONG_PTR EndingAddress,
44                           IN PMMVAD Vad,
45                           IN PEPROCESS Process)
46 {
47     PMMPTE PointerPte, LastPte;
48     PMMPDE PointerPde;
49     ULONG CommittedPages;
50 
51     /* Compute starting and ending PTE and PDE addresses */
52     PointerPde = MiAddressToPde(StartingAddress);
53     PointerPte = MiAddressToPte(StartingAddress);
54     LastPte = MiAddressToPte(EndingAddress);
55 
56     /* Handle commited pages first */
57     if (Vad->u.VadFlags.MemCommit == 1)
58     {
59         /* This is a committed VAD, so Assume the whole range is committed */
60         CommittedPages = (ULONG)BYTES_TO_PAGES(EndingAddress - StartingAddress);
61 
62         /* Is the PDE demand-zero? */
63         PointerPde = MiPteToPde(PointerPte);
64         if (PointerPde->u.Long != 0)
65         {
66             /* It is not. Is it valid? */
67             if (PointerPde->u.Hard.Valid == 0)
68             {
69                 /* Fault it in */
70                 PointerPte = MiPteToAddress(PointerPde);
71                 MiMakeSystemAddressValid(PointerPte, Process);
72             }
73         }
74         else
75         {
76             /* It is, skip it and move to the next PDE, unless we're done */
77             PointerPde++;
78             PointerPte = MiPteToAddress(PointerPde);
79             if (PointerPte > LastPte) return CommittedPages;
80         }
81 
82         /* Now loop all the PTEs in the range */
83         while (PointerPte <= LastPte)
84         {
85             /* Have we crossed a PDE boundary? */
86             if (MiIsPteOnPdeBoundary(PointerPte))
87             {
88                 /* Is this PDE demand zero? */
89                 PointerPde = MiPteToPde(PointerPte);
90                 if (PointerPde->u.Long != 0)
91                 {
92                     /* It isn't -- is it valid? */
93                     if (PointerPde->u.Hard.Valid == 0)
94                     {
95                         /* Nope, fault it in */
96                         PointerPte = MiPteToAddress(PointerPde);
97                         MiMakeSystemAddressValid(PointerPte, Process);
98                     }
99                 }
100                 else
101                 {
102                     /* It is, skip it and move to the next PDE */
103                     PointerPde++;
104                     PointerPte = MiPteToAddress(PointerPde);
105                     continue;
106                 }
107             }
108 
109             /* Is this PTE demand zero? */
110             if (PointerPte->u.Long != 0)
111             {
112                 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113                 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
114                     (PointerPte->u.Hard.Valid == 0) &&
115                     ((PointerPte->u.Soft.Prototype == 0) ||
116                      (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
117                 {
118                     /* It is, so remove it from the count of commited pages */
119                     CommittedPages--;
120                 }
121             }
122 
123             /* Move to the next PTE */
124             PointerPte++;
125         }
126 
127         /* Return how many committed pages there still are */
128         return CommittedPages;
129     }
130 
131     /* This is a non-commited VAD, so assume none of it is committed */
132     CommittedPages = 0;
133 
134     /* Is the PDE demand-zero? */
135     PointerPde = MiPteToPde(PointerPte);
136     if (PointerPde->u.Long != 0)
137     {
138         /* It isn't -- is it invalid? */
139         if (PointerPde->u.Hard.Valid == 0)
140         {
141             /* It is, so page it in */
142             PointerPte = MiPteToAddress(PointerPde);
143             MiMakeSystemAddressValid(PointerPte, Process);
144         }
145     }
146     else
147     {
148         /* It is, so skip it and move to the next PDE */
149         PointerPde++;
150         PointerPte = MiPteToAddress(PointerPde);
151         if (PointerPte > LastPte) return CommittedPages;
152     }
153 
154     /* Loop all the PTEs in this PDE */
155     while (PointerPte <= LastPte)
156     {
157         /* Have we crossed a PDE boundary? */
158         if (MiIsPteOnPdeBoundary(PointerPte))
159         {
160             /* Is this new PDE demand-zero? */
161             PointerPde = MiPteToPde(PointerPte);
162             if (PointerPde->u.Long != 0)
163             {
164                 /* It isn't. Is it valid? */
165                 if (PointerPde->u.Hard.Valid == 0)
166                 {
167                     /* It isn't, so make it valid */
168                     PointerPte = MiPteToAddress(PointerPde);
169                     MiMakeSystemAddressValid(PointerPte, Process);
170                 }
171             }
172             else
173             {
174                 /* It is, so skip it and move to the next one */
175                 PointerPde++;
176                 PointerPte = MiPteToAddress(PointerPde);
177                 continue;
178             }
179         }
180 
181         /* Is this PTE demand-zero? */
182         if (PointerPte->u.Long != 0)
183         {
184             /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185             if ((PointerPte->u.Soft.Protection != MM_DECOMMIT) ||
186                 (PointerPte->u.Hard.Valid == 1) ||
187                 ((PointerPte->u.Soft.Prototype == 1) &&
188                  (PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)))
189             {
190                 /* It is! So we'll treat this as a committed page */
191                 CommittedPages++;
192             }
193         }
194 
195         /* Move to the next PTE */
196         PointerPte++;
197     }
198 
199     /* Return how many committed pages we found in this VAD */
200     return CommittedPages;
201 }
202 
203 ULONG
204 NTAPI
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
206                          IN PEPROCESS CurrentProcess)
207 {
208     NTSTATUS Status;
209     BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
210     PETHREAD CurrentThread = PsGetCurrentThread();
211 
212     /* Must be a non-pool page table, since those are double-mapped already */
213     ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
214     ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
215            (PageTableVirtualAddress > MmPagedPoolEnd));
216 
217     /* Working set lock or PFN lock should be held */
218     ASSERT(KeAreAllApcsDisabled() == TRUE);
219 
220     /* Check if the page table is valid */
221     while (!MmIsAddressValid(PageTableVirtualAddress))
222     {
223         /* Release the working set lock */
224         MiUnlockProcessWorkingSetForFault(CurrentProcess,
225                                           CurrentThread,
226                                           &WsSafe,
227                                           &WsShared);
228 
229         /* Fault it in */
230         Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
231         if (!NT_SUCCESS(Status))
232         {
233             /* This should not fail */
234             KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
235                          1,
236                          Status,
237                          (ULONG_PTR)CurrentProcess,
238                          (ULONG_PTR)PageTableVirtualAddress);
239         }
240 
241         /* Lock the working set again */
242         MiLockProcessWorkingSetForFault(CurrentProcess,
243                                         CurrentThread,
244                                         WsSafe,
245                                         WsShared);
246 
247         /* This flag will be useful later when we do better locking */
248         LockChange = TRUE;
249     }
250 
251     /* Let caller know what the lock state is */
252     return LockChange;
253 }
254 
255 ULONG
256 NTAPI
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
258                             IN KIRQL OldIrql)
259 {
260     NTSTATUS Status;
261     BOOLEAN LockChange = FALSE;
262 
263     /* Must be e kernel address */
264     ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
265 
266     /* Check if the page is valid */
267     while (!MmIsAddressValid(VirtualAddress))
268     {
269         /* Release the PFN database */
270         MiReleasePfnLock(OldIrql);
271 
272         /* Fault it in */
273         Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
274         if (!NT_SUCCESS(Status))
275         {
276             /* This should not fail */
277             KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
278                          3,
279                          Status,
280                          0,
281                          (ULONG_PTR)VirtualAddress);
282         }
283 
284         /* This flag will be useful later when we do better locking */
285         LockChange = TRUE;
286 
287         /* Lock the PFN database */
288         OldIrql = MiAcquirePfnLock();
289     }
290 
291     /* Let caller know what the lock state is */
292     return LockChange;
293 }
294 
295 PFN_COUNT
296 NTAPI
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
298                          IN PFN_NUMBER PageCount,
299                          IN ULONG Flags,
300                          OUT PPFN_NUMBER ValidPages)
301 {
302     PFN_COUNT ActualPages = 0;
303     PETHREAD CurrentThread = PsGetCurrentThread();
304     PMMPFN Pfn1, Pfn2;
305     PFN_NUMBER PageFrameIndex, PageTableIndex;
306     KIRQL OldIrql;
307     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
308 
309     /* Lock the system working set */
310     MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
311 
312     /* Loop all pages */
313     while (PageCount)
314     {
315         /* Make sure there's some data about the page */
316         if (PointerPte->u.Long)
317         {
318             /* As always, only handle current ARM3 scenarios */
319             ASSERT(PointerPte->u.Soft.Prototype == 0);
320             ASSERT(PointerPte->u.Soft.Transition == 0);
321 
322             /* Normally this is one possibility -- freeing a valid page */
323             if (PointerPte->u.Hard.Valid)
324             {
325                 /* Get the page PFN */
326                 PageFrameIndex = PFN_FROM_PTE(PointerPte);
327                 Pfn1 = MiGetPfnEntry(PageFrameIndex);
328 
329                 /* Should not have any working set data yet */
330                 ASSERT(Pfn1->u1.WsIndex == 0);
331 
332                 /* Actual valid, legitimate, pages */
333                 if (ValidPages) (*ValidPages)++;
334 
335                 /* Get the page table entry */
336                 PageTableIndex = Pfn1->u4.PteFrame;
337                 Pfn2 = MiGetPfnEntry(PageTableIndex);
338 
339                 /* Lock the PFN database */
340                 OldIrql = MiAcquirePfnLock();
341 
342                 /* Delete it the page */
343                 MI_SET_PFN_DELETED(Pfn1);
344                 MiDecrementShareCount(Pfn1, PageFrameIndex);
345 
346                 /* Decrement the page table too */
347                 MiDecrementShareCount(Pfn2, PageTableIndex);
348 
349                 /* Release the PFN database */
350                 MiReleasePfnLock(OldIrql);
351 
352                 /* Destroy the PTE */
353                 MI_ERASE_PTE(PointerPte);
354             }
355             else
356             {
357                 /*
358                  * The only other ARM3 possibility is a demand zero page, which would
359                  * mean freeing some of the paged pool pages that haven't even been
360                  * touched yet, as part of a larger allocation.
361                  *
362                  * Right now, we shouldn't expect any page file information in the PTE
363                  */
364                 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
365 
366                 /* Destroy the PTE */
367                 MI_ERASE_PTE(PointerPte);
368             }
369 
370             /* Actual legitimate pages */
371             ActualPages++;
372         }
373 
374         /* Keep going */
375         PointerPte++;
376         PageCount--;
377     }
378 
379     /* Release the working set */
380     MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
381 
382     /* Flush the entire TLB */
383     KeFlushEntireTb(TRUE, TRUE);
384 
385     /* Done */
386     return ActualPages;
387 }
388 
389 VOID
390 NTAPI
391 MiDeletePte(IN PMMPTE PointerPte,
392             IN PVOID VirtualAddress,
393             IN PEPROCESS CurrentProcess,
394             IN PMMPTE PrototypePte)
395 {
396     PMMPFN Pfn1;
397     MMPTE TempPte;
398     PFN_NUMBER PageFrameIndex;
399     PMMPDE PointerPde;
400 
401     /* PFN lock must be held */
402     MI_ASSERT_PFN_LOCK_HELD();
403 
404     /* Capture the PTE */
405     TempPte = *PointerPte;
406 
407     /* See if the PTE is valid */
408     if (TempPte.u.Hard.Valid == 0)
409     {
410         /* Prototype and paged out PTEs not supported yet */
411         ASSERT(TempPte.u.Soft.Prototype == 0);
412         ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1));
413 
414         if (TempPte.u.Soft.Transition)
415         {
416             /* Get the PFN entry */
417             PageFrameIndex = PFN_FROM_PTE(&TempPte);
418             Pfn1 = MiGetPfnEntry(PageFrameIndex);
419 
420             DPRINT("Pte %p is transitional!\n", PointerPte);
421 
422             /* Make sure the saved PTE address is valid */
423             ASSERT((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) == PointerPte);
424 
425             /* Destroy the PTE */
426             MI_ERASE_PTE(PointerPte);
427 
428             /* Drop the reference on the page table. */
429             MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
430 
431             ASSERT(Pfn1->u3.e1.PrototypePte == 0);
432 
433             /* Make the page free. For prototypes, it will be made free when deleting the section object */
434             if (Pfn1->u3.e2.ReferenceCount == 0)
435             {
436                 /* And it should be in standby or modified list */
437                 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList));
438 
439                 /* Unlink it and set its reference count to one */
440                 MiUnlinkPageFromList(Pfn1);
441                 Pfn1->u3.e2.ReferenceCount++;
442 
443                 /* This will put it back in free list and clean properly up */
444                 MI_SET_PFN_DELETED(Pfn1);
445                 MiDecrementReferenceCount(Pfn1, PageFrameIndex);
446             }
447             return;
448         }
449     }
450 
451     /* Get the PFN entry */
452     PageFrameIndex = PFN_FROM_PTE(&TempPte);
453     Pfn1 = MiGetPfnEntry(PageFrameIndex);
454 
455     /* Check if this is a valid, prototype PTE */
456     if (Pfn1->u3.e1.PrototypePte == 1)
457     {
458         /* Get the PDE and make sure it's faulted in */
459         PointerPde = MiPteToPde(PointerPte);
460         if (PointerPde->u.Hard.Valid == 0)
461         {
462 #if (_MI_PAGING_LEVELS == 2)
463             /* Could be paged pool access from a new process -- synchronize the page directories */
464             if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
465             {
466 #endif
467                 /* The PDE must be valid at this point */
468                 KeBugCheckEx(MEMORY_MANAGEMENT,
469                              0x61940,
470                              (ULONG_PTR)PointerPte,
471                              PointerPte->u.Long,
472                              (ULONG_PTR)VirtualAddress);
473             }
474 #if (_MI_PAGING_LEVELS == 2)
475         }
476 #endif
477         /* Drop the share count on the page table */
478         PointerPde = MiPteToPde(PointerPte);
479         MiDecrementShareCount(MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber),
480             PointerPde->u.Hard.PageFrameNumber);
481 
482         /* Drop the share count */
483         MiDecrementShareCount(Pfn1, PageFrameIndex);
484 
485         /* Either a fork, or this is the shared user data page */
486         if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
487         {
488             /* If it's not the shared user page, then crash, since there's no fork() yet */
489             if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
490                  (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
491             {
492                 /* Must be some sort of memory corruption */
493                 KeBugCheckEx(MEMORY_MANAGEMENT,
494                              0x400,
495                              (ULONG_PTR)PointerPte,
496                              (ULONG_PTR)PrototypePte,
497                              (ULONG_PTR)Pfn1->PteAddress);
498             }
499         }
500 
501         /* Erase it */
502         MI_ERASE_PTE(PointerPte);
503     }
504     else
505     {
506         /* Make sure the saved PTE address is valid */
507         if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
508         {
509             /* The PFN entry is illegal, or invalid */
510             KeBugCheckEx(MEMORY_MANAGEMENT,
511                          0x401,
512                          (ULONG_PTR)PointerPte,
513                          PointerPte->u.Long,
514                          (ULONG_PTR)Pfn1->PteAddress);
515         }
516 
517         /* Erase the PTE */
518         MI_ERASE_PTE(PointerPte);
519 
520         /* There should only be 1 shared reference count */
521         ASSERT(Pfn1->u2.ShareCount == 1);
522 
523         /* Drop the reference on the page table. */
524         MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
525 
526         /* Mark the PFN for deletion and dereference what should be the last ref */
527         MI_SET_PFN_DELETED(Pfn1);
528         MiDecrementShareCount(Pfn1, PageFrameIndex);
529 
530         /* We should eventually do this */
531         //CurrentProcess->NumberOfPrivatePages--;
532     }
533 
534     /* Flush the TLB */
535     KeFlushCurrentTb();
536 }
537 
538 VOID
539 NTAPI
540 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
541                          IN ULONG_PTR EndingAddress,
542                          IN PMMVAD Vad)
543 {
544     PMMPTE PointerPte, PrototypePte, LastPrototypePte;
545     PMMPDE PointerPde;
546 #if (_MI_PAGING_LEVELS >= 3)
547     PMMPPE PointerPpe;
548 #endif
549 #if (_MI_PAGING_LEVELS >= 4)
550     PMMPPE PointerPxe;
551 #endif
552     MMPTE TempPte;
553     PEPROCESS CurrentProcess;
554     KIRQL OldIrql;
555     BOOLEAN AddressGap = FALSE;
556     PSUBSECTION Subsection;
557 
558     /* Get out if this is a fake VAD, RosMm will free the marea pages */
559     if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
560 
561     /* Get the current process */
562     CurrentProcess = PsGetCurrentProcess();
563 
564     /* Check if this is a section VAD or a VM VAD */
565     if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
566     {
567         /* Don't worry about prototypes */
568         PrototypePte = LastPrototypePte = NULL;
569     }
570     else
571     {
572         /* Get the prototype PTE */
573         PrototypePte = Vad->FirstPrototypePte;
574         LastPrototypePte = Vad->FirstPrototypePte + 1;
575     }
576 
577     /* In all cases, we don't support fork() yet */
578     ASSERT(CurrentProcess->CloneRoot == NULL);
579 
580     /* Loop the PTE for each VA (EndingAddress is inclusive!) */
581     while (Va <= EndingAddress)
582     {
583 #if (_MI_PAGING_LEVELS >= 4)
584         /* Get the PXE and check if it's valid */
585         PointerPxe = MiAddressToPxe((PVOID)Va);
586         if (!PointerPxe->u.Hard.Valid)
587         {
588             /* Check for unmapped range and skip it */
589             if (!PointerPxe->u.Long)
590             {
591                 /* There are gaps in the address space */
592                 AddressGap = TRUE;
593 
594                 /* Update Va and continue looping */
595                 Va = (ULONG_PTR)MiPxeToAddress(PointerPxe + 1);
596                 continue;
597             }
598 
599             /* Make the PXE valid */
600             MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), CurrentProcess);
601         }
602 #endif
603 #if (_MI_PAGING_LEVELS >= 3)
604         /* Get the PPE and check if it's valid */
605         PointerPpe = MiAddressToPpe((PVOID)Va);
606         if (!PointerPpe->u.Hard.Valid)
607         {
608             /* Check for unmapped range and skip it */
609             if (!PointerPpe->u.Long)
610             {
611                 /* There are gaps in the address space */
612                 AddressGap = TRUE;
613 
614                 /* Update Va and continue looping */
615                 Va = (ULONG_PTR)MiPpeToAddress(PointerPpe + 1);
616                 continue;
617             }
618 
619             /* Make the PPE valid */
620             MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), CurrentProcess);
621         }
622 #endif
623         /* Skip invalid PDEs */
624         PointerPde = MiAddressToPde((PVOID)Va);
625         if (!PointerPde->u.Long)
626         {
627             /* There are gaps in the address space */
628             AddressGap = TRUE;
629 
630             /* Check if all the PDEs are invalid, so there's nothing to free */
631             Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1);
632             continue;
633         }
634 
635         /* Now check if the PDE is mapped in */
636         if (!PointerPde->u.Hard.Valid)
637         {
638             /* It isn't, so map it in */
639             PointerPte = MiPteToAddress(PointerPde);
640             MiMakeSystemAddressValid(PointerPte, CurrentProcess);
641         }
642 
643         /* Now we should have a valid PDE, mapped in, and still have some VA */
644         ASSERT(PointerPde->u.Hard.Valid == 1);
645         ASSERT(Va <= EndingAddress);
646 
647         /* Check if this is a section VAD with gaps in it */
648         if ((AddressGap) && (LastPrototypePte))
649         {
650             /* We need to skip to the next correct prototype PTE */
651             PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
652 
653             /* And we need the subsection to skip to the next last prototype PTE */
654             Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
655             if (Subsection)
656             {
657                 /* Found it! */
658                 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
659             }
660             else
661             {
662                 /* No more subsections, we are done with prototype PTEs */
663                 PrototypePte = NULL;
664             }
665         }
666 
667         /* Lock the PFN Database while we delete the PTEs */
668         OldIrql = MiAcquirePfnLock();
669         PointerPte = MiAddressToPte(Va);
670         do
671         {
672             /* Capture the PDE and make sure it exists */
673             TempPte = *PointerPte;
674             if (TempPte.u.Long)
675             {
676                 MiDecrementPageTableReferences((PVOID)Va);
677 
678                 /* Check if the PTE is actually mapped in */
679                 if (MI_IS_MAPPED_PTE(&TempPte))
680                 {
681                     /* Are we dealing with section VAD? */
682                     if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
683                     {
684                         /* We need to skip to the next correct prototype PTE */
685                         PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
686 
687                         /* And we need the subsection to skip to the next last prototype PTE */
688                         Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
689                         if (Subsection)
690                         {
691                             /* Found it! */
692                             LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
693                         }
694                         else
695                         {
696                             /* No more subsections, we are done with prototype PTEs */
697                             PrototypePte = NULL;
698                         }
699                     }
700 
701                     /* Check for prototype PTE */
702                     if ((TempPte.u.Hard.Valid == 0) &&
703                         (TempPte.u.Soft.Prototype == 1))
704                     {
705                         /* Just nuke it */
706                         MI_ERASE_PTE(PointerPte);
707                     }
708                     else
709                     {
710                         /* Delete the PTE proper */
711                         MiDeletePte(PointerPte,
712                                     (PVOID)Va,
713                                     CurrentProcess,
714                                     PrototypePte);
715                     }
716                 }
717                 else
718                 {
719                     /* The PTE was never mapped, just nuke it here */
720                     MI_ERASE_PTE(PointerPte);
721                 }
722             }
723 
724             /* Update the address and PTE for it */
725             Va += PAGE_SIZE;
726             PointerPte++;
727             PrototypePte++;
728 
729             /* Making sure the PDE is still valid */
730             ASSERT(PointerPde->u.Hard.Valid == 1);
731         }
732         while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
733 
734         /* The PDE should still be valid at this point */
735         ASSERT(PointerPde->u.Hard.Valid == 1);
736 
737         /* Check remaining PTE count (go back 1 page due to above loop) */
738         if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
739         {
740             if (PointerPde->u.Long != 0)
741             {
742                 /* Delete the PTE proper */
743                 MiDeletePte(PointerPde,
744                             MiPteToAddress(PointerPde),
745                             CurrentProcess,
746                             NULL);
747             }
748         }
749 
750         /* Release the lock and get out if we're done */
751         MiReleasePfnLock(OldIrql);
752         if (Va > EndingAddress) return;
753 
754         /* Otherwise, we exited because we hit a new PDE boundary, so start over */
755         PointerPde = MiAddressToPde(Va);
756         AddressGap = FALSE;
757     }
758 }
759 
760 LONG
761 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
762                    OUT PBOOLEAN HaveBadAddress,
763                    OUT PULONG_PTR BadAddress)
764 {
765     PEXCEPTION_RECORD ExceptionRecord;
766     PAGED_CODE();
767 
768     //
769     // Assume default
770     //
771     *HaveBadAddress = FALSE;
772 
773     //
774     // Get the exception record
775     //
776     ExceptionRecord = ExceptionInfo->ExceptionRecord;
777 
778     //
779     // Look at the exception code
780     //
781     if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
782         (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
783         (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
784     {
785         //
786         // We can tell the address if we have more than one parameter
787         //
788         if (ExceptionRecord->NumberParameters > 1)
789         {
790             //
791             // Return the address
792             //
793             *HaveBadAddress = TRUE;
794             *BadAddress = ExceptionRecord->ExceptionInformation[1];
795         }
796     }
797 
798     //
799     // Continue executing the next handler
800     //
801     return EXCEPTION_EXECUTE_HANDLER;
802 }
803 
804 NTSTATUS
805 NTAPI
806 MiDoMappedCopy(IN PEPROCESS SourceProcess,
807                IN PVOID SourceAddress,
808                IN PEPROCESS TargetProcess,
809                OUT PVOID TargetAddress,
810                IN SIZE_T BufferSize,
811                IN KPROCESSOR_MODE PreviousMode,
812                OUT PSIZE_T ReturnSize)
813 {
814     PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
815     PMDL Mdl = (PMDL)MdlBuffer;
816     SIZE_T TotalSize, CurrentSize, RemainingSize;
817     volatile BOOLEAN FailedInProbe = FALSE;
818     volatile BOOLEAN PagesLocked = FALSE;
819     PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
820     volatile PVOID MdlAddress = NULL;
821     KAPC_STATE ApcState;
822     BOOLEAN HaveBadAddress;
823     ULONG_PTR BadAddress;
824     NTSTATUS Status = STATUS_SUCCESS;
825     PAGED_CODE();
826 
827     //
828     // Calculate the maximum amount of data to move
829     //
830     TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
831     if (BufferSize <= TotalSize) TotalSize = BufferSize;
832     CurrentSize = TotalSize;
833     RemainingSize = BufferSize;
834 
835     //
836     // Loop as long as there is still data
837     //
838     while (RemainingSize > 0)
839     {
840         //
841         // Check if this transfer will finish everything off
842         //
843         if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
844 
845         //
846         // Attach to the source address space
847         //
848         KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
849 
850         //
851         // Check state for this pass
852         //
853         ASSERT(MdlAddress == NULL);
854         ASSERT(PagesLocked == FALSE);
855         ASSERT(FailedInProbe == FALSE);
856 
857         //
858         // Protect user-mode copy
859         //
860         _SEH2_TRY
861         {
862             //
863             // If this is our first time, probe the buffer
864             //
865             if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
866             {
867                 //
868                 // Catch a failure here
869                 //
870                 FailedInProbe = TRUE;
871 
872                 //
873                 // Do the probe
874                 //
875                 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
876 
877                 //
878                 // Passed
879                 //
880                 FailedInProbe = FALSE;
881             }
882 
883             //
884             // Initialize and probe and lock the MDL
885             //
886             MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
887             MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
888             PagesLocked = TRUE;
889         }
890         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
891         {
892             Status = _SEH2_GetExceptionCode();
893         }
894         _SEH2_END
895 
896         /* Detach from source process */
897         KeUnstackDetachProcess(&ApcState);
898 
899         if (Status != STATUS_SUCCESS)
900         {
901             goto Exit;
902         }
903 
904         //
905         // Now map the pages
906         //
907         MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
908                                                   KernelMode,
909                                                   MmCached,
910                                                   NULL,
911                                                   FALSE,
912                                                   HighPagePriority);
913         if (!MdlAddress)
914         {
915             Status = STATUS_INSUFFICIENT_RESOURCES;
916             goto Exit;
917         }
918 
919         //
920         // Grab to the target process
921         //
922         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
923 
924         _SEH2_TRY
925         {
926             //
927             // Check if this is our first time through
928             //
929             if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
930             {
931                 //
932                 // Catch a failure here
933                 //
934                 FailedInProbe = TRUE;
935 
936                 //
937                 // Do the probe
938                 //
939                 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
940 
941                 //
942                 // Passed
943                 //
944                 FailedInProbe = FALSE;
945             }
946 
947             //
948             // Now do the actual move
949             //
950             RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
951         }
952         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
953                                         &HaveBadAddress,
954                                         &BadAddress))
955         {
956             *ReturnSize = BufferSize - RemainingSize;
957             //
958             // Check if we failed during the probe
959             //
960             if (FailedInProbe)
961             {
962                 //
963                 // Exit
964                 //
965                 Status = _SEH2_GetExceptionCode();
966             }
967             else
968             {
969                 //
970                 // Othewise we failed during the move.
971                 // Check if we know exactly where we stopped copying
972                 //
973                 if (HaveBadAddress)
974                 {
975                     //
976                     // Return the exact number of bytes copied
977                     //
978                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
979                 }
980                 //
981                 // Return partial copy
982                 //
983                 Status = STATUS_PARTIAL_COPY;
984             }
985         }
986         _SEH2_END;
987 
988         /* Detach from target process */
989         KeUnstackDetachProcess(&ApcState);
990 
991         //
992         // Check for SEH status
993         //
994         if (Status != STATUS_SUCCESS)
995         {
996             goto Exit;
997         }
998 
999         //
1000         // Unmap and unlock
1001         //
1002         MmUnmapLockedPages(MdlAddress, Mdl);
1003         MdlAddress = NULL;
1004         MmUnlockPages(Mdl);
1005         PagesLocked = FALSE;
1006 
1007         //
1008         // Update location and size
1009         //
1010         RemainingSize -= CurrentSize;
1011         CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1012         CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
1013     }
1014 
1015 Exit:
1016     if (MdlAddress != NULL)
1017         MmUnmapLockedPages(MdlAddress, Mdl);
1018     if (PagesLocked)
1019         MmUnlockPages(Mdl);
1020 
1021     //
1022     // All bytes read
1023     //
1024     if (Status == STATUS_SUCCESS)
1025         *ReturnSize = BufferSize;
1026     return Status;
1027 }
1028 
1029 NTSTATUS
1030 NTAPI
1031 MiDoPoolCopy(IN PEPROCESS SourceProcess,
1032              IN PVOID SourceAddress,
1033              IN PEPROCESS TargetProcess,
1034              OUT PVOID TargetAddress,
1035              IN SIZE_T BufferSize,
1036              IN KPROCESSOR_MODE PreviousMode,
1037              OUT PSIZE_T ReturnSize)
1038 {
1039     UCHAR StackBuffer[MI_POOL_COPY_BYTES];
1040     SIZE_T TotalSize, CurrentSize, RemainingSize;
1041     volatile BOOLEAN FailedInProbe = FALSE, HavePoolAddress = FALSE;
1042     PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
1043     PVOID PoolAddress;
1044     KAPC_STATE ApcState;
1045     BOOLEAN HaveBadAddress;
1046     ULONG_PTR BadAddress;
1047     NTSTATUS Status = STATUS_SUCCESS;
1048     PAGED_CODE();
1049 
1050     DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1051         BufferSize, SourceProcess, SourceAddress, TargetProcess, TargetAddress);
1052 
1053     //
1054     // Calculate the maximum amount of data to move
1055     //
1056     TotalSize = MI_MAX_TRANSFER_SIZE;
1057     if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
1058     CurrentSize = TotalSize;
1059     RemainingSize = BufferSize;
1060 
1061     //
1062     // Check if we can use the stack
1063     //
1064     if (BufferSize <= MI_POOL_COPY_BYTES)
1065     {
1066         //
1067         // Use it
1068         //
1069         PoolAddress = (PVOID)StackBuffer;
1070     }
1071     else
1072     {
1073         //
1074         // Allocate pool
1075         //
1076         PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1077         if (!PoolAddress) ASSERT(FALSE);
1078         HavePoolAddress = TRUE;
1079     }
1080 
1081     //
1082     // Loop as long as there is still data
1083     //
1084     while (RemainingSize > 0)
1085     {
1086         //
1087         // Check if this transfer will finish everything off
1088         //
1089         if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1090 
1091         //
1092         // Attach to the source address space
1093         //
1094         KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1095 
1096         /* Check that state is sane */
1097         ASSERT(FailedInProbe == FALSE);
1098         ASSERT(Status == STATUS_SUCCESS);
1099 
1100         //
1101         // Protect user-mode copy
1102         //
1103         _SEH2_TRY
1104         {
1105             //
1106             // If this is our first time, probe the buffer
1107             //
1108             if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1109             {
1110                 //
1111                 // Catch a failure here
1112                 //
1113                 FailedInProbe = TRUE;
1114 
1115                 //
1116                 // Do the probe
1117                 //
1118                 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
1119 
1120                 //
1121                 // Passed
1122                 //
1123                 FailedInProbe = FALSE;
1124             }
1125 
1126             //
1127             // Do the copy
1128             //
1129             RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1130         }
1131         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1132                                         &HaveBadAddress,
1133                                         &BadAddress))
1134         {
1135             *ReturnSize = BufferSize - RemainingSize;
1136 
1137             //
1138             // Check if we failed during the probe
1139             //
1140             if (FailedInProbe)
1141             {
1142                 //
1143                 // Exit
1144                 //
1145                 Status = _SEH2_GetExceptionCode();
1146             }
1147             else
1148             {
1149                 //
1150                 // We failed during the move.
1151                 // Check if we know exactly where we stopped copying
1152                 //
1153                 if (HaveBadAddress)
1154                 {
1155                     //
1156                     // Return the exact number of bytes copied
1157                     //
1158                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1159                 }
1160                 //
1161                 // Return partial copy
1162                 //
1163                 Status = STATUS_PARTIAL_COPY;
1164             }
1165         }
1166         _SEH2_END
1167 
1168         /* Let go of the source */
1169         KeUnstackDetachProcess(&ApcState);
1170 
1171         if (Status != STATUS_SUCCESS)
1172         {
1173             goto Exit;
1174         }
1175 
1176         /* Grab the target process */
1177         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1178 
1179         _SEH2_TRY
1180         {
1181             //
1182             // Check if this is our first time through
1183             //
1184             if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
1185             {
1186                 //
1187                 // Catch a failure here
1188                 //
1189                 FailedInProbe = TRUE;
1190 
1191                 //
1192                 // Do the probe
1193                 //
1194                 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
1195 
1196                 //
1197                 // Passed
1198                 //
1199                 FailedInProbe = FALSE;
1200             }
1201 
1202             //
1203             // Now do the actual move
1204             //
1205             RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1206         }
1207         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1208                                         &HaveBadAddress,
1209                                         &BadAddress))
1210         {
1211             *ReturnSize = BufferSize - RemainingSize;
1212             //
1213             // Check if we failed during the probe
1214             //
1215             if (FailedInProbe)
1216             {
1217                 //
1218                 // Exit
1219                 //
1220                 Status = _SEH2_GetExceptionCode();
1221             }
1222             else
1223             {
1224                 //
1225                 // Otherwise we failed during the move.
1226                 // Check if we know exactly where we stopped copying
1227                 //
1228                 if (HaveBadAddress)
1229                 {
1230                     //
1231                     // Return the exact number of bytes copied
1232                     //
1233                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1234                 }
1235                 //
1236                 // Return partial copy
1237                 //
1238                 Status = STATUS_PARTIAL_COPY;
1239             }
1240         }
1241         _SEH2_END;
1242 
1243         //
1244         // Detach from target
1245         //
1246         KeUnstackDetachProcess(&ApcState);
1247 
1248         //
1249         // Check for SEH status
1250         //
1251         if (Status != STATUS_SUCCESS)
1252         {
1253             goto Exit;
1254         }
1255 
1256         //
1257         // Update location and size
1258         //
1259         RemainingSize -= CurrentSize;
1260         CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1261         CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1262                                        CurrentSize);
1263     }
1264 
1265 Exit:
1266     //
1267     // Check if we had allocated pool
1268     //
1269     if (HavePoolAddress)
1270         ExFreePoolWithTag(PoolAddress, 'VmRw');
1271 
1272     //
1273     // All bytes read
1274     //
1275     if (Status == STATUS_SUCCESS)
1276         *ReturnSize = BufferSize;
1277     return Status;
1278 }
1279 
1280 NTSTATUS
1281 NTAPI
1282 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1283                     IN PVOID SourceAddress,
1284                     IN PEPROCESS TargetProcess,
1285                     OUT PVOID TargetAddress,
1286                     IN SIZE_T BufferSize,
1287                     IN KPROCESSOR_MODE PreviousMode,
1288                     OUT PSIZE_T ReturnSize)
1289 {
1290     NTSTATUS Status;
1291     PEPROCESS Process = SourceProcess;
1292 
1293     //
1294     // Don't accept zero-sized buffers
1295     //
1296     if (!BufferSize) return STATUS_SUCCESS;
1297 
1298     //
1299     // If we are copying from ourselves, lock the target instead
1300     //
1301     if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1302 
1303     //
1304     // Acquire rundown protection
1305     //
1306     if (!ExAcquireRundownProtection(&Process->RundownProtect))
1307     {
1308         //
1309         // Fail
1310         //
1311         return STATUS_PROCESS_IS_TERMINATING;
1312     }
1313 
1314     //
1315     // See if we should use the pool copy
1316     //
1317     if (BufferSize > MI_POOL_COPY_BYTES)
1318     {
1319         //
1320         // Use MDL-copy
1321         //
1322         Status = MiDoMappedCopy(SourceProcess,
1323                                 SourceAddress,
1324                                 TargetProcess,
1325                                 TargetAddress,
1326                                 BufferSize,
1327                                 PreviousMode,
1328                                 ReturnSize);
1329     }
1330     else
1331     {
1332         //
1333         // Do pool copy
1334         //
1335         Status = MiDoPoolCopy(SourceProcess,
1336                               SourceAddress,
1337                               TargetProcess,
1338                               TargetAddress,
1339                               BufferSize,
1340                               PreviousMode,
1341                               ReturnSize);
1342     }
1343 
1344     //
1345     // Release the lock
1346     //
1347     ExReleaseRundownProtection(&Process->RundownProtect);
1348     return Status;
1349 }
1350 
1351 NTSTATUS
1352 NTAPI
1353 MmFlushVirtualMemory(IN PEPROCESS Process,
1354                      IN OUT PVOID *BaseAddress,
1355                      IN OUT PSIZE_T RegionSize,
1356                      OUT PIO_STATUS_BLOCK IoStatusBlock)
1357 {
1358     PAGED_CODE();
1359     UNIMPLEMENTED;
1360 
1361     //
1362     // Fake success
1363     //
1364     return STATUS_SUCCESS;
1365 }
1366 
1367 ULONG
1368 NTAPI
1369 MiGetPageProtection(IN PMMPTE PointerPte)
1370 {
1371     MMPTE TempPte;
1372     PMMPFN Pfn;
1373     PEPROCESS CurrentProcess;
1374     PETHREAD CurrentThread;
1375     BOOLEAN WsSafe, WsShared;
1376     ULONG Protect;
1377     KIRQL OldIrql;
1378     PAGED_CODE();
1379 
1380     /* Copy this PTE's contents */
1381     TempPte = *PointerPte;
1382 
1383     /* Assure it's not totally zero */
1384     ASSERT(TempPte.u.Long);
1385 
1386     /* Check for a special prototype format */
1387     if ((TempPte.u.Soft.Valid == 0) &&
1388         (TempPte.u.Soft.Prototype == 1))
1389     {
1390         /* Check if the prototype PTE is not yet pointing to a PTE */
1391         if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1392         {
1393             /* The prototype PTE contains the protection */
1394             return MmProtectToValue[TempPte.u.Soft.Protection];
1395         }
1396 
1397         /* Get a pointer to the underlying shared PTE */
1398         PointerPte = MiProtoPteToPte(&TempPte);
1399 
1400         /* Since the PTE we want to read can be paged out at any time, we need
1401            to release the working set lock first, so that it can be paged in */
1402         CurrentThread = PsGetCurrentThread();
1403         CurrentProcess = PsGetCurrentProcess();
1404         MiUnlockProcessWorkingSetForFault(CurrentProcess,
1405                                           CurrentThread,
1406                                           &WsSafe,
1407                                           &WsShared);
1408 
1409         /* Now read the PTE value */
1410         TempPte = *PointerPte;
1411 
1412         /* Check if that one is invalid */
1413         if (!TempPte.u.Hard.Valid)
1414         {
1415             /* We get the protection directly from this PTE */
1416             Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1417         }
1418         else
1419         {
1420             /* The PTE is valid, so we might need to get the protection from
1421                the PFN. Lock the PFN database */
1422             OldIrql = MiAcquirePfnLock();
1423 
1424             /* Check if the PDE is still valid */
1425             if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
1426             {
1427                 /* It's not, make it valid */
1428                 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
1429             }
1430 
1431             /* Now it's safe to read the PTE value again */
1432             TempPte = *PointerPte;
1433             ASSERT(TempPte.u.Long != 0);
1434 
1435             /* Check again if the PTE is invalid */
1436             if (!TempPte.u.Hard.Valid)
1437             {
1438                 /* The PTE is not valid, so we can use it's protection field */
1439                 Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1440             }
1441             else
1442             {
1443                 /* The PTE is valid, so we can find the protection in the
1444                    OriginalPte field of the PFN */
1445                 Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
1446                 Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1447             }
1448 
1449             /* Release the PFN database */
1450             MiReleasePfnLock(OldIrql);
1451         }
1452 
1453         /* Lock the working set again */
1454         MiLockProcessWorkingSetForFault(CurrentProcess,
1455                                         CurrentThread,
1456                                         WsSafe,
1457                                         WsShared);
1458 
1459         return Protect;
1460     }
1461 
1462     /* In the easy case of transition or demand zero PTE just return its protection */
1463     if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1464 
1465     /* If we get here, the PTE is valid, so look up the page in PFN database */
1466     Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1467     if (!Pfn->u3.e1.PrototypePte)
1468     {
1469         /* Return protection of the original pte */
1470         ASSERT(Pfn->u4.AweAllocation == 0);
1471         return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1472     }
1473 
1474     /* This is software PTE */
1475     DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1476     DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1477     DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1478     DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1479     return MmProtectToValue[TempPte.u.Soft.Protection];
1480 }
1481 
1482 ULONG
1483 NTAPI
1484 MiQueryAddressState(IN PVOID Va,
1485                     IN PMMVAD Vad,
1486                     IN PEPROCESS TargetProcess,
1487                     OUT PULONG ReturnedProtect,
1488                     OUT PVOID *NextVa)
1489 {
1490 
1491     PMMPTE PointerPte, ProtoPte;
1492     PMMPDE PointerPde;
1493 #if (_MI_PAGING_LEVELS >= 3)
1494     PMMPPE PointerPpe;
1495 #endif
1496 #if (_MI_PAGING_LEVELS >= 4)
1497     PMMPXE PointerPxe;
1498 #endif
1499     MMPTE TempPte, TempProtoPte;
1500     BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1501     ULONG State = MEM_RESERVE, Protect = 0;
1502     ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1503            (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1504 
1505     /* Only normal VADs supported */
1506     ASSERT(Vad->u.VadFlags.VadType == VadNone);
1507 
1508     /* Get the PDE and PTE for the address */
1509     PointerPde = MiAddressToPde(Va);
1510     PointerPte = MiAddressToPte(Va);
1511 #if (_MI_PAGING_LEVELS >= 3)
1512     PointerPpe = MiAddressToPpe(Va);
1513 #endif
1514 #if (_MI_PAGING_LEVELS >= 4)
1515     PointerPxe = MiAddressToPxe(Va);
1516 #endif
1517 
1518     /* Return the next range */
1519     *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1520 
1521     do
1522     {
1523 #if (_MI_PAGING_LEVELS >= 4)
1524         /* Does the PXE exist? */
1525         if (PointerPxe->u.Long == 0)
1526         {
1527             /* It does not, next range starts at the next PXE */
1528             *NextVa = MiPxeToAddress(PointerPxe + 1);
1529             break;
1530         }
1531 
1532         /* Is the PXE valid? */
1533         if (PointerPxe->u.Hard.Valid == 0)
1534         {
1535             /* Is isn't, fault it in (make the PPE accessible) */
1536             MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1537         }
1538 #endif
1539 #if (_MI_PAGING_LEVELS >= 3)
1540         /* Does the PPE exist? */
1541         if (PointerPpe->u.Long == 0)
1542         {
1543             /* It does not, next range starts at the next PPE */
1544             *NextVa = MiPpeToAddress(PointerPpe + 1);
1545             break;
1546         }
1547 
1548         /* Is the PPE valid? */
1549         if (PointerPpe->u.Hard.Valid == 0)
1550         {
1551             /* Is isn't, fault it in (make the PDE accessible) */
1552             MiMakeSystemAddressValid(PointerPde, TargetProcess);
1553         }
1554 #endif
1555 
1556         /* Does the PDE exist? */
1557         if (PointerPde->u.Long == 0)
1558         {
1559             /* It does not, next range starts at the next PDE */
1560             *NextVa = MiPdeToAddress(PointerPde + 1);
1561             break;
1562         }
1563 
1564         /* Is the PDE valid? */
1565         if (PointerPde->u.Hard.Valid == 0)
1566         {
1567             /* Is isn't, fault it in (make the PTE accessible) */
1568             MiMakeSystemAddressValid(PointerPte, TargetProcess);
1569         }
1570 
1571         /* We have a PTE that we can access now! */
1572         ValidPte = TRUE;
1573 
1574     } while (FALSE);
1575 
1576     /* Is it safe to try reading the PTE? */
1577     if (ValidPte)
1578     {
1579         /* FIXME: watch out for large pages */
1580         ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1581 
1582         /* Capture the PTE */
1583         TempPte = *PointerPte;
1584         if (TempPte.u.Long != 0)
1585         {
1586             /* The PTE is valid, so it's not zeroed out */
1587             DemandZeroPte = FALSE;
1588 
1589             /* Is it a decommited, invalid, or faulted PTE? */
1590             if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1591                 (TempPte.u.Hard.Valid == 0) &&
1592                 ((TempPte.u.Soft.Prototype == 0) ||
1593                  (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1594             {
1595                 /* Otherwise our defaults should hold */
1596                 ASSERT(Protect == 0);
1597                 ASSERT(State == MEM_RESERVE);
1598             }
1599             else
1600             {
1601                 /* This means it's committed */
1602                 State = MEM_COMMIT;
1603 
1604                 /* We don't support these */
1605                 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1606                 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1607                 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1608 
1609                 /* Get protection state of this page */
1610                 Protect = MiGetPageProtection(PointerPte);
1611 
1612                 /* Check if this is an image-backed VAD */
1613                 if ((TempPte.u.Soft.Valid == 0) &&
1614                     (TempPte.u.Soft.Prototype == 1) &&
1615                     (Vad->u.VadFlags.PrivateMemory == 0) &&
1616                     (Vad->ControlArea))
1617                 {
1618                     DPRINT1("Not supported\n");
1619                     ASSERT(FALSE);
1620                 }
1621             }
1622         }
1623     }
1624 
1625     /* Check if this was a demand-zero PTE, since we need to find the state */
1626     if (DemandZeroPte)
1627     {
1628         /* Not yet handled */
1629         ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1630         ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1631 
1632         /* Check if this is private commited memory, or an section-backed VAD */
1633         if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1634         {
1635             /* Tell caller about the next range */
1636             *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1637 
1638             /* Get the prototype PTE for this VAD */
1639             ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1640                                                     (ULONG_PTR)Va >> PAGE_SHIFT);
1641             if (ProtoPte)
1642             {
1643                 /* We should unlock the working set, but it's not being held! */
1644 
1645                 /* Is the prototype PTE actually valid (committed)? */
1646                 TempProtoPte = *ProtoPte;
1647                 if (TempProtoPte.u.Long)
1648                 {
1649                     /* Unless this is a memory-mapped file, handle it like private VAD */
1650                     State = MEM_COMMIT;
1651                     ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1652                     Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1653                 }
1654 
1655                 /* We should re-lock the working set */
1656             }
1657         }
1658         else if (Vad->u.VadFlags.MemCommit)
1659         {
1660             /* This is committed memory */
1661             State = MEM_COMMIT;
1662 
1663             /* Convert the protection */
1664             Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1665         }
1666     }
1667 
1668     /* Return the protection code */
1669     *ReturnedProtect = Protect;
1670     return State;
1671 }
1672 
1673 NTSTATUS
1674 NTAPI
1675 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1676                               IN PVOID BaseAddress,
1677                               OUT PVOID MemoryInformation,
1678                               IN SIZE_T MemoryInformationLength,
1679                               OUT PSIZE_T ReturnLength)
1680 {
1681     PEPROCESS TargetProcess;
1682     NTSTATUS Status = STATUS_SUCCESS;
1683     PMMVAD Vad = NULL;
1684     PVOID Address, NextAddress;
1685     BOOLEAN Found = FALSE;
1686     ULONG NewProtect, NewState;
1687     ULONG_PTR BaseVpn;
1688     MEMORY_BASIC_INFORMATION MemoryInfo;
1689     KAPC_STATE ApcState;
1690     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1691     PMEMORY_AREA MemoryArea;
1692     SIZE_T ResultLength;
1693 
1694     /* Check for illegal addresses in user-space, or the shared memory area */
1695     if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1696         (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1697     {
1698         Address = PAGE_ALIGN(BaseAddress);
1699 
1700         /* Make up an info structure describing this range */
1701         MemoryInfo.BaseAddress = Address;
1702         MemoryInfo.AllocationProtect = PAGE_READONLY;
1703         MemoryInfo.Type = MEM_PRIVATE;
1704 
1705         /* Special case for shared data */
1706         if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1707         {
1708             MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1709             MemoryInfo.State = MEM_COMMIT;
1710             MemoryInfo.Protect = PAGE_READONLY;
1711             MemoryInfo.RegionSize = PAGE_SIZE;
1712         }
1713         else
1714         {
1715             MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1716             MemoryInfo.State = MEM_RESERVE;
1717             MemoryInfo.Protect = PAGE_NOACCESS;
1718             MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1719         }
1720 
1721         /* Return the data, NtQueryInformation already probed it*/
1722         if (PreviousMode != KernelMode)
1723         {
1724             _SEH2_TRY
1725             {
1726                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1727                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1728             }
1729              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1730             {
1731                 Status = _SEH2_GetExceptionCode();
1732             }
1733             _SEH2_END;
1734         }
1735         else
1736         {
1737             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1738             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1739         }
1740 
1741         return Status;
1742     }
1743 
1744     /* Check if this is for a local or remote process */
1745     if (ProcessHandle == NtCurrentProcess())
1746     {
1747         TargetProcess = PsGetCurrentProcess();
1748     }
1749     else
1750     {
1751         /* Reference the target process */
1752         Status = ObReferenceObjectByHandle(ProcessHandle,
1753                                            PROCESS_QUERY_INFORMATION,
1754                                            PsProcessType,
1755                                            ExGetPreviousMode(),
1756                                            (PVOID*)&TargetProcess,
1757                                            NULL);
1758         if (!NT_SUCCESS(Status)) return Status;
1759 
1760         /* Attach to it now */
1761         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1762     }
1763 
1764     /* Lock the address space and make sure the process isn't already dead */
1765     MmLockAddressSpace(&TargetProcess->Vm);
1766     if (TargetProcess->VmDeleted)
1767     {
1768         /* Unlock the address space of the process */
1769         MmUnlockAddressSpace(&TargetProcess->Vm);
1770 
1771         /* Check if we were attached */
1772         if (ProcessHandle != NtCurrentProcess())
1773         {
1774             /* Detach and dereference the process */
1775             KeUnstackDetachProcess(&ApcState);
1776             ObDereferenceObject(TargetProcess);
1777         }
1778 
1779         /* Bail out */
1780         DPRINT1("Process is dying\n");
1781         return STATUS_PROCESS_IS_TERMINATING;
1782     }
1783 
1784     /* Loop the VADs */
1785     ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1786     if (TargetProcess->VadRoot.NumberGenericTableElements)
1787     {
1788         /* Scan on the right */
1789         Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1790         BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1791         while (Vad)
1792         {
1793             /* Check if this VAD covers the allocation range */
1794             if ((BaseVpn >= Vad->StartingVpn) &&
1795                 (BaseVpn <= Vad->EndingVpn))
1796             {
1797                 /* We're done */
1798                 Found = TRUE;
1799                 break;
1800             }
1801 
1802             /* Check if this VAD is too high */
1803             if (BaseVpn < Vad->StartingVpn)
1804             {
1805                 /* Stop if there is no left child */
1806                 if (!Vad->LeftChild) break;
1807 
1808                 /* Search on the left next */
1809                 Vad = Vad->LeftChild;
1810             }
1811             else
1812             {
1813                 /* Then this VAD is too low, keep searching on the right */
1814                 ASSERT(BaseVpn > Vad->EndingVpn);
1815 
1816                 /* Stop if there is no right child */
1817                 if (!Vad->RightChild) break;
1818 
1819                 /* Search on the right next */
1820                 Vad = Vad->RightChild;
1821             }
1822         }
1823     }
1824 
1825     /* Was a VAD found? */
1826     if (!Found)
1827     {
1828         Address = PAGE_ALIGN(BaseAddress);
1829 
1830         /* Calculate region size */
1831         if (Vad)
1832         {
1833             if (Vad->StartingVpn >= BaseVpn)
1834             {
1835                 /* Region size is the free space till the start of that VAD */
1836                 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1837             }
1838             else
1839             {
1840                 /* Get the next VAD */
1841                 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1842                 if (Vad)
1843                 {
1844                     /* Region size is the free space till the start of that VAD */
1845                     MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1846                 }
1847                 else
1848                 {
1849                     /* Maximum possible region size with that base address */
1850                     MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1851                 }
1852             }
1853         }
1854         else
1855         {
1856             /* Maximum possible region size with that base address */
1857             MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1858         }
1859 
1860         /* Unlock the address space of the process */
1861         MmUnlockAddressSpace(&TargetProcess->Vm);
1862 
1863         /* Check if we were attached */
1864         if (ProcessHandle != NtCurrentProcess())
1865         {
1866             /* Detach and derefernece the process */
1867             KeUnstackDetachProcess(&ApcState);
1868             ObDereferenceObject(TargetProcess);
1869         }
1870 
1871         /* Build the rest of the initial information block */
1872         MemoryInfo.BaseAddress = Address;
1873         MemoryInfo.AllocationBase = NULL;
1874         MemoryInfo.AllocationProtect = 0;
1875         MemoryInfo.State = MEM_FREE;
1876         MemoryInfo.Protect = PAGE_NOACCESS;
1877         MemoryInfo.Type = 0;
1878 
1879         /* Return the data, NtQueryInformation already probed it*/
1880         if (PreviousMode != KernelMode)
1881         {
1882             _SEH2_TRY
1883             {
1884                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1885                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1886             }
1887              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1888             {
1889                 Status = _SEH2_GetExceptionCode();
1890             }
1891             _SEH2_END;
1892         }
1893         else
1894         {
1895             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1896             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1897         }
1898 
1899         return Status;
1900     }
1901 
1902     /* Set the correct memory type based on what kind of VAD this is */
1903     if ((Vad->u.VadFlags.PrivateMemory) ||
1904         (Vad->u.VadFlags.VadType == VadRotatePhysical))
1905     {
1906         MemoryInfo.Type = MEM_PRIVATE;
1907     }
1908     else if (Vad->u.VadFlags.VadType == VadImageMap)
1909     {
1910         MemoryInfo.Type = MEM_IMAGE;
1911     }
1912     else
1913     {
1914         MemoryInfo.Type = MEM_MAPPED;
1915     }
1916 
1917     /* Find the memory area the specified address belongs to */
1918     MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1919     ASSERT(MemoryArea != NULL);
1920 
1921     /* Determine information dependent on the memory area type */
1922     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1923     {
1924         Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1925         if (!NT_SUCCESS(Status))
1926         {
1927             DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1928                     MemoryArea, MA_GetStartingAddress(MemoryArea), MA_GetEndingAddress(MemoryArea), BaseAddress);
1929             ASSERT(NT_SUCCESS(Status));
1930         }
1931     }
1932     else
1933     {
1934         /* Build the initial information block */
1935         Address = PAGE_ALIGN(BaseAddress);
1936         MemoryInfo.BaseAddress = Address;
1937         MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1938         MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1939         MemoryInfo.Type = MEM_PRIVATE;
1940 
1941         /* Acquire the working set lock (shared is enough) */
1942         MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1943 
1944         /* Find the largest chunk of memory which has the same state and protection mask */
1945         MemoryInfo.State = MiQueryAddressState(Address,
1946                                                Vad,
1947                                                TargetProcess,
1948                                                &MemoryInfo.Protect,
1949                                                &NextAddress);
1950         Address = NextAddress;
1951         while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1952         {
1953             /* Keep going unless the state or protection mask changed */
1954             NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1955             if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1956             Address = NextAddress;
1957         }
1958 
1959         /* Release the working set lock */
1960         MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1961 
1962         /* Check if we went outside of the VAD */
1963          if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1964          {
1965             /* Set the end of the VAD as the end address */
1966             Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1967          }
1968 
1969         /* Now that we know the last VA address, calculate the region size */
1970         MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1971     }
1972 
1973     /* Unlock the address space of the process */
1974     MmUnlockAddressSpace(&TargetProcess->Vm);
1975 
1976     /* Check if we were attached */
1977     if (ProcessHandle != NtCurrentProcess())
1978     {
1979         /* Detach and derefernece the process */
1980         KeUnstackDetachProcess(&ApcState);
1981         ObDereferenceObject(TargetProcess);
1982     }
1983 
1984     /* Return the data, NtQueryInformation already probed it */
1985     if (PreviousMode != KernelMode)
1986     {
1987         _SEH2_TRY
1988         {
1989             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1990             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1991         }
1992          _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1993         {
1994             Status = _SEH2_GetExceptionCode();
1995         }
1996         _SEH2_END;
1997     }
1998     else
1999     {
2000         *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2001         if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2002     }
2003 
2004     /* All went well */
2005     DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2006             "State: %lx Type: %lx Size: %lx\n",
2007             MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2008             MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2009             MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2010 
2011     return Status;
2012 }
2013 
2014 BOOLEAN
2015 NTAPI
2016 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
2017                          IN ULONG_PTR EndingAddress,
2018                          IN PMMVAD Vad,
2019                          IN PEPROCESS Process)
2020 {
2021     PMMPTE PointerPte, LastPte;
2022     PMMPDE PointerPde;
2023     BOOLEAN OnBoundary = TRUE;
2024     PAGED_CODE();
2025 
2026     /* Get the PDE and PTE addresses */
2027     PointerPde = MiAddressToPde(StartingAddress);
2028     PointerPte = MiAddressToPte(StartingAddress);
2029     LastPte = MiAddressToPte(EndingAddress);
2030 
2031     /* Loop all the PTEs */
2032     while (PointerPte <= LastPte)
2033     {
2034         /* Check if we've hit an new PDE boundary */
2035         if (OnBoundary)
2036         {
2037             /* Is this PDE demand zero? */
2038             PointerPde = MiPteToPde(PointerPte);
2039             if (PointerPde->u.Long != 0)
2040             {
2041                 /* It isn't -- is it valid? */
2042                 if (PointerPde->u.Hard.Valid == 0)
2043                 {
2044                     /* Nope, fault it in */
2045                     MiMakeSystemAddressValid(PointerPte, Process);
2046                 }
2047             }
2048             else
2049             {
2050                 /* The PTE was already valid, so move to the next one */
2051                 PointerPde++;
2052                 PointerPte = MiPdeToPte(PointerPde);
2053 
2054                 /* Is the entire VAD committed? If not, fail */
2055                 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2056 
2057                 /* New loop iteration with our new, on-boundary PTE. */
2058                 continue;
2059             }
2060         }
2061 
2062         /* Is the PTE demand zero? */
2063         if (PointerPte->u.Long == 0)
2064         {
2065             /* Is the entire VAD committed? If not, fail */
2066             if (!Vad->u.VadFlags.MemCommit) return FALSE;
2067         }
2068         else
2069         {
2070             /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2071             if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
2072                 (PointerPte->u.Hard.Valid == 0) &&
2073                 ((PointerPte->u.Soft.Prototype == 0) ||
2074                  (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
2075             {
2076                 /* Then part of the range is decommitted, so fail */
2077                 return FALSE;
2078             }
2079         }
2080 
2081         /* Move to the next PTE */
2082         PointerPte++;
2083         OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
2084     }
2085 
2086     /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2087     return TRUE;
2088 }
2089 
2090 NTSTATUS
2091 NTAPI
2092 MiRosProtectVirtualMemory(IN PEPROCESS Process,
2093                           IN OUT PVOID *BaseAddress,
2094                           IN OUT PSIZE_T NumberOfBytesToProtect,
2095                           IN ULONG NewAccessProtection,
2096                           OUT PULONG OldAccessProtection OPTIONAL)
2097 {
2098     PMEMORY_AREA MemoryArea;
2099     PMMSUPPORT AddressSpace;
2100     ULONG OldAccessProtection_;
2101     NTSTATUS Status;
2102 
2103     *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
2104     *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
2105 
2106     AddressSpace = &Process->Vm;
2107     MmLockAddressSpace(AddressSpace);
2108     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
2109     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
2110     {
2111         MmUnlockAddressSpace(AddressSpace);
2112         return STATUS_UNSUCCESSFUL;
2113     }
2114 
2115     if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
2116 
2117     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
2118     Status = MmProtectSectionView(AddressSpace,
2119                                   MemoryArea,
2120                                   *BaseAddress,
2121                                   *NumberOfBytesToProtect,
2122                                   NewAccessProtection,
2123                                   OldAccessProtection);
2124 
2125     MmUnlockAddressSpace(AddressSpace);
2126 
2127     return Status;
2128 }
2129 
2130 NTSTATUS
2131 NTAPI
2132 MiProtectVirtualMemory(IN PEPROCESS Process,
2133                        IN OUT PVOID *BaseAddress,
2134                        IN OUT PSIZE_T NumberOfBytesToProtect,
2135                        IN ULONG NewAccessProtection,
2136                        OUT PULONG OldAccessProtection OPTIONAL)
2137 {
2138     PMEMORY_AREA MemoryArea;
2139     PMMVAD Vad;
2140     PMMSUPPORT AddressSpace;
2141     ULONG_PTR StartingAddress, EndingAddress;
2142     PMMPTE PointerPte, LastPte;
2143     PMMPDE PointerPde;
2144     MMPTE PteContents;
2145     PMMPFN Pfn1;
2146     ULONG ProtectionMask, OldProtect;
2147     BOOLEAN Committed;
2148     NTSTATUS Status = STATUS_SUCCESS;
2149     PETHREAD Thread = PsGetCurrentThread();
2150     TABLE_SEARCH_RESULT Result;
2151 
2152     /* Calculate base address for the VAD */
2153     StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
2154     EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
2155 
2156     /* Calculate the protection mask and make sure it's valid */
2157     ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
2158     if (ProtectionMask == MM_INVALID_PROTECTION)
2159     {
2160         DPRINT1("Invalid protection mask\n");
2161         return STATUS_INVALID_PAGE_PROTECTION;
2162     }
2163 
2164     /* Check for ROS specific memory area */
2165     MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
2166     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2167     {
2168         /* Evil hack */
2169         return MiRosProtectVirtualMemory(Process,
2170                                          BaseAddress,
2171                                          NumberOfBytesToProtect,
2172                                          NewAccessProtection,
2173                                          OldAccessProtection);
2174     }
2175 
2176     /* Lock the address space and make sure the process isn't already dead */
2177     AddressSpace = MmGetCurrentAddressSpace();
2178     MmLockAddressSpace(AddressSpace);
2179     if (Process->VmDeleted)
2180     {
2181         DPRINT1("Process is dying\n");
2182         Status = STATUS_PROCESS_IS_TERMINATING;
2183         goto FailPath;
2184     }
2185 
2186     /* Get the VAD for this address range, and make sure it exists */
2187     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2188                                        EndingAddress >> PAGE_SHIFT,
2189                                        &Process->VadRoot,
2190                                        (PMMADDRESS_NODE*)&Vad);
2191     if (Result != TableFoundNode)
2192     {
2193         DPRINT("Could not find a VAD for this allocation\n");
2194         Status = STATUS_CONFLICTING_ADDRESSES;
2195         goto FailPath;
2196     }
2197 
2198     /* Make sure the address is within this VAD's boundaries */
2199     if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2200         (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2201     {
2202         Status = STATUS_CONFLICTING_ADDRESSES;
2203         goto FailPath;
2204     }
2205 
2206     /* These kinds of VADs are not supported atm  */
2207     if ((Vad->u.VadFlags.VadType == VadAwe) ||
2208         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
2209         (Vad->u.VadFlags.VadType == VadLargePages))
2210     {
2211         DPRINT1("Illegal VAD for attempting to set protection\n");
2212         Status = STATUS_CONFLICTING_ADDRESSES;
2213         goto FailPath;
2214     }
2215 
2216     /* Check for a VAD whose protection can't be changed */
2217     if (Vad->u.VadFlags.NoChange == 1)
2218     {
2219         DPRINT1("Trying to change protection of a NoChange VAD\n");
2220         Status = STATUS_INVALID_PAGE_PROTECTION;
2221         goto FailPath;
2222     }
2223 
2224     /* Is this section, or private memory? */
2225     if (Vad->u.VadFlags.PrivateMemory == 0)
2226     {
2227         /* Not yet supported */
2228         if (Vad->u.VadFlags.VadType == VadLargePageSection)
2229         {
2230             DPRINT1("Illegal VAD for attempting to set protection\n");
2231             Status = STATUS_CONFLICTING_ADDRESSES;
2232             goto FailPath;
2233         }
2234 
2235         /* Rotate VADs are not yet supported */
2236         if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2237         {
2238             DPRINT1("Illegal VAD for attempting to set protection\n");
2239             Status = STATUS_CONFLICTING_ADDRESSES;
2240             goto FailPath;
2241         }
2242 
2243         /* Not valid on section files */
2244         if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2245         {
2246             /* Fail */
2247             DPRINT1("Invalid protection flags for section\n");
2248             Status = STATUS_INVALID_PARAMETER_4;
2249             goto FailPath;
2250         }
2251 
2252         /* Check if data or page file mapping protection PTE is compatible */
2253         if (!Vad->ControlArea->u.Flags.Image)
2254         {
2255             /* Not yet */
2256             DPRINT1("Fixme: Not checking for valid protection\n");
2257         }
2258 
2259         /* This is a section, and this is not yet supported */
2260         DPRINT1("Section protection not yet supported\n");
2261         OldProtect = 0;
2262     }
2263     else
2264     {
2265         /* Private memory, check protection flags */
2266         if ((NewAccessProtection & PAGE_WRITECOPY) ||
2267             (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2268         {
2269             DPRINT1("Invalid protection flags for private memory\n");
2270             Status = STATUS_INVALID_PARAMETER_4;
2271             goto FailPath;
2272         }
2273 
2274         /* Lock the working set */
2275         MiLockProcessWorkingSetUnsafe(Process, Thread);
2276 
2277         /* Check if all pages in this range are committed */
2278         Committed = MiIsEntireRangeCommitted(StartingAddress,
2279                                              EndingAddress,
2280                                              Vad,
2281                                              Process);
2282         if (!Committed)
2283         {
2284             /* Fail */
2285             DPRINT1("The entire range is not committed\n");
2286             Status = STATUS_NOT_COMMITTED;
2287             MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2288             goto FailPath;
2289         }
2290 
2291         /* Compute starting and ending PTE and PDE addresses */
2292         PointerPde = MiAddressToPde(StartingAddress);
2293         PointerPte = MiAddressToPte(StartingAddress);
2294         LastPte = MiAddressToPte(EndingAddress);
2295 
2296         /* Make this PDE valid */
2297         MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2298 
2299         /* Save protection of the first page */
2300         if (PointerPte->u.Long != 0)
2301         {
2302             /* Capture the page protection and make the PDE valid */
2303             OldProtect = MiGetPageProtection(PointerPte);
2304             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2305         }
2306         else
2307         {
2308             /* Grab the old protection from the VAD itself */
2309             OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2310         }
2311 
2312         /* Loop all the PTEs now */
2313         while (PointerPte <= LastPte)
2314         {
2315             /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2316             if (MiIsPteOnPdeBoundary(PointerPte))
2317             {
2318                 PointerPde = MiPteToPde(PointerPte);
2319                 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2320             }
2321 
2322             /* Capture the PTE and check if it was empty */
2323             PteContents = *PointerPte;
2324             if (PteContents.u.Long == 0)
2325             {
2326                 /* This used to be a zero PTE and it no longer is, so we must add a
2327                    reference to the pagetable. */
2328                 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2329             }
2330 
2331             /* Check what kind of PTE we are dealing with */
2332             if (PteContents.u.Hard.Valid == 1)
2333             {
2334                 /* Get the PFN entry */
2335                 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2336 
2337                 /* We don't support this yet */
2338                 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2339 
2340                 /* Check if the page should not be accessible at all */
2341                 if ((NewAccessProtection & PAGE_NOACCESS) ||
2342                     (NewAccessProtection & PAGE_GUARD))
2343                 {
2344                     KIRQL OldIrql = MiAcquirePfnLock();
2345 
2346                     /* Mark the PTE as transition and change its protection */
2347                     PteContents.u.Hard.Valid = 0;
2348                     PteContents.u.Soft.Transition = 1;
2349                     PteContents.u.Trans.Protection = ProtectionMask;
2350                     /* Decrease PFN share count and write the PTE */
2351                     MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2352                     // FIXME: remove the page from the WS
2353                     MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2354 #ifdef CONFIG_SMP
2355                     // FIXME: Should invalidate entry in every CPU TLB
2356                     ASSERT(FALSE);
2357 #endif
2358                     KeInvalidateTlbEntry(MiPteToAddress(PointerPte));
2359 
2360                     /* We are done for this PTE */
2361                     MiReleasePfnLock(OldIrql);
2362                 }
2363                 else
2364                 {
2365                     /* Write the protection mask and write it with a TLB flush */
2366                     Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2367                     MiFlushTbAndCapture(Vad,
2368                                         PointerPte,
2369                                         ProtectionMask,
2370                                         Pfn1,
2371                                         TRUE);
2372                 }
2373             }
2374             else
2375             {
2376                 /* We don't support these cases yet */
2377                 ASSERT(PteContents.u.Soft.Prototype == 0);
2378                 //ASSERT(PteContents.u.Soft.Transition == 0);
2379 
2380                 /* The PTE is already demand-zero, just update the protection mask */
2381                 PteContents.u.Soft.Protection = ProtectionMask;
2382                 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2383                 ASSERT(PointerPte->u.Long != 0);
2384             }
2385 
2386             /* Move to the next PTE */
2387             PointerPte++;
2388         }
2389 
2390         /* Unlock the working set */
2391         MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2392     }
2393 
2394     /* Unlock the address space */
2395     MmUnlockAddressSpace(AddressSpace);
2396 
2397     /* Return parameters and success */
2398     *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2399     *BaseAddress = (PVOID)StartingAddress;
2400     *OldAccessProtection = OldProtect;
2401     return STATUS_SUCCESS;
2402 
2403 FailPath:
2404     /* Unlock the address space and return the failure code */
2405     MmUnlockAddressSpace(AddressSpace);
2406     return Status;
2407 }
2408 
2409 VOID
2410 NTAPI
2411 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde,
2412                            IN PEPROCESS TargetProcess,
2413                            IN KIRQL OldIrql)
2414 {
2415    PMMPTE PointerPte, PointerPpe, PointerPxe;
2416 
2417    //
2418    // Sanity checks. The latter is because we only use this function with the
2419    // PFN lock not held, so it may go away in the future.
2420    //
2421    ASSERT(KeAreAllApcsDisabled() == TRUE);
2422    ASSERT(OldIrql == MM_NOIRQL);
2423 
2424    //
2425    // Also get the PPE and PXE. This is okay not to #ifdef because they will
2426    // return the same address as the PDE on 2-level page table systems.
2427    //
2428    // If everything is already valid, there is nothing to do.
2429    //
2430    PointerPpe = MiAddressToPte(PointerPde);
2431    PointerPxe = MiAddressToPde(PointerPde);
2432    if ((PointerPxe->u.Hard.Valid) &&
2433        (PointerPpe->u.Hard.Valid) &&
2434        (PointerPde->u.Hard.Valid))
2435    {
2436        return;
2437    }
2438 
2439    //
2440    // At least something is invalid, so begin by getting the PTE for the PDE itself
2441    // and then lookup each additional level. We must do it in this precise order
2442    // because the pagfault.c code (as well as in Windows) depends that the next
2443    // level up (higher) must be valid when faulting a lower level
2444    //
2445    PointerPte = MiPteToAddress(PointerPde);
2446    do
2447    {
2448        //
2449        // Make sure APCs continued to be disabled
2450        //
2451        ASSERT(KeAreAllApcsDisabled() == TRUE);
2452 
2453        //
2454        // First, make the PXE valid if needed
2455        //
2456        if (!PointerPxe->u.Hard.Valid)
2457        {
2458            MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2459            ASSERT(PointerPxe->u.Hard.Valid == 1);
2460        }
2461 
2462        //
2463        // Next, the PPE
2464        //
2465        if (!PointerPpe->u.Hard.Valid)
2466        {
2467            MiMakeSystemAddressValid(PointerPde, TargetProcess);
2468            ASSERT(PointerPpe->u.Hard.Valid == 1);
2469        }
2470 
2471        //
2472        // And finally, make the PDE itself valid.
2473        //
2474        MiMakeSystemAddressValid(PointerPte, TargetProcess);
2475 
2476        //
2477        // This should've worked the first time so the loop is really just for
2478        // show -- ASSERT that we're actually NOT going to be looping.
2479        //
2480        ASSERT(PointerPxe->u.Hard.Valid == 1);
2481        ASSERT(PointerPpe->u.Hard.Valid == 1);
2482        ASSERT(PointerPde->u.Hard.Valid == 1);
2483    } while (!(PointerPxe->u.Hard.Valid) ||
2484             !(PointerPpe->u.Hard.Valid) ||
2485             !(PointerPde->u.Hard.Valid));
2486 }
2487 
2488 VOID
2489 NTAPI
2490 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2491                       IN ULONG Count)
2492 {
2493     KIRQL OldIrql;
2494     ULONG i;
2495     MMPTE TempPte;
2496     PFN_NUMBER PageFrameIndex;
2497     PMMPFN Pfn1, Pfn2;
2498 
2499     //
2500     // Acquire the PFN lock and loop all the PTEs in the list
2501     //
2502     OldIrql = MiAcquirePfnLock();
2503     for (i = 0; i != Count; i++)
2504     {
2505         //
2506         // The PTE must currently be valid
2507         //
2508         TempPte = *ValidPteList[i];
2509         ASSERT(TempPte.u.Hard.Valid == 1);
2510 
2511         //
2512         // Get the PFN entry for the page itself, and then for its page table
2513         //
2514         PageFrameIndex = PFN_FROM_PTE(&TempPte);
2515         Pfn1 = MiGetPfnEntry(PageFrameIndex);
2516         Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2517 
2518         //
2519         // Decrement the share count on the page table, and then on the page
2520         // itself
2521         //
2522         MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2523         MI_SET_PFN_DELETED(Pfn1);
2524         MiDecrementShareCount(Pfn1, PageFrameIndex);
2525 
2526         //
2527         // Make the page decommitted
2528         //
2529         MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2530     }
2531 
2532     //
2533     // All the PTEs have been dereferenced and made invalid, flush the TLB now
2534     // and then release the PFN lock
2535     //
2536     KeFlushCurrentTb();
2537     MiReleasePfnLock(OldIrql);
2538 }
2539 
2540 ULONG
2541 NTAPI
2542 MiDecommitPages(IN PVOID StartingAddress,
2543                 IN PMMPTE EndingPte,
2544                 IN PEPROCESS Process,
2545                 IN PMMVAD Vad)
2546 {
2547     PMMPTE PointerPte, CommitPte = NULL;
2548     PMMPDE PointerPde;
2549     ULONG CommitReduction = 0;
2550     PMMPTE ValidPteList[256];
2551     ULONG PteCount = 0;
2552     PMMPFN Pfn1;
2553     MMPTE PteContents;
2554     PETHREAD CurrentThread = PsGetCurrentThread();
2555 
2556     //
2557     // Get the PTE and PTE for the address, and lock the working set
2558     // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2559     // commited range ends so that we can do the right accounting.
2560     //
2561     PointerPde = MiAddressToPde(StartingAddress);
2562     PointerPte = MiAddressToPte(StartingAddress);
2563     if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2564     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2565 
2566     //
2567     // Make the PDE valid, and now loop through each page's worth of data
2568     //
2569     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2570     while (PointerPte <= EndingPte)
2571     {
2572         //
2573         // Check if we've crossed a PDE boundary
2574         //
2575         if (MiIsPteOnPdeBoundary(PointerPte))
2576         {
2577             //
2578             // Get the new PDE and flush the valid PTEs we had built up until
2579             // now. This helps reduce the amount of TLB flushing we have to do.
2580             // Note that Windows does a much better job using timestamps and
2581             // such, and does not flush the entire TLB all the time, but right
2582             // now we have bigger problems to worry about than TLB flushing.
2583             //
2584             PointerPde = MiAddressToPde(StartingAddress);
2585             if (PteCount)
2586             {
2587                 MiProcessValidPteList(ValidPteList, PteCount);
2588                 PteCount = 0;
2589             }
2590 
2591             //
2592             // Make this PDE valid
2593             //
2594             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2595         }
2596 
2597         //
2598         // Read this PTE. It might be active or still demand-zero.
2599         //
2600         PteContents = *PointerPte;
2601         if (PteContents.u.Long)
2602         {
2603             //
2604             // The PTE is active. It might be valid and in a working set, or
2605             // it might be a prototype PTE or paged out or even in transition.
2606             //
2607             if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2608             {
2609                 //
2610                 // It's already decommited, so there's nothing for us to do here
2611                 //
2612                 CommitReduction++;
2613             }
2614             else
2615             {
2616                 //
2617                 // Remove it from the counters, and check if it was valid or not
2618                 //
2619                 //Process->NumberOfPrivatePages--;
2620                 if (PteContents.u.Hard.Valid)
2621                 {
2622                     //
2623                     // It's valid. At this point make sure that it is not a ROS
2624                     // PFN. Also, we don't support ProtoPTEs in this code path.
2625                     //
2626                     Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2627                     ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2628                     ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2629 
2630                     //
2631                     // Flush any pending PTEs that we had not yet flushed, if our
2632                     // list has gotten too big, then add this PTE to the flush list.
2633                     //
2634                     if (PteCount == 256)
2635                     {
2636                         MiProcessValidPteList(ValidPteList, PteCount);
2637                         PteCount = 0;
2638                     }
2639                     ValidPteList[PteCount++] = PointerPte;
2640                 }
2641                 else
2642                 {
2643                     //
2644                     // We do not support any of these other scenarios at the moment
2645                     //
2646                     ASSERT(PteContents.u.Soft.Prototype == 0);
2647                     ASSERT(PteContents.u.Soft.Transition == 0);
2648                     ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2649 
2650                     //
2651                     // So the only other possibility is that it is still a demand
2652                     // zero PTE, in which case we undo the accounting we did
2653                     // earlier and simply make the page decommitted.
2654                     //
2655                     //Process->NumberOfPrivatePages++;
2656                     MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2657                 }
2658             }
2659         }
2660         else
2661         {
2662             //
2663             // This used to be a zero PTE and it no longer is, so we must add a
2664             // reference to the pagetable.
2665             //
2666             MiIncrementPageTableReferences(StartingAddress);
2667 
2668             //
2669             // Next, we account for decommitted PTEs and make the PTE as such
2670             //
2671             if (PointerPte > CommitPte) CommitReduction++;
2672             MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2673         }
2674 
2675         //
2676         // Move to the next PTE and the next address
2677         //
2678         PointerPte++;
2679         StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2680     }
2681 
2682     //
2683     // Flush any dangling PTEs from the loop in the last page table, and then
2684     // release the working set and return the commit reduction accounting.
2685     //
2686     if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2687     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2688     return CommitReduction;
2689 }
2690 
2691 /* PUBLIC FUNCTIONS ***********************************************************/
2692 
2693 /*
2694  * @unimplemented
2695  */
2696 PVOID
2697 NTAPI
2698 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2699 {
2700     UNIMPLEMENTED;
2701     return 0;
2702 }
2703 
2704 /*
2705  * @unimplemented
2706  */
2707 PVOID
2708 NTAPI
2709 MmSecureVirtualMemory(IN PVOID Address,
2710                       IN SIZE_T Length,
2711                       IN ULONG Mode)
2712 {
2713     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2714     return Address;
2715 }
2716 
2717 /*
2718  * @unimplemented
2719  */
2720 VOID
2721 NTAPI
2722 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2723 {
2724     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2725 }
2726 
2727 /* SYSTEM CALLS ***************************************************************/
2728 
2729 NTSTATUS
2730 NTAPI
2731 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2732                     IN PVOID BaseAddress,
2733                     OUT PVOID Buffer,
2734                     IN SIZE_T NumberOfBytesToRead,
2735                     OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2736 {
2737     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2738     PEPROCESS Process;
2739     NTSTATUS Status = STATUS_SUCCESS;
2740     SIZE_T BytesRead = 0;
2741     PAGED_CODE();
2742 
2743     //
2744     // Check if we came from user mode
2745     //
2746     if (PreviousMode != KernelMode)
2747     {
2748         //
2749         // Validate the read addresses
2750         //
2751         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2752             (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2753             (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2754             (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2755         {
2756             //
2757             // Don't allow to write into kernel space
2758             //
2759             return STATUS_ACCESS_VIOLATION;
2760         }
2761 
2762         //
2763         // Enter SEH for probe
2764         //
2765         _SEH2_TRY
2766         {
2767             //
2768             // Probe the output value
2769             //
2770             if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2771         }
2772         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2773         {
2774             //
2775             // Get exception code
2776             //
2777             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2778         }
2779         _SEH2_END;
2780     }
2781 
2782     //
2783     // Don't do zero-byte transfers
2784     //
2785     if (NumberOfBytesToRead)
2786     {
2787         //
2788         // Reference the process
2789         //
2790         Status = ObReferenceObjectByHandle(ProcessHandle,
2791                                            PROCESS_VM_READ,
2792                                            PsProcessType,
2793                                            PreviousMode,
2794                                            (PVOID*)(&Process),
2795                                            NULL);
2796         if (NT_SUCCESS(Status))
2797         {
2798             //
2799             // Do the copy
2800             //
2801             Status = MmCopyVirtualMemory(Process,
2802                                          BaseAddress,
2803                                          PsGetCurrentProcess(),
2804                                          Buffer,
2805                                          NumberOfBytesToRead,
2806                                          PreviousMode,
2807                                          &BytesRead);
2808 
2809             //
2810             // Dereference the process
2811             //
2812             ObDereferenceObject(Process);
2813         }
2814     }
2815 
2816     //
2817     // Check if the caller sent this parameter
2818     //
2819     if (NumberOfBytesRead)
2820     {
2821         //
2822         // Enter SEH to guard write
2823         //
2824         _SEH2_TRY
2825         {
2826             //
2827             // Return the number of bytes read
2828             //
2829             *NumberOfBytesRead = BytesRead;
2830         }
2831         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2832         {
2833         }
2834         _SEH2_END;
2835     }
2836 
2837     //
2838     // Return status
2839     //
2840     return Status;
2841 }
2842 
2843 NTSTATUS
2844 NTAPI
2845 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2846                      IN PVOID BaseAddress,
2847                      IN PVOID Buffer,
2848                      IN SIZE_T NumberOfBytesToWrite,
2849                      OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2850 {
2851     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2852     PEPROCESS Process;
2853     NTSTATUS Status = STATUS_SUCCESS;
2854     SIZE_T BytesWritten = 0;
2855     PAGED_CODE();
2856 
2857     //
2858     // Check if we came from user mode
2859     //
2860     if (PreviousMode != KernelMode)
2861     {
2862         //
2863         // Validate the read addresses
2864         //
2865         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2866             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2867             (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2868             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2869         {
2870             //
2871             // Don't allow to write into kernel space
2872             //
2873             return STATUS_ACCESS_VIOLATION;
2874         }
2875 
2876         //
2877         // Enter SEH for probe
2878         //
2879         _SEH2_TRY
2880         {
2881             //
2882             // Probe the output value
2883             //
2884             if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2885         }
2886         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2887         {
2888             //
2889             // Get exception code
2890             //
2891             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2892         }
2893         _SEH2_END;
2894     }
2895 
2896     //
2897     // Don't do zero-byte transfers
2898     //
2899     if (NumberOfBytesToWrite)
2900     {
2901         //
2902         // Reference the process
2903         //
2904         Status = ObReferenceObjectByHandle(ProcessHandle,
2905                                            PROCESS_VM_WRITE,
2906                                            PsProcessType,
2907                                            PreviousMode,
2908                                            (PVOID*)&Process,
2909                                            NULL);
2910         if (NT_SUCCESS(Status))
2911         {
2912             //
2913             // Do the copy
2914             //
2915             Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2916                                          Buffer,
2917                                          Process,
2918                                          BaseAddress,
2919                                          NumberOfBytesToWrite,
2920                                          PreviousMode,
2921                                          &BytesWritten);
2922 
2923             //
2924             // Dereference the process
2925             //
2926             ObDereferenceObject(Process);
2927         }
2928     }
2929 
2930     //
2931     // Check if the caller sent this parameter
2932     //
2933     if (NumberOfBytesWritten)
2934     {
2935         //
2936         // Enter SEH to guard write
2937         //
2938         _SEH2_TRY
2939         {
2940             //
2941             // Return the number of bytes written
2942             //
2943             *NumberOfBytesWritten = BytesWritten;
2944         }
2945         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2946         {
2947         }
2948         _SEH2_END;
2949     }
2950 
2951     //
2952     // Return status
2953     //
2954     return Status;
2955 }
2956 
2957 NTSTATUS
2958 NTAPI
2959 NtFlushInstructionCache(_In_ HANDLE ProcessHandle,
2960                         _In_opt_ PVOID BaseAddress,
2961                         _In_ SIZE_T FlushSize)
2962 {
2963     KAPC_STATE ApcState;
2964     PKPROCESS Process;
2965     NTSTATUS Status;
2966     PAGED_CODE();
2967 
2968     /* Is a base address given? */
2969     if (BaseAddress != NULL)
2970     {
2971         /* If the requested size is 0, there is nothing to do */
2972         if (FlushSize == 0)
2973         {
2974             return STATUS_SUCCESS;
2975         }
2976 
2977         /* Is this a user mode call? */
2978         if (ExGetPreviousMode() != KernelMode)
2979         {
2980             /* Make sure the base address is in user space */
2981             if (BaseAddress > MmHighestUserAddress)
2982             {
2983                 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress);
2984                 return STATUS_ACCESS_VIOLATION;
2985             }
2986         }
2987     }
2988 
2989     /* Is another process requested? */
2990     if (ProcessHandle != NtCurrentProcess())
2991     {
2992         /* Reference the process */
2993         Status = ObReferenceObjectByHandle(ProcessHandle,
2994                                            PROCESS_VM_WRITE,
2995                                            PsProcessType,
2996                                            ExGetPreviousMode(),
2997                                            (PVOID*)&Process,
2998                                            NULL);
2999         if (!NT_SUCCESS(Status))
3000         {
3001             DPRINT1("Failed to reference the process %p\n", ProcessHandle);
3002             return Status;
3003         }
3004 
3005         /* Attach to the process */
3006         KeStackAttachProcess(Process, &ApcState);
3007     }
3008 
3009     /* Forward to Ke */
3010     KeSweepICache(BaseAddress, FlushSize);
3011 
3012     /* Check if we attached */
3013     if (ProcessHandle != NtCurrentProcess())
3014     {
3015         /* Detach from the process and dereference it */
3016         KeUnstackDetachProcess(&ApcState);
3017         ObDereferenceObject(Process);
3018     }
3019 
3020     /* All done, return to caller */
3021     return STATUS_SUCCESS;
3022 }
3023 
3024 NTSTATUS
3025 NTAPI
3026 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
3027                        IN OUT PVOID *UnsafeBaseAddress,
3028                        IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
3029                        IN ULONG NewAccessProtection,
3030                        OUT PULONG UnsafeOldAccessProtection)
3031 {
3032     PEPROCESS Process;
3033     ULONG OldAccessProtection;
3034     ULONG Protection;
3035     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3036     PVOID BaseAddress = NULL;
3037     SIZE_T NumberOfBytesToProtect = 0;
3038     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3039     NTSTATUS Status;
3040     BOOLEAN Attached = FALSE;
3041     KAPC_STATE ApcState;
3042     PAGED_CODE();
3043 
3044     //
3045     // Check for valid protection flags
3046     //
3047     Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
3048     if (Protection != PAGE_NOACCESS &&
3049         Protection != PAGE_READONLY &&
3050         Protection != PAGE_READWRITE &&
3051         Protection != PAGE_WRITECOPY &&
3052         Protection != PAGE_EXECUTE &&
3053         Protection != PAGE_EXECUTE_READ &&
3054         Protection != PAGE_EXECUTE_READWRITE &&
3055         Protection != PAGE_EXECUTE_WRITECOPY)
3056     {
3057         //
3058         // Fail
3059         //
3060         return STATUS_INVALID_PAGE_PROTECTION;
3061     }
3062 
3063     //
3064     // Check if we came from user mode
3065     //
3066     if (PreviousMode != KernelMode)
3067     {
3068         //
3069         // Enter SEH for probing
3070         //
3071         _SEH2_TRY
3072         {
3073             //
3074             // Validate all outputs
3075             //
3076             ProbeForWritePointer(UnsafeBaseAddress);
3077             ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
3078             ProbeForWriteUlong(UnsafeOldAccessProtection);
3079 
3080             //
3081             // Capture them
3082             //
3083             BaseAddress = *UnsafeBaseAddress;
3084             NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3085         }
3086         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3087         {
3088             //
3089             // Get exception code
3090             //
3091             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3092         }
3093         _SEH2_END;
3094     }
3095     else
3096     {
3097         //
3098         // Capture directly
3099         //
3100         BaseAddress = *UnsafeBaseAddress;
3101         NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3102     }
3103 
3104     //
3105     // Catch illegal base address
3106     //
3107     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3108 
3109     //
3110     // Catch illegal region size
3111     //
3112     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
3113     {
3114         //
3115         // Fail
3116         //
3117         return STATUS_INVALID_PARAMETER_3;
3118     }
3119 
3120     //
3121     // 0 is also illegal
3122     //
3123     if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
3124 
3125     //
3126     // Get a reference to the process
3127     //
3128     Status = ObReferenceObjectByHandle(ProcessHandle,
3129                                        PROCESS_VM_OPERATION,
3130                                        PsProcessType,
3131                                        PreviousMode,
3132                                        (PVOID*)(&Process),
3133                                        NULL);
3134     if (!NT_SUCCESS(Status)) return Status;
3135 
3136     //
3137     // Check if we should attach
3138     //
3139     if (CurrentProcess != Process)
3140     {
3141         //
3142         // Do it
3143         //
3144         KeStackAttachProcess(&Process->Pcb, &ApcState);
3145         Attached = TRUE;
3146     }
3147 
3148     //
3149     // Do the actual work
3150     //
3151     Status = MiProtectVirtualMemory(Process,
3152                                     &BaseAddress,
3153                                     &NumberOfBytesToProtect,
3154                                     NewAccessProtection,
3155                                     &OldAccessProtection);
3156 
3157     //
3158     // Detach if needed
3159     //
3160     if (Attached) KeUnstackDetachProcess(&ApcState);
3161 
3162     //
3163     // Release reference
3164     //
3165     ObDereferenceObject(Process);
3166 
3167     //
3168     // Enter SEH to return data
3169     //
3170     _SEH2_TRY
3171     {
3172         //
3173         // Return data to user
3174         //
3175         *UnsafeOldAccessProtection = OldAccessProtection;
3176         *UnsafeBaseAddress = BaseAddress;
3177         *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
3178     }
3179     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3180     {
3181     }
3182     _SEH2_END;
3183 
3184     //
3185     // Return status
3186     //
3187     return Status;
3188 }
3189 
3190 FORCEINLINE
3191 BOOLEAN
3192 MI_IS_LOCKED_VA(
3193     PMMPFN Pfn1,
3194     ULONG LockType)
3195 {
3196     // HACK until we have proper WSLIST support
3197     PMMWSLE Wsle = &Pfn1->Wsle;
3198 
3199     if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
3200         return TRUE;
3201     if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
3202         return TRUE;
3203 
3204     return FALSE;
3205 }
3206 
3207 FORCEINLINE
3208 VOID
3209 MI_LOCK_VA(
3210     PMMPFN Pfn1,
3211     ULONG LockType)
3212 {
3213     // HACK until we have proper WSLIST support
3214     PMMWSLE Wsle = &Pfn1->Wsle;
3215 
3216     if (!Wsle->u1.e1.LockedInWs &&
3217         !Wsle->u1.e1.LockedInMemory)
3218     {
3219         MiReferenceProbedPageAndBumpLockCount(Pfn1);
3220     }
3221 
3222     if (LockType & MAP_PROCESS)
3223         Wsle->u1.e1.LockedInWs = 1;
3224     if (LockType & MAP_SYSTEM)
3225         Wsle->u1.e1.LockedInMemory = 1;
3226 }
3227 
3228 FORCEINLINE
3229 VOID
3230 MI_UNLOCK_VA(
3231     PMMPFN Pfn1,
3232     ULONG LockType)
3233 {
3234     // HACK until we have proper WSLIST support
3235     PMMWSLE Wsle = &Pfn1->Wsle;
3236 
3237     if (LockType & MAP_PROCESS)
3238         Wsle->u1.e1.LockedInWs = 0;
3239     if (LockType & MAP_SYSTEM)
3240         Wsle->u1.e1.LockedInMemory = 0;
3241 
3242     if (!Wsle->u1.e1.LockedInWs &&
3243         !Wsle->u1.e1.LockedInMemory)
3244     {
3245         MiDereferencePfnAndDropLockCount(Pfn1);
3246     }
3247 }
3248 
3249 static
3250 NTSTATUS
3251 MiCheckVadsForLockOperation(
3252     _Inout_ PVOID *BaseAddress,
3253     _Inout_ PSIZE_T RegionSize,
3254     _Inout_ PVOID *EndAddress)
3255 
3256 {
3257     PMMVAD Vad;
3258     PVOID CurrentVa;
3259 
3260     /* Get the base address and align the start address */
3261     *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
3262     *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
3263     *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
3264 
3265     /* First loop and check all VADs */
3266     CurrentVa = *BaseAddress;
3267     while (CurrentVa < *EndAddress)
3268     {
3269         /* Get VAD */
3270         Vad = MiLocateAddress(CurrentVa);
3271         if (Vad == NULL)
3272         {
3273             /// FIXME: this might be a memory area for a section view...
3274             return STATUS_ACCESS_VIOLATION;
3275         }
3276 
3277         /* Check VAD type */
3278         if ((Vad->u.VadFlags.VadType != VadNone) &&
3279             (Vad->u.VadFlags.VadType != VadImageMap) &&
3280             (Vad->u.VadFlags.VadType != VadWriteWatch))
3281         {
3282             *EndAddress = CurrentVa;
3283             *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3284             return STATUS_INCOMPATIBLE_FILE_MAP;
3285         }
3286 
3287         CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3288     }
3289 
3290     *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3291     return STATUS_SUCCESS;
3292 }
3293 
3294 static
3295 NTSTATUS
3296 MiLockVirtualMemory(
3297     IN OUT PVOID *BaseAddress,
3298     IN OUT PSIZE_T RegionSize,
3299     IN ULONG MapType)
3300 {
3301     PEPROCESS CurrentProcess;
3302     PMMSUPPORT AddressSpace;
3303     PVOID CurrentVa, EndAddress;
3304     PMMPTE PointerPte, LastPte;
3305     PMMPDE PointerPde;
3306 #if (_MI_PAGING_LEVELS >= 3)
3307     PMMPDE PointerPpe;
3308 #endif
3309 #if (_MI_PAGING_LEVELS == 4)
3310     PMMPDE PointerPxe;
3311 #endif
3312     PMMPFN Pfn1;
3313     NTSTATUS Status, TempStatus;
3314 
3315     /* Lock the address space */
3316     AddressSpace = MmGetCurrentAddressSpace();
3317     MmLockAddressSpace(AddressSpace);
3318 
3319     /* Make sure we still have an address space */
3320     CurrentProcess = PsGetCurrentProcess();
3321     if (CurrentProcess->VmDeleted)
3322     {
3323         Status = STATUS_PROCESS_IS_TERMINATING;
3324         goto Cleanup;
3325     }
3326 
3327     /* Check the VADs in the requested range */
3328     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3329     if (!NT_SUCCESS(Status))
3330     {
3331         goto Cleanup;
3332     }
3333 
3334     /* Enter SEH for probing */
3335     _SEH2_TRY
3336     {
3337         /* Loop all pages and probe them */
3338         CurrentVa = *BaseAddress;
3339         while (CurrentVa < EndAddress)
3340         {
3341             (void)(*(volatile CHAR*)CurrentVa);
3342             CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3343         }
3344     }
3345     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3346     {
3347         Status = _SEH2_GetExceptionCode();
3348         goto Cleanup;
3349     }
3350     _SEH2_END;
3351 
3352     /* All pages were accessible, since we hold the address space lock, nothing
3353        can be de-committed. Assume success for now. */
3354     Status = STATUS_SUCCESS;
3355 
3356     /* Get the PTE and PDE */
3357     PointerPte = MiAddressToPte(*BaseAddress);
3358     PointerPde = MiAddressToPde(*BaseAddress);
3359 #if (_MI_PAGING_LEVELS >= 3)
3360     PointerPpe = MiAddressToPpe(*BaseAddress);
3361 #endif
3362 #if (_MI_PAGING_LEVELS == 4)
3363     PointerPxe = MiAddressToPxe(*BaseAddress);
3364 #endif
3365 
3366     /* Get the last PTE */
3367     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3368 
3369     /* Lock the process working set */
3370     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3371 
3372     /* Loop the pages */
3373     do
3374     {
3375         /* Check for a page that is not accessible */
3376         while (
3377 #if (_MI_PAGING_LEVELS == 4)
3378                (PointerPxe->u.Hard.Valid == 0) ||
3379 #endif
3380 #if (_MI_PAGING_LEVELS >= 3)
3381                (PointerPpe->u.Hard.Valid == 0) ||
3382 #endif
3383                (PointerPde->u.Hard.Valid == 0) ||
3384                (PointerPte->u.Hard.Valid == 0))
3385         {
3386             /* Release process working set */
3387             MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3388 
3389             /* Access the page */
3390             CurrentVa = MiPteToAddress(PointerPte);
3391 
3392             //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3393             TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL);
3394             if (!NT_SUCCESS(TempStatus))
3395             {
3396                 // This should only happen, when remote backing storage is not accessible
3397                 ASSERT(FALSE);
3398                 Status = TempStatus;
3399                 goto Cleanup;
3400             }
3401 
3402             /* Lock the process working set */
3403             MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3404         }
3405 
3406         /* Get the PFN */
3407         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3408         ASSERT(Pfn1 != NULL);
3409 
3410         /* Check the previous lock status */
3411         if (MI_IS_LOCKED_VA(Pfn1, MapType))
3412         {
3413             Status = STATUS_WAS_LOCKED;
3414         }
3415 
3416         /* Lock it */
3417         MI_LOCK_VA(Pfn1, MapType);
3418 
3419         /* Go to the next PTE */
3420         PointerPte++;
3421 
3422         /* Check if we're on a PDE boundary */
3423         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3424 #if (_MI_PAGING_LEVELS >= 3)
3425         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3426 #endif
3427 #if (_MI_PAGING_LEVELS == 4)
3428         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3429 #endif
3430     } while (PointerPte <= LastPte);
3431 
3432     /* Release process working set */
3433     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3434 
3435 Cleanup:
3436     /* Unlock address space */
3437     MmUnlockAddressSpace(AddressSpace);
3438 
3439     return Status;
3440 }
3441 
3442 NTSTATUS
3443 NTAPI
3444 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3445                     IN OUT PVOID *BaseAddress,
3446                     IN OUT PSIZE_T NumberOfBytesToLock,
3447                     IN ULONG MapType)
3448 {
3449     PEPROCESS Process;
3450     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3451     NTSTATUS Status;
3452     BOOLEAN Attached = FALSE;
3453     KAPC_STATE ApcState;
3454     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3455     PVOID CapturedBaseAddress;
3456     SIZE_T CapturedBytesToLock;
3457     PAGED_CODE();
3458 
3459     //
3460     // Validate flags
3461     //
3462     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3463     {
3464         //
3465         // Invalid set of flags
3466         //
3467         return STATUS_INVALID_PARAMETER;
3468     }
3469 
3470     //
3471     // At least one flag must be specified
3472     //
3473     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3474     {
3475         //
3476         // No flag given
3477         //
3478         return STATUS_INVALID_PARAMETER;
3479     }
3480 
3481     //
3482     // Enter SEH for probing
3483     //
3484     _SEH2_TRY
3485     {
3486         //
3487         // Validate output data
3488         //
3489         ProbeForWritePointer(BaseAddress);
3490         ProbeForWriteSize_t(NumberOfBytesToLock);
3491 
3492         //
3493         // Capture it
3494         //
3495         CapturedBaseAddress = *BaseAddress;
3496         CapturedBytesToLock = *NumberOfBytesToLock;
3497     }
3498     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3499     {
3500         //
3501         // Get exception code
3502         //
3503         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3504     }
3505     _SEH2_END;
3506 
3507     //
3508     // Catch illegal base address
3509     //
3510     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3511 
3512     //
3513     // Catch illegal region size
3514     //
3515     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3516     {
3517         //
3518         // Fail
3519         //
3520         return STATUS_INVALID_PARAMETER;
3521     }
3522 
3523     //
3524     // 0 is also illegal
3525     //
3526     if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3527 
3528     //
3529     // Get a reference to the process
3530     //
3531     Status = ObReferenceObjectByHandle(ProcessHandle,
3532                                        PROCESS_VM_OPERATION,
3533                                        PsProcessType,
3534                                        PreviousMode,
3535                                        (PVOID*)(&Process),
3536                                        NULL);
3537     if (!NT_SUCCESS(Status)) return Status;
3538 
3539     //
3540     // Check if this is is system-mapped
3541     //
3542     if (MapType & MAP_SYSTEM)
3543     {
3544         //
3545         // Check for required privilege
3546         //
3547         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3548         {
3549             //
3550             // Fail: Don't have it
3551             //
3552             ObDereferenceObject(Process);
3553             return STATUS_PRIVILEGE_NOT_HELD;
3554         }
3555     }
3556 
3557     //
3558     // Check if we should attach
3559     //
3560     if (CurrentProcess != Process)
3561     {
3562         //
3563         // Do it
3564         //
3565         KeStackAttachProcess(&Process->Pcb, &ApcState);
3566         Attached = TRUE;
3567     }
3568 
3569     //
3570     // Call the internal function
3571     //
3572     Status = MiLockVirtualMemory(&CapturedBaseAddress,
3573                                  &CapturedBytesToLock,
3574                                  MapType);
3575 
3576     //
3577     // Detach if needed
3578     //
3579     if (Attached) KeUnstackDetachProcess(&ApcState);
3580 
3581     //
3582     // Release reference
3583     //
3584     ObDereferenceObject(Process);
3585 
3586     //
3587     // Enter SEH to return data
3588     //
3589     _SEH2_TRY
3590     {
3591         //
3592         // Return data to user
3593         //
3594         *BaseAddress = CapturedBaseAddress;
3595         *NumberOfBytesToLock = CapturedBytesToLock;
3596     }
3597     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3598     {
3599         //
3600         // Get exception code
3601         //
3602         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3603     }
3604     _SEH2_END;
3605 
3606     //
3607     // Return status
3608     //
3609     return Status;
3610 }
3611 
3612 
3613 static
3614 NTSTATUS
3615 MiUnlockVirtualMemory(
3616     IN OUT PVOID *BaseAddress,
3617     IN OUT PSIZE_T RegionSize,
3618     IN ULONG MapType)
3619 {
3620     PEPROCESS CurrentProcess;
3621     PMMSUPPORT AddressSpace;
3622     PVOID EndAddress;
3623     PMMPTE PointerPte, LastPte;
3624     PMMPDE PointerPde;
3625 #if (_MI_PAGING_LEVELS >= 3)
3626     PMMPDE PointerPpe;
3627 #endif
3628 #if (_MI_PAGING_LEVELS == 4)
3629     PMMPDE PointerPxe;
3630 #endif
3631     PMMPFN Pfn1;
3632     NTSTATUS Status;
3633 
3634     /* Lock the address space */
3635     AddressSpace = MmGetCurrentAddressSpace();
3636     MmLockAddressSpace(AddressSpace);
3637 
3638     /* Make sure we still have an address space */
3639     CurrentProcess = PsGetCurrentProcess();
3640     if (CurrentProcess->VmDeleted)
3641     {
3642         Status = STATUS_PROCESS_IS_TERMINATING;
3643         goto Cleanup;
3644     }
3645 
3646     /* Check the VADs in the requested range */
3647     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3648 
3649     /* Note: only bail out, if we hit an area without a VAD. If we hit an
3650        incompatible VAD we continue, like Windows does */
3651     if (Status == STATUS_ACCESS_VIOLATION)
3652     {
3653         Status = STATUS_NOT_LOCKED;
3654         goto Cleanup;
3655     }
3656 
3657     /* Get the PTE and PDE */
3658     PointerPte = MiAddressToPte(*BaseAddress);
3659     PointerPde = MiAddressToPde(*BaseAddress);
3660 #if (_MI_PAGING_LEVELS >= 3)
3661     PointerPpe = MiAddressToPpe(*BaseAddress);
3662 #endif
3663 #if (_MI_PAGING_LEVELS == 4)
3664     PointerPxe = MiAddressToPxe(*BaseAddress);
3665 #endif
3666 
3667     /* Get the last PTE */
3668     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3669 
3670     /* Lock the process working set */
3671     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3672 
3673     /* Loop the pages */
3674     do
3675     {
3676         /* Check for a page that is not present */
3677         if (
3678 #if (_MI_PAGING_LEVELS == 4)
3679                (PointerPxe->u.Hard.Valid == 0) ||
3680 #endif
3681 #if (_MI_PAGING_LEVELS >= 3)
3682                (PointerPpe->u.Hard.Valid == 0) ||
3683 #endif
3684                (PointerPde->u.Hard.Valid == 0) ||
3685                (PointerPte->u.Hard.Valid == 0))
3686         {
3687             /* Remember it, but keep going */
3688             Status = STATUS_NOT_LOCKED;
3689         }
3690         else
3691         {
3692             /* Get the PFN */
3693             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3694             ASSERT(Pfn1 != NULL);
3695 
3696             /* Check if all of the requested locks are present */
3697             if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3698                 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3699             {
3700                 /* Remember it, but keep going */
3701                 Status = STATUS_NOT_LOCKED;
3702 
3703                 /* Check if no lock is present */
3704                 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3705                 {
3706                     DPRINT1("FIXME: Should remove the page from WS\n");
3707                 }
3708             }
3709         }
3710 
3711         /* Go to the next PTE */
3712         PointerPte++;
3713 
3714         /* Check if we're on a PDE boundary */
3715         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3716 #if (_MI_PAGING_LEVELS >= 3)
3717         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3718 #endif
3719 #if (_MI_PAGING_LEVELS == 4)
3720         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3721 #endif
3722     } while (PointerPte <= LastPte);
3723 
3724     /* Check if we hit a page that was not locked */
3725     if (Status == STATUS_NOT_LOCKED)
3726     {
3727         goto CleanupWithWsLock;
3728     }
3729 
3730     /* All pages in the region were locked, so unlock them all */
3731 
3732     /* Get the PTE and PDE */
3733     PointerPte = MiAddressToPte(*BaseAddress);
3734     PointerPde = MiAddressToPde(*BaseAddress);
3735 #if (_MI_PAGING_LEVELS >= 3)
3736     PointerPpe = MiAddressToPpe(*BaseAddress);
3737 #endif
3738 #if (_MI_PAGING_LEVELS == 4)
3739     PointerPxe = MiAddressToPxe(*BaseAddress);
3740 #endif
3741 
3742     /* Loop the pages */
3743     do
3744     {
3745         /* Unlock it */
3746         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3747         MI_UNLOCK_VA(Pfn1, MapType);
3748 
3749         /* Go to the next PTE */
3750         PointerPte++;
3751 
3752         /* Check if we're on a PDE boundary */
3753         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3754 #if (_MI_PAGING_LEVELS >= 3)
3755         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3756 #endif
3757 #if (_MI_PAGING_LEVELS == 4)
3758         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3759 #endif
3760     } while (PointerPte <= LastPte);
3761 
3762     /* Everything is done */
3763     Status = STATUS_SUCCESS;
3764 
3765 CleanupWithWsLock:
3766 
3767     /* Release process working set */
3768     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3769 
3770 Cleanup:
3771     /* Unlock address space */
3772     MmUnlockAddressSpace(AddressSpace);
3773 
3774     return Status;
3775 }
3776 
3777 
3778 NTSTATUS
3779 NTAPI
3780 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3781                       IN OUT PVOID *BaseAddress,
3782                       IN OUT PSIZE_T NumberOfBytesToUnlock,
3783                       IN ULONG MapType)
3784 {
3785     PEPROCESS Process;
3786     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3787     NTSTATUS Status;
3788     BOOLEAN Attached = FALSE;
3789     KAPC_STATE ApcState;
3790     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3791     PVOID CapturedBaseAddress;
3792     SIZE_T CapturedBytesToUnlock;
3793     PAGED_CODE();
3794 
3795     //
3796     // Validate flags
3797     //
3798     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3799     {
3800         //
3801         // Invalid set of flags
3802         //
3803         return STATUS_INVALID_PARAMETER;
3804     }
3805 
3806     //
3807     // At least one flag must be specified
3808     //
3809     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3810     {
3811         //
3812         // No flag given
3813         //
3814         return STATUS_INVALID_PARAMETER;
3815     }
3816 
3817     //
3818     // Enter SEH for probing
3819     //
3820     _SEH2_TRY
3821     {
3822         //
3823         // Validate output data
3824         //
3825         ProbeForWritePointer(BaseAddress);
3826         ProbeForWriteSize_t(NumberOfBytesToUnlock);
3827 
3828         //
3829         // Capture it
3830         //
3831         CapturedBaseAddress = *BaseAddress;
3832         CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3833     }
3834     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3835     {
3836         //
3837         // Get exception code
3838         //
3839         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3840     }
3841     _SEH2_END;
3842 
3843     //
3844     // Catch illegal base address
3845     //
3846     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3847 
3848     //
3849     // Catch illegal region size
3850     //
3851     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3852     {
3853         //
3854         // Fail
3855         //
3856         return STATUS_INVALID_PARAMETER;
3857     }
3858 
3859     //
3860     // 0 is also illegal
3861     //
3862     if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3863 
3864     //
3865     // Get a reference to the process
3866     //
3867     Status = ObReferenceObjectByHandle(ProcessHandle,
3868                                        PROCESS_VM_OPERATION,
3869                                        PsProcessType,
3870                                        PreviousMode,
3871                                        (PVOID*)(&Process),
3872                                        NULL);
3873     if (!NT_SUCCESS(Status)) return Status;
3874 
3875     //
3876     // Check if this is is system-mapped
3877     //
3878     if (MapType & MAP_SYSTEM)
3879     {
3880         //
3881         // Check for required privilege
3882         //
3883         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3884         {
3885             //
3886             // Fail: Don't have it
3887             //
3888             ObDereferenceObject(Process);
3889             return STATUS_PRIVILEGE_NOT_HELD;
3890         }
3891     }
3892 
3893     //
3894     // Check if we should attach
3895     //
3896     if (CurrentProcess != Process)
3897     {
3898         //
3899         // Do it
3900         //
3901         KeStackAttachProcess(&Process->Pcb, &ApcState);
3902         Attached = TRUE;
3903     }
3904 
3905     //
3906     // Call the internal function
3907     //
3908     Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3909                                    &CapturedBytesToUnlock,
3910                                    MapType);
3911 
3912     //
3913     // Detach if needed
3914     //
3915     if (Attached) KeUnstackDetachProcess(&ApcState);
3916 
3917     //
3918     // Release reference
3919     //
3920     ObDereferenceObject(Process);
3921 
3922     //
3923     // Enter SEH to return data
3924     //
3925     _SEH2_TRY
3926     {
3927         //
3928         // Return data to user
3929         //
3930         *BaseAddress = CapturedBaseAddress;
3931         *NumberOfBytesToUnlock = CapturedBytesToUnlock;
3932     }
3933     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3934     {
3935         //
3936         // Get exception code
3937         //
3938         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3939     }
3940     _SEH2_END;
3941 
3942     //
3943     // Return status
3944     //
3945     return STATUS_SUCCESS;
3946 }
3947 
3948 NTSTATUS
3949 NTAPI
3950 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3951                      IN OUT PVOID *BaseAddress,
3952                      IN OUT PSIZE_T NumberOfBytesToFlush,
3953                      OUT PIO_STATUS_BLOCK IoStatusBlock)
3954 {
3955     PEPROCESS Process;
3956     NTSTATUS Status;
3957     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3958     PVOID CapturedBaseAddress;
3959     SIZE_T CapturedBytesToFlush;
3960     IO_STATUS_BLOCK LocalStatusBlock;
3961     PAGED_CODE();
3962 
3963     //
3964     // Check if we came from user mode
3965     //
3966     if (PreviousMode != KernelMode)
3967     {
3968         //
3969         // Enter SEH for probing
3970         //
3971         _SEH2_TRY
3972         {
3973             //
3974             // Validate all outputs
3975             //
3976             ProbeForWritePointer(BaseAddress);
3977             ProbeForWriteSize_t(NumberOfBytesToFlush);
3978             ProbeForWriteIoStatusBlock(IoStatusBlock);
3979 
3980             //
3981             // Capture them
3982             //
3983             CapturedBaseAddress = *BaseAddress;
3984             CapturedBytesToFlush = *NumberOfBytesToFlush;
3985         }
3986         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3987         {
3988             //
3989             // Get exception code
3990             //
3991             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3992         }
3993         _SEH2_END;
3994     }
3995     else
3996     {
3997         //
3998         // Capture directly
3999         //
4000         CapturedBaseAddress = *BaseAddress;
4001         CapturedBytesToFlush = *NumberOfBytesToFlush;
4002     }
4003 
4004     //
4005     // Catch illegal base address
4006     //
4007     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4008 
4009     //
4010     // Catch illegal region size
4011     //
4012     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
4013     {
4014         //
4015         // Fail
4016         //
4017         return STATUS_INVALID_PARAMETER;
4018     }
4019 
4020     //
4021     // Get a reference to the process
4022     //
4023     Status = ObReferenceObjectByHandle(ProcessHandle,
4024                                        PROCESS_VM_OPERATION,
4025                                        PsProcessType,
4026                                        PreviousMode,
4027                                        (PVOID*)(&Process),
4028                                        NULL);
4029     if (!NT_SUCCESS(Status)) return Status;
4030 
4031     //
4032     // Do it
4033     //
4034     Status = MmFlushVirtualMemory(Process,
4035                                   &CapturedBaseAddress,
4036                                   &CapturedBytesToFlush,
4037                                   &LocalStatusBlock);
4038 
4039     //
4040     // Release reference
4041     //
4042     ObDereferenceObject(Process);
4043 
4044     //
4045     // Enter SEH to return data
4046     //
4047     _SEH2_TRY
4048     {
4049         //
4050         // Return data to user
4051         //
4052         *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
4053         *NumberOfBytesToFlush = 0;
4054         *IoStatusBlock = LocalStatusBlock;
4055     }
4056     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4057     {
4058     }
4059     _SEH2_END;
4060 
4061     //
4062     // Return status
4063     //
4064     return Status;
4065 }
4066 
4067 /*
4068  * @unimplemented
4069  */
4070 NTSTATUS
4071 NTAPI
4072 NtGetWriteWatch(IN HANDLE ProcessHandle,
4073                 IN ULONG Flags,
4074                 IN PVOID BaseAddress,
4075                 IN SIZE_T RegionSize,
4076                 IN PVOID *UserAddressArray,
4077                 OUT PULONG_PTR EntriesInUserAddressArray,
4078                 OUT PULONG Granularity)
4079 {
4080     PEPROCESS Process;
4081     NTSTATUS Status;
4082     PVOID EndAddress;
4083     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4084     ULONG_PTR CapturedEntryCount;
4085     PAGED_CODE();
4086 
4087     //
4088     // Check if we came from user mode
4089     //
4090     if (PreviousMode != KernelMode)
4091     {
4092         //
4093         // Enter SEH for probing
4094         //
4095         _SEH2_TRY
4096         {
4097             //
4098             // Catch illegal base address
4099             //
4100             if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
4101 
4102             //
4103             // Catch illegal region size
4104             //
4105             if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4106             {
4107                 //
4108                 // Fail
4109                 //
4110                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
4111             }
4112 
4113             //
4114             // Validate all data
4115             //
4116             ProbeForWriteSize_t(EntriesInUserAddressArray);
4117             ProbeForWriteUlong(Granularity);
4118 
4119             //
4120             // Capture them
4121             //
4122             CapturedEntryCount = *EntriesInUserAddressArray;
4123 
4124             //
4125             // Must have a count
4126             //
4127             if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4128 
4129             //
4130             // Can't be larger than the maximum
4131             //
4132             if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
4133             {
4134                 //
4135                 // Fail
4136                 //
4137                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4138             }
4139 
4140             //
4141             // Probe the actual array
4142             //
4143             ProbeForWrite(UserAddressArray,
4144                           CapturedEntryCount * sizeof(PVOID),
4145                           sizeof(PVOID));
4146         }
4147         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4148         {
4149             //
4150             // Get exception code
4151             //
4152             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4153         }
4154         _SEH2_END;
4155     }
4156     else
4157     {
4158         //
4159         // Capture directly
4160         //
4161         CapturedEntryCount = *EntriesInUserAddressArray;
4162         ASSERT(CapturedEntryCount != 0);
4163     }
4164 
4165     //
4166     // Check if this is a local request
4167     //
4168     if (ProcessHandle == NtCurrentProcess())
4169     {
4170         //
4171         // No need to reference the process
4172         //
4173         Process = PsGetCurrentProcess();
4174     }
4175     else
4176     {
4177         //
4178         // Reference the target
4179         //
4180         Status = ObReferenceObjectByHandle(ProcessHandle,
4181                                            PROCESS_VM_OPERATION,
4182                                            PsProcessType,
4183                                            PreviousMode,
4184                                            (PVOID *)&Process,
4185                                            NULL);
4186         if (!NT_SUCCESS(Status)) return Status;
4187     }
4188 
4189     //
4190     // Compute the last address and validate it
4191     //
4192     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4193     if (BaseAddress > EndAddress)
4194     {
4195         //
4196         // Fail
4197         //
4198         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4199         return STATUS_INVALID_PARAMETER_4;
4200     }
4201 
4202     //
4203     // Oops :(
4204     //
4205     UNIMPLEMENTED;
4206 
4207     //
4208     // Dereference if needed
4209     //
4210     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4211 
4212     //
4213     // Enter SEH to return data
4214     //
4215     _SEH2_TRY
4216     {
4217         //
4218         // Return data to user
4219         //
4220         *EntriesInUserAddressArray = 0;
4221         *Granularity = PAGE_SIZE;
4222     }
4223     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4224     {
4225         //
4226         // Get exception code
4227         //
4228         Status = _SEH2_GetExceptionCode();
4229     }
4230     _SEH2_END;
4231 
4232     //
4233     // Return success
4234     //
4235     return STATUS_SUCCESS;
4236 }
4237 
4238 /*
4239  * @unimplemented
4240  */
4241 NTSTATUS
4242 NTAPI
4243 NtResetWriteWatch(IN HANDLE ProcessHandle,
4244                   IN PVOID BaseAddress,
4245                   IN SIZE_T RegionSize)
4246 {
4247     PVOID EndAddress;
4248     PEPROCESS Process;
4249     NTSTATUS Status;
4250     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4251     ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
4252 
4253     //
4254     // Catch illegal base address
4255     //
4256     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
4257 
4258     //
4259     // Catch illegal region size
4260     //
4261     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4262     {
4263         //
4264         // Fail
4265         //
4266         return STATUS_INVALID_PARAMETER_3;
4267     }
4268 
4269     //
4270     // Check if this is a local request
4271     //
4272     if (ProcessHandle == NtCurrentProcess())
4273     {
4274         //
4275         // No need to reference the process
4276         //
4277         Process = PsGetCurrentProcess();
4278     }
4279     else
4280     {
4281         //
4282         // Reference the target
4283         //
4284         Status = ObReferenceObjectByHandle(ProcessHandle,
4285                                            PROCESS_VM_OPERATION,
4286                                            PsProcessType,
4287                                            PreviousMode,
4288                                            (PVOID *)&Process,
4289                                            NULL);
4290         if (!NT_SUCCESS(Status)) return Status;
4291     }
4292 
4293     //
4294     // Compute the last address and validate it
4295     //
4296     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4297     if (BaseAddress > EndAddress)
4298     {
4299         //
4300         // Fail
4301         //
4302         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4303         return STATUS_INVALID_PARAMETER_3;
4304     }
4305 
4306     //
4307     // Oops :(
4308     //
4309     UNIMPLEMENTED;
4310 
4311     //
4312     // Dereference if needed
4313     //
4314     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4315 
4316     //
4317     // Return success
4318     //
4319     return STATUS_SUCCESS;
4320 }
4321 
4322 NTSTATUS
4323 NTAPI
4324 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
4325                      IN PVOID BaseAddress,
4326                      IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4327                      OUT PVOID MemoryInformation,
4328                      IN SIZE_T MemoryInformationLength,
4329                      OUT PSIZE_T ReturnLength)
4330 {
4331     NTSTATUS Status = STATUS_SUCCESS;
4332     KPROCESSOR_MODE PreviousMode;
4333 
4334     DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4335 
4336     /* Bail out if the address is invalid */
4337     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4338 
4339     /* Probe return buffer */
4340     PreviousMode =  ExGetPreviousMode();
4341     if (PreviousMode != KernelMode)
4342     {
4343         _SEH2_TRY
4344         {
4345             ProbeForWrite(MemoryInformation,
4346                           MemoryInformationLength,
4347                           sizeof(ULONG_PTR));
4348 
4349             if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
4350         }
4351         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4352         {
4353             Status = _SEH2_GetExceptionCode();
4354         }
4355         _SEH2_END;
4356 
4357         if (!NT_SUCCESS(Status))
4358         {
4359             return Status;
4360         }
4361     }
4362 
4363     switch(MemoryInformationClass)
4364     {
4365         case MemoryBasicInformation:
4366             /* Validate the size information of the class */
4367             if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4368             {
4369                 /* The size is invalid */
4370                 return STATUS_INFO_LENGTH_MISMATCH;
4371             }
4372             Status = MiQueryMemoryBasicInformation(ProcessHandle,
4373                                                    BaseAddress,
4374                                                    MemoryInformation,
4375                                                    MemoryInformationLength,
4376                                                    ReturnLength);
4377             break;
4378 
4379         case MemorySectionName:
4380             /* Validate the size information of the class */
4381             if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4382             {
4383                 /* The size is invalid */
4384                 return STATUS_INFO_LENGTH_MISMATCH;
4385             }
4386             Status = MiQueryMemorySectionName(ProcessHandle,
4387                                               BaseAddress,
4388                                               MemoryInformation,
4389                                               MemoryInformationLength,
4390                                               ReturnLength);
4391             break;
4392         case MemoryWorkingSetList:
4393         case MemoryBasicVlmInformation:
4394         default:
4395             DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4396             break;
4397     }
4398 
4399     return Status;
4400 }
4401 
4402 /*
4403  * @implemented
4404  */
4405 NTSTATUS
4406 NTAPI
4407 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
4408                         IN OUT PVOID* UBaseAddress,
4409                         IN ULONG_PTR ZeroBits,
4410                         IN OUT PSIZE_T URegionSize,
4411                         IN ULONG AllocationType,
4412                         IN ULONG Protect)
4413 {
4414     PEPROCESS Process;
4415     PMEMORY_AREA MemoryArea;
4416     PMMVAD Vad = NULL, FoundVad;
4417     NTSTATUS Status;
4418     PMMSUPPORT AddressSpace;
4419     PVOID PBaseAddress;
4420     ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4421     ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4422     PEPROCESS CurrentProcess = PsGetCurrentProcess();
4423     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4424     PETHREAD CurrentThread = PsGetCurrentThread();
4425     KAPC_STATE ApcState;
4426     ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4427     BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
4428     MMPTE TempPte;
4429     PMMPTE PointerPte, LastPte;
4430     PMMPDE PointerPde;
4431     TABLE_SEARCH_RESULT Result;
4432     PAGED_CODE();
4433 
4434     /* Check for valid Zero bits */
4435     if (ZeroBits > MI_MAX_ZERO_BITS)
4436     {
4437         DPRINT1("Too many zero bits\n");
4438         return STATUS_INVALID_PARAMETER_3;
4439     }
4440 
4441     /* Check for valid Allocation Types */
4442     if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
4443                     MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
4444     {
4445         DPRINT1("Invalid Allocation Type\n");
4446         return STATUS_INVALID_PARAMETER_5;
4447     }
4448 
4449     /* Check for at least one of these Allocation Types to be set */
4450     if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
4451     {
4452         DPRINT1("No memory allocation base type\n");
4453         return STATUS_INVALID_PARAMETER_5;
4454     }
4455 
4456     /* MEM_RESET is an exclusive flag, make sure that is valid too */
4457     if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
4458     {
4459         DPRINT1("Invalid use of MEM_RESET\n");
4460         return STATUS_INVALID_PARAMETER_5;
4461     }
4462 
4463     /* Check if large pages are being used */
4464     if (AllocationType & MEM_LARGE_PAGES)
4465     {
4466         /* Large page allocations MUST be committed */
4467         if (!(AllocationType & MEM_COMMIT))
4468         {
4469             DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4470             return STATUS_INVALID_PARAMETER_5;
4471         }
4472 
4473         /* These flags are not allowed with large page allocations */
4474         if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
4475         {
4476             DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4477             return STATUS_INVALID_PARAMETER_5;
4478         }
4479     }
4480 
4481     /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4482     if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
4483     {
4484         DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4485         return STATUS_INVALID_PARAMETER_5;
4486     }
4487 
4488     /* Check for valid MEM_PHYSICAL usage */
4489     if (AllocationType & MEM_PHYSICAL)
4490     {
4491         /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4492         if (!(AllocationType & MEM_RESERVE))
4493         {
4494             DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4495             return STATUS_INVALID_PARAMETER_5;
4496         }
4497 
4498         /* Only these flags are allowed with MEM_PHYSIAL */
4499         if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
4500         {
4501             DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4502             return STATUS_INVALID_PARAMETER_5;
4503         }
4504 
4505         /* Then make sure PAGE_READWRITE is used */
4506         if (Protect != PAGE_READWRITE)
4507         {
4508             DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4509             return STATUS_INVALID_PARAMETER_6;
4510         }
4511     }
4512 
4513     /* Calculate the protection mask and make sure it's valid */
4514     ProtectionMask = MiMakeProtectionMask(Protect);
4515     if (ProtectionMask == MM_INVALID_PROTECTION)
4516     {
4517         DPRINT1("Invalid protection mask\n");
4518         return STATUS_INVALID_PAGE_PROTECTION;
4519     }
4520 
4521     /* Enter SEH */
4522     _SEH2_TRY
4523     {
4524         /* Check for user-mode parameters */
4525         if (PreviousMode != KernelMode)
4526         {
4527             /* Make sure they are writable */
4528             ProbeForWritePointer(UBaseAddress);
4529             ProbeForWriteSize_t(URegionSize);
4530         }
4531 
4532         /* Capture their values */
4533         PBaseAddress = *UBaseAddress;
4534         PRegionSize = *URegionSize;
4535     }
4536     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4537     {
4538         /* Return the exception code */
4539         _SEH2_YIELD(return _SEH2_GetExceptionCode());
4540     }
4541     _SEH2_END;
4542 
4543     /* Make sure the allocation isn't past the VAD area */
4544     if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4545     {
4546         DPRINT1("Virtual allocation base above User Space\n");
4547         return STATUS_INVALID_PARAMETER_2;
4548     }
4549 
4550     /* Make sure the allocation wouldn't overflow past the VAD area */
4551     if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4552     {
4553         DPRINT1("Region size would overflow into kernel-memory\n");
4554         return STATUS_INVALID_PARAMETER_4;
4555     }
4556 
4557     /* Make sure there's a size specified */
4558     if (!PRegionSize)
4559     {
4560         DPRINT1("Region size is invalid (zero)\n");
4561         return STATUS_INVALID_PARAMETER_4;
4562     }
4563 
4564     //
4565     // If this is for the current process, just use PsGetCurrentProcess
4566     //
4567     if (ProcessHandle == NtCurrentProcess())
4568     {
4569         Process = CurrentProcess;
4570     }
4571     else
4572     {
4573         //
4574         // Otherwise, reference the process with VM rights and attach to it if
4575         // this isn't the current process. We must attach because we'll be touching
4576         // PTEs and PDEs that belong to user-mode memory, and also touching the
4577         // Working Set which is stored in Hyperspace.
4578         //
4579         Status = ObReferenceObjectByHandle(ProcessHandle,
4580                                            PROCESS_VM_OPERATION,
4581                                            PsProcessType,
4582                                            PreviousMode,
4583                                            (PVOID*)&Process,
4584                                            NULL);
4585         if (!NT_SUCCESS(Status)) return Status;
4586         if (CurrentProcess != Process)
4587         {
4588             KeStackAttachProcess(&Process->Pcb, &ApcState);
4589             Attached = TRUE;
4590         }
4591     }
4592 
4593     DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4594         Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect);
4595 
4596     //
4597     // Check for large page allocations and make sure that the required privilege
4598     // is being held, before attempting to handle them.
4599     //
4600     if ((AllocationType & MEM_LARGE_PAGES) &&
4601         !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
4602     {
4603         /* Fail without it */
4604         DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4605         Status = STATUS_PRIVILEGE_NOT_HELD;
4606         goto FailPathNoLock;
4607     }
4608 
4609     //
4610     // Fail on the things we don't yet support
4611     //
4612     if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
4613     {
4614         DPRINT1("MEM_LARGE_PAGES not supported\n");
4615         Status = STATUS_INVALID_PARAMETER;
4616         goto FailPathNoLock;
4617     }
4618     if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
4619     {
4620         DPRINT1("MEM_PHYSICAL not supported\n");
4621         Status = STATUS_INVALID_PARAMETER;
4622         goto FailPathNoLock;
4623     }
4624     if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
4625     {
4626         DPRINT1("MEM_WRITE_WATCH not supported\n");
4627         Status = STATUS_INVALID_PARAMETER;
4628         goto FailPathNoLock;
4629     }
4630 
4631     //
4632     // Check if the caller is reserving memory, or committing memory and letting
4633     // us pick the base address
4634     //
4635     if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4636     {
4637         //
4638         //  Do not allow COPY_ON_WRITE through this API
4639         //
4640         if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
4641         {
4642             DPRINT1("Copy on write not allowed through this path\n");
4643             Status = STATUS_INVALID_PAGE_PROTECTION;
4644             goto FailPathNoLock;
4645         }
4646 
4647         //
4648         // Does the caller have an address in mind, or is this a blind commit?
4649         //
4650         if (!PBaseAddress)
4651         {
4652             //
4653             // This is a blind commit, all we need is the region size
4654             //
4655             PRegionSize = ROUND_TO_PAGES(PRegionSize);
4656             EndingAddress = 0;
4657             StartingAddress = 0;
4658 
4659             //
4660             // Check if ZeroBits were specified
4661             //
4662             if (ZeroBits != 0)
4663             {
4664                 //
4665                 // Calculate the highest address and check if it's valid
4666                 //
4667                 HighestAddress = MAXULONG_PTR >> ZeroBits;
4668                 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4669                 {
4670                     Status = STATUS_INVALID_PARAMETER_3;
4671                     goto FailPathNoLock;
4672                 }
4673             }
4674         }
4675         else
4676         {
4677             //
4678             // This is a reservation, so compute the starting address on the
4679             // expected 64KB granularity, and see where the ending address will
4680             // fall based on the aligned address and the passed in region size
4681             //
4682             EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4683             PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4684             StartingAddress = (ULONG_PTR)PBaseAddress;
4685         }
4686 
4687         //
4688         // Allocate and initialize the VAD
4689         //
4690         Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4691         if (Vad == NULL)
4692         {
4693             DPRINT1("Failed to allocate a VAD!\n");
4694             Status = STATUS_INSUFFICIENT_RESOURCES;
4695             goto FailPathNoLock;
4696         }
4697 
4698         RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
4699         if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4700         Vad->u.VadFlags.Protection = ProtectionMask;
4701         Vad->u.VadFlags.PrivateMemory = 1;
4702         Vad->ControlArea = NULL; // For Memory-Area hack
4703 
4704         //
4705         // Insert the VAD
4706         //
4707         Status = MiInsertVadEx(Vad,
4708                                &StartingAddress,
4709                                PRegionSize,
4710                                HighestAddress,
4711                                MM_VIRTMEM_GRANULARITY,
4712                                AllocationType);
4713         if (!NT_SUCCESS(Status))
4714         {
4715             DPRINT1("Failed to insert the VAD!\n");
4716             goto FailPathNoLock;
4717         }
4718 
4719         //
4720         // Detach and dereference the target process if
4721         // it was different from the current process
4722         //
4723         if (Attached) KeUnstackDetachProcess(&ApcState);
4724         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4725 
4726         //
4727         // Use SEH to write back the base address and the region size. In the case
4728         // of an exception, we do not return back the exception code, as the memory
4729         // *has* been allocated. The caller would now have to call VirtualQuery
4730         // or do some other similar trick to actually find out where its memory
4731         // allocation ended up
4732         //
4733         _SEH2_TRY
4734         {
4735             *URegionSize = PRegionSize;
4736             *UBaseAddress = (PVOID)StartingAddress;
4737         }
4738         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4739         {
4740             //
4741             // Ignore exception!
4742             //
4743         }
4744         _SEH2_END;
4745         DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress);
4746         return STATUS_SUCCESS;
4747     }
4748 
4749     //
4750     // This is a MEM_COMMIT on top of an existing address which must have been
4751     // MEM_RESERVED already. Compute the start and ending base addresses based
4752     // on the user input, and then compute the actual region size once all the
4753     // alignments have been done.
4754     //
4755     EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4756     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4757     PRegionSize = EndingAddress - StartingAddress + 1;
4758 
4759     //
4760     // Lock the address space and make sure the process isn't already dead
4761     //
4762     AddressSpace = MmGetCurrentAddressSpace();
4763     MmLockAddressSpace(AddressSpace);
4764     if (Process->VmDeleted)
4765     {
4766         DPRINT1("Process is dying\n");
4767         Status = STATUS_PROCESS_IS_TERMINATING;
4768         goto FailPath;
4769     }
4770 
4771     //
4772     // Get the VAD for this address range, and make sure it exists
4773     //
4774     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4775                                        EndingAddress >> PAGE_SHIFT,
4776                                        &Process->VadRoot,
4777                                        (PMMADDRESS_NODE*)&FoundVad);
4778     if (Result != TableFoundNode)
4779     {
4780         DPRINT1("Could not find a VAD for this allocation\n");
4781         Status = STATUS_CONFLICTING_ADDRESSES;
4782         goto FailPath;
4783     }
4784 
4785     if ((AllocationType & MEM_RESET) == MEM_RESET)
4786     {
4787         /// @todo HACK: pretend success
4788         DPRINT("MEM_RESET not supported\n");
4789         Status = STATUS_SUCCESS;
4790         goto FailPath;
4791     }
4792 
4793     //
4794     // These kinds of VADs are illegal for this Windows function when trying to
4795     // commit an existing range
4796     //
4797     if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4798         (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4799         (FoundVad->u.VadFlags.VadType == VadLargePages))
4800     {
4801         DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4802         Status = STATUS_CONFLICTING_ADDRESSES;
4803         goto FailPath;
4804     }
4805 
4806     //
4807     // Make sure that this address range actually fits within the VAD for it
4808     //
4809     if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4810         ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4811     {
4812         DPRINT1("Address range does not fit into the VAD\n");
4813         Status = STATUS_CONFLICTING_ADDRESSES;
4814         goto FailPath;
4815     }
4816 
4817     //
4818     // Make sure this is an ARM3 section
4819     //
4820     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4821     ASSERT(MemoryArea != NULL);
4822     if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4823     {
4824         DPRINT1("Illegal commit of non-ARM3 section!\n");
4825         Status = STATUS_ALREADY_COMMITTED;
4826         goto FailPath;
4827     }
4828 
4829     // Is this a previously reserved section being committed? If so, enter the
4830     // special section path
4831     //
4832     if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4833     {
4834         //
4835         // You cannot commit large page sections through this API
4836         //
4837         if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4838         {
4839             DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4840             Status = STATUS_INVALID_PAGE_PROTECTION;
4841             goto FailPath;
4842         }
4843 
4844         //
4845         // You can only use caching flags on a rotate VAD
4846         //
4847         if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4848             (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4849         {
4850             DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4851             Status = STATUS_INVALID_PAGE_PROTECTION;
4852             goto FailPath;
4853         }
4854 
4855         //
4856         // We should make sure that the section's permissions aren't being
4857         // messed with
4858         //
4859         if (FoundVad->u.VadFlags.NoChange)
4860         {
4861             //
4862             // Make sure it's okay to touch it
4863             // Note: The Windows 2003 kernel has a bug here, passing the
4864             // unaligned base address together with the aligned size,
4865             // potentially covering a region larger than the actual allocation.
4866             // Might be exposed through NtGdiCreateDIBSection w/ section handle
4867             // For now we keep this behavior.
4868             // TODO: analyze possible implications, create test case
4869             //
4870             Status = MiCheckSecuredVad(FoundVad,
4871                                        PBaseAddress,
4872                                        PRegionSize,
4873                                        ProtectionMask);
4874             if (!NT_SUCCESS(Status))
4875             {
4876                 DPRINT1("Secured VAD being messed around with\n");
4877                 goto FailPath;
4878             }
4879         }
4880 
4881         //
4882         // ARM3 does not support file-backed sections, only shared memory
4883         //
4884         ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4885 
4886         //
4887         // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4888         //
4889         if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4890             (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4891         {
4892             DPRINT1("Invalid page protection for rotate VAD\n");
4893             Status = STATUS_INVALID_PAGE_PROTECTION;
4894             goto FailPath;
4895         }
4896 
4897         //
4898         // Compute PTE addresses and the quota charge, then grab the commit lock
4899         //
4900         PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4901         LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4902         QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4903         KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4904 
4905         //
4906         // Get the segment template PTE and start looping each page
4907         //
4908         TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4909         ASSERT(TempPte.u.Long != 0);
4910         while (PointerPte <= LastPte)
4911         {
4912             //
4913             // For each non-already-committed page, write the invalid template PTE
4914             //
4915             if (PointerPte->u.Long == 0)
4916             {
4917                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4918             }
4919             else
4920             {
4921                 QuotaFree++;
4922             }
4923             PointerPte++;
4924         }
4925 
4926         //
4927         // Now do the commit accounting and release the lock
4928         //
4929         ASSERT(QuotaCharge >= QuotaFree);
4930         QuotaCharge -= QuotaFree;
4931         FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4932         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4933 
4934         //
4935         // We are done with committing the section pages
4936         //
4937         Status = STATUS_SUCCESS;
4938         goto FailPath;
4939     }
4940 
4941     //
4942     // This is a specific ReactOS check because we only use normal VADs
4943     //
4944     ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4945 
4946     //
4947     // While this is an actual Windows check
4948     //
4949     ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4950 
4951     //
4952     // Throw out attempts to use copy-on-write through this API path
4953     //
4954     if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4955     {
4956         DPRINT1("Write copy attempted when not allowed\n");
4957         Status = STATUS_INVALID_PAGE_PROTECTION;
4958         goto FailPath;
4959     }
4960 
4961     //
4962     // Initialize a demand-zero PTE
4963     //
4964     TempPte.u.Long = 0;
4965     TempPte.u.Soft.Protection = ProtectionMask;
4966     ASSERT(TempPte.u.Long != 0);
4967 
4968     //
4969     // Get the PTE, PDE and the last PTE for this address range
4970     //
4971     PointerPde = MiAddressToPde(StartingAddress);
4972     PointerPte = MiAddressToPte(StartingAddress);
4973     LastPte = MiAddressToPte(EndingAddress);
4974 
4975     //
4976     // Update the commit charge in the VAD as well as in the process, and check
4977     // if this commit charge was now higher than the last recorded peak, in which
4978     // case we also update the peak
4979     //
4980     FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4981     Process->CommitCharge += (1 + LastPte - PointerPte);
4982     if (Process->CommitCharge > Process->CommitChargePeak)
4983     {
4984         Process->CommitChargePeak = Process->CommitCharge;
4985     }
4986 
4987     //
4988     // Lock the working set while we play with user pages and page tables
4989     //
4990     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4991 
4992     //
4993     // Make the current page table valid, and then loop each page within it
4994     //
4995     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4996     while (PointerPte <= LastPte)
4997     {
4998         //
4999         // Have we crossed into a new page table?
5000         //
5001         if (MiIsPteOnPdeBoundary(PointerPte))
5002         {
5003             //
5004             // Get the PDE and now make it valid too
5005             //
5006             PointerPde = MiPteToPde(PointerPte);
5007             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
5008         }
5009 
5010         //
5011         // Is this a zero PTE as expected?
5012         //
5013         if (PointerPte->u.Long == 0)
5014         {
5015             //
5016             // First increment the count of pages in the page table for this
5017             // process
5018             //
5019             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
5020 
5021             //
5022             // And now write the invalid demand-zero PTE as requested
5023             //
5024             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5025         }
5026         else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
5027         {
5028             //
5029             // If the PTE was already decommitted, there is nothing else to do
5030             // but to write the new demand-zero PTE
5031             //
5032             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5033         }
5034         else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
5035         {
5036             //
5037             // We don't handle these scenarios yet
5038             //
5039             if (PointerPte->u.Soft.Valid == 0)
5040             {
5041                 ASSERT(PointerPte->u.Soft.Prototype == 0);
5042                 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
5043             }
5044 
5045             //
5046             // There's a change in protection, remember this for later, but do
5047             // not yet handle it.
5048             //
5049             ChangeProtection = TRUE;
5050         }
5051 
5052         //
5053         // Move to the next PTE
5054         //
5055         PointerPte++;
5056     }
5057 
5058     //
5059     // Release the working set lock, unlock the address space, and detach from
5060     // the target process if it was not the current process. Also dereference the
5061     // target process if this wasn't the case.
5062     //
5063     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5064     Status = STATUS_SUCCESS;
5065 FailPath:
5066     MmUnlockAddressSpace(AddressSpace);
5067 
5068     if (!NT_SUCCESS(Status))
5069     {
5070         if (Vad != NULL)
5071         {
5072             ExFreePoolWithTag(Vad, 'SdaV');
5073         }
5074     }
5075 
5076     //
5077     // Check if we need to update the protection
5078     //
5079     if (ChangeProtection)
5080     {
5081         PVOID ProtectBaseAddress = (PVOID)StartingAddress;
5082         SIZE_T ProtectSize = PRegionSize;
5083         ULONG OldProtection;
5084 
5085         //
5086         // Change the protection of the region
5087         //
5088         MiProtectVirtualMemory(Process,
5089                                &ProtectBaseAddress,
5090                                &ProtectSize,
5091                                Protect,
5092                                &OldProtection);
5093     }
5094 
5095 FailPathNoLock:
5096     if (Attached) KeUnstackDetachProcess(&ApcState);
5097     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5098 
5099     //
5100     // Only write back results on success
5101     //
5102     if (NT_SUCCESS(Status))
5103     {
5104         //
5105         // Use SEH to write back the base address and the region size. In the case
5106         // of an exception, we strangely do return back the exception code, even
5107         // though the memory *has* been allocated. This mimics Windows behavior and
5108         // there is not much we can do about it.
5109         //
5110         _SEH2_TRY
5111         {
5112             *URegionSize = PRegionSize;
5113             *UBaseAddress = (PVOID)StartingAddress;
5114         }
5115         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5116         {
5117             Status = _SEH2_GetExceptionCode();
5118         }
5119         _SEH2_END;
5120     }
5121 
5122     return Status;
5123 }
5124 
5125 /*
5126  * @implemented
5127  */
5128 NTSTATUS
5129 NTAPI
5130 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
5131                     IN PVOID* UBaseAddress,
5132                     IN PSIZE_T URegionSize,
5133                     IN ULONG FreeType)
5134 {
5135     PMEMORY_AREA MemoryArea;
5136     SIZE_T PRegionSize;
5137     PVOID PBaseAddress;
5138     LONG_PTR AlreadyDecommitted, CommitReduction = 0;
5139     ULONG_PTR StartingAddress, EndingAddress;
5140     PMMVAD Vad;
5141     NTSTATUS Status;
5142     PEPROCESS Process;
5143     PMMSUPPORT AddressSpace;
5144     PETHREAD CurrentThread = PsGetCurrentThread();
5145     PEPROCESS CurrentProcess = PsGetCurrentProcess();
5146     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
5147     KAPC_STATE ApcState;
5148     BOOLEAN Attached = FALSE;
5149     PAGED_CODE();
5150 
5151     //
5152     // Only two flags are supported, exclusively.
5153     //
5154     if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT)
5155     {
5156         DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType);
5157         return STATUS_INVALID_PARAMETER_4;
5158     }
5159 
5160     //
5161     // Enter SEH for probe and capture. On failure, return back to the caller
5162     // with an exception violation.
5163     //
5164     _SEH2_TRY
5165     {
5166         //
5167         // Check for user-mode parameters and make sure that they are writeable
5168         //
5169         if (PreviousMode != KernelMode)
5170         {
5171             ProbeForWritePointer(UBaseAddress);
5172             ProbeForWriteUlong(URegionSize);
5173         }
5174 
5175         //
5176         // Capture the current values
5177         //
5178         PBaseAddress = *UBaseAddress;
5179         PRegionSize = *URegionSize;
5180     }
5181     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5182     {
5183         _SEH2_YIELD(return _SEH2_GetExceptionCode());
5184     }
5185     _SEH2_END;
5186 
5187     //
5188     // Make sure the allocation isn't past the user area
5189     //
5190     if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5191     {
5192         DPRINT1("Virtual free base above User Space\n");
5193         return STATUS_INVALID_PARAMETER_2;
5194     }
5195 
5196     //
5197     // Make sure the allocation wouldn't overflow past the user area
5198     //
5199     if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5200     {
5201         DPRINT1("Region size would overflow into kernel-memory\n");
5202         return STATUS_INVALID_PARAMETER_3;
5203     }
5204 
5205     //
5206     // If this is for the current process, just use PsGetCurrentProcess
5207     //
5208     if (ProcessHandle == NtCurrentProcess())
5209     {
5210         Process = CurrentProcess;
5211     }
5212     else
5213     {
5214         //
5215         // Otherwise, reference the process with VM rights and attach to it if
5216         // this isn't the current process. We must attach because we'll be touching
5217         // PTEs and PDEs that belong to user-mode memory, and also touching the
5218         // Working Set which is stored in Hyperspace.
5219         //
5220         Status = ObReferenceObjectByHandle(ProcessHandle,
5221                                            PROCESS_VM_OPERATION,
5222                                            PsProcessType,
5223                                            PreviousMode,
5224                                            (PVOID*)&Process,
5225                                            NULL);
5226         if (!NT_SUCCESS(Status)) return Status;
5227         if (CurrentProcess != Process)
5228         {
5229             KeStackAttachProcess(&Process->Pcb, &ApcState);
5230             Attached = TRUE;
5231         }
5232     }
5233 
5234     DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n",
5235            Process, PBaseAddress, PRegionSize, FreeType);
5236 
5237     //
5238     // Lock the address space
5239     //
5240     AddressSpace = MmGetCurrentAddressSpace();
5241     MmLockAddressSpace(AddressSpace);
5242 
5243     //
5244     // If the address space is being deleted, fail the de-allocation since it's
5245     // too late to do anything about it
5246     //
5247     if (Process->VmDeleted)
5248     {
5249         DPRINT1("Process is dead\n");
5250         Status = STATUS_PROCESS_IS_TERMINATING;
5251         goto FailPath;
5252     }
5253 
5254     //
5255     // Compute start and end addresses, and locate the VAD
5256     //
5257     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5258     EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5259     Vad = MiLocateAddress((PVOID)StartingAddress);
5260     if (!Vad)
5261     {
5262         DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5263         Status = STATUS_MEMORY_NOT_ALLOCATED;
5264         goto FailPath;
5265     }
5266 
5267     //
5268     // If the range exceeds the VAD's ending VPN, fail this request
5269     //
5270     if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5271     {
5272         DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5273         Status = STATUS_UNABLE_TO_FREE_VM;
5274         goto FailPath;
5275     }
5276 
5277     //
5278     // Only private memory (except rotate VADs) can be freed through here */
5279     //
5280     if ((!(Vad->u.VadFlags.PrivateMemory) &&
5281          (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5282         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5283     {
5284         DPRINT1("Attempt to free section memory\n");
5285         Status = STATUS_UNABLE_TO_DELETE_SECTION;
5286         goto FailPath;
5287     }
5288 
5289     //
5290     // ARM3 does not yet handle protected VM
5291     //
5292     ASSERT(Vad->u.VadFlags.NoChange == 0);
5293 
5294     //
5295     // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5296     // and that is is an ARM3 memory area, and not a section view, as we currently
5297     // don't support freeing those though this interface.
5298     //
5299     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5300     ASSERT(MemoryArea);
5301     ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5302 
5303     //
5304     //  Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5305     //
5306     if (FreeType & MEM_RELEASE)
5307     {
5308         //
5309         // ARM3 only supports this VAD in this path
5310         //
5311         ASSERT(Vad->u.VadFlags.VadType == VadNone);
5312 
5313         //
5314         // Is the caller trying to remove the whole VAD, or remove only a portion
5315         // of it? If no region size is specified, then the assumption is that the
5316         // whole VAD is to be destroyed
5317         //
5318         if (!PRegionSize)
5319         {
5320             //
5321             // The caller must specify the base address identically to the range
5322             // that is stored in the VAD.
5323             //
5324             if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5325             {
5326                 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5327                 Status = STATUS_FREE_VM_NOT_AT_BASE;
5328                 goto FailPath;
5329             }
5330 
5331             //
5332             // Now compute the actual start/end addresses based on the VAD
5333             //
5334             StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5335             EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5336 
5337             //
5338             // Finally lock the working set and remove the VAD from the VAD tree
5339             //
5340             MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5341             ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5342             MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5343         }
5344         else
5345         {
5346             //
5347             // This means the caller wants to release a specific region within
5348             // the range. We have to find out which range this is -- the following
5349             // possibilities exist plus their union (CASE D):
5350             //
5351             // STARTING ADDRESS                                   ENDING ADDRESS
5352             // [<========][========================================][=========>]
5353             //   CASE A                  CASE B                       CASE C
5354             //
5355             //
5356             // First, check for case A or D
5357             //
5358             if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5359             {
5360                 //
5361                 // Check for case D
5362                 //
5363                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5364                 {
5365                     //
5366                     // This is the easiest one to handle -- it is identical to
5367                     // the code path above when the caller sets a zero region size
5368                     // and the whole VAD is destroyed
5369                     //
5370                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5371                     ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5372                     MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5373                 }
5374                 else
5375                 {
5376                     //
5377                     // This case is pretty easy too -- we compute a bunch of
5378                     // pages to decommit, and then push the VAD's starting address
5379                     // a bit further down, then decrement the commit charge
5380                     //
5381                     // NOT YET IMPLEMENTED IN ARM3.
5382                     //
5383                     DPRINT1("Case A not handled\n");
5384                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5385                     goto FailPath;
5386 
5387                     //
5388                     // After analyzing the VAD, set it to NULL so that we don't
5389                     // free it in the exit path
5390                     //
5391                     Vad = NULL;
5392                 }
5393             }
5394             else
5395             {
5396                 //
5397                 // This is case B or case C. First check for case C
5398                 //
5399                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5400                 {
5401                     PMEMORY_AREA MemoryArea;
5402 
5403                     //
5404                     // This is pretty easy and similar to case A. We compute the
5405                     // amount of pages to decommit, update the VAD's commit charge
5406                     // and then change the ending address of the VAD to be a bit
5407                     // smaller.
5408                     //
5409                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5410                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5411                                                                 EndingAddress,
5412                                                                 Vad,
5413                                                                 Process);
5414                     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5415                     // For ReactOS: shrink the corresponding memory area
5416                     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5417                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5418                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5419                     Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT;
5420                     MemoryArea->VadNode.EndingVpn = Vad->EndingVpn;
5421                 }
5422                 else
5423                 {
5424                     //
5425                     // This is case B and the hardest one. Because we are removing
5426                     // a chunk of memory from the very middle of the VAD, we must
5427                     // actually split the VAD into two new VADs and compute the
5428                     // commit charges for each of them, and reinsert new charges.
5429                     //
5430                     // NOT YET IMPLEMENTED IN ARM3.
5431                     //
5432                     DPRINT1("Case B not handled\n");
5433                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5434                     goto FailPath;
5435                 }
5436 
5437                 //
5438                 // After analyzing the VAD, set it to NULL so that we don't
5439                 // free it in the exit path
5440                 //
5441                 Vad = NULL;
5442             }
5443         }
5444 
5445         //
5446         // Now we have a range of pages to dereference, so call the right API
5447         // to do that and then release the working set, since we're done messing
5448         // around with process pages.
5449         //
5450         MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5451         MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5452         Status = STATUS_SUCCESS;
5453 
5454 FinalPath:
5455         //
5456         // Update the process counters
5457         //
5458         PRegionSize = EndingAddress - StartingAddress + 1;
5459         Process->CommitCharge -= CommitReduction;
5460         if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5461 
5462         //
5463         // Unlock the address space and free the VAD in failure cases. Next,
5464         // detach from the target process so we can write the region size and the
5465         // base address to the correct source process, and dereference the target
5466         // process.
5467         //
5468         MmUnlockAddressSpace(AddressSpace);
5469         if (Vad) ExFreePool(Vad);
5470         if (Attached) KeUnstackDetachProcess(&ApcState);
5471         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5472 
5473         //
5474         // Use SEH to safely return the region size and the base address of the
5475         // deallocation. If we get an access violation, don't return a failure code
5476         // as the deallocation *has* happened. The caller will just have to figure
5477         // out another way to find out where it is (such as VirtualQuery).
5478         //
5479         _SEH2_TRY
5480         {
5481             *URegionSize = PRegionSize;
5482             *UBaseAddress = (PVOID)StartingAddress;
5483         }
5484         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5485         {
5486         }
5487         _SEH2_END;
5488         return Status;
5489     }
5490 
5491     //
5492     // This is the decommit path. You cannot decommit from the following VADs in
5493     // Windows, so fail the vall
5494     //
5495     if ((Vad->u.VadFlags.VadType == VadAwe) ||
5496         (Vad->u.VadFlags.VadType == VadLargePages) ||
5497         (Vad->u.VadFlags.VadType == VadRotatePhysical))
5498     {
5499         DPRINT1("Trying to decommit from invalid VAD\n");
5500         Status = STATUS_MEMORY_NOT_ALLOCATED;
5501         goto FailPath;
5502     }
5503 
5504     //
5505     // If the caller did not specify a region size, first make sure that this
5506     // region is actually committed. If it is, then compute the ending address
5507     // based on the VAD.
5508     //
5509     if (!PRegionSize)
5510     {
5511         if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5512         {
5513             DPRINT1("Decomitting non-committed memory\n");
5514             Status = STATUS_FREE_VM_NOT_AT_BASE;
5515             goto FailPath;
5516         }
5517         EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5518     }
5519 
5520     //
5521     // Decommit the PTEs for the range plus the actual backing pages for the
5522     // range, then reduce that amount from the commit charge in the VAD
5523     //
5524     AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress,
5525                                          MiAddressToPte(EndingAddress),
5526                                          Process,
5527                                          Vad);
5528     CommitReduction = MiAddressToPte(EndingAddress) -
5529                       MiAddressToPte(StartingAddress) +
5530                       1 -
5531                       AlreadyDecommitted;
5532 
5533     ASSERT(CommitReduction >= 0);
5534     ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction);
5535     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5536 
5537     //
5538     // We are done, go to the exit path without freeing the VAD as it remains
5539     // valid since we have not released the allocation.
5540     //
5541     Vad = NULL;
5542     Status = STATUS_SUCCESS;
5543     goto FinalPath;
5544 
5545     //
5546     // In the failure path, we detach and derefernece the target process, and
5547     // return whatever failure code was sent.
5548     //
5549 FailPath:
5550     MmUnlockAddressSpace(AddressSpace);
5551     if (Attached) KeUnstackDetachProcess(&ApcState);
5552     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5553     return Status;
5554 }
5555 
5556 
5557 PHYSICAL_ADDRESS
5558 NTAPI
5559 MmGetPhysicalAddress(PVOID Address)
5560 {
5561     PHYSICAL_ADDRESS PhysicalAddress;
5562     MMPDE TempPde;
5563     MMPTE TempPte;
5564 
5565     /* Check if the PXE/PPE/PDE is valid */
5566     if (
5567 #if (_MI_PAGING_LEVELS == 4)
5568         (MiAddressToPxe(Address)->u.Hard.Valid) &&
5569 #endif
5570 #if (_MI_PAGING_LEVELS >= 3)
5571         (MiAddressToPpe(Address)->u.Hard.Valid) &&
5572 #endif
5573         (MiAddressToPde(Address)->u.Hard.Valid))
5574     {
5575         /* Check for large pages */
5576         TempPde = *MiAddressToPde(Address);
5577         if (TempPde.u.Hard.LargePage)
5578         {
5579             /* Physical address is base page + large page offset */
5580             PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5581             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5582             return PhysicalAddress;
5583         }
5584 
5585         /* Check if the PTE is valid */
5586         TempPte = *MiAddressToPte(Address);
5587         if (TempPte.u.Hard.Valid)
5588         {
5589             /* Physical address is base page + page offset */
5590             PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5591             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5592             return PhysicalAddress;
5593         }
5594     }
5595 
5596     KeRosDumpStackFrames(NULL, 20);
5597     DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5598     PhysicalAddress.QuadPart = 0;
5599     return PhysicalAddress;
5600 }
5601 
5602 
5603 /* EOF */
5604