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