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