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