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