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