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