xref: /reactos/ntoskrnl/mm/ARM3/virtual.c (revision 2b91b296)
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)0xBADBADA3);
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     if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4782     {
4783         DPRINT1("Illegal commit of non-ARM3 section!\n");
4784         Status = STATUS_ALREADY_COMMITTED;
4785         goto FailPath;
4786     }
4787 
4788     // Is this a previously reserved section being committed? If so, enter the
4789     // special section path
4790     //
4791     if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4792     {
4793         //
4794         // You cannot commit large page sections through this API
4795         //
4796         if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4797         {
4798             DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4799             Status = STATUS_INVALID_PAGE_PROTECTION;
4800             goto FailPath;
4801         }
4802 
4803         //
4804         // You can only use caching flags on a rotate VAD
4805         //
4806         if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4807             (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4808         {
4809             DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4810             Status = STATUS_INVALID_PAGE_PROTECTION;
4811             goto FailPath;
4812         }
4813 
4814         //
4815         // We should make sure that the section's permissions aren't being
4816         // messed with
4817         //
4818         if (FoundVad->u.VadFlags.NoChange)
4819         {
4820             //
4821             // Make sure it's okay to touch it
4822             // Note: The Windows 2003 kernel has a bug here, passing the
4823             // unaligned base address together with the aligned size,
4824             // potentially covering a region larger than the actual allocation.
4825             // Might be exposed through NtGdiCreateDIBSection w/ section handle
4826             // For now we keep this behavior.
4827             // TODO: analyze possible implications, create test case
4828             //
4829             Status = MiCheckSecuredVad(FoundVad,
4830                                        PBaseAddress,
4831                                        PRegionSize,
4832                                        ProtectionMask);
4833             if (!NT_SUCCESS(Status))
4834             {
4835                 DPRINT1("Secured VAD being messed around with\n");
4836                 goto FailPath;
4837             }
4838         }
4839 
4840         //
4841         // ARM3 does not support file-backed sections, only shared memory
4842         //
4843         ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4844 
4845         //
4846         // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4847         //
4848         if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4849             (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4850         {
4851             DPRINT1("Invalid page protection for rotate VAD\n");
4852             Status = STATUS_INVALID_PAGE_PROTECTION;
4853             goto FailPath;
4854         }
4855 
4856         //
4857         // Compute PTE addresses and the quota charge, then grab the commit lock
4858         //
4859         PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4860         LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4861         QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4862         KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4863 
4864         //
4865         // Get the segment template PTE and start looping each page
4866         //
4867         TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4868         ASSERT(TempPte.u.Long != 0);
4869         while (PointerPte <= LastPte)
4870         {
4871             //
4872             // For each non-already-committed page, write the invalid template PTE
4873             //
4874             if (PointerPte->u.Long == 0)
4875             {
4876                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4877             }
4878             else
4879             {
4880                 QuotaFree++;
4881             }
4882             PointerPte++;
4883         }
4884 
4885         //
4886         // Now do the commit accounting and release the lock
4887         //
4888         ASSERT(QuotaCharge >= QuotaFree);
4889         QuotaCharge -= QuotaFree;
4890         FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4891         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4892 
4893         //
4894         // We are done with committing the section pages
4895         //
4896         Status = STATUS_SUCCESS;
4897         goto FailPath;
4898     }
4899 
4900     //
4901     // This is a specific ReactOS check because we only use normal VADs
4902     //
4903     ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4904 
4905     //
4906     // While this is an actual Windows check
4907     //
4908     ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4909 
4910     //
4911     // Throw out attempts to use copy-on-write through this API path
4912     //
4913     if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4914     {
4915         DPRINT1("Write copy attempted when not allowed\n");
4916         Status = STATUS_INVALID_PAGE_PROTECTION;
4917         goto FailPath;
4918     }
4919 
4920     //
4921     // Initialize a demand-zero PTE
4922     //
4923     TempPte.u.Long = 0;
4924     TempPte.u.Soft.Protection = ProtectionMask;
4925     ASSERT(TempPte.u.Long != 0);
4926 
4927     //
4928     // Get the PTE, PDE and the last PTE for this address range
4929     //
4930     PointerPde = MiAddressToPde(StartingAddress);
4931     PointerPte = MiAddressToPte(StartingAddress);
4932     LastPte = MiAddressToPte(EndingAddress);
4933 
4934     //
4935     // Update the commit charge in the VAD as well as in the process, and check
4936     // if this commit charge was now higher than the last recorded peak, in which
4937     // case we also update the peak
4938     //
4939     FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4940     Process->CommitCharge += (1 + LastPte - PointerPte);
4941     if (Process->CommitCharge > Process->CommitChargePeak)
4942     {
4943         Process->CommitChargePeak = Process->CommitCharge;
4944     }
4945 
4946     //
4947     // Lock the working set while we play with user pages and page tables
4948     //
4949     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4950 
4951     //
4952     // Make the current page table valid, and then loop each page within it
4953     //
4954     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4955     while (PointerPte <= LastPte)
4956     {
4957         //
4958         // Have we crossed into a new page table?
4959         //
4960         if (MiIsPteOnPdeBoundary(PointerPte))
4961         {
4962             //
4963             // Get the PDE and now make it valid too
4964             //
4965             PointerPde = MiPteToPde(PointerPte);
4966             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4967         }
4968 
4969         //
4970         // Is this a zero PTE as expected?
4971         //
4972         if (PointerPte->u.Long == 0)
4973         {
4974             //
4975             // First increment the count of pages in the page table for this
4976             // process
4977             //
4978             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
4979 
4980             //
4981             // And now write the invalid demand-zero PTE as requested
4982             //
4983             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4984         }
4985         else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
4986         {
4987             //
4988             // If the PTE was already decommitted, there is nothing else to do
4989             // but to write the new demand-zero PTE
4990             //
4991             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4992         }
4993         else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
4994         {
4995             //
4996             // We don't handle these scenarios yet
4997             //
4998             if (PointerPte->u.Soft.Valid == 0)
4999             {
5000                 ASSERT(PointerPte->u.Soft.Prototype == 0);
5001                 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
5002             }
5003 
5004             //
5005             // There's a change in protection, remember this for later, but do
5006             // not yet handle it.
5007             //
5008             ChangeProtection = TRUE;
5009         }
5010 
5011         //
5012         // Move to the next PTE
5013         //
5014         PointerPte++;
5015     }
5016 
5017     //
5018     // Release the working set lock, unlock the address space, and detach from
5019     // the target process if it was not the current process. Also dereference the
5020     // target process if this wasn't the case.
5021     //
5022     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5023     Status = STATUS_SUCCESS;
5024 FailPath:
5025     MmUnlockAddressSpace(AddressSpace);
5026 
5027     if (!NT_SUCCESS(Status))
5028     {
5029         if (Vad != NULL)
5030         {
5031             ExFreePoolWithTag(Vad, 'SdaV');
5032         }
5033     }
5034 
5035     //
5036     // Check if we need to update the protection
5037     //
5038     if (ChangeProtection)
5039     {
5040         PVOID ProtectBaseAddress = (PVOID)StartingAddress;
5041         SIZE_T ProtectSize = PRegionSize;
5042         ULONG OldProtection;
5043 
5044         //
5045         // Change the protection of the region
5046         //
5047         MiProtectVirtualMemory(Process,
5048                                &ProtectBaseAddress,
5049                                &ProtectSize,
5050                                Protect,
5051                                &OldProtection);
5052     }
5053 
5054 FailPathNoLock:
5055     if (Attached) KeUnstackDetachProcess(&ApcState);
5056     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5057 
5058     //
5059     // Only write back results on success
5060     //
5061     if (NT_SUCCESS(Status))
5062     {
5063         //
5064         // Use SEH to write back the base address and the region size. In the case
5065         // of an exception, we strangely do return back the exception code, even
5066         // though the memory *has* been allocated. This mimics Windows behavior and
5067         // there is not much we can do about it.
5068         //
5069         _SEH2_TRY
5070         {
5071             *URegionSize = PRegionSize;
5072             *UBaseAddress = (PVOID)StartingAddress;
5073         }
5074         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5075         {
5076             Status = _SEH2_GetExceptionCode();
5077         }
5078         _SEH2_END;
5079     }
5080 
5081     return Status;
5082 }
5083 
5084 /*
5085  * @implemented
5086  */
5087 NTSTATUS
5088 NTAPI
5089 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
5090                     IN PVOID* UBaseAddress,
5091                     IN PSIZE_T URegionSize,
5092                     IN ULONG FreeType)
5093 {
5094     PMEMORY_AREA MemoryArea;
5095     SIZE_T PRegionSize;
5096     PVOID PBaseAddress;
5097     LONG_PTR AlreadyDecommitted, CommitReduction = 0;
5098     ULONG_PTR StartingAddress, EndingAddress;
5099     PMMVAD Vad;
5100     NTSTATUS Status;
5101     PEPROCESS Process;
5102     PMMSUPPORT AddressSpace;
5103     PETHREAD CurrentThread = PsGetCurrentThread();
5104     PEPROCESS CurrentProcess = PsGetCurrentProcess();
5105     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
5106     KAPC_STATE ApcState;
5107     BOOLEAN Attached = FALSE;
5108     PAGED_CODE();
5109 
5110     //
5111     // Only two flags are supported, exclusively.
5112     //
5113     if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT)
5114     {
5115         DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType);
5116         return STATUS_INVALID_PARAMETER_4;
5117     }
5118 
5119     //
5120     // Enter SEH for probe and capture. On failure, return back to the caller
5121     // with an exception violation.
5122     //
5123     _SEH2_TRY
5124     {
5125         //
5126         // Check for user-mode parameters and make sure that they are writeable
5127         //
5128         if (PreviousMode != KernelMode)
5129         {
5130             ProbeForWritePointer(UBaseAddress);
5131             ProbeForWriteUlong(URegionSize);
5132         }
5133 
5134         //
5135         // Capture the current values
5136         //
5137         PBaseAddress = *UBaseAddress;
5138         PRegionSize = *URegionSize;
5139     }
5140     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5141     {
5142         _SEH2_YIELD(return _SEH2_GetExceptionCode());
5143     }
5144     _SEH2_END;
5145 
5146     //
5147     // Make sure the allocation isn't past the user area
5148     //
5149     if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5150     {
5151         DPRINT1("Virtual free base above User Space\n");
5152         return STATUS_INVALID_PARAMETER_2;
5153     }
5154 
5155     //
5156     // Make sure the allocation wouldn't overflow past the user area
5157     //
5158     if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5159     {
5160         DPRINT1("Region size would overflow into kernel-memory\n");
5161         return STATUS_INVALID_PARAMETER_3;
5162     }
5163 
5164     //
5165     // If this is for the current process, just use PsGetCurrentProcess
5166     //
5167     if (ProcessHandle == NtCurrentProcess())
5168     {
5169         Process = CurrentProcess;
5170     }
5171     else
5172     {
5173         //
5174         // Otherwise, reference the process with VM rights and attach to it if
5175         // this isn't the current process. We must attach because we'll be touching
5176         // PTEs and PDEs that belong to user-mode memory, and also touching the
5177         // Working Set which is stored in Hyperspace.
5178         //
5179         Status = ObReferenceObjectByHandle(ProcessHandle,
5180                                            PROCESS_VM_OPERATION,
5181                                            PsProcessType,
5182                                            PreviousMode,
5183                                            (PVOID*)&Process,
5184                                            NULL);
5185         if (!NT_SUCCESS(Status)) return Status;
5186         if (CurrentProcess != Process)
5187         {
5188             KeStackAttachProcess(&Process->Pcb, &ApcState);
5189             Attached = TRUE;
5190         }
5191     }
5192 
5193     DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n",
5194            Process, PBaseAddress, PRegionSize, FreeType);
5195 
5196     //
5197     // Lock the address space
5198     //
5199     AddressSpace = MmGetCurrentAddressSpace();
5200     MmLockAddressSpace(AddressSpace);
5201 
5202     //
5203     // If the address space is being deleted, fail the de-allocation since it's
5204     // too late to do anything about it
5205     //
5206     if (Process->VmDeleted)
5207     {
5208         DPRINT1("Process is dead\n");
5209         Status = STATUS_PROCESS_IS_TERMINATING;
5210         goto FailPath;
5211     }
5212 
5213     //
5214     // Compute start and end addresses, and locate the VAD
5215     //
5216     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5217     EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5218     Vad = MiLocateAddress((PVOID)StartingAddress);
5219     if (!Vad)
5220     {
5221         DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5222         Status = STATUS_MEMORY_NOT_ALLOCATED;
5223         goto FailPath;
5224     }
5225 
5226     //
5227     // If the range exceeds the VAD's ending VPN, fail this request
5228     //
5229     if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5230     {
5231         DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5232         Status = STATUS_UNABLE_TO_FREE_VM;
5233         goto FailPath;
5234     }
5235 
5236     //
5237     // Only private memory (except rotate VADs) can be freed through here */
5238     //
5239     if ((!(Vad->u.VadFlags.PrivateMemory) &&
5240          (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5241         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5242     {
5243         DPRINT1("Attempt to free section memory\n");
5244         Status = STATUS_UNABLE_TO_DELETE_SECTION;
5245         goto FailPath;
5246     }
5247 
5248     //
5249     // ARM3 does not yet handle protected VM
5250     //
5251     ASSERT(Vad->u.VadFlags.NoChange == 0);
5252 
5253     //
5254     // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5255     // and that is is an ARM3 memory area, and not a section view, as we currently
5256     // don't support freeing those though this interface.
5257     //
5258     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5259     ASSERT(MemoryArea);
5260     ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5261 
5262     //
5263     //  Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5264     //
5265     if (FreeType & MEM_RELEASE)
5266     {
5267         //
5268         // ARM3 only supports this VAD in this path
5269         //
5270         ASSERT(Vad->u.VadFlags.VadType == VadNone);
5271 
5272         //
5273         // Is the caller trying to remove the whole VAD, or remove only a portion
5274         // of it? If no region size is specified, then the assumption is that the
5275         // whole VAD is to be destroyed
5276         //
5277         if (!PRegionSize)
5278         {
5279             //
5280             // The caller must specify the base address identically to the range
5281             // that is stored in the VAD.
5282             //
5283             if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5284             {
5285                 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5286                 Status = STATUS_FREE_VM_NOT_AT_BASE;
5287                 goto FailPath;
5288             }
5289 
5290             //
5291             // Now compute the actual start/end addresses based on the VAD
5292             //
5293             StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5294             EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5295 
5296             //
5297             // Finally lock the working set and remove the VAD from the VAD tree
5298             //
5299             MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5300             ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5301             MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5302         }
5303         else
5304         {
5305             //
5306             // This means the caller wants to release a specific region within
5307             // the range. We have to find out which range this is -- the following
5308             // possibilities exist plus their union (CASE D):
5309             //
5310             // STARTING ADDRESS                                   ENDING ADDRESS
5311             // [<========][========================================][=========>]
5312             //   CASE A                  CASE B                       CASE C
5313             //
5314             //
5315             // First, check for case A or D
5316             //
5317             if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5318             {
5319                 //
5320                 // Check for case D
5321                 //
5322                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5323                 {
5324                     //
5325                     // This is the easiest one to handle -- it is identical to
5326                     // the code path above when the caller sets a zero region size
5327                     // and the whole VAD is destroyed
5328                     //
5329                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5330                     ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5331                     MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5332                 }
5333                 else
5334                 {
5335                     //
5336                     // This case is pretty easy too -- we compute a bunch of
5337                     // pages to decommit, and then push the VAD's starting address
5338                     // a bit further down, then decrement the commit charge
5339                     //
5340                     // NOT YET IMPLEMENTED IN ARM3.
5341                     //
5342                     DPRINT1("Case A not handled\n");
5343                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5344                     goto FailPath;
5345 
5346                     //
5347                     // After analyzing the VAD, set it to NULL so that we don't
5348                     // free it in the exit path
5349                     //
5350                     Vad = NULL;
5351                 }
5352             }
5353             else
5354             {
5355                 //
5356                 // This is case B or case C. First check for case C
5357                 //
5358                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5359                 {
5360                     PMEMORY_AREA MemoryArea;
5361 
5362                     //
5363                     // This is pretty easy and similar to case A. We compute the
5364                     // amount of pages to decommit, update the VAD's commit charge
5365                     // and then change the ending address of the VAD to be a bit
5366                     // smaller.
5367                     //
5368                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5369                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5370                                                                 EndingAddress,
5371                                                                 Vad,
5372                                                                 Process);
5373                     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5374                     // For ReactOS: shrink the corresponding memory area
5375                     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5376                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5377                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5378                     Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT;
5379                     MemoryArea->VadNode.EndingVpn = Vad->EndingVpn;
5380                 }
5381                 else
5382                 {
5383                     //
5384                     // This is case B and the hardest one. Because we are removing
5385                     // a chunk of memory from the very middle of the VAD, we must
5386                     // actually split the VAD into two new VADs and compute the
5387                     // commit charges for each of them, and reinsert new charges.
5388                     //
5389                     // NOT YET IMPLEMENTED IN ARM3.
5390                     //
5391                     DPRINT1("Case B not handled\n");
5392                     Status = STATUS_FREE_VM_NOT_AT_BASE;
5393                     goto FailPath;
5394                 }
5395 
5396                 //
5397                 // After analyzing the VAD, set it to NULL so that we don't
5398                 // free it in the exit path
5399                 //
5400                 Vad = NULL;
5401             }
5402         }
5403 
5404         //
5405         // Now we have a range of pages to dereference, so call the right API
5406         // to do that and then release the working set, since we're done messing
5407         // around with process pages.
5408         //
5409         MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5410         MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5411         Status = STATUS_SUCCESS;
5412 
5413 FinalPath:
5414         //
5415         // Update the process counters
5416         //
5417         PRegionSize = EndingAddress - StartingAddress + 1;
5418         Process->CommitCharge -= CommitReduction;
5419         if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5420 
5421         //
5422         // Unlock the address space and free the VAD in failure cases. Next,
5423         // detach from the target process so we can write the region size and the
5424         // base address to the correct source process, and dereference the target
5425         // process.
5426         //
5427         MmUnlockAddressSpace(AddressSpace);
5428         if (Vad) ExFreePool(Vad);
5429         if (Attached) KeUnstackDetachProcess(&ApcState);
5430         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5431 
5432         //
5433         // Use SEH to safely return the region size and the base address of the
5434         // deallocation. If we get an access violation, don't return a failure code
5435         // as the deallocation *has* happened. The caller will just have to figure
5436         // out another way to find out where it is (such as VirtualQuery).
5437         //
5438         _SEH2_TRY
5439         {
5440             *URegionSize = PRegionSize;
5441             *UBaseAddress = (PVOID)StartingAddress;
5442         }
5443         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5444         {
5445         }
5446         _SEH2_END;
5447         return Status;
5448     }
5449 
5450     //
5451     // This is the decommit path. You cannot decommit from the following VADs in
5452     // Windows, so fail the vall
5453     //
5454     if ((Vad->u.VadFlags.VadType == VadAwe) ||
5455         (Vad->u.VadFlags.VadType == VadLargePages) ||
5456         (Vad->u.VadFlags.VadType == VadRotatePhysical))
5457     {
5458         DPRINT1("Trying to decommit from invalid VAD\n");
5459         Status = STATUS_MEMORY_NOT_ALLOCATED;
5460         goto FailPath;
5461     }
5462 
5463     //
5464     // If the caller did not specify a region size, first make sure that this
5465     // region is actually committed. If it is, then compute the ending address
5466     // based on the VAD.
5467     //
5468     if (!PRegionSize)
5469     {
5470         if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5471         {
5472             DPRINT1("Decomitting non-committed memory\n");
5473             Status = STATUS_FREE_VM_NOT_AT_BASE;
5474             goto FailPath;
5475         }
5476         EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5477     }
5478 
5479     //
5480     // Decommit the PTEs for the range plus the actual backing pages for the
5481     // range, then reduce that amount from the commit charge in the VAD
5482     //
5483     AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress,
5484                                          MiAddressToPte(EndingAddress),
5485                                          Process,
5486                                          Vad);
5487     CommitReduction = MiAddressToPte(EndingAddress) -
5488                       MiAddressToPte(StartingAddress) +
5489                       1 -
5490                       AlreadyDecommitted;
5491 
5492     ASSERT(CommitReduction >= 0);
5493     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5494     ASSERT(Vad->u.VadFlags.CommitCharge >= 0);
5495 
5496     //
5497     // We are done, go to the exit path without freeing the VAD as it remains
5498     // valid since we have not released the allocation.
5499     //
5500     Vad = NULL;
5501     Status = STATUS_SUCCESS;
5502     goto FinalPath;
5503 
5504     //
5505     // In the failure path, we detach and derefernece the target process, and
5506     // return whatever failure code was sent.
5507     //
5508 FailPath:
5509     MmUnlockAddressSpace(AddressSpace);
5510     if (Attached) KeUnstackDetachProcess(&ApcState);
5511     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5512     return Status;
5513 }
5514 
5515 
5516 PHYSICAL_ADDRESS
5517 NTAPI
5518 MmGetPhysicalAddress(PVOID Address)
5519 {
5520     PHYSICAL_ADDRESS PhysicalAddress;
5521     MMPDE TempPde;
5522     MMPTE TempPte;
5523 
5524     /* Check if the PXE/PPE/PDE is valid */
5525     if (
5526 #if (_MI_PAGING_LEVELS == 4)
5527         (MiAddressToPxe(Address)->u.Hard.Valid) &&
5528 #endif
5529 #if (_MI_PAGING_LEVELS >= 3)
5530         (MiAddressToPpe(Address)->u.Hard.Valid) &&
5531 #endif
5532         (MiAddressToPde(Address)->u.Hard.Valid))
5533     {
5534         /* Check for large pages */
5535         TempPde = *MiAddressToPde(Address);
5536         if (TempPde.u.Hard.LargePage)
5537         {
5538             /* Physical address is base page + large page offset */
5539             PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5540             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5541             return PhysicalAddress;
5542         }
5543 
5544         /* Check if the PTE is valid */
5545         TempPte = *MiAddressToPte(Address);
5546         if (TempPte.u.Hard.Valid)
5547         {
5548             /* Physical address is base page + page offset */
5549             PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5550             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5551             return PhysicalAddress;
5552         }
5553     }
5554 
5555     KeRosDumpStackFrames(NULL, 20);
5556     DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5557     PhysicalAddress.QuadPart = 0;
5558     return PhysicalAddress;
5559 }
5560 
5561 
5562 /* EOF */
5563