xref: /reactos/ntoskrnl/mm/ARM3/virtual.c (revision 7b1049c8)
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     /* For now we call the old Mm */
1360     return MmRosFlushVirtualMemory(Process, BaseAddress, RegionSize, IoStatusBlock);
1361 }
1362 
1363 ULONG
1364 NTAPI
1365 MiGetPageProtection(IN PMMPTE PointerPte)
1366 {
1367     MMPTE TempPte;
1368     PMMPFN Pfn;
1369     PEPROCESS CurrentProcess;
1370     PETHREAD CurrentThread;
1371     BOOLEAN WsSafe, WsShared;
1372     ULONG Protect;
1373     KIRQL OldIrql;
1374     PAGED_CODE();
1375 
1376     /* Copy this PTE's contents */
1377     TempPte = *PointerPte;
1378 
1379     /* Assure it's not totally zero */
1380     ASSERT(TempPte.u.Long);
1381 
1382     /* Check for a special prototype format */
1383     if ((TempPte.u.Soft.Valid == 0) &&
1384         (TempPte.u.Soft.Prototype == 1))
1385     {
1386         /* Check if the prototype PTE is not yet pointing to a PTE */
1387         if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1388         {
1389             /* The prototype PTE contains the protection */
1390             return MmProtectToValue[TempPte.u.Soft.Protection];
1391         }
1392 
1393         /* Get a pointer to the underlying shared PTE */
1394         PointerPte = MiProtoPteToPte(&TempPte);
1395 
1396         /* Since the PTE we want to read can be paged out at any time, we need
1397            to release the working set lock first, so that it can be paged in */
1398         CurrentThread = PsGetCurrentThread();
1399         CurrentProcess = PsGetCurrentProcess();
1400         MiUnlockProcessWorkingSetForFault(CurrentProcess,
1401                                           CurrentThread,
1402                                           &WsSafe,
1403                                           &WsShared);
1404 
1405         /* Now read the PTE value */
1406         TempPte = *PointerPte;
1407 
1408         /* Check if that one is invalid */
1409         if (!TempPte.u.Hard.Valid)
1410         {
1411             /* We get the protection directly from this PTE */
1412             Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1413         }
1414         else
1415         {
1416             /* The PTE is valid, so we might need to get the protection from
1417                the PFN. Lock the PFN database */
1418             OldIrql = MiAcquirePfnLock();
1419 
1420             /* Check if the PDE is still valid */
1421             if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
1422             {
1423                 /* It's not, make it valid */
1424                 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
1425             }
1426 
1427             /* Now it's safe to read the PTE value again */
1428             TempPte = *PointerPte;
1429             ASSERT(TempPte.u.Long != 0);
1430 
1431             /* Check again if the PTE is invalid */
1432             if (!TempPte.u.Hard.Valid)
1433             {
1434                 /* The PTE is not valid, so we can use it's protection field */
1435                 Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1436             }
1437             else
1438             {
1439                 /* The PTE is valid, so we can find the protection in the
1440                    OriginalPte field of the PFN */
1441                 Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
1442                 Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1443             }
1444 
1445             /* Release the PFN database */
1446             MiReleasePfnLock(OldIrql);
1447         }
1448 
1449         /* Lock the working set again */
1450         MiLockProcessWorkingSetForFault(CurrentProcess,
1451                                         CurrentThread,
1452                                         WsSafe,
1453                                         WsShared);
1454 
1455         return Protect;
1456     }
1457 
1458     /* In the easy case of transition or demand zero PTE just return its protection */
1459     if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1460 
1461     /* If we get here, the PTE is valid, so look up the page in PFN database */
1462     Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1463     if (!Pfn->u3.e1.PrototypePte)
1464     {
1465         /* Return protection of the original pte */
1466         ASSERT(Pfn->u4.AweAllocation == 0);
1467         return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1468     }
1469 
1470     /* This is software PTE */
1471     DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1472     DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1473     DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1474     DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1475     return MmProtectToValue[TempPte.u.Soft.Protection];
1476 }
1477 
1478 ULONG
1479 NTAPI
1480 MiQueryAddressState(IN PVOID Va,
1481                     IN PMMVAD Vad,
1482                     IN PEPROCESS TargetProcess,
1483                     OUT PULONG ReturnedProtect,
1484                     OUT PVOID *NextVa)
1485 {
1486 
1487     PMMPTE PointerPte, ProtoPte;
1488     PMMPDE PointerPde;
1489 #if (_MI_PAGING_LEVELS >= 3)
1490     PMMPPE PointerPpe;
1491 #endif
1492 #if (_MI_PAGING_LEVELS >= 4)
1493     PMMPXE PointerPxe;
1494 #endif
1495     MMPTE TempPte, TempProtoPte;
1496     BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1497     ULONG State = MEM_RESERVE, Protect = 0;
1498     ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1499            (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1500 
1501     /* Only normal VADs supported */
1502     ASSERT(Vad->u.VadFlags.VadType == VadNone);
1503 
1504     /* Get the PDE and PTE for the address */
1505     PointerPde = MiAddressToPde(Va);
1506     PointerPte = MiAddressToPte(Va);
1507 #if (_MI_PAGING_LEVELS >= 3)
1508     PointerPpe = MiAddressToPpe(Va);
1509 #endif
1510 #if (_MI_PAGING_LEVELS >= 4)
1511     PointerPxe = MiAddressToPxe(Va);
1512 #endif
1513 
1514     /* Return the next range */
1515     *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1516 
1517     do
1518     {
1519 #if (_MI_PAGING_LEVELS >= 4)
1520         /* Does the PXE exist? */
1521         if (PointerPxe->u.Long == 0)
1522         {
1523             /* It does not, next range starts at the next PXE */
1524             *NextVa = MiPxeToAddress(PointerPxe + 1);
1525             break;
1526         }
1527 
1528         /* Is the PXE valid? */
1529         if (PointerPxe->u.Hard.Valid == 0)
1530         {
1531             /* Is isn't, fault it in (make the PPE accessible) */
1532             MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1533         }
1534 #endif
1535 #if (_MI_PAGING_LEVELS >= 3)
1536         /* Does the PPE exist? */
1537         if (PointerPpe->u.Long == 0)
1538         {
1539             /* It does not, next range starts at the next PPE */
1540             *NextVa = MiPpeToAddress(PointerPpe + 1);
1541             break;
1542         }
1543 
1544         /* Is the PPE valid? */
1545         if (PointerPpe->u.Hard.Valid == 0)
1546         {
1547             /* Is isn't, fault it in (make the PDE accessible) */
1548             MiMakeSystemAddressValid(PointerPde, TargetProcess);
1549         }
1550 #endif
1551 
1552         /* Does the PDE exist? */
1553         if (PointerPde->u.Long == 0)
1554         {
1555             /* It does not, next range starts at the next PDE */
1556             *NextVa = MiPdeToAddress(PointerPde + 1);
1557             break;
1558         }
1559 
1560         /* Is the PDE valid? */
1561         if (PointerPde->u.Hard.Valid == 0)
1562         {
1563             /* Is isn't, fault it in (make the PTE accessible) */
1564             MiMakeSystemAddressValid(PointerPte, TargetProcess);
1565         }
1566 
1567         /* We have a PTE that we can access now! */
1568         ValidPte = TRUE;
1569 
1570     } while (FALSE);
1571 
1572     /* Is it safe to try reading the PTE? */
1573     if (ValidPte)
1574     {
1575         /* FIXME: watch out for large pages */
1576         ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1577 
1578         /* Capture the PTE */
1579         TempPte = *PointerPte;
1580         if (TempPte.u.Long != 0)
1581         {
1582             /* The PTE is valid, so it's not zeroed out */
1583             DemandZeroPte = FALSE;
1584 
1585             /* Is it a decommited, invalid, or faulted PTE? */
1586             if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1587                 (TempPte.u.Hard.Valid == 0) &&
1588                 ((TempPte.u.Soft.Prototype == 0) ||
1589                  (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1590             {
1591                 /* Otherwise our defaults should hold */
1592                 ASSERT(Protect == 0);
1593                 ASSERT(State == MEM_RESERVE);
1594             }
1595             else
1596             {
1597                 /* This means it's committed */
1598                 State = MEM_COMMIT;
1599 
1600                 /* We don't support these */
1601                 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1602                 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1603                 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1604 
1605                 /* Get protection state of this page */
1606                 Protect = MiGetPageProtection(PointerPte);
1607 
1608                 /* Check if this is an image-backed VAD */
1609                 if ((TempPte.u.Soft.Valid == 0) &&
1610                     (TempPte.u.Soft.Prototype == 1) &&
1611                     (Vad->u.VadFlags.PrivateMemory == 0) &&
1612                     (Vad->ControlArea))
1613                 {
1614                     DPRINT1("Not supported\n");
1615                     ASSERT(FALSE);
1616                 }
1617             }
1618         }
1619     }
1620 
1621     /* Check if this was a demand-zero PTE, since we need to find the state */
1622     if (DemandZeroPte)
1623     {
1624         /* Not yet handled */
1625         ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1626         ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1627 
1628         /* Check if this is private commited memory, or an section-backed VAD */
1629         if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1630         {
1631             /* Tell caller about the next range */
1632             *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1633 
1634             /* Get the prototype PTE for this VAD */
1635             ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1636                                                     (ULONG_PTR)Va >> PAGE_SHIFT);
1637             if (ProtoPte)
1638             {
1639                 /* We should unlock the working set, but it's not being held! */
1640 
1641                 /* Is the prototype PTE actually valid (committed)? */
1642                 TempProtoPte = *ProtoPte;
1643                 if (TempProtoPte.u.Long)
1644                 {
1645                     /* Unless this is a memory-mapped file, handle it like private VAD */
1646                     State = MEM_COMMIT;
1647                     ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1648                     Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1649                 }
1650 
1651                 /* We should re-lock the working set */
1652             }
1653         }
1654         else if (Vad->u.VadFlags.MemCommit)
1655         {
1656             /* This is committed memory */
1657             State = MEM_COMMIT;
1658 
1659             /* Convert the protection */
1660             Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1661         }
1662     }
1663 
1664     /* Return the protection code */
1665     *ReturnedProtect = Protect;
1666     return State;
1667 }
1668 
1669 NTSTATUS
1670 NTAPI
1671 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1672                               IN PVOID BaseAddress,
1673                               OUT PVOID MemoryInformation,
1674                               IN SIZE_T MemoryInformationLength,
1675                               OUT PSIZE_T ReturnLength)
1676 {
1677     PEPROCESS TargetProcess;
1678     NTSTATUS Status = STATUS_SUCCESS;
1679     PMMVAD Vad = NULL;
1680     PVOID Address, NextAddress;
1681     BOOLEAN Found = FALSE;
1682     ULONG NewProtect, NewState;
1683     ULONG_PTR BaseVpn;
1684     MEMORY_BASIC_INFORMATION MemoryInfo;
1685     KAPC_STATE ApcState;
1686     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1687     PMEMORY_AREA MemoryArea;
1688     SIZE_T ResultLength;
1689 
1690     /* Check for illegal addresses in user-space, or the shared memory area */
1691     if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1692         (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1693     {
1694         Address = PAGE_ALIGN(BaseAddress);
1695 
1696         /* Make up an info structure describing this range */
1697         MemoryInfo.BaseAddress = Address;
1698         MemoryInfo.AllocationProtect = PAGE_READONLY;
1699         MemoryInfo.Type = MEM_PRIVATE;
1700 
1701         /* Special case for shared data */
1702         if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1703         {
1704             MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1705             MemoryInfo.State = MEM_COMMIT;
1706             MemoryInfo.Protect = PAGE_READONLY;
1707             MemoryInfo.RegionSize = PAGE_SIZE;
1708         }
1709         else
1710         {
1711             MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1712             MemoryInfo.State = MEM_RESERVE;
1713             MemoryInfo.Protect = PAGE_NOACCESS;
1714             MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1715         }
1716 
1717         /* Return the data, NtQueryInformation already probed it*/
1718         if (PreviousMode != KernelMode)
1719         {
1720             _SEH2_TRY
1721             {
1722                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1723                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1724             }
1725              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1726             {
1727                 Status = _SEH2_GetExceptionCode();
1728             }
1729             _SEH2_END;
1730         }
1731         else
1732         {
1733             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1734             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1735         }
1736 
1737         return Status;
1738     }
1739 
1740     /* Check if this is for a local or remote process */
1741     if (ProcessHandle == NtCurrentProcess())
1742     {
1743         TargetProcess = PsGetCurrentProcess();
1744     }
1745     else
1746     {
1747         /* Reference the target process */
1748         Status = ObReferenceObjectByHandle(ProcessHandle,
1749                                            PROCESS_QUERY_INFORMATION,
1750                                            PsProcessType,
1751                                            ExGetPreviousMode(),
1752                                            (PVOID*)&TargetProcess,
1753                                            NULL);
1754         if (!NT_SUCCESS(Status)) return Status;
1755 
1756         /* Attach to it now */
1757         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1758     }
1759 
1760     /* Lock the address space and make sure the process isn't already dead */
1761     MmLockAddressSpace(&TargetProcess->Vm);
1762     if (TargetProcess->VmDeleted)
1763     {
1764         /* Unlock the address space of the process */
1765         MmUnlockAddressSpace(&TargetProcess->Vm);
1766 
1767         /* Check if we were attached */
1768         if (ProcessHandle != NtCurrentProcess())
1769         {
1770             /* Detach and dereference the process */
1771             KeUnstackDetachProcess(&ApcState);
1772             ObDereferenceObject(TargetProcess);
1773         }
1774 
1775         /* Bail out */
1776         DPRINT1("Process is dying\n");
1777         return STATUS_PROCESS_IS_TERMINATING;
1778     }
1779 
1780     /* Loop the VADs */
1781     ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1782     if (TargetProcess->VadRoot.NumberGenericTableElements)
1783     {
1784         /* Scan on the right */
1785         Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1786         BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1787         while (Vad)
1788         {
1789             /* Check if this VAD covers the allocation range */
1790             if ((BaseVpn >= Vad->StartingVpn) &&
1791                 (BaseVpn <= Vad->EndingVpn))
1792             {
1793                 /* We're done */
1794                 Found = TRUE;
1795                 break;
1796             }
1797 
1798             /* Check if this VAD is too high */
1799             if (BaseVpn < Vad->StartingVpn)
1800             {
1801                 /* Stop if there is no left child */
1802                 if (!Vad->LeftChild) break;
1803 
1804                 /* Search on the left next */
1805                 Vad = Vad->LeftChild;
1806             }
1807             else
1808             {
1809                 /* Then this VAD is too low, keep searching on the right */
1810                 ASSERT(BaseVpn > Vad->EndingVpn);
1811 
1812                 /* Stop if there is no right child */
1813                 if (!Vad->RightChild) break;
1814 
1815                 /* Search on the right next */
1816                 Vad = Vad->RightChild;
1817             }
1818         }
1819     }
1820 
1821     /* Was a VAD found? */
1822     if (!Found)
1823     {
1824         Address = PAGE_ALIGN(BaseAddress);
1825 
1826         /* Calculate region size */
1827         if (Vad)
1828         {
1829             if (Vad->StartingVpn >= BaseVpn)
1830             {
1831                 /* Region size is the free space till the start of that VAD */
1832                 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1833             }
1834             else
1835             {
1836                 /* Get the next VAD */
1837                 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1838                 if (Vad)
1839                 {
1840                     /* Region size is the free space till the start of that VAD */
1841                     MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1842                 }
1843                 else
1844                 {
1845                     /* Maximum possible region size with that base address */
1846                     MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1847                 }
1848             }
1849         }
1850         else
1851         {
1852             /* Maximum possible region size with that base address */
1853             MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1854         }
1855 
1856         /* Unlock the address space of the process */
1857         MmUnlockAddressSpace(&TargetProcess->Vm);
1858 
1859         /* Check if we were attached */
1860         if (ProcessHandle != NtCurrentProcess())
1861         {
1862             /* Detach and derefernece the process */
1863             KeUnstackDetachProcess(&ApcState);
1864             ObDereferenceObject(TargetProcess);
1865         }
1866 
1867         /* Build the rest of the initial information block */
1868         MemoryInfo.BaseAddress = Address;
1869         MemoryInfo.AllocationBase = NULL;
1870         MemoryInfo.AllocationProtect = 0;
1871         MemoryInfo.State = MEM_FREE;
1872         MemoryInfo.Protect = PAGE_NOACCESS;
1873         MemoryInfo.Type = 0;
1874 
1875         /* Return the data, NtQueryInformation already probed it*/
1876         if (PreviousMode != KernelMode)
1877         {
1878             _SEH2_TRY
1879             {
1880                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1881                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1882             }
1883              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1884             {
1885                 Status = _SEH2_GetExceptionCode();
1886             }
1887             _SEH2_END;
1888         }
1889         else
1890         {
1891             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1892             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1893         }
1894 
1895         return Status;
1896     }
1897 
1898     /* Set the correct memory type based on what kind of VAD this is */
1899     if ((Vad->u.VadFlags.PrivateMemory) ||
1900         (Vad->u.VadFlags.VadType == VadRotatePhysical))
1901     {
1902         MemoryInfo.Type = MEM_PRIVATE;
1903     }
1904     else if (Vad->u.VadFlags.VadType == VadImageMap)
1905     {
1906         MemoryInfo.Type = MEM_IMAGE;
1907     }
1908     else
1909     {
1910         MemoryInfo.Type = MEM_MAPPED;
1911     }
1912 
1913     /* Find the memory area the specified address belongs to */
1914     MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1915     ASSERT(MemoryArea != NULL);
1916 
1917     /* Determine information dependent on the memory area type */
1918     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1919     {
1920         Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1921         if (!NT_SUCCESS(Status))
1922         {
1923             DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1924                     MemoryArea, MA_GetStartingAddress(MemoryArea), MA_GetEndingAddress(MemoryArea), BaseAddress);
1925             ASSERT(NT_SUCCESS(Status));
1926         }
1927     }
1928     else
1929     {
1930         /* Build the initial information block */
1931         Address = PAGE_ALIGN(BaseAddress);
1932         MemoryInfo.BaseAddress = Address;
1933         MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1934         MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1935         MemoryInfo.Type = MEM_PRIVATE;
1936 
1937         /* Acquire the working set lock (shared is enough) */
1938         MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1939 
1940         /* Find the largest chunk of memory which has the same state and protection mask */
1941         MemoryInfo.State = MiQueryAddressState(Address,
1942                                                Vad,
1943                                                TargetProcess,
1944                                                &MemoryInfo.Protect,
1945                                                &NextAddress);
1946         Address = NextAddress;
1947         while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1948         {
1949             /* Keep going unless the state or protection mask changed */
1950             NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1951             if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1952             Address = NextAddress;
1953         }
1954 
1955         /* Release the working set lock */
1956         MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1957 
1958         /* Check if we went outside of the VAD */
1959          if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1960          {
1961             /* Set the end of the VAD as the end address */
1962             Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1963          }
1964 
1965         /* Now that we know the last VA address, calculate the region size */
1966         MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1967     }
1968 
1969     /* Unlock the address space of the process */
1970     MmUnlockAddressSpace(&TargetProcess->Vm);
1971 
1972     /* Check if we were attached */
1973     if (ProcessHandle != NtCurrentProcess())
1974     {
1975         /* Detach and derefernece the process */
1976         KeUnstackDetachProcess(&ApcState);
1977         ObDereferenceObject(TargetProcess);
1978     }
1979 
1980     /* Return the data, NtQueryInformation already probed it */
1981     if (PreviousMode != KernelMode)
1982     {
1983         _SEH2_TRY
1984         {
1985             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1986             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1987         }
1988          _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1989         {
1990             Status = _SEH2_GetExceptionCode();
1991         }
1992         _SEH2_END;
1993     }
1994     else
1995     {
1996         *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1997         if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1998     }
1999 
2000     /* All went well */
2001     DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2002             "State: %lx Type: %lx Size: %lx\n",
2003             MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2004             MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2005             MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2006 
2007     return Status;
2008 }
2009 
2010 BOOLEAN
2011 NTAPI
2012 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
2013                          IN ULONG_PTR EndingAddress,
2014                          IN PMMVAD Vad,
2015                          IN PEPROCESS Process)
2016 {
2017     PMMPTE PointerPte, LastPte;
2018     PMMPDE PointerPde;
2019     BOOLEAN OnBoundary = TRUE;
2020     PAGED_CODE();
2021 
2022     /* Get the PDE and PTE addresses */
2023     PointerPde = MiAddressToPde(StartingAddress);
2024     PointerPte = MiAddressToPte(StartingAddress);
2025     LastPte = MiAddressToPte(EndingAddress);
2026 
2027     /* Loop all the PTEs */
2028     while (PointerPte <= LastPte)
2029     {
2030         /* Check if we've hit an new PDE boundary */
2031         if (OnBoundary)
2032         {
2033             /* Is this PDE demand zero? */
2034             PointerPde = MiPteToPde(PointerPte);
2035             if (PointerPde->u.Long != 0)
2036             {
2037                 /* It isn't -- is it valid? */
2038                 if (PointerPde->u.Hard.Valid == 0)
2039                 {
2040                     /* Nope, fault it in */
2041                     MiMakeSystemAddressValid(PointerPte, Process);
2042                 }
2043             }
2044             else
2045             {
2046                 /* The PTE was already valid, so move to the next one */
2047                 PointerPde++;
2048                 PointerPte = MiPdeToPte(PointerPde);
2049 
2050                 /* Is the entire VAD committed? If not, fail */
2051                 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2052 
2053                 /* New loop iteration with our new, on-boundary PTE. */
2054                 continue;
2055             }
2056         }
2057 
2058         /* Is the PTE demand zero? */
2059         if (PointerPte->u.Long == 0)
2060         {
2061             /* Is the entire VAD committed? If not, fail */
2062             if (!Vad->u.VadFlags.MemCommit) return FALSE;
2063         }
2064         else
2065         {
2066             /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2067             if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
2068                 (PointerPte->u.Hard.Valid == 0) &&
2069                 ((PointerPte->u.Soft.Prototype == 0) ||
2070                  (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
2071             {
2072                 /* Then part of the range is decommitted, so fail */
2073                 return FALSE;
2074             }
2075         }
2076 
2077         /* Move to the next PTE */
2078         PointerPte++;
2079         OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
2080     }
2081 
2082     /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2083     return TRUE;
2084 }
2085 
2086 NTSTATUS
2087 NTAPI
2088 MiRosProtectVirtualMemory(IN PEPROCESS Process,
2089                           IN OUT PVOID *BaseAddress,
2090                           IN OUT PSIZE_T NumberOfBytesToProtect,
2091                           IN ULONG NewAccessProtection,
2092                           OUT PULONG OldAccessProtection OPTIONAL)
2093 {
2094     PMEMORY_AREA MemoryArea;
2095     PMMSUPPORT AddressSpace;
2096     ULONG OldAccessProtection_;
2097     NTSTATUS Status;
2098 
2099     *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
2100     *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
2101 
2102     AddressSpace = &Process->Vm;
2103     MmLockAddressSpace(AddressSpace);
2104     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
2105     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
2106     {
2107         MmUnlockAddressSpace(AddressSpace);
2108         return STATUS_UNSUCCESSFUL;
2109     }
2110 
2111     if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
2112 
2113     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
2114     Status = MmProtectSectionView(AddressSpace,
2115                                   MemoryArea,
2116                                   *BaseAddress,
2117                                   *NumberOfBytesToProtect,
2118                                   NewAccessProtection,
2119                                   OldAccessProtection);
2120 
2121     MmUnlockAddressSpace(AddressSpace);
2122 
2123     return Status;
2124 }
2125 
2126 NTSTATUS
2127 NTAPI
2128 MiProtectVirtualMemory(IN PEPROCESS Process,
2129                        IN OUT PVOID *BaseAddress,
2130                        IN OUT PSIZE_T NumberOfBytesToProtect,
2131                        IN ULONG NewAccessProtection,
2132                        OUT PULONG OldAccessProtection OPTIONAL)
2133 {
2134     PMEMORY_AREA MemoryArea;
2135     PMMVAD Vad;
2136     PMMSUPPORT AddressSpace;
2137     ULONG_PTR StartingAddress, EndingAddress;
2138     PMMPTE PointerPte, LastPte;
2139     PMMPDE PointerPde;
2140     MMPTE PteContents;
2141     PMMPFN Pfn1;
2142     ULONG ProtectionMask, OldProtect;
2143     BOOLEAN Committed;
2144     NTSTATUS Status = STATUS_SUCCESS;
2145     PETHREAD Thread = PsGetCurrentThread();
2146     TABLE_SEARCH_RESULT Result;
2147 
2148     /* Calculate base address for the VAD */
2149     StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
2150     EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
2151 
2152     /* Calculate the protection mask and make sure it's valid */
2153     ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
2154     if (ProtectionMask == MM_INVALID_PROTECTION)
2155     {
2156         DPRINT1("Invalid protection mask\n");
2157         return STATUS_INVALID_PAGE_PROTECTION;
2158     }
2159 
2160     /* Check for ROS specific memory area */
2161     MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
2162     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2163     {
2164         /* Evil hack */
2165         return MiRosProtectVirtualMemory(Process,
2166                                          BaseAddress,
2167                                          NumberOfBytesToProtect,
2168                                          NewAccessProtection,
2169                                          OldAccessProtection);
2170     }
2171 
2172     /* Lock the address space and make sure the process isn't already dead */
2173     AddressSpace = MmGetCurrentAddressSpace();
2174     MmLockAddressSpace(AddressSpace);
2175     if (Process->VmDeleted)
2176     {
2177         DPRINT1("Process is dying\n");
2178         Status = STATUS_PROCESS_IS_TERMINATING;
2179         goto FailPath;
2180     }
2181 
2182     /* Get the VAD for this address range, and make sure it exists */
2183     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2184                                        EndingAddress >> PAGE_SHIFT,
2185                                        &Process->VadRoot,
2186                                        (PMMADDRESS_NODE*)&Vad);
2187     if (Result != TableFoundNode)
2188     {
2189         DPRINT("Could not find a VAD for this allocation\n");
2190         Status = STATUS_CONFLICTING_ADDRESSES;
2191         goto FailPath;
2192     }
2193 
2194     /* Make sure the address is within this VAD's boundaries */
2195     if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2196         (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2197     {
2198         Status = STATUS_CONFLICTING_ADDRESSES;
2199         goto FailPath;
2200     }
2201 
2202     /* These kinds of VADs are not supported atm  */
2203     if ((Vad->u.VadFlags.VadType == VadAwe) ||
2204         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
2205         (Vad->u.VadFlags.VadType == VadLargePages))
2206     {
2207         DPRINT1("Illegal VAD for attempting to set protection\n");
2208         Status = STATUS_CONFLICTING_ADDRESSES;
2209         goto FailPath;
2210     }
2211 
2212     /* Check for a VAD whose protection can't be changed */
2213     if (Vad->u.VadFlags.NoChange == 1)
2214     {
2215         DPRINT1("Trying to change protection of a NoChange VAD\n");
2216         Status = STATUS_INVALID_PAGE_PROTECTION;
2217         goto FailPath;
2218     }
2219 
2220     /* Is this section, or private memory? */
2221     if (Vad->u.VadFlags.PrivateMemory == 0)
2222     {
2223         /* Not yet supported */
2224         if (Vad->u.VadFlags.VadType == VadLargePageSection)
2225         {
2226             DPRINT1("Illegal VAD for attempting to set protection\n");
2227             Status = STATUS_CONFLICTING_ADDRESSES;
2228             goto FailPath;
2229         }
2230 
2231         /* Rotate VADs are not yet supported */
2232         if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2233         {
2234             DPRINT1("Illegal VAD for attempting to set protection\n");
2235             Status = STATUS_CONFLICTING_ADDRESSES;
2236             goto FailPath;
2237         }
2238 
2239         /* Not valid on section files */
2240         if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2241         {
2242             /* Fail */
2243             DPRINT1("Invalid protection flags for section\n");
2244             Status = STATUS_INVALID_PARAMETER_4;
2245             goto FailPath;
2246         }
2247 
2248         /* Check if data or page file mapping protection PTE is compatible */
2249         if (!Vad->ControlArea->u.Flags.Image)
2250         {
2251             /* Not yet */
2252             DPRINT1("Fixme: Not checking for valid protection\n");
2253         }
2254 
2255         /* This is a section, and this is not yet supported */
2256         DPRINT1("Section protection not yet supported\n");
2257         OldProtect = 0;
2258     }
2259     else
2260     {
2261         /* Private memory, check protection flags */
2262         if ((NewAccessProtection & PAGE_WRITECOPY) ||
2263             (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2264         {
2265             DPRINT1("Invalid protection flags for private memory\n");
2266             Status = STATUS_INVALID_PARAMETER_4;
2267             goto FailPath;
2268         }
2269 
2270         /* Lock the working set */
2271         MiLockProcessWorkingSetUnsafe(Process, Thread);
2272 
2273         /* Check if all pages in this range are committed */
2274         Committed = MiIsEntireRangeCommitted(StartingAddress,
2275                                              EndingAddress,
2276                                              Vad,
2277                                              Process);
2278         if (!Committed)
2279         {
2280             /* Fail */
2281             DPRINT1("The entire range is not committed\n");
2282             Status = STATUS_NOT_COMMITTED;
2283             MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2284             goto FailPath;
2285         }
2286 
2287         /* Compute starting and ending PTE and PDE addresses */
2288         PointerPde = MiAddressToPde(StartingAddress);
2289         PointerPte = MiAddressToPte(StartingAddress);
2290         LastPte = MiAddressToPte(EndingAddress);
2291 
2292         /* Make this PDE valid */
2293         MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2294 
2295         /* Save protection of the first page */
2296         if (PointerPte->u.Long != 0)
2297         {
2298             /* Capture the page protection and make the PDE valid */
2299             OldProtect = MiGetPageProtection(PointerPte);
2300             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2301         }
2302         else
2303         {
2304             /* Grab the old protection from the VAD itself */
2305             OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2306         }
2307 
2308         /* Loop all the PTEs now */
2309         while (PointerPte <= LastPte)
2310         {
2311             /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2312             if (MiIsPteOnPdeBoundary(PointerPte))
2313             {
2314                 PointerPde = MiPteToPde(PointerPte);
2315                 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2316             }
2317 
2318             /* Capture the PTE and check if it was empty */
2319             PteContents = *PointerPte;
2320             if (PteContents.u.Long == 0)
2321             {
2322                 /* This used to be a zero PTE and it no longer is, so we must add a
2323                    reference to the pagetable. */
2324                 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2325             }
2326 
2327             /* Check what kind of PTE we are dealing with */
2328             if (PteContents.u.Hard.Valid == 1)
2329             {
2330                 /* Get the PFN entry */
2331                 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2332 
2333                 /* We don't support this yet */
2334                 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2335 
2336                 /* Check if the page should not be accessible at all */
2337                 if ((NewAccessProtection & PAGE_NOACCESS) ||
2338                     (NewAccessProtection & PAGE_GUARD))
2339                 {
2340                     KIRQL OldIrql = MiAcquirePfnLock();
2341 
2342                     /* Mark the PTE as transition and change its protection */
2343                     PteContents.u.Hard.Valid = 0;
2344                     PteContents.u.Soft.Transition = 1;
2345                     PteContents.u.Trans.Protection = ProtectionMask;
2346                     /* Decrease PFN share count and write the PTE */
2347                     MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2348                     // FIXME: remove the page from the WS
2349                     MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2350 #ifdef CONFIG_SMP
2351                     // FIXME: Should invalidate entry in every CPU TLB
2352                     ASSERT(FALSE);
2353 #endif
2354                     KeInvalidateTlbEntry(MiPteToAddress(PointerPte));
2355 
2356                     /* We are done for this PTE */
2357                     MiReleasePfnLock(OldIrql);
2358                 }
2359                 else
2360                 {
2361                     /* Write the protection mask and write it with a TLB flush */
2362                     Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2363                     MiFlushTbAndCapture(Vad,
2364                                         PointerPte,
2365                                         ProtectionMask,
2366                                         Pfn1,
2367                                         TRUE);
2368                 }
2369             }
2370             else
2371             {
2372                 /* We don't support these cases yet */
2373                 ASSERT(PteContents.u.Soft.Prototype == 0);
2374                 //ASSERT(PteContents.u.Soft.Transition == 0);
2375 
2376                 /* The PTE is already demand-zero, just update the protection mask */
2377                 PteContents.u.Soft.Protection = ProtectionMask;
2378                 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2379                 ASSERT(PointerPte->u.Long != 0);
2380             }
2381 
2382             /* Move to the next PTE */
2383             PointerPte++;
2384         }
2385 
2386         /* Unlock the working set */
2387         MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2388     }
2389 
2390     /* Unlock the address space */
2391     MmUnlockAddressSpace(AddressSpace);
2392 
2393     /* Return parameters and success */
2394     *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2395     *BaseAddress = (PVOID)StartingAddress;
2396     *OldAccessProtection = OldProtect;
2397     return STATUS_SUCCESS;
2398 
2399 FailPath:
2400     /* Unlock the address space and return the failure code */
2401     MmUnlockAddressSpace(AddressSpace);
2402     return Status;
2403 }
2404 
2405 VOID
2406 NTAPI
2407 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde,
2408                            IN PEPROCESS TargetProcess,
2409                            IN KIRQL OldIrql)
2410 {
2411    PMMPTE PointerPte, PointerPpe, PointerPxe;
2412 
2413    //
2414    // Sanity checks. The latter is because we only use this function with the
2415    // PFN lock not held, so it may go away in the future.
2416    //
2417    ASSERT(KeAreAllApcsDisabled() == TRUE);
2418    ASSERT(OldIrql == MM_NOIRQL);
2419 
2420    //
2421    // Also get the PPE and PXE. This is okay not to #ifdef because they will
2422    // return the same address as the PDE on 2-level page table systems.
2423    //
2424    // If everything is already valid, there is nothing to do.
2425    //
2426    PointerPpe = MiAddressToPte(PointerPde);
2427    PointerPxe = MiAddressToPde(PointerPde);
2428    if ((PointerPxe->u.Hard.Valid) &&
2429        (PointerPpe->u.Hard.Valid) &&
2430        (PointerPde->u.Hard.Valid))
2431    {
2432        return;
2433    }
2434 
2435    //
2436    // At least something is invalid, so begin by getting the PTE for the PDE itself
2437    // and then lookup each additional level. We must do it in this precise order
2438    // because the pagfault.c code (as well as in Windows) depends that the next
2439    // level up (higher) must be valid when faulting a lower level
2440    //
2441    PointerPte = MiPteToAddress(PointerPde);
2442    do
2443    {
2444        //
2445        // Make sure APCs continued to be disabled
2446        //
2447        ASSERT(KeAreAllApcsDisabled() == TRUE);
2448 
2449        //
2450        // First, make the PXE valid if needed
2451        //
2452        if (!PointerPxe->u.Hard.Valid)
2453        {
2454            MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2455            ASSERT(PointerPxe->u.Hard.Valid == 1);
2456        }
2457 
2458        //
2459        // Next, the PPE
2460        //
2461        if (!PointerPpe->u.Hard.Valid)
2462        {
2463            MiMakeSystemAddressValid(PointerPde, TargetProcess);
2464            ASSERT(PointerPpe->u.Hard.Valid == 1);
2465        }
2466 
2467        //
2468        // And finally, make the PDE itself valid.
2469        //
2470        MiMakeSystemAddressValid(PointerPte, TargetProcess);
2471 
2472        //
2473        // This should've worked the first time so the loop is really just for
2474        // show -- ASSERT that we're actually NOT going to be looping.
2475        //
2476        ASSERT(PointerPxe->u.Hard.Valid == 1);
2477        ASSERT(PointerPpe->u.Hard.Valid == 1);
2478        ASSERT(PointerPde->u.Hard.Valid == 1);
2479    } while (!(PointerPxe->u.Hard.Valid) ||
2480             !(PointerPpe->u.Hard.Valid) ||
2481             !(PointerPde->u.Hard.Valid));
2482 }
2483 
2484 VOID
2485 NTAPI
2486 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2487                       IN ULONG Count)
2488 {
2489     KIRQL OldIrql;
2490     ULONG i;
2491     MMPTE TempPte;
2492     PFN_NUMBER PageFrameIndex;
2493     PMMPFN Pfn1, Pfn2;
2494 
2495     //
2496     // Acquire the PFN lock and loop all the PTEs in the list
2497     //
2498     OldIrql = MiAcquirePfnLock();
2499     for (i = 0; i != Count; i++)
2500     {
2501         //
2502         // The PTE must currently be valid
2503         //
2504         TempPte = *ValidPteList[i];
2505         ASSERT(TempPte.u.Hard.Valid == 1);
2506 
2507         //
2508         // Get the PFN entry for the page itself, and then for its page table
2509         //
2510         PageFrameIndex = PFN_FROM_PTE(&TempPte);
2511         Pfn1 = MiGetPfnEntry(PageFrameIndex);
2512         Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2513 
2514         //
2515         // Decrement the share count on the page table, and then on the page
2516         // itself
2517         //
2518         MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2519         MI_SET_PFN_DELETED(Pfn1);
2520         MiDecrementShareCount(Pfn1, PageFrameIndex);
2521 
2522         //
2523         // Make the page decommitted
2524         //
2525         MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2526     }
2527 
2528     //
2529     // All the PTEs have been dereferenced and made invalid, flush the TLB now
2530     // and then release the PFN lock
2531     //
2532     KeFlushCurrentTb();
2533     MiReleasePfnLock(OldIrql);
2534 }
2535 
2536 ULONG
2537 NTAPI
2538 MiDecommitPages(IN PVOID StartingAddress,
2539                 IN PMMPTE EndingPte,
2540                 IN PEPROCESS Process,
2541                 IN PMMVAD Vad)
2542 {
2543     PMMPTE PointerPte, CommitPte = NULL;
2544     PMMPDE PointerPde;
2545     ULONG CommitReduction = 0;
2546     PMMPTE ValidPteList[256];
2547     ULONG PteCount = 0;
2548     PMMPFN Pfn1;
2549     MMPTE PteContents;
2550     PETHREAD CurrentThread = PsGetCurrentThread();
2551 
2552     //
2553     // Get the PTE and PTE for the address, and lock the working set
2554     // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2555     // commited range ends so that we can do the right accounting.
2556     //
2557     PointerPde = MiAddressToPde(StartingAddress);
2558     PointerPte = MiAddressToPte(StartingAddress);
2559     if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2560     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2561 
2562     //
2563     // Make the PDE valid, and now loop through each page's worth of data
2564     //
2565     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2566     while (PointerPte <= EndingPte)
2567     {
2568         //
2569         // Check if we've crossed a PDE boundary
2570         //
2571         if (MiIsPteOnPdeBoundary(PointerPte))
2572         {
2573             //
2574             // Get the new PDE and flush the valid PTEs we had built up until
2575             // now. This helps reduce the amount of TLB flushing we have to do.
2576             // Note that Windows does a much better job using timestamps and
2577             // such, and does not flush the entire TLB all the time, but right
2578             // now we have bigger problems to worry about than TLB flushing.
2579             //
2580             PointerPde = MiAddressToPde(StartingAddress);
2581             if (PteCount)
2582             {
2583                 MiProcessValidPteList(ValidPteList, PteCount);
2584                 PteCount = 0;
2585             }
2586 
2587             //
2588             // Make this PDE valid
2589             //
2590             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2591         }
2592 
2593         //
2594         // Read this PTE. It might be active or still demand-zero.
2595         //
2596         PteContents = *PointerPte;
2597         if (PteContents.u.Long)
2598         {
2599             //
2600             // The PTE is active. It might be valid and in a working set, or
2601             // it might be a prototype PTE or paged out or even in transition.
2602             //
2603             if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2604             {
2605                 //
2606                 // It's already decommited, so there's nothing for us to do here
2607                 //
2608                 CommitReduction++;
2609             }
2610             else
2611             {
2612                 //
2613                 // Remove it from the counters, and check if it was valid or not
2614                 //
2615                 //Process->NumberOfPrivatePages--;
2616                 if (PteContents.u.Hard.Valid)
2617                 {
2618                     //
2619                     // It's valid. At this point make sure that it is not a ROS
2620                     // PFN. Also, we don't support ProtoPTEs in this code path.
2621                     //
2622                     Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2623                     ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2624                     ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2625 
2626                     //
2627                     // Flush any pending PTEs that we had not yet flushed, if our
2628                     // list has gotten too big, then add this PTE to the flush list.
2629                     //
2630                     if (PteCount == 256)
2631                     {
2632                         MiProcessValidPteList(ValidPteList, PteCount);
2633                         PteCount = 0;
2634                     }
2635                     ValidPteList[PteCount++] = PointerPte;
2636                 }
2637                 else
2638                 {
2639                     //
2640                     // We do not support any of these other scenarios at the moment
2641                     //
2642                     ASSERT(PteContents.u.Soft.Prototype == 0);
2643                     ASSERT(PteContents.u.Soft.Transition == 0);
2644                     ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2645 
2646                     //
2647                     // So the only other possibility is that it is still a demand
2648                     // zero PTE, in which case we undo the accounting we did
2649                     // earlier and simply make the page decommitted.
2650                     //
2651                     //Process->NumberOfPrivatePages++;
2652                     MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2653                 }
2654             }
2655         }
2656         else
2657         {
2658             //
2659             // This used to be a zero PTE and it no longer is, so we must add a
2660             // reference to the pagetable.
2661             //
2662             MiIncrementPageTableReferences(StartingAddress);
2663 
2664             //
2665             // Next, we account for decommitted PTEs and make the PTE as such
2666             //
2667             if (PointerPte > CommitPte) CommitReduction++;
2668             MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2669         }
2670 
2671         //
2672         // Move to the next PTE and the next address
2673         //
2674         PointerPte++;
2675         StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2676     }
2677 
2678     //
2679     // Flush any dangling PTEs from the loop in the last page table, and then
2680     // release the working set and return the commit reduction accounting.
2681     //
2682     if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2683     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2684     return CommitReduction;
2685 }
2686 
2687 /* PUBLIC FUNCTIONS ***********************************************************/
2688 
2689 /*
2690  * @unimplemented
2691  */
2692 PVOID
2693 NTAPI
2694 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2695 {
2696     UNIMPLEMENTED;
2697     return 0;
2698 }
2699 
2700 /*
2701  * @unimplemented
2702  */
2703 PVOID
2704 NTAPI
2705 MmSecureVirtualMemory(IN PVOID Address,
2706                       IN SIZE_T Length,
2707                       IN ULONG Mode)
2708 {
2709     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2710     return Address;
2711 }
2712 
2713 /*
2714  * @unimplemented
2715  */
2716 VOID
2717 NTAPI
2718 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2719 {
2720     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2721 }
2722 
2723 /* SYSTEM CALLS ***************************************************************/
2724 
2725 NTSTATUS
2726 NTAPI
2727 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2728                     IN PVOID BaseAddress,
2729                     OUT PVOID Buffer,
2730                     IN SIZE_T NumberOfBytesToRead,
2731                     OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2732 {
2733     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2734     PEPROCESS Process;
2735     NTSTATUS Status = STATUS_SUCCESS;
2736     SIZE_T BytesRead = 0;
2737     PAGED_CODE();
2738 
2739     //
2740     // Check if we came from user mode
2741     //
2742     if (PreviousMode != KernelMode)
2743     {
2744         //
2745         // Validate the read addresses
2746         //
2747         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2748             (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2749             (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2750             (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2751         {
2752             //
2753             // Don't allow to write into kernel space
2754             //
2755             return STATUS_ACCESS_VIOLATION;
2756         }
2757 
2758         //
2759         // Enter SEH for probe
2760         //
2761         _SEH2_TRY
2762         {
2763             //
2764             // Probe the output value
2765             //
2766             if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2767         }
2768         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2769         {
2770             //
2771             // Get exception code
2772             //
2773             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2774         }
2775         _SEH2_END;
2776     }
2777 
2778     //
2779     // Don't do zero-byte transfers
2780     //
2781     if (NumberOfBytesToRead)
2782     {
2783         //
2784         // Reference the process
2785         //
2786         Status = ObReferenceObjectByHandle(ProcessHandle,
2787                                            PROCESS_VM_READ,
2788                                            PsProcessType,
2789                                            PreviousMode,
2790                                            (PVOID*)(&Process),
2791                                            NULL);
2792         if (NT_SUCCESS(Status))
2793         {
2794             //
2795             // Do the copy
2796             //
2797             Status = MmCopyVirtualMemory(Process,
2798                                          BaseAddress,
2799                                          PsGetCurrentProcess(),
2800                                          Buffer,
2801                                          NumberOfBytesToRead,
2802                                          PreviousMode,
2803                                          &BytesRead);
2804 
2805             //
2806             // Dereference the process
2807             //
2808             ObDereferenceObject(Process);
2809         }
2810     }
2811 
2812     //
2813     // Check if the caller sent this parameter
2814     //
2815     if (NumberOfBytesRead)
2816     {
2817         //
2818         // Enter SEH to guard write
2819         //
2820         _SEH2_TRY
2821         {
2822             //
2823             // Return the number of bytes read
2824             //
2825             *NumberOfBytesRead = BytesRead;
2826         }
2827         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2828         {
2829         }
2830         _SEH2_END;
2831     }
2832 
2833     //
2834     // Return status
2835     //
2836     return Status;
2837 }
2838 
2839 NTSTATUS
2840 NTAPI
2841 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2842                      IN PVOID BaseAddress,
2843                      IN PVOID Buffer,
2844                      IN SIZE_T NumberOfBytesToWrite,
2845                      OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2846 {
2847     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2848     PEPROCESS Process;
2849     NTSTATUS Status = STATUS_SUCCESS;
2850     SIZE_T BytesWritten = 0;
2851     PAGED_CODE();
2852 
2853     //
2854     // Check if we came from user mode
2855     //
2856     if (PreviousMode != KernelMode)
2857     {
2858         //
2859         // Validate the read addresses
2860         //
2861         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2862             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2863             (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2864             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2865         {
2866             //
2867             // Don't allow to write into kernel space
2868             //
2869             return STATUS_ACCESS_VIOLATION;
2870         }
2871 
2872         //
2873         // Enter SEH for probe
2874         //
2875         _SEH2_TRY
2876         {
2877             //
2878             // Probe the output value
2879             //
2880             if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2881         }
2882         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2883         {
2884             //
2885             // Get exception code
2886             //
2887             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2888         }
2889         _SEH2_END;
2890     }
2891 
2892     //
2893     // Don't do zero-byte transfers
2894     //
2895     if (NumberOfBytesToWrite)
2896     {
2897         //
2898         // Reference the process
2899         //
2900         Status = ObReferenceObjectByHandle(ProcessHandle,
2901                                            PROCESS_VM_WRITE,
2902                                            PsProcessType,
2903                                            PreviousMode,
2904                                            (PVOID*)&Process,
2905                                            NULL);
2906         if (NT_SUCCESS(Status))
2907         {
2908             //
2909             // Do the copy
2910             //
2911             Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2912                                          Buffer,
2913                                          Process,
2914                                          BaseAddress,
2915                                          NumberOfBytesToWrite,
2916                                          PreviousMode,
2917                                          &BytesWritten);
2918 
2919             //
2920             // Dereference the process
2921             //
2922             ObDereferenceObject(Process);
2923         }
2924     }
2925 
2926     //
2927     // Check if the caller sent this parameter
2928     //
2929     if (NumberOfBytesWritten)
2930     {
2931         //
2932         // Enter SEH to guard write
2933         //
2934         _SEH2_TRY
2935         {
2936             //
2937             // Return the number of bytes written
2938             //
2939             *NumberOfBytesWritten = BytesWritten;
2940         }
2941         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2942         {
2943         }
2944         _SEH2_END;
2945     }
2946 
2947     //
2948     // Return status
2949     //
2950     return Status;
2951 }
2952 
2953 NTSTATUS
2954 NTAPI
2955 NtFlushInstructionCache(_In_ HANDLE ProcessHandle,
2956                         _In_opt_ PVOID BaseAddress,
2957                         _In_ SIZE_T FlushSize)
2958 {
2959     KAPC_STATE ApcState;
2960     PKPROCESS Process;
2961     NTSTATUS Status;
2962     PAGED_CODE();
2963 
2964     /* Is a base address given? */
2965     if (BaseAddress != NULL)
2966     {
2967         /* If the requested size is 0, there is nothing to do */
2968         if (FlushSize == 0)
2969         {
2970             return STATUS_SUCCESS;
2971         }
2972 
2973         /* Is this a user mode call? */
2974         if (ExGetPreviousMode() != KernelMode)
2975         {
2976             /* Make sure the base address is in user space */
2977             if (BaseAddress > MmHighestUserAddress)
2978             {
2979                 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress);
2980                 return STATUS_ACCESS_VIOLATION;
2981             }
2982         }
2983     }
2984 
2985     /* Is another process requested? */
2986     if (ProcessHandle != NtCurrentProcess())
2987     {
2988         /* Reference the process */
2989         Status = ObReferenceObjectByHandle(ProcessHandle,
2990                                            PROCESS_VM_WRITE,
2991                                            PsProcessType,
2992                                            ExGetPreviousMode(),
2993                                            (PVOID*)&Process,
2994                                            NULL);
2995         if (!NT_SUCCESS(Status))
2996         {
2997             DPRINT1("Failed to reference the process %p\n", ProcessHandle);
2998             return Status;
2999         }
3000 
3001         /* Attach to the process */
3002         KeStackAttachProcess(Process, &ApcState);
3003     }
3004 
3005     /* Forward to Ke */
3006     KeSweepICache(BaseAddress, FlushSize);
3007 
3008     /* Check if we attached */
3009     if (ProcessHandle != NtCurrentProcess())
3010     {
3011         /* Detach from the process and dereference it */
3012         KeUnstackDetachProcess(&ApcState);
3013         ObDereferenceObject(Process);
3014     }
3015 
3016     /* All done, return to caller */
3017     return STATUS_SUCCESS;
3018 }
3019 
3020 NTSTATUS
3021 NTAPI
3022 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
3023                        IN OUT PVOID *UnsafeBaseAddress,
3024                        IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
3025                        IN ULONG NewAccessProtection,
3026                        OUT PULONG UnsafeOldAccessProtection)
3027 {
3028     PEPROCESS Process;
3029     ULONG OldAccessProtection;
3030     ULONG Protection;
3031     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3032     PVOID BaseAddress = NULL;
3033     SIZE_T NumberOfBytesToProtect = 0;
3034     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3035     NTSTATUS Status;
3036     BOOLEAN Attached = FALSE;
3037     KAPC_STATE ApcState;
3038     PAGED_CODE();
3039 
3040     //
3041     // Check for valid protection flags
3042     //
3043     Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
3044     if (Protection != PAGE_NOACCESS &&
3045         Protection != PAGE_READONLY &&
3046         Protection != PAGE_READWRITE &&
3047         Protection != PAGE_WRITECOPY &&
3048         Protection != PAGE_EXECUTE &&
3049         Protection != PAGE_EXECUTE_READ &&
3050         Protection != PAGE_EXECUTE_READWRITE &&
3051         Protection != PAGE_EXECUTE_WRITECOPY)
3052     {
3053         //
3054         // Fail
3055         //
3056         return STATUS_INVALID_PAGE_PROTECTION;
3057     }
3058 
3059     //
3060     // Check if we came from user mode
3061     //
3062     if (PreviousMode != KernelMode)
3063     {
3064         //
3065         // Enter SEH for probing
3066         //
3067         _SEH2_TRY
3068         {
3069             //
3070             // Validate all outputs
3071             //
3072             ProbeForWritePointer(UnsafeBaseAddress);
3073             ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
3074             ProbeForWriteUlong(UnsafeOldAccessProtection);
3075 
3076             //
3077             // Capture them
3078             //
3079             BaseAddress = *UnsafeBaseAddress;
3080             NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3081         }
3082         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3083         {
3084             //
3085             // Get exception code
3086             //
3087             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3088         }
3089         _SEH2_END;
3090     }
3091     else
3092     {
3093         //
3094         // Capture directly
3095         //
3096         BaseAddress = *UnsafeBaseAddress;
3097         NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3098     }
3099 
3100     //
3101     // Catch illegal base address
3102     //
3103     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3104 
3105     //
3106     // Catch illegal region size
3107     //
3108     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
3109     {
3110         //
3111         // Fail
3112         //
3113         return STATUS_INVALID_PARAMETER_3;
3114     }
3115 
3116     //
3117     // 0 is also illegal
3118     //
3119     if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
3120 
3121     //
3122     // Get a reference to the process
3123     //
3124     Status = ObReferenceObjectByHandle(ProcessHandle,
3125                                        PROCESS_VM_OPERATION,
3126                                        PsProcessType,
3127                                        PreviousMode,
3128                                        (PVOID*)(&Process),
3129                                        NULL);
3130     if (!NT_SUCCESS(Status)) return Status;
3131 
3132     //
3133     // Check if we should attach
3134     //
3135     if (CurrentProcess != Process)
3136     {
3137         //
3138         // Do it
3139         //
3140         KeStackAttachProcess(&Process->Pcb, &ApcState);
3141         Attached = TRUE;
3142     }
3143 
3144     //
3145     // Do the actual work
3146     //
3147     Status = MiProtectVirtualMemory(Process,
3148                                     &BaseAddress,
3149                                     &NumberOfBytesToProtect,
3150                                     NewAccessProtection,
3151                                     &OldAccessProtection);
3152 
3153     //
3154     // Detach if needed
3155     //
3156     if (Attached) KeUnstackDetachProcess(&ApcState);
3157 
3158     //
3159     // Release reference
3160     //
3161     ObDereferenceObject(Process);
3162 
3163     //
3164     // Enter SEH to return data
3165     //
3166     _SEH2_TRY
3167     {
3168         //
3169         // Return data to user
3170         //
3171         *UnsafeOldAccessProtection = OldAccessProtection;
3172         *UnsafeBaseAddress = BaseAddress;
3173         *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
3174     }
3175     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3176     {
3177     }
3178     _SEH2_END;
3179 
3180     //
3181     // Return status
3182     //
3183     return Status;
3184 }
3185 
3186 FORCEINLINE
3187 BOOLEAN
3188 MI_IS_LOCKED_VA(
3189     PMMPFN Pfn1,
3190     ULONG LockType)
3191 {
3192     // HACK until we have proper WSLIST support
3193     PMMWSLE Wsle = &Pfn1->Wsle;
3194 
3195     if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
3196         return TRUE;
3197     if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
3198         return TRUE;
3199 
3200     return FALSE;
3201 }
3202 
3203 FORCEINLINE
3204 VOID
3205 MI_LOCK_VA(
3206     PMMPFN Pfn1,
3207     ULONG LockType)
3208 {
3209     // HACK until we have proper WSLIST support
3210     PMMWSLE Wsle = &Pfn1->Wsle;
3211 
3212     if (!Wsle->u1.e1.LockedInWs &&
3213         !Wsle->u1.e1.LockedInMemory)
3214     {
3215         MiReferenceProbedPageAndBumpLockCount(Pfn1);
3216     }
3217 
3218     if (LockType & MAP_PROCESS)
3219         Wsle->u1.e1.LockedInWs = 1;
3220     if (LockType & MAP_SYSTEM)
3221         Wsle->u1.e1.LockedInMemory = 1;
3222 }
3223 
3224 FORCEINLINE
3225 VOID
3226 MI_UNLOCK_VA(
3227     PMMPFN Pfn1,
3228     ULONG LockType)
3229 {
3230     // HACK until we have proper WSLIST support
3231     PMMWSLE Wsle = &Pfn1->Wsle;
3232 
3233     if (LockType & MAP_PROCESS)
3234         Wsle->u1.e1.LockedInWs = 0;
3235     if (LockType & MAP_SYSTEM)
3236         Wsle->u1.e1.LockedInMemory = 0;
3237 
3238     if (!Wsle->u1.e1.LockedInWs &&
3239         !Wsle->u1.e1.LockedInMemory)
3240     {
3241         MiDereferencePfnAndDropLockCount(Pfn1);
3242     }
3243 }
3244 
3245 static
3246 NTSTATUS
3247 MiCheckVadsForLockOperation(
3248     _Inout_ PVOID *BaseAddress,
3249     _Inout_ PSIZE_T RegionSize,
3250     _Inout_ PVOID *EndAddress)
3251 
3252 {
3253     PMMVAD Vad;
3254     PVOID CurrentVa;
3255 
3256     /* Get the base address and align the start address */
3257     *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
3258     *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
3259     *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
3260 
3261     /* First loop and check all VADs */
3262     CurrentVa = *BaseAddress;
3263     while (CurrentVa < *EndAddress)
3264     {
3265         /* Get VAD */
3266         Vad = MiLocateAddress(CurrentVa);
3267         if (Vad == NULL)
3268         {
3269             /// FIXME: this might be a memory area for a section view...
3270             return STATUS_ACCESS_VIOLATION;
3271         }
3272 
3273         /* Check VAD type */
3274         if ((Vad->u.VadFlags.VadType != VadNone) &&
3275             (Vad->u.VadFlags.VadType != VadImageMap) &&
3276             (Vad->u.VadFlags.VadType != VadWriteWatch))
3277         {
3278             *EndAddress = CurrentVa;
3279             *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3280             return STATUS_INCOMPATIBLE_FILE_MAP;
3281         }
3282 
3283         CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3284     }
3285 
3286     *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3287     return STATUS_SUCCESS;
3288 }
3289 
3290 static
3291 NTSTATUS
3292 MiLockVirtualMemory(
3293     IN OUT PVOID *BaseAddress,
3294     IN OUT PSIZE_T RegionSize,
3295     IN ULONG MapType)
3296 {
3297     PEPROCESS CurrentProcess;
3298     PMMSUPPORT AddressSpace;
3299     PVOID CurrentVa, EndAddress;
3300     PMMPTE PointerPte, LastPte;
3301     PMMPDE PointerPde;
3302 #if (_MI_PAGING_LEVELS >= 3)
3303     PMMPDE PointerPpe;
3304 #endif
3305 #if (_MI_PAGING_LEVELS == 4)
3306     PMMPDE PointerPxe;
3307 #endif
3308     PMMPFN Pfn1;
3309     NTSTATUS Status, TempStatus;
3310 
3311     /* Lock the address space */
3312     AddressSpace = MmGetCurrentAddressSpace();
3313     MmLockAddressSpace(AddressSpace);
3314 
3315     /* Make sure we still have an address space */
3316     CurrentProcess = PsGetCurrentProcess();
3317     if (CurrentProcess->VmDeleted)
3318     {
3319         Status = STATUS_PROCESS_IS_TERMINATING;
3320         goto Cleanup;
3321     }
3322 
3323     /* Check the VADs in the requested range */
3324     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3325     if (!NT_SUCCESS(Status))
3326     {
3327         goto Cleanup;
3328     }
3329 
3330     /* Enter SEH for probing */
3331     _SEH2_TRY
3332     {
3333         /* Loop all pages and probe them */
3334         CurrentVa = *BaseAddress;
3335         while (CurrentVa < EndAddress)
3336         {
3337             (void)(*(volatile CHAR*)CurrentVa);
3338             CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3339         }
3340     }
3341     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3342     {
3343         Status = _SEH2_GetExceptionCode();
3344         goto Cleanup;
3345     }
3346     _SEH2_END;
3347 
3348     /* All pages were accessible, since we hold the address space lock, nothing
3349        can be de-committed. Assume success for now. */
3350     Status = STATUS_SUCCESS;
3351 
3352     /* Get the PTE and PDE */
3353     PointerPte = MiAddressToPte(*BaseAddress);
3354     PointerPde = MiAddressToPde(*BaseAddress);
3355 #if (_MI_PAGING_LEVELS >= 3)
3356     PointerPpe = MiAddressToPpe(*BaseAddress);
3357 #endif
3358 #if (_MI_PAGING_LEVELS == 4)
3359     PointerPxe = MiAddressToPxe(*BaseAddress);
3360 #endif
3361 
3362     /* Get the last PTE */
3363     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3364 
3365     /* Lock the process working set */
3366     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3367 
3368     /* Loop the pages */
3369     do
3370     {
3371         /* Check for a page that is not accessible */
3372         while (
3373 #if (_MI_PAGING_LEVELS == 4)
3374                (PointerPxe->u.Hard.Valid == 0) ||
3375 #endif
3376 #if (_MI_PAGING_LEVELS >= 3)
3377                (PointerPpe->u.Hard.Valid == 0) ||
3378 #endif
3379                (PointerPde->u.Hard.Valid == 0) ||
3380                (PointerPte->u.Hard.Valid == 0))
3381         {
3382             /* Release process working set */
3383             MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3384 
3385             /* Access the page */
3386             CurrentVa = MiPteToAddress(PointerPte);
3387 
3388             //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3389             TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL);
3390             if (!NT_SUCCESS(TempStatus))
3391             {
3392                 // This should only happen, when remote backing storage is not accessible
3393                 ASSERT(FALSE);
3394                 Status = TempStatus;
3395                 goto Cleanup;
3396             }
3397 
3398             /* Lock the process working set */
3399             MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3400         }
3401 
3402         /* Get the PFN */
3403         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3404         ASSERT(Pfn1 != NULL);
3405 
3406         /* Check the previous lock status */
3407         if (MI_IS_LOCKED_VA(Pfn1, MapType))
3408         {
3409             Status = STATUS_WAS_LOCKED;
3410         }
3411 
3412         /* Lock it */
3413         MI_LOCK_VA(Pfn1, MapType);
3414 
3415         /* Go to the next PTE */
3416         PointerPte++;
3417 
3418         /* Check if we're on a PDE boundary */
3419         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3420 #if (_MI_PAGING_LEVELS >= 3)
3421         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3422 #endif
3423 #if (_MI_PAGING_LEVELS == 4)
3424         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3425 #endif
3426     } while (PointerPte <= LastPte);
3427 
3428     /* Release process working set */
3429     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3430 
3431 Cleanup:
3432     /* Unlock address space */
3433     MmUnlockAddressSpace(AddressSpace);
3434 
3435     return Status;
3436 }
3437 
3438 NTSTATUS
3439 NTAPI
3440 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3441                     IN OUT PVOID *BaseAddress,
3442                     IN OUT PSIZE_T NumberOfBytesToLock,
3443                     IN ULONG MapType)
3444 {
3445     PEPROCESS Process;
3446     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3447     NTSTATUS Status;
3448     BOOLEAN Attached = FALSE;
3449     KAPC_STATE ApcState;
3450     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3451     PVOID CapturedBaseAddress;
3452     SIZE_T CapturedBytesToLock;
3453     PAGED_CODE();
3454 
3455     //
3456     // Validate flags
3457     //
3458     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3459     {
3460         //
3461         // Invalid set of flags
3462         //
3463         return STATUS_INVALID_PARAMETER;
3464     }
3465 
3466     //
3467     // At least one flag must be specified
3468     //
3469     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3470     {
3471         //
3472         // No flag given
3473         //
3474         return STATUS_INVALID_PARAMETER;
3475     }
3476 
3477     //
3478     // Enter SEH for probing
3479     //
3480     _SEH2_TRY
3481     {
3482         //
3483         // Validate output data
3484         //
3485         ProbeForWritePointer(BaseAddress);
3486         ProbeForWriteSize_t(NumberOfBytesToLock);
3487 
3488         //
3489         // Capture it
3490         //
3491         CapturedBaseAddress = *BaseAddress;
3492         CapturedBytesToLock = *NumberOfBytesToLock;
3493     }
3494     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3495     {
3496         //
3497         // Get exception code
3498         //
3499         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3500     }
3501     _SEH2_END;
3502 
3503     //
3504     // Catch illegal base address
3505     //
3506     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3507 
3508     //
3509     // Catch illegal region size
3510     //
3511     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3512     {
3513         //
3514         // Fail
3515         //
3516         return STATUS_INVALID_PARAMETER;
3517     }
3518 
3519     //
3520     // 0 is also illegal
3521     //
3522     if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3523 
3524     //
3525     // Get a reference to the process
3526     //
3527     Status = ObReferenceObjectByHandle(ProcessHandle,
3528                                        PROCESS_VM_OPERATION,
3529                                        PsProcessType,
3530                                        PreviousMode,
3531                                        (PVOID*)(&Process),
3532                                        NULL);
3533     if (!NT_SUCCESS(Status)) return Status;
3534 
3535     //
3536     // Check if this is is system-mapped
3537     //
3538     if (MapType & MAP_SYSTEM)
3539     {
3540         //
3541         // Check for required privilege
3542         //
3543         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3544         {
3545             //
3546             // Fail: Don't have it
3547             //
3548             ObDereferenceObject(Process);
3549             return STATUS_PRIVILEGE_NOT_HELD;
3550         }
3551     }
3552 
3553     //
3554     // Check if we should attach
3555     //
3556     if (CurrentProcess != Process)
3557     {
3558         //
3559         // Do it
3560         //
3561         KeStackAttachProcess(&Process->Pcb, &ApcState);
3562         Attached = TRUE;
3563     }
3564 
3565     //
3566     // Call the internal function
3567     //
3568     Status = MiLockVirtualMemory(&CapturedBaseAddress,
3569                                  &CapturedBytesToLock,
3570                                  MapType);
3571 
3572     //
3573     // Detach if needed
3574     //
3575     if (Attached) KeUnstackDetachProcess(&ApcState);
3576 
3577     //
3578     // Release reference
3579     //
3580     ObDereferenceObject(Process);
3581 
3582     //
3583     // Enter SEH to return data
3584     //
3585     _SEH2_TRY
3586     {
3587         //
3588         // Return data to user
3589         //
3590         *BaseAddress = CapturedBaseAddress;
3591         *NumberOfBytesToLock = CapturedBytesToLock;
3592     }
3593     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3594     {
3595         //
3596         // Get exception code
3597         //
3598         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3599     }
3600     _SEH2_END;
3601 
3602     //
3603     // Return status
3604     //
3605     return Status;
3606 }
3607 
3608 
3609 static
3610 NTSTATUS
3611 MiUnlockVirtualMemory(
3612     IN OUT PVOID *BaseAddress,
3613     IN OUT PSIZE_T RegionSize,
3614     IN ULONG MapType)
3615 {
3616     PEPROCESS CurrentProcess;
3617     PMMSUPPORT AddressSpace;
3618     PVOID EndAddress;
3619     PMMPTE PointerPte, LastPte;
3620     PMMPDE PointerPde;
3621 #if (_MI_PAGING_LEVELS >= 3)
3622     PMMPDE PointerPpe;
3623 #endif
3624 #if (_MI_PAGING_LEVELS == 4)
3625     PMMPDE PointerPxe;
3626 #endif
3627     PMMPFN Pfn1;
3628     NTSTATUS Status;
3629 
3630     /* Lock the address space */
3631     AddressSpace = MmGetCurrentAddressSpace();
3632     MmLockAddressSpace(AddressSpace);
3633 
3634     /* Make sure we still have an address space */
3635     CurrentProcess = PsGetCurrentProcess();
3636     if (CurrentProcess->VmDeleted)
3637     {
3638         Status = STATUS_PROCESS_IS_TERMINATING;
3639         goto Cleanup;
3640     }
3641 
3642     /* Check the VADs in the requested range */
3643     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3644 
3645     /* Note: only bail out, if we hit an area without a VAD. If we hit an
3646        incompatible VAD we continue, like Windows does */
3647     if (Status == STATUS_ACCESS_VIOLATION)
3648     {
3649         Status = STATUS_NOT_LOCKED;
3650         goto Cleanup;
3651     }
3652 
3653     /* Get the PTE and PDE */
3654     PointerPte = MiAddressToPte(*BaseAddress);
3655     PointerPde = MiAddressToPde(*BaseAddress);
3656 #if (_MI_PAGING_LEVELS >= 3)
3657     PointerPpe = MiAddressToPpe(*BaseAddress);
3658 #endif
3659 #if (_MI_PAGING_LEVELS == 4)
3660     PointerPxe = MiAddressToPxe(*BaseAddress);
3661 #endif
3662 
3663     /* Get the last PTE */
3664     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3665 
3666     /* Lock the process working set */
3667     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3668 
3669     /* Loop the pages */
3670     do
3671     {
3672         /* Check for a page that is not present */
3673         if (
3674 #if (_MI_PAGING_LEVELS == 4)
3675                (PointerPxe->u.Hard.Valid == 0) ||
3676 #endif
3677 #if (_MI_PAGING_LEVELS >= 3)
3678                (PointerPpe->u.Hard.Valid == 0) ||
3679 #endif
3680                (PointerPde->u.Hard.Valid == 0) ||
3681                (PointerPte->u.Hard.Valid == 0))
3682         {
3683             /* Remember it, but keep going */
3684             Status = STATUS_NOT_LOCKED;
3685         }
3686         else
3687         {
3688             /* Get the PFN */
3689             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3690             ASSERT(Pfn1 != NULL);
3691 
3692             /* Check if all of the requested locks are present */
3693             if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3694                 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3695             {
3696                 /* Remember it, but keep going */
3697                 Status = STATUS_NOT_LOCKED;
3698 
3699                 /* Check if no lock is present */
3700                 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3701                 {
3702                     DPRINT1("FIXME: Should remove the page from WS\n");
3703                 }
3704             }
3705         }
3706 
3707         /* Go to the next PTE */
3708         PointerPte++;
3709 
3710         /* Check if we're on a PDE boundary */
3711         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3712 #if (_MI_PAGING_LEVELS >= 3)
3713         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3714 #endif
3715 #if (_MI_PAGING_LEVELS == 4)
3716         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3717 #endif
3718     } while (PointerPte <= LastPte);
3719 
3720     /* Check if we hit a page that was not locked */
3721     if (Status == STATUS_NOT_LOCKED)
3722     {
3723         goto CleanupWithWsLock;
3724     }
3725 
3726     /* All pages in the region were locked, so unlock them all */
3727 
3728     /* Get the PTE and PDE */
3729     PointerPte = MiAddressToPte(*BaseAddress);
3730     PointerPde = MiAddressToPde(*BaseAddress);
3731 #if (_MI_PAGING_LEVELS >= 3)
3732     PointerPpe = MiAddressToPpe(*BaseAddress);
3733 #endif
3734 #if (_MI_PAGING_LEVELS == 4)
3735     PointerPxe = MiAddressToPxe(*BaseAddress);
3736 #endif
3737 
3738     /* Loop the pages */
3739     do
3740     {
3741         /* Unlock it */
3742         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3743         MI_UNLOCK_VA(Pfn1, MapType);
3744 
3745         /* Go to the next PTE */
3746         PointerPte++;
3747 
3748         /* Check if we're on a PDE boundary */
3749         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3750 #if (_MI_PAGING_LEVELS >= 3)
3751         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3752 #endif
3753 #if (_MI_PAGING_LEVELS == 4)
3754         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3755 #endif
3756     } while (PointerPte <= LastPte);
3757 
3758     /* Everything is done */
3759     Status = STATUS_SUCCESS;
3760 
3761 CleanupWithWsLock:
3762 
3763     /* Release process working set */
3764     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3765 
3766 Cleanup:
3767     /* Unlock address space */
3768     MmUnlockAddressSpace(AddressSpace);
3769 
3770     return Status;
3771 }
3772 
3773 
3774 NTSTATUS
3775 NTAPI
3776 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3777                       IN OUT PVOID *BaseAddress,
3778                       IN OUT PSIZE_T NumberOfBytesToUnlock,
3779                       IN ULONG MapType)
3780 {
3781     PEPROCESS Process;
3782     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3783     NTSTATUS Status;
3784     BOOLEAN Attached = FALSE;
3785     KAPC_STATE ApcState;
3786     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3787     PVOID CapturedBaseAddress;
3788     SIZE_T CapturedBytesToUnlock;
3789     PAGED_CODE();
3790 
3791     //
3792     // Validate flags
3793     //
3794     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3795     {
3796         //
3797         // Invalid set of flags
3798         //
3799         return STATUS_INVALID_PARAMETER;
3800     }
3801 
3802     //
3803     // At least one flag must be specified
3804     //
3805     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3806     {
3807         //
3808         // No flag given
3809         //
3810         return STATUS_INVALID_PARAMETER;
3811     }
3812 
3813     //
3814     // Enter SEH for probing
3815     //
3816     _SEH2_TRY
3817     {
3818         //
3819         // Validate output data
3820         //
3821         ProbeForWritePointer(BaseAddress);
3822         ProbeForWriteSize_t(NumberOfBytesToUnlock);
3823 
3824         //
3825         // Capture it
3826         //
3827         CapturedBaseAddress = *BaseAddress;
3828         CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3829     }
3830     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3831     {
3832         //
3833         // Get exception code
3834         //
3835         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3836     }
3837     _SEH2_END;
3838 
3839     //
3840     // Catch illegal base address
3841     //
3842     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3843 
3844     //
3845     // Catch illegal region size
3846     //
3847     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3848     {
3849         //
3850         // Fail
3851         //
3852         return STATUS_INVALID_PARAMETER;
3853     }
3854 
3855     //
3856     // 0 is also illegal
3857     //
3858     if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3859 
3860     //
3861     // Get a reference to the process
3862     //
3863     Status = ObReferenceObjectByHandle(ProcessHandle,
3864                                        PROCESS_VM_OPERATION,
3865                                        PsProcessType,
3866                                        PreviousMode,
3867                                        (PVOID*)(&Process),
3868                                        NULL);
3869     if (!NT_SUCCESS(Status)) return Status;
3870 
3871     //
3872     // Check if this is is system-mapped
3873     //
3874     if (MapType & MAP_SYSTEM)
3875     {
3876         //
3877         // Check for required privilege
3878         //
3879         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3880         {
3881             //
3882             // Fail: Don't have it
3883             //
3884             ObDereferenceObject(Process);
3885             return STATUS_PRIVILEGE_NOT_HELD;
3886         }
3887     }
3888 
3889     //
3890     // Check if we should attach
3891     //
3892     if (CurrentProcess != Process)
3893     {
3894         //
3895         // Do it
3896         //
3897         KeStackAttachProcess(&Process->Pcb, &ApcState);
3898         Attached = TRUE;
3899     }
3900 
3901     //
3902     // Call the internal function
3903     //
3904     Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3905                                    &CapturedBytesToUnlock,
3906                                    MapType);
3907 
3908     //
3909     // Detach if needed
3910     //
3911     if (Attached) KeUnstackDetachProcess(&ApcState);
3912 
3913     //
3914     // Release reference
3915     //
3916     ObDereferenceObject(Process);
3917 
3918     //
3919     // Enter SEH to return data
3920     //
3921     _SEH2_TRY
3922     {
3923         //
3924         // Return data to user
3925         //
3926         *BaseAddress = CapturedBaseAddress;
3927         *NumberOfBytesToUnlock = CapturedBytesToUnlock;
3928     }
3929     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3930     {
3931         //
3932         // Get exception code
3933         //
3934         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3935     }
3936     _SEH2_END;
3937 
3938     //
3939     // Return status
3940     //
3941     return STATUS_SUCCESS;
3942 }
3943 
3944 NTSTATUS
3945 NTAPI
3946 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3947                      IN OUT PVOID *BaseAddress,
3948                      IN OUT PSIZE_T NumberOfBytesToFlush,
3949                      OUT PIO_STATUS_BLOCK IoStatusBlock)
3950 {
3951     PEPROCESS Process;
3952     NTSTATUS Status;
3953     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3954     PVOID CapturedBaseAddress;
3955     SIZE_T CapturedBytesToFlush;
3956     IO_STATUS_BLOCK LocalStatusBlock;
3957     PAGED_CODE();
3958 
3959     //
3960     // Check if we came from user mode
3961     //
3962     if (PreviousMode != KernelMode)
3963     {
3964         //
3965         // Enter SEH for probing
3966         //
3967         _SEH2_TRY
3968         {
3969             //
3970             // Validate all outputs
3971             //
3972             ProbeForWritePointer(BaseAddress);
3973             ProbeForWriteSize_t(NumberOfBytesToFlush);
3974             ProbeForWriteIoStatusBlock(IoStatusBlock);
3975 
3976             //
3977             // Capture them
3978             //
3979             CapturedBaseAddress = *BaseAddress;
3980             CapturedBytesToFlush = *NumberOfBytesToFlush;
3981         }
3982         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3983         {
3984             //
3985             // Get exception code
3986             //
3987             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3988         }
3989         _SEH2_END;
3990     }
3991     else
3992     {
3993         //
3994         // Capture directly
3995         //
3996         CapturedBaseAddress = *BaseAddress;
3997         CapturedBytesToFlush = *NumberOfBytesToFlush;
3998     }
3999 
4000     //
4001     // Catch illegal base address
4002     //
4003     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4004 
4005     //
4006     // Catch illegal region size
4007     //
4008     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
4009     {
4010         //
4011         // Fail
4012         //
4013         return STATUS_INVALID_PARAMETER;
4014     }
4015 
4016     //
4017     // Get a reference to the process
4018     //
4019     Status = ObReferenceObjectByHandle(ProcessHandle,
4020                                        PROCESS_VM_OPERATION,
4021                                        PsProcessType,
4022                                        PreviousMode,
4023                                        (PVOID*)(&Process),
4024                                        NULL);
4025     if (!NT_SUCCESS(Status)) return Status;
4026 
4027     //
4028     // Do it
4029     //
4030     Status = MmFlushVirtualMemory(Process,
4031                                   &CapturedBaseAddress,
4032                                   &CapturedBytesToFlush,
4033                                   &LocalStatusBlock);
4034 
4035     //
4036     // Release reference
4037     //
4038     ObDereferenceObject(Process);
4039 
4040     //
4041     // Enter SEH to return data
4042     //
4043     _SEH2_TRY
4044     {
4045         //
4046         // Return data to user
4047         //
4048         *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
4049         *NumberOfBytesToFlush = 0;
4050         *IoStatusBlock = LocalStatusBlock;
4051     }
4052     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4053     {
4054     }
4055     _SEH2_END;
4056 
4057     //
4058     // Return status
4059     //
4060     return Status;
4061 }
4062 
4063 /*
4064  * @unimplemented
4065  */
4066 NTSTATUS
4067 NTAPI
4068 NtGetWriteWatch(IN HANDLE ProcessHandle,
4069                 IN ULONG Flags,
4070                 IN PVOID BaseAddress,
4071                 IN SIZE_T RegionSize,
4072                 IN PVOID *UserAddressArray,
4073                 OUT PULONG_PTR EntriesInUserAddressArray,
4074                 OUT PULONG Granularity)
4075 {
4076     PEPROCESS Process;
4077     NTSTATUS Status;
4078     PVOID EndAddress;
4079     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4080     ULONG_PTR CapturedEntryCount;
4081     PAGED_CODE();
4082 
4083     //
4084     // Check if we came from user mode
4085     //
4086     if (PreviousMode != KernelMode)
4087     {
4088         //
4089         // Enter SEH for probing
4090         //
4091         _SEH2_TRY
4092         {
4093             //
4094             // Catch illegal base address
4095             //
4096             if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
4097 
4098             //
4099             // Catch illegal region size
4100             //
4101             if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4102             {
4103                 //
4104                 // Fail
4105                 //
4106                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
4107             }
4108 
4109             //
4110             // Validate all data
4111             //
4112             ProbeForWriteSize_t(EntriesInUserAddressArray);
4113             ProbeForWriteUlong(Granularity);
4114 
4115             //
4116             // Capture them
4117             //
4118             CapturedEntryCount = *EntriesInUserAddressArray;
4119 
4120             //
4121             // Must have a count
4122             //
4123             if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4124 
4125             //
4126             // Can't be larger than the maximum
4127             //
4128             if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
4129             {
4130                 //
4131                 // Fail
4132                 //
4133                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4134             }
4135 
4136             //
4137             // Probe the actual array
4138             //
4139             ProbeForWrite(UserAddressArray,
4140                           CapturedEntryCount * sizeof(PVOID),
4141                           sizeof(PVOID));
4142         }
4143         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4144         {
4145             //
4146             // Get exception code
4147             //
4148             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4149         }
4150         _SEH2_END;
4151     }
4152     else
4153     {
4154         //
4155         // Capture directly
4156         //
4157         CapturedEntryCount = *EntriesInUserAddressArray;
4158         ASSERT(CapturedEntryCount != 0);
4159     }
4160 
4161     //
4162     // Check if this is a local request
4163     //
4164     if (ProcessHandle == NtCurrentProcess())
4165     {
4166         //
4167         // No need to reference the process
4168         //
4169         Process = PsGetCurrentProcess();
4170     }
4171     else
4172     {
4173         //
4174         // Reference the target
4175         //
4176         Status = ObReferenceObjectByHandle(ProcessHandle,
4177                                            PROCESS_VM_OPERATION,
4178                                            PsProcessType,
4179                                            PreviousMode,
4180                                            (PVOID *)&Process,
4181                                            NULL);
4182         if (!NT_SUCCESS(Status)) return Status;
4183     }
4184 
4185     //
4186     // Compute the last address and validate it
4187     //
4188     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4189     if (BaseAddress > EndAddress)
4190     {
4191         //
4192         // Fail
4193         //
4194         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4195         return STATUS_INVALID_PARAMETER_4;
4196     }
4197 
4198     //
4199     // Oops :(
4200     //
4201     UNIMPLEMENTED;
4202 
4203     //
4204     // Dereference if needed
4205     //
4206     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4207 
4208     //
4209     // Enter SEH to return data
4210     //
4211     _SEH2_TRY
4212     {
4213         //
4214         // Return data to user
4215         //
4216         *EntriesInUserAddressArray = 0;
4217         *Granularity = PAGE_SIZE;
4218     }
4219     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4220     {
4221         //
4222         // Get exception code
4223         //
4224         Status = _SEH2_GetExceptionCode();
4225     }
4226     _SEH2_END;
4227 
4228     //
4229     // Return success
4230     //
4231     return STATUS_SUCCESS;
4232 }
4233 
4234 /*
4235  * @unimplemented
4236  */
4237 NTSTATUS
4238 NTAPI
4239 NtResetWriteWatch(IN HANDLE ProcessHandle,
4240                   IN PVOID BaseAddress,
4241                   IN SIZE_T RegionSize)
4242 {
4243     PVOID EndAddress;
4244     PEPROCESS Process;
4245     NTSTATUS Status;
4246     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4247     ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
4248 
4249     //
4250     // Catch illegal base address
4251     //
4252     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
4253 
4254     //
4255     // Catch illegal region size
4256     //
4257     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4258     {
4259         //
4260         // Fail
4261         //
4262         return STATUS_INVALID_PARAMETER_3;
4263     }
4264 
4265     //
4266     // Check if this is a local request
4267     //
4268     if (ProcessHandle == NtCurrentProcess())
4269     {
4270         //
4271         // No need to reference the process
4272         //
4273         Process = PsGetCurrentProcess();
4274     }
4275     else
4276     {
4277         //
4278         // Reference the target
4279         //
4280         Status = ObReferenceObjectByHandle(ProcessHandle,
4281                                            PROCESS_VM_OPERATION,
4282                                            PsProcessType,
4283                                            PreviousMode,
4284                                            (PVOID *)&Process,
4285                                            NULL);
4286         if (!NT_SUCCESS(Status)) return Status;
4287     }
4288 
4289     //
4290     // Compute the last address and validate it
4291     //
4292     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4293     if (BaseAddress > EndAddress)
4294     {
4295         //
4296         // Fail
4297         //
4298         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4299         return STATUS_INVALID_PARAMETER_3;
4300     }
4301 
4302     //
4303     // Oops :(
4304     //
4305     UNIMPLEMENTED;
4306 
4307     //
4308     // Dereference if needed
4309     //
4310     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4311 
4312     //
4313     // Return success
4314     //
4315     return STATUS_SUCCESS;
4316 }
4317 
4318 NTSTATUS
4319 NTAPI
4320 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
4321                      IN PVOID BaseAddress,
4322                      IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4323                      OUT PVOID MemoryInformation,
4324                      IN SIZE_T MemoryInformationLength,
4325                      OUT PSIZE_T ReturnLength)
4326 {
4327     NTSTATUS Status = STATUS_SUCCESS;
4328     KPROCESSOR_MODE PreviousMode;
4329 
4330     DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4331 
4332     /* Bail out if the address is invalid */
4333     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4334 
4335     /* Probe return buffer */
4336     PreviousMode =  ExGetPreviousMode();
4337     if (PreviousMode != KernelMode)
4338     {
4339         _SEH2_TRY
4340         {
4341             ProbeForWrite(MemoryInformation,
4342                           MemoryInformationLength,
4343                           sizeof(ULONG_PTR));
4344 
4345             if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
4346         }
4347         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4348         {
4349             Status = _SEH2_GetExceptionCode();
4350         }
4351         _SEH2_END;
4352 
4353         if (!NT_SUCCESS(Status))
4354         {
4355             return Status;
4356         }
4357     }
4358 
4359     switch(MemoryInformationClass)
4360     {
4361         case MemoryBasicInformation:
4362             /* Validate the size information of the class */
4363             if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4364             {
4365                 /* The size is invalid */
4366                 return STATUS_INFO_LENGTH_MISMATCH;
4367             }
4368             Status = MiQueryMemoryBasicInformation(ProcessHandle,
4369                                                    BaseAddress,
4370                                                    MemoryInformation,
4371                                                    MemoryInformationLength,
4372                                                    ReturnLength);
4373             break;
4374 
4375         case MemorySectionName:
4376             /* Validate the size information of the class */
4377             if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4378             {
4379                 /* The size is invalid */
4380                 return STATUS_INFO_LENGTH_MISMATCH;
4381             }
4382             Status = MiQueryMemorySectionName(ProcessHandle,
4383                                               BaseAddress,
4384                                               MemoryInformation,
4385                                               MemoryInformationLength,
4386                                               ReturnLength);
4387             break;
4388         case MemoryWorkingSetList:
4389         case MemoryBasicVlmInformation:
4390         default:
4391             DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4392             break;
4393     }
4394 
4395     return Status;
4396 }
4397 
4398 /*
4399  * @implemented
4400  */
4401 NTSTATUS
4402 NTAPI
4403 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
4404                         IN OUT PVOID* UBaseAddress,
4405                         IN ULONG_PTR ZeroBits,
4406                         IN OUT PSIZE_T URegionSize,
4407                         IN ULONG AllocationType,
4408                         IN ULONG Protect)
4409 {
4410     PEPROCESS Process;
4411     PMEMORY_AREA MemoryArea;
4412     PMMVAD Vad = NULL, FoundVad;
4413     NTSTATUS Status;
4414     PMMSUPPORT AddressSpace;
4415     PVOID PBaseAddress;
4416     ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4417     ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4418     PEPROCESS CurrentProcess = PsGetCurrentProcess();
4419     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4420     PETHREAD CurrentThread = PsGetCurrentThread();
4421     KAPC_STATE ApcState;
4422     ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4423     BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
4424     MMPTE TempPte;
4425     PMMPTE PointerPte, LastPte;
4426     PMMPDE PointerPde;
4427     TABLE_SEARCH_RESULT Result;
4428     PAGED_CODE();
4429 
4430     /* Check for valid Zero bits */
4431     if (ZeroBits > MI_MAX_ZERO_BITS)
4432     {
4433         DPRINT1("Too many zero bits\n");
4434         return STATUS_INVALID_PARAMETER_3;
4435     }
4436 
4437     /* Check for valid Allocation Types */
4438     if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
4439                     MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
4440     {
4441         DPRINT1("Invalid Allocation Type\n");
4442         return STATUS_INVALID_PARAMETER_5;
4443     }
4444 
4445     /* Check for at least one of these Allocation Types to be set */
4446     if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
4447     {
4448         DPRINT1("No memory allocation base type\n");
4449         return STATUS_INVALID_PARAMETER_5;
4450     }
4451 
4452     /* MEM_RESET is an exclusive flag, make sure that is valid too */
4453     if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
4454     {
4455         DPRINT1("Invalid use of MEM_RESET\n");
4456         return STATUS_INVALID_PARAMETER_5;
4457     }
4458 
4459     /* Check if large pages are being used */
4460     if (AllocationType & MEM_LARGE_PAGES)
4461     {
4462         /* Large page allocations MUST be committed */
4463         if (!(AllocationType & MEM_COMMIT))
4464         {
4465             DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4466             return STATUS_INVALID_PARAMETER_5;
4467         }
4468 
4469         /* These flags are not allowed with large page allocations */
4470         if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
4471         {
4472             DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4473             return STATUS_INVALID_PARAMETER_5;
4474         }
4475     }
4476 
4477     /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4478     if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
4479     {
4480         DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4481         return STATUS_INVALID_PARAMETER_5;
4482     }
4483 
4484     /* Check for valid MEM_PHYSICAL usage */
4485     if (AllocationType & MEM_PHYSICAL)
4486     {
4487         /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4488         if (!(AllocationType & MEM_RESERVE))
4489         {
4490             DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4491             return STATUS_INVALID_PARAMETER_5;
4492         }
4493 
4494         /* Only these flags are allowed with MEM_PHYSIAL */
4495         if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
4496         {
4497             DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4498             return STATUS_INVALID_PARAMETER_5;
4499         }
4500 
4501         /* Then make sure PAGE_READWRITE is used */
4502         if (Protect != PAGE_READWRITE)
4503         {
4504             DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4505             return STATUS_INVALID_PARAMETER_6;
4506         }
4507     }
4508 
4509     /* Calculate the protection mask and make sure it's valid */
4510     ProtectionMask = MiMakeProtectionMask(Protect);
4511     if (ProtectionMask == MM_INVALID_PROTECTION)
4512     {
4513         DPRINT1("Invalid protection mask\n");
4514         return STATUS_INVALID_PAGE_PROTECTION;
4515     }
4516 
4517     /* Enter SEH */
4518     _SEH2_TRY
4519     {
4520         /* Check for user-mode parameters */
4521         if (PreviousMode != KernelMode)
4522         {
4523             /* Make sure they are writable */
4524             ProbeForWritePointer(UBaseAddress);
4525             ProbeForWriteSize_t(URegionSize);
4526         }
4527 
4528         /* Capture their values */
4529         PBaseAddress = *UBaseAddress;
4530         PRegionSize = *URegionSize;
4531     }
4532     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4533     {
4534         /* Return the exception code */
4535         _SEH2_YIELD(return _SEH2_GetExceptionCode());
4536     }
4537     _SEH2_END;
4538 
4539     /* Make sure the allocation isn't past the VAD area */
4540     if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4541     {
4542         DPRINT1("Virtual allocation base above User Space\n");
4543         return STATUS_INVALID_PARAMETER_2;
4544     }
4545 
4546     /* Make sure the allocation wouldn't overflow past the VAD area */
4547     if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4548     {
4549         DPRINT1("Region size would overflow into kernel-memory\n");
4550         return STATUS_INVALID_PARAMETER_4;
4551     }
4552 
4553     /* Make sure there's a size specified */
4554     if (!PRegionSize)
4555     {
4556         DPRINT1("Region size is invalid (zero)\n");
4557         return STATUS_INVALID_PARAMETER_4;
4558     }
4559 
4560     //
4561     // If this is for the current process, just use PsGetCurrentProcess
4562     //
4563     if (ProcessHandle == NtCurrentProcess())
4564     {
4565         Process = CurrentProcess;
4566     }
4567     else
4568     {
4569         //
4570         // Otherwise, reference the process with VM rights and attach to it if
4571         // this isn't the current process. We must attach because we'll be touching
4572         // PTEs and PDEs that belong to user-mode memory, and also touching the
4573         // Working Set which is stored in Hyperspace.
4574         //
4575         Status = ObReferenceObjectByHandle(ProcessHandle,
4576                                            PROCESS_VM_OPERATION,
4577                                            PsProcessType,
4578                                            PreviousMode,
4579                                            (PVOID*)&Process,
4580                                            NULL);
4581         if (!NT_SUCCESS(Status)) return Status;
4582         if (CurrentProcess != Process)
4583         {
4584             KeStackAttachProcess(&Process->Pcb, &ApcState);
4585             Attached = TRUE;
4586         }
4587     }
4588 
4589     DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4590         Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect);
4591 
4592     //
4593     // Check for large page allocations and make sure that the required privilege
4594     // is being held, before attempting to handle them.
4595     //
4596     if ((AllocationType & MEM_LARGE_PAGES) &&
4597         !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
4598     {
4599         /* Fail without it */
4600         DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4601         Status = STATUS_PRIVILEGE_NOT_HELD;
4602         goto FailPathNoLock;
4603     }
4604 
4605     //
4606     // Fail on the things we don't yet support
4607     //
4608     if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
4609     {
4610         DPRINT1("MEM_LARGE_PAGES not supported\n");
4611         Status = STATUS_INVALID_PARAMETER;
4612         goto FailPathNoLock;
4613     }
4614     if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
4615     {
4616         DPRINT1("MEM_PHYSICAL not supported\n");
4617         Status = STATUS_INVALID_PARAMETER;
4618         goto FailPathNoLock;
4619     }
4620     if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
4621     {
4622         DPRINT1("MEM_WRITE_WATCH not supported\n");
4623         Status = STATUS_INVALID_PARAMETER;
4624         goto FailPathNoLock;
4625     }
4626 
4627     //
4628     // Check if the caller is reserving memory, or committing memory and letting
4629     // us pick the base address
4630     //
4631     if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4632     {
4633         //
4634         //  Do not allow COPY_ON_WRITE through this API
4635         //
4636         if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
4637         {
4638             DPRINT1("Copy on write not allowed through this path\n");
4639             Status = STATUS_INVALID_PAGE_PROTECTION;
4640             goto FailPathNoLock;
4641         }
4642 
4643         //
4644         // Does the caller have an address in mind, or is this a blind commit?
4645         //
4646         if (!PBaseAddress)
4647         {
4648             //
4649             // This is a blind commit, all we need is the region size
4650             //
4651             PRegionSize = ROUND_TO_PAGES(PRegionSize);
4652             EndingAddress = 0;
4653             StartingAddress = 0;
4654 
4655             //
4656             // Check if ZeroBits were specified
4657             //
4658             if (ZeroBits != 0)
4659             {
4660                 //
4661                 // Calculate the highest address and check if it's valid
4662                 //
4663                 HighestAddress = MAXULONG_PTR >> ZeroBits;
4664                 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4665                 {
4666                     Status = STATUS_INVALID_PARAMETER_3;
4667                     goto FailPathNoLock;
4668                 }
4669             }
4670         }
4671         else
4672         {
4673             //
4674             // This is a reservation, so compute the starting address on the
4675             // expected 64KB granularity, and see where the ending address will
4676             // fall based on the aligned address and the passed in region size
4677             //
4678             EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4679             PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4680             StartingAddress = (ULONG_PTR)PBaseAddress;
4681         }
4682 
4683         //
4684         // Allocate and initialize the VAD
4685         //
4686         Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4687         if (Vad == NULL)
4688         {
4689             DPRINT1("Failed to allocate a VAD!\n");
4690             Status = STATUS_INSUFFICIENT_RESOURCES;
4691             goto FailPathNoLock;
4692         }
4693 
4694         RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
4695         if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4696         Vad->u.VadFlags.Protection = ProtectionMask;
4697         Vad->u.VadFlags.PrivateMemory = 1;
4698         Vad->ControlArea = NULL; // For Memory-Area hack
4699 
4700         //
4701         // Insert the VAD
4702         //
4703         Status = MiInsertVadEx(Vad,
4704                                &StartingAddress,
4705                                PRegionSize,
4706                                HighestAddress,
4707                                MM_VIRTMEM_GRANULARITY,
4708                                AllocationType);
4709         if (!NT_SUCCESS(Status))
4710         {
4711             DPRINT1("Failed to insert the VAD!\n");
4712             goto FailPathNoLock;
4713         }
4714 
4715         //
4716         // Detach and dereference the target process if
4717         // it was different from the current process
4718         //
4719         if (Attached) KeUnstackDetachProcess(&ApcState);
4720         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4721 
4722         //
4723         // Use SEH to write back the base address and the region size. In the case
4724         // of an exception, we do not return back the exception code, as the memory
4725         // *has* been allocated. The caller would now have to call VirtualQuery
4726         // or do some other similar trick to actually find out where its memory
4727         // allocation ended up
4728         //
4729         _SEH2_TRY
4730         {
4731             *URegionSize = PRegionSize;
4732             *UBaseAddress = (PVOID)StartingAddress;
4733         }
4734         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4735         {
4736             //
4737             // Ignore exception!
4738             //
4739         }
4740         _SEH2_END;
4741         DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress);
4742         return STATUS_SUCCESS;
4743     }
4744 
4745     //
4746     // This is a MEM_COMMIT on top of an existing address which must have been
4747     // MEM_RESERVED already. Compute the start and ending base addresses based
4748     // on the user input, and then compute the actual region size once all the
4749     // alignments have been done.
4750     //
4751     EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4752     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4753     PRegionSize = EndingAddress - StartingAddress + 1;
4754 
4755     //
4756     // Lock the address space and make sure the process isn't already dead
4757     //
4758     AddressSpace = MmGetCurrentAddressSpace();
4759     MmLockAddressSpace(AddressSpace);
4760     if (Process->VmDeleted)
4761     {
4762         DPRINT1("Process is dying\n");
4763         Status = STATUS_PROCESS_IS_TERMINATING;
4764         goto FailPath;
4765     }
4766 
4767     //
4768     // Get the VAD for this address range, and make sure it exists
4769     //
4770     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4771                                        EndingAddress >> PAGE_SHIFT,
4772                                        &Process->VadRoot,
4773                                        (PMMADDRESS_NODE*)&FoundVad);
4774     if (Result != TableFoundNode)
4775     {
4776         DPRINT1("Could not find a VAD for this allocation\n");
4777         Status = STATUS_CONFLICTING_ADDRESSES;
4778         goto FailPath;
4779     }
4780 
4781     if ((AllocationType & MEM_RESET) == MEM_RESET)
4782     {
4783         /// @todo HACK: pretend success
4784         DPRINT("MEM_RESET not supported\n");
4785         Status = STATUS_SUCCESS;
4786         goto FailPath;
4787     }
4788 
4789     //
4790     // These kinds of VADs are illegal for this Windows function when trying to
4791     // commit an existing range
4792     //
4793     if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4794         (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4795         (FoundVad->u.VadFlags.VadType == VadLargePages))
4796     {
4797         DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4798         Status = STATUS_CONFLICTING_ADDRESSES;
4799         goto FailPath;
4800     }
4801 
4802     //
4803     // Make sure that this address range actually fits within the VAD for it
4804     //
4805     if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4806         ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4807     {
4808         DPRINT1("Address range does not fit into the VAD\n");
4809         Status = STATUS_CONFLICTING_ADDRESSES;
4810         goto FailPath;
4811     }
4812 
4813     //
4814     // Make sure this is an ARM3 section
4815     //
4816     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4817     ASSERT(MemoryArea != NULL);
4818     if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4819     {
4820         DPRINT1("Illegal commit of non-ARM3 section!\n");
4821         Status = STATUS_ALREADY_COMMITTED;
4822         goto FailPath;
4823     }
4824 
4825     // Is this a previously reserved section being committed? If so, enter the
4826     // special section path
4827     //
4828     if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4829     {
4830         //
4831         // You cannot commit large page sections through this API
4832         //
4833         if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4834         {
4835             DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4836             Status = STATUS_INVALID_PAGE_PROTECTION;
4837             goto FailPath;
4838         }
4839 
4840         //
4841         // You can only use caching flags on a rotate VAD
4842         //
4843         if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4844             (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4845         {
4846             DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4847             Status = STATUS_INVALID_PAGE_PROTECTION;
4848             goto FailPath;
4849         }
4850 
4851         //
4852         // We should make sure that the section's permissions aren't being
4853         // messed with
4854         //
4855         if (FoundVad->u.VadFlags.NoChange)
4856         {
4857             //
4858             // Make sure it's okay to touch it
4859             // Note: The Windows 2003 kernel has a bug here, passing the
4860             // unaligned base address together with the aligned size,
4861             // potentially covering a region larger than the actual allocation.
4862             // Might be exposed through NtGdiCreateDIBSection w/ section handle
4863             // For now we keep this behavior.
4864             // TODO: analyze possible implications, create test case
4865             //
4866             Status = MiCheckSecuredVad(FoundVad,
4867                                        PBaseAddress,
4868                                        PRegionSize,
4869                                        ProtectionMask);
4870             if (!NT_SUCCESS(Status))
4871             {
4872                 DPRINT1("Secured VAD being messed around with\n");
4873                 goto FailPath;
4874             }
4875         }
4876 
4877         //
4878         // ARM3 does not support file-backed sections, only shared memory
4879         //
4880         ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4881 
4882         //
4883         // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4884         //
4885         if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4886             (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4887         {
4888             DPRINT1("Invalid page protection for rotate VAD\n");
4889             Status = STATUS_INVALID_PAGE_PROTECTION;
4890             goto FailPath;
4891         }
4892 
4893         //
4894         // Compute PTE addresses and the quota charge, then grab the commit lock
4895         //
4896         PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4897         LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4898         QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4899         KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4900 
4901         //
4902         // Get the segment template PTE and start looping each page
4903         //
4904         TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4905         ASSERT(TempPte.u.Long != 0);
4906         while (PointerPte <= LastPte)
4907         {
4908             //
4909             // For each non-already-committed page, write the invalid template PTE
4910             //
4911             if (PointerPte->u.Long == 0)
4912             {
4913                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4914             }
4915             else
4916             {
4917                 QuotaFree++;
4918             }
4919             PointerPte++;
4920         }
4921 
4922         //
4923         // Now do the commit accounting and release the lock
4924         //
4925         ASSERT(QuotaCharge >= QuotaFree);
4926         QuotaCharge -= QuotaFree;
4927         FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4928         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4929 
4930         //
4931         // We are done with committing the section pages
4932         //
4933         Status = STATUS_SUCCESS;
4934         goto FailPath;
4935     }
4936 
4937     //
4938     // This is a specific ReactOS check because we only use normal VADs
4939     //
4940     ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4941 
4942     //
4943     // While this is an actual Windows check
4944     //
4945     ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4946 
4947     //
4948     // Throw out attempts to use copy-on-write through this API path
4949     //
4950     if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4951     {
4952         DPRINT1("Write copy attempted when not allowed\n");
4953         Status = STATUS_INVALID_PAGE_PROTECTION;
4954         goto FailPath;
4955     }
4956 
4957     //
4958     // Initialize a demand-zero PTE
4959     //
4960     TempPte.u.Long = 0;
4961     TempPte.u.Soft.Protection = ProtectionMask;
4962     ASSERT(TempPte.u.Long != 0);
4963 
4964     //
4965     // Get the PTE, PDE and the last PTE for this address range
4966     //
4967     PointerPde = MiAddressToPde(StartingAddress);
4968     PointerPte = MiAddressToPte(StartingAddress);
4969     LastPte = MiAddressToPte(EndingAddress);
4970 
4971     //
4972     // Update the commit charge in the VAD as well as in the process, and check
4973     // if this commit charge was now higher than the last recorded peak, in which
4974     // case we also update the peak
4975     //
4976     FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4977     Process->CommitCharge += (1 + LastPte - PointerPte);
4978     if (Process->CommitCharge > Process->CommitChargePeak)
4979     {
4980         Process->CommitChargePeak = Process->CommitCharge;
4981     }
4982 
4983     //
4984     // Lock the working set while we play with user pages and page tables
4985     //
4986     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4987 
4988     //
4989     // Make the current page table valid, and then loop each page within it
4990     //
4991     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4992     while (PointerPte <= LastPte)
4993     {
4994         //
4995         // Have we crossed into a new page table?
4996         //
4997         if (MiIsPteOnPdeBoundary(PointerPte))
4998         {
4999             //
5000             // Get the PDE and now make it valid too
5001             //
5002             PointerPde = MiPteToPde(PointerPte);
5003             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
5004         }
5005 
5006         //
5007         // Is this a zero PTE as expected?
5008         //
5009         if (PointerPte->u.Long == 0)
5010         {
5011             //
5012             // First increment the count of pages in the page table for this
5013             // process
5014             //
5015             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
5016 
5017             //
5018             // And now write the invalid demand-zero PTE as requested
5019             //
5020             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5021         }
5022         else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
5023         {
5024             //
5025             // If the PTE was already decommitted, there is nothing else to do
5026             // but to write the new demand-zero PTE
5027             //
5028             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5029         }
5030         else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
5031         {
5032             //
5033             // We don't handle these scenarios yet
5034             //
5035             if (PointerPte->u.Soft.Valid == 0)
5036             {
5037                 ASSERT(PointerPte->u.Soft.Prototype == 0);
5038                 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
5039             }
5040 
5041             //
5042             // There's a change in protection, remember this for later, but do
5043             // not yet handle it.
5044             //
5045             ChangeProtection = TRUE;
5046         }
5047 
5048         //
5049         // Move to the next PTE
5050         //
5051         PointerPte++;
5052     }
5053 
5054     //
5055     // Release the working set lock, unlock the address space, and detach from
5056     // the target process if it was not the current process. Also dereference the
5057     // target process if this wasn't the case.
5058     //
5059     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5060     Status = STATUS_SUCCESS;
5061 FailPath:
5062     MmUnlockAddressSpace(AddressSpace);
5063 
5064     if (!NT_SUCCESS(Status))
5065     {
5066         if (Vad != NULL)
5067         {
5068             ExFreePoolWithTag(Vad, 'SdaV');
5069         }
5070     }
5071 
5072     //
5073     // Check if we need to update the protection
5074     //
5075     if (ChangeProtection)
5076     {
5077         PVOID ProtectBaseAddress = (PVOID)StartingAddress;
5078         SIZE_T ProtectSize = PRegionSize;
5079         ULONG OldProtection;
5080 
5081         //
5082         // Change the protection of the region
5083         //
5084         MiProtectVirtualMemory(Process,
5085                                &ProtectBaseAddress,
5086                                &ProtectSize,
5087                                Protect,
5088                                &OldProtection);
5089     }
5090 
5091 FailPathNoLock:
5092     if (Attached) KeUnstackDetachProcess(&ApcState);
5093     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5094 
5095     //
5096     // Only write back results on success
5097     //
5098     if (NT_SUCCESS(Status))
5099     {
5100         //
5101         // Use SEH to write back the base address and the region size. In the case
5102         // of an exception, we strangely do return back the exception code, even
5103         // though the memory *has* been allocated. This mimics Windows behavior and
5104         // there is not much we can do about it.
5105         //
5106         _SEH2_TRY
5107         {
5108             *URegionSize = PRegionSize;
5109             *UBaseAddress = (PVOID)StartingAddress;
5110         }
5111         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5112         {
5113             Status = _SEH2_GetExceptionCode();
5114         }
5115         _SEH2_END;
5116     }
5117 
5118     return Status;
5119 }
5120 
5121 /*
5122  * @implemented
5123  */
5124 NTSTATUS
5125 NTAPI
5126 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
5127                     IN PVOID* UBaseAddress,
5128                     IN PSIZE_T URegionSize,
5129                     IN ULONG FreeType)
5130 {
5131     PMEMORY_AREA MemoryArea;
5132     SIZE_T PRegionSize;
5133     PVOID PBaseAddress;
5134     LONG_PTR AlreadyDecommitted, CommitReduction = 0;
5135     ULONG_PTR StartingAddress, EndingAddress;
5136     PMMVAD Vad;
5137     NTSTATUS Status;
5138     PEPROCESS Process;
5139     PMMSUPPORT AddressSpace;
5140     PETHREAD CurrentThread = PsGetCurrentThread();
5141     PEPROCESS CurrentProcess = PsGetCurrentProcess();
5142     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
5143     KAPC_STATE ApcState;
5144     BOOLEAN Attached = FALSE;
5145     PAGED_CODE();
5146 
5147     //
5148     // Only two flags are supported, exclusively.
5149     //
5150     if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT)
5151     {
5152         DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType);
5153         return STATUS_INVALID_PARAMETER_4;
5154     }
5155 
5156     //
5157     // Enter SEH for probe and capture. On failure, return back to the caller
5158     // with an exception violation.
5159     //
5160     _SEH2_TRY
5161     {
5162         //
5163         // Check for user-mode parameters and make sure that they are writeable
5164         //
5165         if (PreviousMode != KernelMode)
5166         {
5167             ProbeForWritePointer(UBaseAddress);
5168             ProbeForWriteUlong(URegionSize);
5169         }
5170 
5171         //
5172         // Capture the current values
5173         //
5174         PBaseAddress = *UBaseAddress;
5175         PRegionSize = *URegionSize;
5176     }
5177     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5178     {
5179         _SEH2_YIELD(return _SEH2_GetExceptionCode());
5180     }
5181     _SEH2_END;
5182 
5183     //
5184     // Make sure the allocation isn't past the user area
5185     //
5186     if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5187     {
5188         DPRINT1("Virtual free base above User Space\n");
5189         return STATUS_INVALID_PARAMETER_2;
5190     }
5191 
5192     //
5193     // Make sure the allocation wouldn't overflow past the user area
5194     //
5195     if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5196     {
5197         DPRINT1("Region size would overflow into kernel-memory\n");
5198         return STATUS_INVALID_PARAMETER_3;
5199     }
5200 
5201     //
5202     // If this is for the current process, just use PsGetCurrentProcess
5203     //
5204     if (ProcessHandle == NtCurrentProcess())
5205     {
5206         Process = CurrentProcess;
5207     }
5208     else
5209     {
5210         //
5211         // Otherwise, reference the process with VM rights and attach to it if
5212         // this isn't the current process. We must attach because we'll be touching
5213         // PTEs and PDEs that belong to user-mode memory, and also touching the
5214         // Working Set which is stored in Hyperspace.
5215         //
5216         Status = ObReferenceObjectByHandle(ProcessHandle,
5217                                            PROCESS_VM_OPERATION,
5218                                            PsProcessType,
5219                                            PreviousMode,
5220                                            (PVOID*)&Process,
5221                                            NULL);
5222         if (!NT_SUCCESS(Status)) return Status;
5223         if (CurrentProcess != Process)
5224         {
5225             KeStackAttachProcess(&Process->Pcb, &ApcState);
5226             Attached = TRUE;
5227         }
5228     }
5229 
5230     DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n",
5231            Process, PBaseAddress, PRegionSize, FreeType);
5232 
5233     //
5234     // Lock the address space
5235     //
5236     AddressSpace = MmGetCurrentAddressSpace();
5237     MmLockAddressSpace(AddressSpace);
5238 
5239     //
5240     // If the address space is being deleted, fail the de-allocation since it's
5241     // too late to do anything about it
5242     //
5243     if (Process->VmDeleted)
5244     {
5245         DPRINT1("Process is dead\n");
5246         Status = STATUS_PROCESS_IS_TERMINATING;
5247         goto FailPath;
5248     }
5249 
5250     //
5251     // Compute start and end addresses, and locate the VAD
5252     //
5253     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5254     EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5255     Vad = MiLocateAddress((PVOID)StartingAddress);
5256     if (!Vad)
5257     {
5258         DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5259         Status = STATUS_MEMORY_NOT_ALLOCATED;
5260         goto FailPath;
5261     }
5262 
5263     //
5264     // If the range exceeds the VAD's ending VPN, fail this request
5265     //
5266     if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5267     {
5268         DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5269         Status = STATUS_UNABLE_TO_FREE_VM;
5270         goto FailPath;
5271     }
5272 
5273     //
5274     // Only private memory (except rotate VADs) can be freed through here */
5275     //
5276     if ((!(Vad->u.VadFlags.PrivateMemory) &&
5277          (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5278         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5279     {
5280         DPRINT1("Attempt to free section memory\n");
5281         Status = STATUS_UNABLE_TO_DELETE_SECTION;
5282         goto FailPath;
5283     }
5284 
5285     //
5286     // ARM3 does not yet handle protected VM
5287     //
5288     ASSERT(Vad->u.VadFlags.NoChange == 0);
5289 
5290     //
5291     // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5292     // and that is is an ARM3 memory area, and not a section view, as we currently
5293     // don't support freeing those though this interface.
5294     //
5295     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5296     ASSERT(MemoryArea);
5297     ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5298 
5299     //
5300     //  Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5301     //
5302     if (FreeType & MEM_RELEASE)
5303     {
5304         //
5305         // ARM3 only supports this VAD in this path
5306         //
5307         ASSERT(Vad->u.VadFlags.VadType == VadNone);
5308 
5309         //
5310         // Is the caller trying to remove the whole VAD, or remove only a portion
5311         // of it? If no region size is specified, then the assumption is that the
5312         // whole VAD is to be destroyed
5313         //
5314         if (!PRegionSize)
5315         {
5316             //
5317             // The caller must specify the base address identically to the range
5318             // that is stored in the VAD.
5319             //
5320             if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5321             {
5322                 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5323                 Status = STATUS_FREE_VM_NOT_AT_BASE;
5324                 goto FailPath;
5325             }
5326 
5327             //
5328             // Now compute the actual start/end addresses based on the VAD
5329             //
5330             StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5331             EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5332 
5333             //
5334             // Finally lock the working set and remove the VAD from the VAD tree
5335             //
5336             MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5337             ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5338             MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5339         }
5340         else
5341         {
5342             //
5343             // This means the caller wants to release a specific region within
5344             // the range. We have to find out which range this is -- the following
5345             // possibilities exist plus their union (CASE D):
5346             //
5347             // STARTING ADDRESS                                   ENDING ADDRESS
5348             // [<========][========================================][=========>]
5349             //   CASE A                  CASE B                       CASE C
5350             //
5351             //
5352             // First, check for case A or D
5353             //
5354             if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5355             {
5356                 //
5357                 // Check for case D
5358                 //
5359                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5360                 {
5361                     //
5362                     // This is the easiest one to handle -- it is identical to
5363                     // the code path above when the caller sets a zero region size
5364                     // and the whole VAD is destroyed
5365                     //
5366                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5367                     ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5368                     MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5369                 }
5370                 else
5371                 {
5372                     //
5373                     // This case is pretty easy too -- we compute a bunch of
5374                     // pages to decommit, and then push the VAD's starting address
5375                     // a bit further down, then decrement the commit charge
5376                     //
5377                     // NOT YET IMPLEMENTED IN ARM3.
5378                     //
5379                     DPRINT1("Case A not handled\n");
5380                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5381                     goto FailPath;
5382 
5383                     //
5384                     // After analyzing the VAD, set it to NULL so that we don't
5385                     // free it in the exit path
5386                     //
5387                     Vad = NULL;
5388                 }
5389             }
5390             else
5391             {
5392                 //
5393                 // This is case B or case C. First check for case C
5394                 //
5395                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5396                 {
5397                     PMEMORY_AREA MemoryArea;
5398 
5399                     //
5400                     // This is pretty easy and similar to case A. We compute the
5401                     // amount of pages to decommit, update the VAD's commit charge
5402                     // and then change the ending address of the VAD to be a bit
5403                     // smaller.
5404                     //
5405                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5406                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5407                                                                 EndingAddress,
5408                                                                 Vad,
5409                                                                 Process);
5410                     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5411                     // For ReactOS: shrink the corresponding memory area
5412                     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5413                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5414                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5415                     Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT;
5416                     MemoryArea->VadNode.EndingVpn = Vad->EndingVpn;
5417                 }
5418                 else
5419                 {
5420                     //
5421                     // This is case B and the hardest one. Because we are removing
5422                     // a chunk of memory from the very middle of the VAD, we must
5423                     // actually split the VAD into two new VADs and compute the
5424                     // commit charges for each of them, and reinsert new charges.
5425                     //
5426                     // NOT YET IMPLEMENTED IN ARM3.
5427                     //
5428                     DPRINT1("Case B not handled\n");
5429                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5430                     goto FailPath;
5431                 }
5432 
5433                 //
5434                 // After analyzing the VAD, set it to NULL so that we don't
5435                 // free it in the exit path
5436                 //
5437                 Vad = NULL;
5438             }
5439         }
5440 
5441         //
5442         // Now we have a range of pages to dereference, so call the right API
5443         // to do that and then release the working set, since we're done messing
5444         // around with process pages.
5445         //
5446         MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5447         MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5448         Status = STATUS_SUCCESS;
5449 
5450 FinalPath:
5451         //
5452         // Update the process counters
5453         //
5454         PRegionSize = EndingAddress - StartingAddress + 1;
5455         Process->CommitCharge -= CommitReduction;
5456         if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5457 
5458         //
5459         // Unlock the address space and free the VAD in failure cases. Next,
5460         // detach from the target process so we can write the region size and the
5461         // base address to the correct source process, and dereference the target
5462         // process.
5463         //
5464         MmUnlockAddressSpace(AddressSpace);
5465         if (Vad) ExFreePool(Vad);
5466         if (Attached) KeUnstackDetachProcess(&ApcState);
5467         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5468 
5469         //
5470         // Use SEH to safely return the region size and the base address of the
5471         // deallocation. If we get an access violation, don't return a failure code
5472         // as the deallocation *has* happened. The caller will just have to figure
5473         // out another way to find out where it is (such as VirtualQuery).
5474         //
5475         _SEH2_TRY
5476         {
5477             *URegionSize = PRegionSize;
5478             *UBaseAddress = (PVOID)StartingAddress;
5479         }
5480         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5481         {
5482         }
5483         _SEH2_END;
5484         return Status;
5485     }
5486 
5487     //
5488     // This is the decommit path. You cannot decommit from the following VADs in
5489     // Windows, so fail the vall
5490     //
5491     if ((Vad->u.VadFlags.VadType == VadAwe) ||
5492         (Vad->u.VadFlags.VadType == VadLargePages) ||
5493         (Vad->u.VadFlags.VadType == VadRotatePhysical))
5494     {
5495         DPRINT1("Trying to decommit from invalid VAD\n");
5496         Status = STATUS_MEMORY_NOT_ALLOCATED;
5497         goto FailPath;
5498     }
5499 
5500     //
5501     // If the caller did not specify a region size, first make sure that this
5502     // region is actually committed. If it is, then compute the ending address
5503     // based on the VAD.
5504     //
5505     if (!PRegionSize)
5506     {
5507         if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5508         {
5509             DPRINT1("Decomitting non-committed memory\n");
5510             Status = STATUS_FREE_VM_NOT_AT_BASE;
5511             goto FailPath;
5512         }
5513         EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5514     }
5515 
5516     //
5517     // Decommit the PTEs for the range plus the actual backing pages for the
5518     // range, then reduce that amount from the commit charge in the VAD
5519     //
5520     AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress,
5521                                          MiAddressToPte(EndingAddress),
5522                                          Process,
5523                                          Vad);
5524     CommitReduction = MiAddressToPte(EndingAddress) -
5525                       MiAddressToPte(StartingAddress) +
5526                       1 -
5527                       AlreadyDecommitted;
5528 
5529     ASSERT(CommitReduction >= 0);
5530     ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction);
5531     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5532 
5533     //
5534     // We are done, go to the exit path without freeing the VAD as it remains
5535     // valid since we have not released the allocation.
5536     //
5537     Vad = NULL;
5538     Status = STATUS_SUCCESS;
5539     goto FinalPath;
5540 
5541     //
5542     // In the failure path, we detach and derefernece the target process, and
5543     // return whatever failure code was sent.
5544     //
5545 FailPath:
5546     MmUnlockAddressSpace(AddressSpace);
5547     if (Attached) KeUnstackDetachProcess(&ApcState);
5548     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5549     return Status;
5550 }
5551 
5552 
5553 PHYSICAL_ADDRESS
5554 NTAPI
5555 MmGetPhysicalAddress(PVOID Address)
5556 {
5557     PHYSICAL_ADDRESS PhysicalAddress;
5558     MMPDE TempPde;
5559     MMPTE TempPte;
5560 
5561     /* Check if the PXE/PPE/PDE is valid */
5562     if (
5563 #if (_MI_PAGING_LEVELS == 4)
5564         (MiAddressToPxe(Address)->u.Hard.Valid) &&
5565 #endif
5566 #if (_MI_PAGING_LEVELS >= 3)
5567         (MiAddressToPpe(Address)->u.Hard.Valid) &&
5568 #endif
5569         (MiAddressToPde(Address)->u.Hard.Valid))
5570     {
5571         /* Check for large pages */
5572         TempPde = *MiAddressToPde(Address);
5573         if (TempPde.u.Hard.LargePage)
5574         {
5575             /* Physical address is base page + large page offset */
5576             PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5577             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5578             return PhysicalAddress;
5579         }
5580 
5581         /* Check if the PTE is valid */
5582         TempPte = *MiAddressToPte(Address);
5583         if (TempPte.u.Hard.Valid)
5584         {
5585             /* Physical address is base page + page offset */
5586             PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5587             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5588             return PhysicalAddress;
5589         }
5590     }
5591 
5592     KeRosDumpStackFrames(NULL, 20);
5593     DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5594     PhysicalAddress.QuadPart = 0;
5595     return PhysicalAddress;
5596 }
5597 
5598 
5599 /* EOF */
5600