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