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