ReactOS 0.4.15-dev-6679-g945ee4b
virtual.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9/* INCLUDES *******************************************************************/
10
11#include <ntoskrnl.h>
12#define NDEBUG
13#include <debug.h>
14
15#define MODULE_INVOLVED_IN_ARM3
16#include <mm/ARM3/miarm.h>
17
18#define MI_MAPPED_COPY_PAGES 14
19#define MI_POOL_COPY_BYTES 512
20#define MI_MAX_TRANSFER_SIZE 64 * 1024
21
25 IN OUT PSIZE_T NumberOfBytesToProtect,
26 IN ULONG NewAccessProtection,
27 OUT PULONG OldAccessProtection OPTIONAL);
28
29VOID
32 IN PMMPTE PointerPte,
33 IN ULONG ProtectionMask,
34 IN PMMPFN Pfn1,
35 IN BOOLEAN CaptureDirtyBit);
36
37
38/* PRIVATE FUNCTIONS **********************************************************/
39
43 IN ULONG_PTR EndingAddress,
44 IN PMMVAD Vad,
46{
47 PMMPTE PointerPte, LastPte;
48 PMMPDE PointerPde;
49 BOOLEAN OnPdeBoundary = TRUE;
50#if _MI_PAGING_LEVELS >= 3
51 PMMPPE PointerPpe;
52 BOOLEAN OnPpeBoundary = TRUE;
53#if _MI_PAGING_LEVELS == 4
54 PMMPXE PointerPxe;
55 BOOLEAN OnPxeBoundary = TRUE;
56#endif
57#endif
58
59 /* Make sure this all makes sense */
60 ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared);
61 ASSERT(EndingAddress >= StartingAddress);
62 PointerPte = MiAddressToPte(StartingAddress);
63 LastPte = MiAddressToPte(EndingAddress);
64
65 /*
66 * In case this is a committed VAD, assume the whole range is committed
67 * and count the individually decommitted pages.
68 * In case it is not, assume the range is not committed and count the individually committed pages.
69 */
70 ULONG_PTR CommittedPages = Vad->u.VadFlags.MemCommit ? BYTES_TO_PAGES(EndingAddress - StartingAddress) : 0;
71
72 while (PointerPte <= LastPte)
73 {
74#if _MI_PAGING_LEVELS == 4
75 /* Check if PXE was ever paged in. */
76 if (OnPxeBoundary)
77 {
78 PointerPxe = MiPteToPxe(PointerPte);
79
80 /* Check that this loop is sane */
81 ASSERT(OnPpeBoundary);
82 ASSERT(OnPdeBoundary);
83
84 if (PointerPxe->u.Long == 0)
85 {
86 PointerPxe++;
87 PointerPte = MiPxeToPte(PointerPde);
88 continue;
89 }
90
91 if (PointerPxe->u.Hard.Valid == 0)
93 }
94 ASSERT(PointerPxe->u.Hard.Valid == 1);
95#endif
96
97#if _MI_PAGING_LEVELS >= 3
98 /* Now PPE */
99 if (OnPpeBoundary)
100 {
101 PointerPpe = MiPteToPpe(PointerPte);
102
103 /* Sanity again */
104 ASSERT(OnPdeBoundary);
105
106 if (PointerPpe->u.Long == 0)
107 {
108 PointerPpe++;
109 PointerPte = MiPpeToPte(PointerPpe);
110#if _MI_PAGING_LEVELS == 4
111 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
112#endif
113 continue;
114 }
115
116 if (PointerPpe->u.Hard.Valid == 0)
118 }
119 ASSERT(PointerPpe->u.Hard.Valid == 1);
120#endif
121
122 /* Last level is the PDE */
123 if (OnPdeBoundary)
124 {
125 PointerPde = MiPteToPde(PointerPte);
126 if (PointerPde->u.Long == 0)
127 {
128 PointerPde++;
129 PointerPte = MiPdeToPte(PointerPde);
130#if _MI_PAGING_LEVELS >= 3
131 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
132#if _MI_PAGING_LEVELS == 4
133 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
134#endif
135#endif
136 continue;
137 }
138
139 if (PointerPde->u.Hard.Valid == 0)
141 }
142 ASSERT(PointerPde->u.Hard.Valid == 1);
143
144 /* Is this PTE demand zero? */
145 if (PointerPte->u.Long != 0)
146 {
147 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
148 if ((PointerPte->u.Hard.Valid == 0) &&
149 (PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
150 ((PointerPte->u.Soft.Prototype == 0) ||
151 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
152 {
153 /* It is, so remove it from the count of committed pages if we have to */
154 if (Vad->u.VadFlags.MemCommit)
155 CommittedPages--;
156 }
157 else if (!Vad->u.VadFlags.MemCommit)
158 {
159 /* It is a valid, non-decommited, non-paged out PTE. Count it in. */
160 CommittedPages++;
161 }
162 }
163
164 /* Move to the next PTE */
165 PointerPte++;
166 /* Manage page tables */
167 OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte);
168#if _MI_PAGING_LEVELS >= 3
169 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
170#if _MI_PAGING_LEVELS == 4
171 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
172#endif
173#endif
174 }
175
176 /* Make sure we didn't mess this up */
177 ASSERT(CommittedPages <= BYTES_TO_PAGES(EndingAddress - StartingAddress));
178 return CommittedPages;
179}
180
181ULONG
182NTAPI
183MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
184 IN PEPROCESS CurrentProcess)
185{
187 BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
188 PETHREAD CurrentThread = PsGetCurrentThread();
189
190 /* Must be a non-pool page table, since those are double-mapped already */
191 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
192 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
193 (PageTableVirtualAddress > MmPagedPoolEnd));
194
195 /* Working set lock or PFN lock should be held */
197
198 /* Check if the page table is valid */
199 while (!MmIsAddressValid(PageTableVirtualAddress))
200 {
201 /* Release the working set lock */
203 CurrentThread,
204 &WsSafe,
205 &WsShared);
206
207 /* Fault it in */
208 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
209 if (!NT_SUCCESS(Status))
210 {
211 /* This should not fail */
212 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
213 1,
214 Status,
215 (ULONG_PTR)CurrentProcess,
216 (ULONG_PTR)PageTableVirtualAddress);
217 }
218
219 /* Lock the working set again */
220 MiLockProcessWorkingSetForFault(CurrentProcess,
221 CurrentThread,
222 WsSafe,
223 WsShared);
224
225 /* This flag will be useful later when we do better locking */
226 LockChange = TRUE;
227 }
228
229 /* Let caller know what the lock state is */
230 return LockChange;
231}
232
233ULONG
234NTAPI
237{
239 BOOLEAN LockChange = FALSE;
240
241 /* Must be e kernel address */
243
244 /* Check if the page is valid */
246 {
247 /* Release the PFN database */
248 MiReleasePfnLock(OldIrql);
249
250 /* Fault it in */
252 if (!NT_SUCCESS(Status))
253 {
254 /* This should not fail */
255 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
256 3,
257 Status,
258 0,
260 }
261
262 /* This flag will be useful later when we do better locking */
263 LockChange = TRUE;
264
265 /* Lock the PFN database */
266 OldIrql = MiAcquirePfnLock();
267 }
268
269 /* Let caller know what the lock state is */
270 return LockChange;
271}
272
274NTAPI
276 IN PFN_NUMBER PageCount,
277 IN ULONG Flags,
278 OUT PPFN_NUMBER ValidPages)
279{
280 PFN_COUNT ActualPages = 0;
281 PETHREAD CurrentThread = PsGetCurrentThread();
282 PMMPFN Pfn1, Pfn2;
283 PFN_NUMBER PageFrameIndex, PageTableIndex;
286
287 /* Lock the system working set */
288 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
289
290 /* Loop all pages */
291 while (PageCount)
292 {
293 /* Make sure there's some data about the page */
294 if (PointerPte->u.Long)
295 {
296 /* Normally this is one possibility -- freeing a valid page */
297 if (PointerPte->u.Hard.Valid)
298 {
299 /* Get the page PFN */
300 PageFrameIndex = PFN_FROM_PTE(PointerPte);
301 Pfn1 = MiGetPfnEntry(PageFrameIndex);
302
303 /* Should not have any working set data yet */
304 ASSERT(Pfn1->u1.WsIndex == 0);
305
306 /* Actual valid, legitimate, pages */
307 if (ValidPages) (*ValidPages)++;
308
309 /* Get the page table entry */
310 PageTableIndex = Pfn1->u4.PteFrame;
311 Pfn2 = MiGetPfnEntry(PageTableIndex);
312
313 /* Lock the PFN database */
314 OldIrql = MiAcquirePfnLock();
315
316 /* Delete it the page */
317 MI_SET_PFN_DELETED(Pfn1);
318 MiDecrementShareCount(Pfn1, PageFrameIndex);
319
320 /* Decrement the page table too */
321 MiDecrementShareCount(Pfn2, PageTableIndex);
322
323 /* Release the PFN database */
324 MiReleasePfnLock(OldIrql);
325
326 /* Destroy the PTE */
327 MI_ERASE_PTE(PointerPte);
328 }
329 else
330 {
331 /* As always, only handle current ARM3 scenarios */
332 ASSERT(PointerPte->u.Soft.Prototype == 0);
333 ASSERT(PointerPte->u.Soft.Transition == 0);
334
335 /*
336 * The only other ARM3 possibility is a demand zero page, which would
337 * mean freeing some of the paged pool pages that haven't even been
338 * touched yet, as part of a larger allocation.
339 *
340 * Right now, we shouldn't expect any page file information in the PTE
341 */
342 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
343
344 /* Destroy the PTE */
345 MI_ERASE_PTE(PointerPte);
346 }
347
348 /* Actual legitimate pages */
349 ActualPages++;
350 }
351
352 /* Keep going */
353 PointerPte++;
354 PageCount--;
355 }
356
357 /* Release the working set */
358 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
359
360 /* Flush the entire TLB */
362
363 /* Done */
364 return ActualPages;
365}
366
367VOID
368NTAPI
371 IN PEPROCESS CurrentProcess,
373{
374 PMMPFN Pfn1;
376 PFN_NUMBER PageFrameIndex;
377 PMMPDE PointerPde;
378
379 /* PFN lock must be held */
381
382 /* WorkingSet must be exclusively locked */
384
385 /* This must be current process. */
386 ASSERT(CurrentProcess == PsGetCurrentProcess());
387
388 /* Capture the PTE */
389 TempPte = *PointerPte;
390
391 /* See if the PTE is valid */
392 if (TempPte.u.Hard.Valid == 0)
393 {
394 /* Prototype and paged out PTEs not supported yet */
395 ASSERT(TempPte.u.Soft.Prototype == 0);
396 ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1));
397
398 if (TempPte.u.Soft.Transition)
399 {
400 /* Get the PFN entry */
401 PageFrameIndex = PFN_FROM_PTE(&TempPte);
402 Pfn1 = MiGetPfnEntry(PageFrameIndex);
403
404 DPRINT("Pte %p is transitional!\n", PointerPte);
405
406 /* Make sure the saved PTE address is valid */
407 ASSERT((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) == PointerPte);
408
409 /* Destroy the PTE */
410 MI_ERASE_PTE(PointerPte);
411
412 /* Drop the reference on the page table. */
414
415 /* In case of shared page, the prototype PTE must be in transition, not the process one */
416 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
417
418 /* Delete the PFN */
419 MI_SET_PFN_DELETED(Pfn1);
420
421 /* It must be either free (refcount == 0) or being written (refcount == 1) */
422 ASSERT(Pfn1->u3.e2.ReferenceCount == Pfn1->u3.e1.WriteInProgress);
423
424 /* See if we must free it ourselves, or if it will be freed once I/O is over */
425 if (Pfn1->u3.e2.ReferenceCount == 0)
426 {
427 /* And it should be in standby or modified list */
429
430 /* Unlink it and set its reference count to one */
432 Pfn1->u3.e2.ReferenceCount++;
433
434 /* This will put it back in free list and clean properly up */
435 MiDecrementReferenceCount(Pfn1, PageFrameIndex);
436 }
437 return;
438 }
439 }
440
441 /* Get the PFN entry */
442 PageFrameIndex = PFN_FROM_PTE(&TempPte);
443 Pfn1 = MiGetPfnEntry(PageFrameIndex);
444
445 /* Check if this is a valid, prototype PTE */
446 if (Pfn1->u3.e1.PrototypePte == 1)
447 {
448 /* Get the PDE and make sure it's faulted in */
449 PointerPde = MiPteToPde(PointerPte);
450 if (PointerPde->u.Hard.Valid == 0)
451 {
452#if (_MI_PAGING_LEVELS == 2)
453 /* Could be paged pool access from a new process -- synchronize the page directories */
455 {
456#endif
457 /* The PDE must be valid at this point */
458 KeBugCheckEx(MEMORY_MANAGEMENT,
459 0x61940,
460 (ULONG_PTR)PointerPte,
461 PointerPte->u.Long,
463 }
464#if (_MI_PAGING_LEVELS == 2)
465 }
466#endif
467 /* Drop the share count on the page table */
468 PointerPde = MiPteToPde(PointerPte);
470 PointerPde->u.Hard.PageFrameNumber);
471
472 /* Drop the share count */
473 MiDecrementShareCount(Pfn1, PageFrameIndex);
474
475 /* Either a fork, or this is the shared user data page */
476 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
477 {
478 /* If it's not the shared user page, then crash, since there's no fork() yet */
481 {
482 /* Must be some sort of memory corruption */
483 KeBugCheckEx(MEMORY_MANAGEMENT,
484 0x400,
485 (ULONG_PTR)PointerPte,
487 (ULONG_PTR)Pfn1->PteAddress);
488 }
489 }
490
491 /* Erase it */
492 MI_ERASE_PTE(PointerPte);
493 }
494 else
495 {
496 /* Make sure the saved PTE address is valid */
497 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
498 {
499 /* The PFN entry is illegal, or invalid */
500 KeBugCheckEx(MEMORY_MANAGEMENT,
501 0x401,
502 (ULONG_PTR)PointerPte,
503 PointerPte->u.Long,
504 (ULONG_PTR)Pfn1->PteAddress);
505 }
506
507 /* Erase the PTE */
508 MI_ERASE_PTE(PointerPte);
509
510 /* There should only be 1 shared reference count */
511 ASSERT(Pfn1->u2.ShareCount == 1);
512
513 /* Drop the reference on the page table. */
515
516 /* Mark the PFN for deletion and dereference what should be the last ref */
517 MI_SET_PFN_DELETED(Pfn1);
518 MiDecrementShareCount(Pfn1, PageFrameIndex);
519
520 /* We should eventually do this */
521 //CurrentProcess->NumberOfPrivatePages--;
522 }
523
524 /* Flush the TLB */
526}
527
528VOID
529NTAPI
531 IN ULONG_PTR EndingAddress,
532 IN PMMVAD Vad)
533{
534 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
535 PMMPDE PointerPde;
536#if (_MI_PAGING_LEVELS >= 3)
537 PMMPPE PointerPpe;
538#endif
539#if (_MI_PAGING_LEVELS >= 4)
540 PMMPPE PointerPxe;
541#endif
543 PEPROCESS CurrentProcess;
545 BOOLEAN AddressGap = FALSE;
546 PSUBSECTION Subsection;
547
548 /* Get out if this is a fake VAD, RosMm will free the marea pages */
549 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
550
551 /* Get the current process */
552 CurrentProcess = PsGetCurrentProcess();
553
554 /* Check if this is a section VAD or a VM VAD */
555 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
556 {
557 /* Don't worry about prototypes */
558 PrototypePte = LastPrototypePte = NULL;
559 }
560 else
561 {
562 /* Get the prototype PTE */
563 PrototypePte = Vad->FirstPrototypePte;
564 LastPrototypePte = Vad->FirstPrototypePte + 1;
565 }
566
567 /* In all cases, we don't support fork() yet */
568 ASSERT(CurrentProcess->CloneRoot == NULL);
569
570 /* Loop the PTE for each VA (EndingAddress is inclusive!) */
571 while (Va <= EndingAddress)
572 {
573#if (_MI_PAGING_LEVELS >= 4)
574 /* Get the PXE and check if it's valid */
575 PointerPxe = MiAddressToPxe((PVOID)Va);
576 if (!PointerPxe->u.Hard.Valid)
577 {
578 /* Check for unmapped range and skip it */
579 if (!PointerPxe->u.Long)
580 {
581 /* There are gaps in the address space */
582 AddressGap = TRUE;
583
584 /* Update Va and continue looping */
585 Va = (ULONG_PTR)MiPxeToAddress(PointerPxe + 1);
586 continue;
587 }
588
589 /* Make the PXE valid */
590 MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), CurrentProcess);
591 }
592#endif
593#if (_MI_PAGING_LEVELS >= 3)
594 /* Get the PPE and check if it's valid */
595 PointerPpe = MiAddressToPpe((PVOID)Va);
596 if (!PointerPpe->u.Hard.Valid)
597 {
598 /* Check for unmapped range and skip it */
599 if (!PointerPpe->u.Long)
600 {
601 /* There are gaps in the address space */
602 AddressGap = TRUE;
603
604 /* Update Va and continue looping */
605 Va = (ULONG_PTR)MiPpeToAddress(PointerPpe + 1);
606 continue;
607 }
608
609 /* Make the PPE valid */
610 MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), CurrentProcess);
611 }
612#endif
613 /* Skip invalid PDEs */
614 PointerPde = MiAddressToPde((PVOID)Va);
615 if (!PointerPde->u.Long)
616 {
617 /* There are gaps in the address space */
618 AddressGap = TRUE;
619
620 /* Check if all the PDEs are invalid, so there's nothing to free */
621 Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1);
622 continue;
623 }
624
625 /* Now check if the PDE is mapped in */
626 if (!PointerPde->u.Hard.Valid)
627 {
628 /* It isn't, so map it in */
629 PointerPte = MiPteToAddress(PointerPde);
630 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
631 }
632
633 /* Now we should have a valid PDE, mapped in, and still have some VA */
634 ASSERT(PointerPde->u.Hard.Valid == 1);
635 ASSERT(Va <= EndingAddress);
636
637 /* Check if this is a section VAD with gaps in it */
638 if ((AddressGap) && (LastPrototypePte))
639 {
640 /* We need to skip to the next correct prototype PTE */
642
643 /* And we need the subsection to skip to the next last prototype PTE */
644 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
645 if (Subsection)
646 {
647 /* Found it! */
648 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
649 }
650 else
651 {
652 /* No more subsections, we are done with prototype PTEs */
654 }
655 }
656
657 /* Lock the PFN Database while we delete the PTEs */
658 OldIrql = MiAcquirePfnLock();
659 PointerPte = MiAddressToPte(Va);
660 do
661 {
662 /* Making sure the PDE is still valid */
663 ASSERT(PointerPde->u.Hard.Valid == 1);
664
665 /* Capture the PDE and make sure it exists */
666 TempPte = *PointerPte;
667 if (TempPte.u.Long)
668 {
669 /* Check if the PTE is actually mapped in */
671 {
672 /* Are we dealing with section VAD? */
673 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
674 {
675 /* We need to skip to the next correct prototype PTE */
677
678 /* And we need the subsection to skip to the next last prototype PTE */
679 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
680 if (Subsection)
681 {
682 /* Found it! */
683 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
684 }
685 else
686 {
687 /* No more subsections, we are done with prototype PTEs */
689 }
690 }
691
692 /* Check for prototype PTE */
693 if ((TempPte.u.Hard.Valid == 0) &&
694 (TempPte.u.Soft.Prototype == 1))
695 {
696 /* Just nuke it */
697 MI_ERASE_PTE(PointerPte);
698 }
699 else
700 {
701 /* Delete the PTE proper */
702 MiDeletePte(PointerPte,
703 (PVOID)Va,
704 CurrentProcess,
706 }
707 }
708 else
709 {
710 /* The PTE was never mapped, just nuke it here */
711 MI_ERASE_PTE(PointerPte);
712 }
713
715 {
716 ASSERT(PointerPde->u.Long != 0);
717
718 /* Delete the PDE proper */
719 MiDeletePde(PointerPde, CurrentProcess);
720
721 /* Continue with the next PDE */
722 Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1);
723
724 /* Use this to detect address gaps */
725 PointerPte++;
726 break;
727 }
728 }
729
730 /* Update the address and PTE for it */
731 Va += PAGE_SIZE;
732 PointerPte++;
733 PrototypePte++;
734 } while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
735
736 /* Release the lock */
737 MiReleasePfnLock(OldIrql);
738
739 if (Va > EndingAddress) return;
740
741 /* Check if we exited the loop regularly */
742 AddressGap = (PointerPte != MiAddressToPte(Va));
743 }
744}
745
746LONG
748 OUT PBOOLEAN HaveBadAddress,
749 OUT PULONG_PTR BadAddress)
750{
751 PEXCEPTION_RECORD ExceptionRecord;
752 PAGED_CODE();
753
754 //
755 // Assume default
756 //
757 *HaveBadAddress = FALSE;
758
759 //
760 // Get the exception record
761 //
762 ExceptionRecord = ExceptionInfo->ExceptionRecord;
763
764 //
765 // Look at the exception code
766 //
767 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
768 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
769 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
770 {
771 //
772 // We can tell the address if we have more than one parameter
773 //
774 if (ExceptionRecord->NumberParameters > 1)
775 {
776 //
777 // Return the address
778 //
779 *HaveBadAddress = TRUE;
780 *BadAddress = ExceptionRecord->ExceptionInformation[1];
781 }
782 }
783
784 //
785 // Continue executing the next handler
786 //
788}
789
791NTAPI
794 IN PEPROCESS TargetProcess,
798 OUT PSIZE_T ReturnSize)
799{
800 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
801 PMDL Mdl = (PMDL)MdlBuffer;
802 SIZE_T TotalSize, CurrentSize, RemainingSize;
803 volatile BOOLEAN FailedInProbe = FALSE;
804 volatile BOOLEAN PagesLocked = FALSE;
805 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
806 volatile PVOID MdlAddress = NULL;
808 BOOLEAN HaveBadAddress;
809 ULONG_PTR BadAddress;
811 PAGED_CODE();
812
813 //
814 // Calculate the maximum amount of data to move
815 //
816 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
817 if (BufferSize <= TotalSize) TotalSize = BufferSize;
818 CurrentSize = TotalSize;
819 RemainingSize = BufferSize;
820
821 //
822 // Loop as long as there is still data
823 //
824 while (RemainingSize > 0)
825 {
826 //
827 // Check if this transfer will finish everything off
828 //
829 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
830
831 //
832 // Attach to the source address space
833 //
834 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
835
836 //
837 // Check state for this pass
838 //
839 ASSERT(MdlAddress == NULL);
840 ASSERT(PagesLocked == FALSE);
841 ASSERT(FailedInProbe == FALSE);
842
843 //
844 // Protect user-mode copy
845 //
847 {
848 //
849 // If this is our first time, probe the buffer
850 //
851 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
852 {
853 //
854 // Catch a failure here
855 //
856 FailedInProbe = TRUE;
857
858 //
859 // Do the probe
860 //
862
863 //
864 // Passed
865 //
866 FailedInProbe = FALSE;
867 }
868
869 //
870 // Initialize and probe and lock the MDL
871 //
872 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
874 PagesLocked = TRUE;
875 }
877 {
879 }
881
882 /* Detach from source process */
884
885 if (Status != STATUS_SUCCESS)
886 {
887 goto Exit;
888 }
889
890 //
891 // Now map the pages
892 //
895 MmCached,
896 NULL,
897 FALSE,
899 if (!MdlAddress)
900 {
902 goto Exit;
903 }
904
905 //
906 // Grab to the target process
907 //
908 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
909
911 {
912 //
913 // Check if this is our first time through
914 //
915 if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
916 {
917 //
918 // Catch a failure here
919 //
920 FailedInProbe = TRUE;
921
922 //
923 // Do the probe
924 //
926
927 //
928 // Passed
929 //
930 FailedInProbe = FALSE;
931 }
932
933 //
934 // Now do the actual move
935 //
936 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
937 }
939 &HaveBadAddress,
940 &BadAddress))
941 {
942 *ReturnSize = BufferSize - RemainingSize;
943 //
944 // Check if we failed during the probe
945 //
946 if (FailedInProbe)
947 {
948 //
949 // Exit
950 //
952 }
953 else
954 {
955 //
956 // Othewise we failed during the move.
957 // Check if we know exactly where we stopped copying
958 //
959 if (HaveBadAddress)
960 {
961 //
962 // Return the exact number of bytes copied
963 //
964 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
965 }
966 //
967 // Return partial copy
968 //
970 }
971 }
972 _SEH2_END;
973
974 /* Detach from target process */
976
977 //
978 // Check for SEH status
979 //
980 if (Status != STATUS_SUCCESS)
981 {
982 goto Exit;
983 }
984
985 //
986 // Unmap and unlock
987 //
988 MmUnmapLockedPages(MdlAddress, Mdl);
989 MdlAddress = NULL;
991 PagesLocked = FALSE;
992
993 //
994 // Update location and size
995 //
996 RemainingSize -= CurrentSize;
997 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
998 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
999 }
1000
1001Exit:
1002 if (MdlAddress != NULL)
1003 MmUnmapLockedPages(MdlAddress, Mdl);
1004 if (PagesLocked)
1006
1007 //
1008 // All bytes read
1009 //
1010 if (Status == STATUS_SUCCESS)
1011 *ReturnSize = BufferSize;
1012 return Status;
1013}
1014
1016NTAPI
1019 IN PEPROCESS TargetProcess,
1023 OUT PSIZE_T ReturnSize)
1024{
1025 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
1026 SIZE_T TotalSize, CurrentSize, RemainingSize;
1027 volatile BOOLEAN FailedInProbe = FALSE, HavePoolAddress = FALSE;
1028 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
1029 PVOID PoolAddress;
1031 BOOLEAN HaveBadAddress;
1032 ULONG_PTR BadAddress;
1034 PAGED_CODE();
1035
1036 DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1037 BufferSize, SourceProcess, SourceAddress, TargetProcess, TargetAddress);
1038
1039 //
1040 // Calculate the maximum amount of data to move
1041 //
1042 TotalSize = MI_MAX_TRANSFER_SIZE;
1043 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
1044 CurrentSize = TotalSize;
1045 RemainingSize = BufferSize;
1046
1047 //
1048 // Check if we can use the stack
1049 //
1051 {
1052 //
1053 // Use it
1054 //
1055 PoolAddress = (PVOID)StackBuffer;
1056 }
1057 else
1058 {
1059 //
1060 // Allocate pool
1061 //
1062 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1063 if (!PoolAddress) ASSERT(FALSE);
1064 HavePoolAddress = TRUE;
1065 }
1066
1067 //
1068 // Loop as long as there is still data
1069 //
1070 while (RemainingSize > 0)
1071 {
1072 //
1073 // Check if this transfer will finish everything off
1074 //
1075 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1076
1077 //
1078 // Attach to the source address space
1079 //
1080 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1081
1082 /* Check that state is sane */
1083 ASSERT(FailedInProbe == FALSE);
1085
1086 //
1087 // Protect user-mode copy
1088 //
1089 _SEH2_TRY
1090 {
1091 //
1092 // If this is our first time, probe the buffer
1093 //
1094 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1095 {
1096 //
1097 // Catch a failure here
1098 //
1099 FailedInProbe = TRUE;
1100
1101 //
1102 // Do the probe
1103 //
1105
1106 //
1107 // Passed
1108 //
1109 FailedInProbe = FALSE;
1110 }
1111
1112 //
1113 // Do the copy
1114 //
1115 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1116 }
1118 &HaveBadAddress,
1119 &BadAddress))
1120 {
1121 *ReturnSize = BufferSize - RemainingSize;
1122
1123 //
1124 // Check if we failed during the probe
1125 //
1126 if (FailedInProbe)
1127 {
1128 //
1129 // Exit
1130 //
1132 }
1133 else
1134 {
1135 //
1136 // We failed during the move.
1137 // Check if we know exactly where we stopped copying
1138 //
1139 if (HaveBadAddress)
1140 {
1141 //
1142 // Return the exact number of bytes copied
1143 //
1144 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1145 }
1146 //
1147 // Return partial copy
1148 //
1150 }
1151 }
1152 _SEH2_END
1153
1154 /* Let go of the source */
1156
1157 if (Status != STATUS_SUCCESS)
1158 {
1159 goto Exit;
1160 }
1161
1162 /* Grab the target process */
1163 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1164
1165 _SEH2_TRY
1166 {
1167 //
1168 // Check if this is our first time through
1169 //
1170 if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
1171 {
1172 //
1173 // Catch a failure here
1174 //
1175 FailedInProbe = TRUE;
1176
1177 //
1178 // Do the probe
1179 //
1181
1182 //
1183 // Passed
1184 //
1185 FailedInProbe = FALSE;
1186 }
1187
1188 //
1189 // Now do the actual move
1190 //
1191 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1192 }
1194 &HaveBadAddress,
1195 &BadAddress))
1196 {
1197 *ReturnSize = BufferSize - RemainingSize;
1198 //
1199 // Check if we failed during the probe
1200 //
1201 if (FailedInProbe)
1202 {
1203 //
1204 // Exit
1205 //
1207 }
1208 else
1209 {
1210 //
1211 // Otherwise we failed during the move.
1212 // Check if we know exactly where we stopped copying
1213 //
1214 if (HaveBadAddress)
1215 {
1216 //
1217 // Return the exact number of bytes copied
1218 //
1219 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1220 }
1221 //
1222 // Return partial copy
1223 //
1225 }
1226 }
1227 _SEH2_END;
1228
1229 //
1230 // Detach from target
1231 //
1233
1234 //
1235 // Check for SEH status
1236 //
1237 if (Status != STATUS_SUCCESS)
1238 {
1239 goto Exit;
1240 }
1241
1242 //
1243 // Update location and size
1244 //
1245 RemainingSize -= CurrentSize;
1246 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1247 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1248 CurrentSize);
1249 }
1250
1251Exit:
1252 //
1253 // Check if we had allocated pool
1254 //
1255 if (HavePoolAddress)
1256 ExFreePoolWithTag(PoolAddress, 'VmRw');
1257
1258 //
1259 // All bytes read
1260 //
1261 if (Status == STATUS_SUCCESS)
1262 *ReturnSize = BufferSize;
1263 return Status;
1264}
1265
1267NTAPI
1270 IN PEPROCESS TargetProcess,
1274 OUT PSIZE_T ReturnSize)
1275{
1277 PEPROCESS Process = SourceProcess;
1278
1279 //
1280 // Don't accept zero-sized buffers
1281 //
1282 if (!BufferSize) return STATUS_SUCCESS;
1283
1284 //
1285 // If we are copying from ourselves, lock the target instead
1286 //
1287 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1288
1289 //
1290 // Acquire rundown protection
1291 //
1292 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1293 {
1294 //
1295 // Fail
1296 //
1298 }
1299
1300 //
1301 // See if we should use the pool copy
1302 //
1304 {
1305 //
1306 // Use MDL-copy
1307 //
1308 Status = MiDoMappedCopy(SourceProcess,
1310 TargetProcess,
1312 BufferSize,
1314 ReturnSize);
1315 }
1316 else
1317 {
1318 //
1319 // Do pool copy
1320 //
1321 Status = MiDoPoolCopy(SourceProcess,
1323 TargetProcess,
1325 BufferSize,
1327 ReturnSize);
1328 }
1329
1330 //
1331 // Release the lock
1332 //
1333 ExReleaseRundownProtection(&Process->RundownProtect);
1334 return Status;
1335}
1336
1338NTAPI
1343{
1344 PAGED_CODE();
1345
1347
1349}
1350
1351ULONG
1352NTAPI
1354{
1355 MMPTE TempPte;
1356 PMMPFN Pfn;
1357 PEPROCESS CurrentProcess;
1358 PETHREAD CurrentThread;
1359 BOOLEAN WsSafe, WsShared;
1360 ULONG Protect;
1361 KIRQL OldIrql;
1362 PAGED_CODE();
1363
1364 /* Copy this PTE's contents */
1365 TempPte = *PointerPte;
1366
1367 /* Assure it's not totally zero */
1368 ASSERT(TempPte.u.Long);
1369
1370 /* Check for a special prototype format */
1371 if ((TempPte.u.Soft.Valid == 0) &&
1372 (TempPte.u.Soft.Prototype == 1))
1373 {
1374 /* Check if the prototype PTE is not yet pointing to a PTE */
1375 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1376 {
1377 /* The prototype PTE contains the protection */
1378 return MmProtectToValue[TempPte.u.Soft.Protection];
1379 }
1380
1381 /* Get a pointer to the underlying shared PTE */
1382 PointerPte = MiProtoPteToPte(&TempPte);
1383
1384 /* Since the PTE we want to read can be paged out at any time, we need
1385 to release the working set lock first, so that it can be paged in */
1386 CurrentThread = PsGetCurrentThread();
1387 CurrentProcess = PsGetCurrentProcess();
1388 MiUnlockProcessWorkingSetForFault(CurrentProcess,
1389 CurrentThread,
1390 &WsSafe,
1391 &WsShared);
1392
1393 /* Now read the PTE value */
1394 TempPte = *PointerPte;
1395
1396 /* Check if that one is invalid */
1397 if (!TempPte.u.Hard.Valid)
1398 {
1399 /* We get the protection directly from this PTE */
1400 Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1401 }
1402 else
1403 {
1404 /* The PTE is valid, so we might need to get the protection from
1405 the PFN. Lock the PFN database */
1406 OldIrql = MiAcquirePfnLock();
1407
1408 /* Check if the PDE is still valid */
1409 if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
1410 {
1411 /* It's not, make it valid */
1413 }
1414
1415 /* Now it's safe to read the PTE value again */
1416 TempPte = *PointerPte;
1417 ASSERT(TempPte.u.Long != 0);
1418
1419 /* Check again if the PTE is invalid */
1420 if (!TempPte.u.Hard.Valid)
1421 {
1422 /* The PTE is not valid, so we can use it's protection field */
1423 Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1424 }
1425 else
1426 {
1427 /* The PTE is valid, so we can find the protection in the
1428 OriginalPte field of the PFN */
1431 }
1432
1433 /* Release the PFN database */
1434 MiReleasePfnLock(OldIrql);
1435 }
1436
1437 /* Lock the working set again */
1438 MiLockProcessWorkingSetForFault(CurrentProcess,
1439 CurrentThread,
1440 WsSafe,
1441 WsShared);
1442
1443 return Protect;
1444 }
1445
1446 /* In the easy case of transition or demand zero PTE just return its protection */
1447 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1448
1449 /* If we get here, the PTE is valid, so look up the page in PFN database */
1451 if (!Pfn->u3.e1.PrototypePte)
1452 {
1453 /* Return protection of the original pte */
1454 ASSERT(Pfn->u4.AweAllocation == 0);
1456 }
1457
1458 /* This is software PTE */
1459 DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1460 DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1461 DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1462 DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1463 return MmProtectToValue[TempPte.u.Soft.Protection];
1464}
1465
1466ULONG
1467NTAPI
1469 IN PMMVAD Vad,
1470 IN PEPROCESS TargetProcess,
1471 OUT PULONG ReturnedProtect,
1472 OUT PVOID *NextVa)
1473{
1474
1475 PMMPTE PointerPte, ProtoPte;
1476 PMMPDE PointerPde;
1477#if (_MI_PAGING_LEVELS >= 3)
1478 PMMPPE PointerPpe;
1479#endif
1480#if (_MI_PAGING_LEVELS >= 4)
1481 PMMPXE PointerPxe;
1482#endif
1483 MMPTE TempPte, TempProtoPte;
1484 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1486 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1487 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1488
1489 /* Only normal VADs supported */
1490 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1491
1492 /* Get the PDE and PTE for the address */
1493 PointerPde = MiAddressToPde(Va);
1494 PointerPte = MiAddressToPte(Va);
1495#if (_MI_PAGING_LEVELS >= 3)
1496 PointerPpe = MiAddressToPpe(Va);
1497#endif
1498#if (_MI_PAGING_LEVELS >= 4)
1499 PointerPxe = MiAddressToPxe(Va);
1500#endif
1501
1502 /* Return the next range */
1503 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1504
1505 do
1506 {
1507#if (_MI_PAGING_LEVELS >= 4)
1508 /* Does the PXE exist? */
1509 if (PointerPxe->u.Long == 0)
1510 {
1511 /* It does not, next range starts at the next PXE */
1512 *NextVa = MiPxeToAddress(PointerPxe + 1);
1513 break;
1514 }
1515
1516 /* Is the PXE valid? */
1517 if (PointerPxe->u.Hard.Valid == 0)
1518 {
1519 /* Is isn't, fault it in (make the PPE accessible) */
1520 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1521 }
1522#endif
1523#if (_MI_PAGING_LEVELS >= 3)
1524 /* Does the PPE exist? */
1525 if (PointerPpe->u.Long == 0)
1526 {
1527 /* It does not, next range starts at the next PPE */
1528 *NextVa = MiPpeToAddress(PointerPpe + 1);
1529 break;
1530 }
1531
1532 /* Is the PPE valid? */
1533 if (PointerPpe->u.Hard.Valid == 0)
1534 {
1535 /* Is isn't, fault it in (make the PDE accessible) */
1536 MiMakeSystemAddressValid(PointerPde, TargetProcess);
1537 }
1538#endif
1539
1540 /* Does the PDE exist? */
1541 if (PointerPde->u.Long == 0)
1542 {
1543 /* It does not, next range starts at the next PDE */
1544 *NextVa = MiPdeToAddress(PointerPde + 1);
1545 break;
1546 }
1547
1548 /* Is the PDE valid? */
1549 if (PointerPde->u.Hard.Valid == 0)
1550 {
1551 /* Is isn't, fault it in (make the PTE accessible) */
1552 MiMakeSystemAddressValid(PointerPte, TargetProcess);
1553 }
1554
1555 /* We have a PTE that we can access now! */
1556 ValidPte = TRUE;
1557
1558 } while (FALSE);
1559
1560 /* Is it safe to try reading the PTE? */
1561 if (ValidPte)
1562 {
1563 /* FIXME: watch out for large pages */
1564 ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1565
1566 /* Capture the PTE */
1567 TempPte = *PointerPte;
1568 if (TempPte.u.Long != 0)
1569 {
1570 /* The PTE is valid, so it's not zeroed out */
1572
1573 /* Is it a decommited, invalid, or faulted PTE? */
1574 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1575 (TempPte.u.Hard.Valid == 0) &&
1576 ((TempPte.u.Soft.Prototype == 0) ||
1577 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1578 {
1579 /* Otherwise our defaults should hold */
1580 ASSERT(Protect == 0);
1582 }
1583 else
1584 {
1585 /* This means it's committed */
1586 State = MEM_COMMIT;
1587
1588 /* We don't support these */
1589 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1590 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1591 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1592
1593 /* Get protection state of this page */
1594 Protect = MiGetPageProtection(PointerPte);
1595
1596 /* Check if this is an image-backed VAD */
1597 if ((TempPte.u.Soft.Valid == 0) &&
1598 (TempPte.u.Soft.Prototype == 1) &&
1599 (Vad->u.VadFlags.PrivateMemory == 0) &&
1600 (Vad->ControlArea))
1601 {
1602 DPRINT1("Not supported\n");
1603 ASSERT(FALSE);
1604 }
1605 }
1606 }
1607 }
1608
1609 /* Check if this was a demand-zero PTE, since we need to find the state */
1610 if (DemandZeroPte)
1611 {
1612 /* Not yet handled */
1613 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1614 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1615
1616 /* Check if this is private commited memory, or an section-backed VAD */
1617 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1618 {
1619 /* Tell caller about the next range */
1620 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1621
1622 /* Get the prototype PTE for this VAD */
1623 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1624 (ULONG_PTR)Va >> PAGE_SHIFT);
1625 if (ProtoPte)
1626 {
1627 /* We should unlock the working set, but it's not being held! */
1628
1629 /* Is the prototype PTE actually valid (committed)? */
1630 TempProtoPte = *ProtoPte;
1631 if (TempProtoPte.u.Long)
1632 {
1633 /* Unless this is a memory-mapped file, handle it like private VAD */
1634 State = MEM_COMMIT;
1635 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1636 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1637 }
1638
1639 /* We should re-lock the working set */
1640 }
1641 }
1642 else if (Vad->u.VadFlags.MemCommit)
1643 {
1644 /* This is committed memory */
1645 State = MEM_COMMIT;
1646
1647 /* Convert the protection */
1648 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1649 }
1650 }
1651
1652 /* Return the protection code */
1653 *ReturnedProtect = Protect;
1654 return State;
1655}
1656
1658NTAPI
1661 OUT PVOID MemoryInformation,
1662 IN SIZE_T MemoryInformationLength,
1664{
1665 PEPROCESS TargetProcess;
1667 PMMVAD Vad = NULL;
1668 PVOID Address, NextAddress;
1670 ULONG NewProtect, NewState;
1671 ULONG_PTR BaseVpn;
1672 MEMORY_BASIC_INFORMATION MemoryInfo;
1677
1678 /* Check for illegal addresses in user-space, or the shared memory area */
1681 {
1683
1684 /* Make up an info structure describing this range */
1685 MemoryInfo.BaseAddress = Address;
1686 MemoryInfo.AllocationProtect = PAGE_READONLY;
1687 MemoryInfo.Type = MEM_PRIVATE;
1688
1689 /* Special case for shared data */
1691 {
1693 MemoryInfo.State = MEM_COMMIT;
1694 MemoryInfo.Protect = PAGE_READONLY;
1695 MemoryInfo.RegionSize = PAGE_SIZE;
1696 }
1697 else
1698 {
1700 MemoryInfo.State = MEM_RESERVE;
1701 MemoryInfo.Protect = PAGE_NOACCESS;
1703 }
1704
1705 /* Return the data, NtQueryInformation already probed it*/
1706 if (PreviousMode != KernelMode)
1707 {
1708 _SEH2_TRY
1709 {
1710 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1712 }
1714 {
1716 }
1717 _SEH2_END;
1718 }
1719 else
1720 {
1721 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1723 }
1724
1725 return Status;
1726 }
1727
1728 /* Check if this is for a local or remote process */
1730 {
1731 TargetProcess = PsGetCurrentProcess();
1732 }
1733 else
1734 {
1735 /* Reference the target process */
1740 (PVOID*)&TargetProcess,
1741 NULL);
1742 if (!NT_SUCCESS(Status)) return Status;
1743
1744 /* Attach to it now */
1745 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1746 }
1747
1748 /* Lock the address space and make sure the process isn't already dead */
1749 MmLockAddressSpace(&TargetProcess->Vm);
1750 if (TargetProcess->VmDeleted)
1751 {
1752 /* Unlock the address space of the process */
1753 MmUnlockAddressSpace(&TargetProcess->Vm);
1754
1755 /* Check if we were attached */
1757 {
1758 /* Detach and dereference the process */
1760 ObDereferenceObject(TargetProcess);
1761 }
1762
1763 /* Bail out */
1764 DPRINT1("Process is dying\n");
1766 }
1767
1768 /* Loop the VADs */
1770 if (TargetProcess->VadRoot.NumberGenericTableElements)
1771 {
1772 /* Scan on the right */
1773 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1774 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1775 while (Vad)
1776 {
1777 /* Check if this VAD covers the allocation range */
1778 if ((BaseVpn >= Vad->StartingVpn) &&
1779 (BaseVpn <= Vad->EndingVpn))
1780 {
1781 /* We're done */
1782 Found = TRUE;
1783 break;
1784 }
1785
1786 /* Check if this VAD is too high */
1787 if (BaseVpn < Vad->StartingVpn)
1788 {
1789 /* Stop if there is no left child */
1790 if (!Vad->LeftChild) break;
1791
1792 /* Search on the left next */
1793 Vad = Vad->LeftChild;
1794 }
1795 else
1796 {
1797 /* Then this VAD is too low, keep searching on the right */
1798 ASSERT(BaseVpn > Vad->EndingVpn);
1799
1800 /* Stop if there is no right child */
1801 if (!Vad->RightChild) break;
1802
1803 /* Search on the right next */
1804 Vad = Vad->RightChild;
1805 }
1806 }
1807 }
1808
1809 /* Was a VAD found? */
1810 if (!Found)
1811 {
1813
1814 /* Calculate region size */
1815 if (Vad)
1816 {
1817 if (Vad->StartingVpn >= BaseVpn)
1818 {
1819 /* Region size is the free space till the start of that VAD */
1820 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1821 }
1822 else
1823 {
1824 /* Get the next VAD */
1826 if (Vad)
1827 {
1828 /* Region size is the free space till the start of that VAD */
1829 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1830 }
1831 else
1832 {
1833 /* Maximum possible region size with that base address */
1835 }
1836 }
1837 }
1838 else
1839 {
1840 /* Maximum possible region size with that base address */
1842 }
1843
1844 /* Unlock the address space of the process */
1845 MmUnlockAddressSpace(&TargetProcess->Vm);
1846
1847 /* Check if we were attached */
1849 {
1850 /* Detach and dereference the process */
1852 ObDereferenceObject(TargetProcess);
1853 }
1854
1855 /* Build the rest of the initial information block */
1856 MemoryInfo.BaseAddress = Address;
1857 MemoryInfo.AllocationBase = NULL;
1858 MemoryInfo.AllocationProtect = 0;
1859 MemoryInfo.State = MEM_FREE;
1860 MemoryInfo.Protect = PAGE_NOACCESS;
1861 MemoryInfo.Type = 0;
1862
1863 /* Return the data, NtQueryInformation already probed it*/
1864 if (PreviousMode != KernelMode)
1865 {
1866 _SEH2_TRY
1867 {
1868 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1870 }
1872 {
1874 }
1875 _SEH2_END;
1876 }
1877 else
1878 {
1879 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1881 }
1882
1883 return Status;
1884 }
1885
1886 /* Set the correct memory type based on what kind of VAD this is */
1887 if ((Vad->u.VadFlags.PrivateMemory) ||
1889 {
1890 MemoryInfo.Type = MEM_PRIVATE;
1891 }
1892 else if (Vad->u.VadFlags.VadType == VadImageMap)
1893 {
1894 MemoryInfo.Type = MEM_IMAGE;
1895 }
1896 else
1897 {
1898 MemoryInfo.Type = MEM_MAPPED;
1899 }
1900
1901 /* Find the memory area the specified address belongs to */
1904
1905 /* Determine information dependent on the memory area type */
1907 {
1909 if (!NT_SUCCESS(Status))
1910 {
1911 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1914 }
1915 }
1916 else
1917 {
1918 /* Build the initial information block */
1920 MemoryInfo.BaseAddress = Address;
1921 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1923 MemoryInfo.Type = MEM_PRIVATE;
1924
1925 /* Acquire the working set lock (shared is enough) */
1927
1928 /* Find the largest chunk of memory which has the same state and protection mask */
1929 MemoryInfo.State = MiQueryAddressState(Address,
1930 Vad,
1931 TargetProcess,
1932 &MemoryInfo.Protect,
1933 &NextAddress);
1934 Address = NextAddress;
1935 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1936 {
1937 /* Keep going unless the state or protection mask changed */
1938 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1939 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1940 Address = NextAddress;
1941 }
1942
1943 /* Release the working set lock */
1945
1946 /* Check if we went outside of the VAD */
1947 if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1948 {
1949 /* Set the end of the VAD as the end address */
1950 Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1951 }
1952
1953 /* Now that we know the last VA address, calculate the region size */
1954 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1955 }
1956
1957 /* Unlock the address space of the process */
1958 MmUnlockAddressSpace(&TargetProcess->Vm);
1959
1960 /* Check if we were attached */
1962 {
1963 /* Detach and dereference the process */
1965 ObDereferenceObject(TargetProcess);
1966 }
1967
1968 /* Return the data, NtQueryInformation already probed it */
1969 if (PreviousMode != KernelMode)
1970 {
1971 _SEH2_TRY
1972 {
1973 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1975 }
1977 {
1979 }
1980 _SEH2_END;
1981 }
1982 else
1983 {
1984 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1986 }
1987
1988 /* All went well */
1989 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1990 "State: %lx Type: %lx Size: %lx\n",
1991 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1992 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1993 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1994
1995 return Status;
1996}
1997
1998BOOLEAN
1999NTAPI
2001 IN ULONG_PTR EndingAddress,
2002 IN PMMVAD Vad,
2004{
2005 PMMPTE PointerPte, LastPte;
2006 PMMPDE PointerPde;
2007 BOOLEAN OnPdeBoundary = TRUE;
2008#if _MI_PAGING_LEVELS >= 3
2009 PMMPPE PointerPpe;
2010 BOOLEAN OnPpeBoundary = TRUE;
2011#if _MI_PAGING_LEVELS == 4
2012 PMMPXE PointerPxe;
2013 BOOLEAN OnPxeBoundary = TRUE;
2014#endif
2015#endif
2016
2017 PAGED_CODE();
2018
2019 /* Check that we hols the right locks */
2020 ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared);
2021
2022 /* Get the PTE addresses */
2023 PointerPte = MiAddressToPte(StartingAddress);
2024 LastPte = MiAddressToPte(EndingAddress);
2025
2026 /* Loop all the PTEs */
2027 while (PointerPte <= LastPte)
2028 {
2029#if _MI_PAGING_LEVELS == 4
2030 /* Check for new PXE boundary */
2031 if (OnPxeBoundary)
2032 {
2033 PointerPxe = MiPteToPxe(PointerPte);
2034
2035 /* Check that this loop is sane */
2036 ASSERT(OnPpeBoundary);
2037 ASSERT(OnPdeBoundary);
2038
2039 if (PointerPxe->u.Long != 0)
2040 {
2041 /* Make it valid if needed */
2042 if (PointerPxe->u.Hard.Valid == 0)
2044 }
2045 else
2046 {
2047 /* Is the entire VAD committed? If not, fail */
2048 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2049
2050 PointerPxe++;
2051 PointerPte = MiPxeToPte(PointerPte);
2052 continue;
2053 }
2054 }
2055#endif
2056
2057#if _MI_PAGING_LEVELS >= 3
2058 /* Check for new PPE boundary */
2059 if (OnPpeBoundary)
2060 {
2061 PointerPpe = MiPteToPpe(PointerPte);
2062
2063 /* Check that this loop is sane */
2064 ASSERT(OnPdeBoundary);
2065
2066 if (PointerPpe->u.Long != 0)
2067 {
2068 /* Make it valid if needed */
2069 if (PointerPpe->u.Hard.Valid == 0)
2071 }
2072 else
2073 {
2074 /* Is the entire VAD committed? If not, fail */
2075 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2076
2077 PointerPpe++;
2078 PointerPte = MiPpeToPte(PointerPpe);
2079#if _MI_PAGING_LEVELS == 4
2080 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2081#endif
2082 continue;
2083 }
2084 }
2085#endif
2086 /* Check if we've hit a new PDE boundary */
2087 if (OnPdeBoundary)
2088 {
2089 /* Is this PDE demand zero? */
2090 PointerPde = MiPteToPde(PointerPte);
2091 if (PointerPde->u.Long != 0)
2092 {
2093 /* It isn't -- is it valid? */
2094 if (PointerPde->u.Hard.Valid == 0)
2095 {
2096 /* Nope, fault it in */
2097 MiMakeSystemAddressValid(PointerPte, Process);
2098 }
2099 }
2100 else
2101 {
2102 /* Is the entire VAD committed? If not, fail */
2103 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2104
2105 /* The PTE was already valid, so move to the next one */
2106 PointerPde++;
2107 PointerPte = MiPdeToPte(PointerPde);
2108#if _MI_PAGING_LEVELS >= 3
2109 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
2110#if _MI_PAGING_LEVELS == 4
2111 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2112#endif
2113#endif
2114
2115 /* New loop iteration with our new, on-boundary PTE. */
2116 continue;
2117 }
2118 }
2119
2120 /* Is the PTE demand zero? */
2121 if (PointerPte->u.Long == 0)
2122 {
2123 /* Is the entire VAD committed? If not, fail */
2124 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2125 }
2126 else
2127 {
2128 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2129 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
2130 (PointerPte->u.Hard.Valid == 0) &&
2131 ((PointerPte->u.Soft.Prototype == 0) ||
2132 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
2133 {
2134 /* Then part of the range is decommitted, so fail */
2135 return FALSE;
2136 }
2137 }
2138
2139 /* Move to the next PTE */
2140 PointerPte++;
2141 OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte);
2142#if _MI_PAGING_LEVELS >= 3
2143 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
2144#if _MI_PAGING_LEVELS == 4
2145 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2146#endif
2147#endif
2148 }
2149
2150 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2151 return TRUE;
2152}
2153
2155NTAPI
2158 IN OUT PSIZE_T NumberOfBytesToProtect,
2159 IN ULONG NewAccessProtection,
2160 OUT PULONG OldAccessProtection OPTIONAL)
2161{
2164 ULONG OldAccessProtection_;
2166
2167 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
2169
2170 AddressSpace = &Process->Vm;
2174 {
2176 return STATUS_UNSUCCESSFUL;
2177 }
2178
2179 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
2180
2183 MemoryArea,
2184 *BaseAddress,
2185 *NumberOfBytesToProtect,
2186 NewAccessProtection,
2187 OldAccessProtection);
2188
2190
2191 return Status;
2192}
2193
2195NTAPI
2198 IN OUT PSIZE_T NumberOfBytesToProtect,
2199 IN ULONG NewAccessProtection,
2200 OUT PULONG OldAccessProtection OPTIONAL)
2201{
2203 PMMVAD Vad;
2205 ULONG_PTR StartingAddress, EndingAddress;
2206 PMMPTE PointerPte, LastPte;
2207 PMMPDE PointerPde;
2208 MMPTE PteContents;
2209 PMMPFN Pfn1;
2210 ULONG ProtectionMask, OldProtect;
2211 BOOLEAN Committed;
2215
2216 /* Calculate base address for the VAD */
2217 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
2218 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
2219
2220 /* Calculate the protection mask and make sure it's valid */
2221 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
2222 if (ProtectionMask == MM_INVALID_PROTECTION)
2223 {
2224 DPRINT1("Invalid protection mask\n");
2226 }
2227
2228 /* Check for ROS specific memory area */
2231 {
2232 /* Evil hack */
2235 NumberOfBytesToProtect,
2236 NewAccessProtection,
2237 OldAccessProtection);
2238 }
2239
2240 /* Lock the address space and make sure the process isn't already dead */
2243 if (Process->VmDeleted)
2244 {
2245 DPRINT1("Process is dying\n");
2247 goto FailPath;
2248 }
2249
2250 /* Get the VAD for this address range, and make sure it exists */
2251 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2252 EndingAddress >> PAGE_SHIFT,
2253 &Process->VadRoot,
2254 (PMMADDRESS_NODE*)&Vad);
2255 if (Result != TableFoundNode)
2256 {
2257 DPRINT("Could not find a VAD for this allocation\n");
2259 goto FailPath;
2260 }
2261
2262 /* Make sure the address is within this VAD's boundaries */
2263 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2264 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2265 {
2267 goto FailPath;
2268 }
2269
2270 /* These kinds of VADs are not supported atm */
2271 if ((Vad->u.VadFlags.VadType == VadAwe) ||
2273 (Vad->u.VadFlags.VadType == VadLargePages))
2274 {
2275 DPRINT1("Illegal VAD for attempting to set protection\n");
2277 goto FailPath;
2278 }
2279
2280 /* Check for a VAD whose protection can't be changed */
2281 if (Vad->u.VadFlags.NoChange == 1)
2282 {
2283 DPRINT1("Trying to change protection of a NoChange VAD\n");
2285 goto FailPath;
2286 }
2287
2288 /* Is this section, or private memory? */
2289 if (Vad->u.VadFlags.PrivateMemory == 0)
2290 {
2291 /* Not yet supported */
2293 {
2294 DPRINT1("Illegal VAD for attempting to set protection\n");
2296 goto FailPath;
2297 }
2298
2299 /* Rotate VADs are not yet supported */
2300 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2301 {
2302 DPRINT1("Illegal VAD for attempting to set protection\n");
2304 goto FailPath;
2305 }
2306
2307 /* Not valid on section files */
2308 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2309 {
2310 /* Fail */
2311 DPRINT1("Invalid protection flags for section\n");
2313 goto FailPath;
2314 }
2315
2316 /* Check if data or page file mapping protection PTE is compatible */
2317 if (!Vad->ControlArea->u.Flags.Image)
2318 {
2319 /* Not yet */
2320 DPRINT1("Fixme: Not checking for valid protection\n");
2321 }
2322
2323 /* This is a section, and this is not yet supported */
2324 DPRINT1("Section protection not yet supported\n");
2325 OldProtect = 0;
2326 }
2327 else
2328 {
2329 /* Private memory, check protection flags */
2330 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2331 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2332 {
2333 DPRINT1("Invalid protection flags for private memory\n");
2335 goto FailPath;
2336 }
2337
2338 /* Lock the working set */
2340
2341 /* Check if all pages in this range are committed */
2342 Committed = MiIsEntireRangeCommitted(StartingAddress,
2343 EndingAddress,
2344 Vad,
2345 Process);
2346 if (!Committed)
2347 {
2348 /* Fail */
2349 DPRINT1("The entire range is not committed\n");
2352 goto FailPath;
2353 }
2354
2355 /* Compute starting and ending PTE and PDE addresses */
2356 PointerPde = MiAddressToPde(StartingAddress);
2357 PointerPte = MiAddressToPte(StartingAddress);
2358 LastPte = MiAddressToPte(EndingAddress);
2359
2360 /* Make this PDE valid */
2362
2363 /* Save protection of the first page */
2364 if (PointerPte->u.Long != 0)
2365 {
2366 /* Capture the page protection and make the PDE valid */
2367 OldProtect = MiGetPageProtection(PointerPte);
2369 }
2370 else
2371 {
2372 /* Grab the old protection from the VAD itself */
2373 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2374 }
2375
2376 /* Loop all the PTEs now */
2377 while (PointerPte <= LastPte)
2378 {
2379 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2380 if (MiIsPteOnPdeBoundary(PointerPte))
2381 {
2382 PointerPde = MiPteToPde(PointerPte);
2384 }
2385
2386 /* Capture the PTE and check if it was empty */
2387 PteContents = *PointerPte;
2388 if (PteContents.u.Long == 0)
2389 {
2390 /* This used to be a zero PTE and it no longer is, so we must add a
2391 reference to the pagetable. */
2393 }
2394
2395 /* Check what kind of PTE we are dealing with */
2396 if (PteContents.u.Hard.Valid == 1)
2397 {
2398 /* Get the PFN entry */
2399 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2400
2401 /* We don't support this yet */
2402 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2403
2404 /* Check if the page should not be accessible at all */
2405 if ((NewAccessProtection & PAGE_NOACCESS) ||
2406 (NewAccessProtection & PAGE_GUARD))
2407 {
2408 KIRQL OldIrql = MiAcquirePfnLock();
2409
2410 /* Mark the PTE as transition and change its protection */
2411 PteContents.u.Hard.Valid = 0;
2412 PteContents.u.Soft.Transition = 1;
2413 PteContents.u.Trans.Protection = ProtectionMask;
2414 /* Decrease PFN share count and write the PTE */
2415 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2416 // FIXME: remove the page from the WS
2417 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2418#ifdef CONFIG_SMP
2419 // FIXME: Should invalidate entry in every CPU TLB
2421#endif
2423
2424 /* We are done for this PTE */
2425 MiReleasePfnLock(OldIrql);
2426 }
2427 else
2428 {
2429 /* Write the protection mask and write it with a TLB flush */
2430 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2432 PointerPte,
2433 ProtectionMask,
2434 Pfn1,
2435 TRUE);
2436 }
2437 }
2438 else
2439 {
2440 /* We don't support these cases yet */
2441 ASSERT(PteContents.u.Soft.Prototype == 0);
2442 //ASSERT(PteContents.u.Soft.Transition == 0);
2443
2444 /* The PTE is already demand-zero, just update the protection mask */
2445 PteContents.u.Soft.Protection = ProtectionMask;
2446 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2447 ASSERT(PointerPte->u.Long != 0);
2448 }
2449
2450 /* Move to the next PTE */
2451 PointerPte++;
2452 }
2453
2454 /* Unlock the working set */
2456 }
2457
2458 /* Unlock the address space */
2460
2461 /* Return parameters and success */
2462 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2463 *BaseAddress = (PVOID)StartingAddress;
2464 *OldAccessProtection = OldProtect;
2465 return STATUS_SUCCESS;
2466
2467FailPath:
2468 /* Unlock the address space and return the failure code */
2470 return Status;
2471}
2472
2473VOID
2474NTAPI
2476 IN PEPROCESS TargetProcess,
2478{
2479 PMMPTE PointerPte;
2480#if _MI_PAGING_LEVELS >= 3
2481 PMMPPE PointerPpe = MiPdeToPpe(PointerPde);
2482#if _MI_PAGING_LEVELS == 4
2483 PMMPXE PointerPxe = MiPdeToPxe(PointerPde);
2484#endif
2485#endif
2486
2487 //
2488 // Sanity checks. The latter is because we only use this function with the
2489 // PFN lock not held, so it may go away in the future.
2490 //
2493
2494 //
2495 // If everything is already valid, there is nothing to do.
2496 //
2497 if (
2498#if _MI_PAGING_LEVELS == 4
2499 (PointerPxe->u.Hard.Valid) &&
2500#endif
2501#if _MI_PAGING_LEVELS >= 3
2502 (PointerPpe->u.Hard.Valid) &&
2503#endif
2504 (PointerPde->u.Hard.Valid))
2505 {
2506 return;
2507 }
2508
2509 //
2510 // At least something is invalid, so begin by getting the PTE for the PDE itself
2511 // and then lookup each additional level. We must do it in this precise order
2512 // because the pagfault.c code (as well as in Windows) depends that the next
2513 // level up (higher) must be valid when faulting a lower level
2514 //
2515 PointerPte = MiPteToAddress(PointerPde);
2516 do
2517 {
2518 //
2519 // Make sure APCs continued to be disabled
2520 //
2522
2523#if _MI_PAGING_LEVELS == 4
2524 //
2525 // First, make the PXE valid if needed
2526 //
2527 if (!PointerPxe->u.Hard.Valid)
2528 {
2529 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2530 ASSERT(PointerPxe->u.Hard.Valid == 1);
2531 }
2532#endif
2533
2534#if _MI_PAGING_LEVELS >= 3
2535 //
2536 // Next, the PPE
2537 //
2538 if (!PointerPpe->u.Hard.Valid)
2539 {
2540 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2541 ASSERT(PointerPpe->u.Hard.Valid == 1);
2542 }
2543#endif
2544
2545 //
2546 // And finally, make the PDE itself valid.
2547 //
2548 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2549
2550 /* Do not increment Page table refcount here for the PDE, this must be managed by caller */
2551
2552 //
2553 // This should've worked the first time so the loop is really just for
2554 // show -- ASSERT that we're actually NOT going to be looping.
2555 //
2556 ASSERT(PointerPde->u.Hard.Valid == 1);
2557 } while (
2558#if _MI_PAGING_LEVELS == 4
2559 !PointerPxe->u.Hard.Valid ||
2560#endif
2561#if _MI_PAGING_LEVELS >= 3
2562 !PointerPpe->u.Hard.Valid ||
2563#endif
2564 !PointerPde->u.Hard.Valid);
2565}
2566
2567VOID
2568NTAPI
2570 IN ULONG Count)
2571{
2572 KIRQL OldIrql;
2573 ULONG i;
2574 MMPTE TempPte;
2575 PFN_NUMBER PageFrameIndex;
2576 PMMPFN Pfn1, Pfn2;
2577
2578 //
2579 // Acquire the PFN lock and loop all the PTEs in the list
2580 //
2581 OldIrql = MiAcquirePfnLock();
2582 for (i = 0; i != Count; i++)
2583 {
2584 //
2585 // The PTE must currently be valid
2586 //
2587 TempPte = *ValidPteList[i];
2588 ASSERT(TempPte.u.Hard.Valid == 1);
2589
2590 //
2591 // Get the PFN entry for the page itself, and then for its page table
2592 //
2593 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2594 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2595 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2596
2597 //
2598 // Decrement the share count on the page table, and then on the page
2599 // itself
2600 //
2601 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2602 MI_SET_PFN_DELETED(Pfn1);
2603 MiDecrementShareCount(Pfn1, PageFrameIndex);
2604
2605 //
2606 // Make the page decommitted
2607 //
2608 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2609 }
2610
2611 //
2612 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2613 // and then release the PFN lock
2614 //
2616 MiReleasePfnLock(OldIrql);
2617}
2618
2619ULONG
2620NTAPI
2621MiDecommitPages(IN PVOID StartingAddress,
2622 IN PMMPTE EndingPte,
2624 IN PMMVAD Vad)
2625{
2626 PMMPTE PointerPte, CommitPte = NULL;
2627 PMMPDE PointerPde;
2628 ULONG CommitReduction = 0;
2629 PMMPTE ValidPteList[256];
2630 ULONG PteCount = 0;
2631 PMMPFN Pfn1;
2632 MMPTE PteContents;
2633 PETHREAD CurrentThread = PsGetCurrentThread();
2634
2635 //
2636 // Get the PTE and PTE for the address, and lock the working set
2637 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2638 // commited range ends so that we can do the right accounting.
2639 //
2640 PointerPde = MiAddressToPde(StartingAddress);
2641 PointerPte = MiAddressToPte(StartingAddress);
2642 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2643 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2644
2645 //
2646 // Make the PDE valid, and now loop through each page's worth of data
2647 //
2649 while (PointerPte <= EndingPte)
2650 {
2651 //
2652 // Check if we've crossed a PDE boundary
2653 //
2654 if (MiIsPteOnPdeBoundary(PointerPte))
2655 {
2656 //
2657 // Get the new PDE and flush the valid PTEs we had built up until
2658 // now. This helps reduce the amount of TLB flushing we have to do.
2659 // Note that Windows does a much better job using timestamps and
2660 // such, and does not flush the entire TLB all the time, but right
2661 // now we have bigger problems to worry about than TLB flushing.
2662 //
2663 PointerPde = MiAddressToPde(StartingAddress);
2664 if (PteCount)
2665 {
2666 MiProcessValidPteList(ValidPteList, PteCount);
2667 PteCount = 0;
2668 }
2669
2670 //
2671 // Make this PDE valid
2672 //
2674 }
2675
2676 //
2677 // Read this PTE. It might be active or still demand-zero.
2678 //
2679 PteContents = *PointerPte;
2680 if (PteContents.u.Long)
2681 {
2682 //
2683 // The PTE is active. It might be valid and in a working set, or
2684 // it might be a prototype PTE or paged out or even in transition.
2685 //
2686 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2687 {
2688 //
2689 // It's already decommited, so there's nothing for us to do here
2690 //
2691 CommitReduction++;
2692 }
2693 else
2694 {
2695 //
2696 // Remove it from the counters, and check if it was valid or not
2697 //
2698 //Process->NumberOfPrivatePages--;
2699 if (PteContents.u.Hard.Valid)
2700 {
2701 //
2702 // It's valid. At this point make sure that it is not a ROS
2703 // PFN. Also, we don't support ProtoPTEs in this code path.
2704 //
2705 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2706 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2707 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2708
2709 //
2710 // Flush any pending PTEs that we had not yet flushed, if our
2711 // list has gotten too big, then add this PTE to the flush list.
2712 //
2713 if (PteCount == 256)
2714 {
2715 MiProcessValidPteList(ValidPteList, PteCount);
2716 PteCount = 0;
2717 }
2718 ValidPteList[PteCount++] = PointerPte;
2719 }
2720 else
2721 {
2722 //
2723 // We do not support any of these other scenarios at the moment
2724 //
2725 ASSERT(PteContents.u.Soft.Prototype == 0);
2726 ASSERT(PteContents.u.Soft.Transition == 0);
2727 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2728
2729 //
2730 // So the only other possibility is that it is still a demand
2731 // zero PTE, in which case we undo the accounting we did
2732 // earlier and simply make the page decommitted.
2733 //
2734 //Process->NumberOfPrivatePages++;
2736 }
2737 }
2738 }
2739 else
2740 {
2741 //
2742 // This used to be a zero PTE and it no longer is, so we must add a
2743 // reference to the pagetable.
2744 //
2745 MiIncrementPageTableReferences(StartingAddress);
2746
2747 //
2748 // Next, we account for decommitted PTEs and make the PTE as such
2749 //
2750 if (PointerPte > CommitPte) CommitReduction++;
2752 }
2753
2754 //
2755 // Move to the next PTE and the next address
2756 //
2757 PointerPte++;
2758 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2759 }
2760
2761 //
2762 // Flush any dangling PTEs from the loop in the last page table, and then
2763 // release the working set and return the commit reduction accounting.
2764 //
2765 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2767 return CommitReduction;
2768}
2769
2770/* PUBLIC FUNCTIONS ***********************************************************/
2771
2772/*
2773 * @unimplemented
2774 */
2775PVOID
2776NTAPI
2778{
2780 return 0;
2781}
2782
2783/*
2784 * @unimplemented
2785 */
2786PVOID
2787NTAPI
2790 IN ULONG Mode)
2791{
2792 static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2793 return Address;
2794}
2795
2796/*
2797 * @unimplemented
2798 */
2799VOID
2800NTAPI
2802{
2803 static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2804}
2805
2806/* SYSTEM CALLS ***************************************************************/
2807
2809NTAPI
2813 IN SIZE_T NumberOfBytesToRead,
2814 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2815{
2819 SIZE_T BytesRead = 0;
2820 PAGED_CODE();
2821
2822 //
2823 // Check if we came from user mode
2824 //
2825 if (PreviousMode != KernelMode)
2826 {
2827 //
2828 // Validate the read addresses
2829 //
2830 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2831 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2832 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2833 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2834 {
2835 //
2836 // Don't allow to write into kernel space
2837 //
2839 }
2840
2841 //
2842 // Enter SEH for probe
2843 //
2844 _SEH2_TRY
2845 {
2846 //
2847 // Probe the output value
2848 //
2849 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2850 }
2852 {
2853 //
2854 // Get exception code
2855 //
2857 }
2858 _SEH2_END;
2859 }
2860
2861 //
2862 // Don't do zero-byte transfers
2863 //
2864 if (NumberOfBytesToRead)
2865 {
2866 //
2867 // Reference the process
2868 //
2873 (PVOID*)(&Process),
2874 NULL);
2875 if (NT_SUCCESS(Status))
2876 {
2877 //
2878 // Do the copy
2879 //
2883 Buffer,
2884 NumberOfBytesToRead,
2886 &BytesRead);
2887
2888 //
2889 // Dereference the process
2890 //
2892 }
2893 }
2894
2895 //
2896 // Check if the caller sent this parameter
2897 //
2898 if (NumberOfBytesRead)
2899 {
2900 //
2901 // Enter SEH to guard write
2902 //
2903 _SEH2_TRY
2904 {
2905 //
2906 // Return the number of bytes read
2907 //
2908 *NumberOfBytesRead = BytesRead;
2909 }
2911 {
2912 }
2913 _SEH2_END;
2914 }
2915
2916 //
2917 // Return status
2918 //
2919 return Status;
2920}
2921
2923NTAPI
2926 IN PVOID Buffer,
2927 IN SIZE_T NumberOfBytesToWrite,
2928 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2929{
2933 SIZE_T BytesWritten = 0;
2934 PAGED_CODE();
2935
2936 //
2937 // Check if we came from user mode
2938 //
2939 if (PreviousMode != KernelMode)
2940 {
2941 //
2942 // Validate the read addresses
2943 //
2944 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2945 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2946 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2947 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2948 {
2949 //
2950 // Don't allow to write into kernel space
2951 //
2953 }
2954
2955 //
2956 // Enter SEH for probe
2957 //
2958 _SEH2_TRY
2959 {
2960 //
2961 // Probe the output value
2962 //
2963 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2964 }
2966 {
2967 //
2968 // Get exception code
2969 //
2971 }
2972 _SEH2_END;
2973 }
2974
2975 //
2976 // Don't do zero-byte transfers
2977 //
2978 if (NumberOfBytesToWrite)
2979 {
2980 //
2981 // Reference the process
2982 //
2987 (PVOID*)&Process,
2988 NULL);
2989 if (NT_SUCCESS(Status))
2990 {
2991 //
2992 // Do the copy
2993 //
2995 Buffer,
2996 Process,
2998 NumberOfBytesToWrite,
3000 &BytesWritten);
3001
3002 //
3003 // Dereference the process
3004 //
3006 }
3007 }
3008
3009 //
3010 // Check if the caller sent this parameter
3011 //
3012 if (NumberOfBytesWritten)
3013 {
3014 //
3015 // Enter SEH to guard write
3016 //
3017 _SEH2_TRY
3018 {
3019 //
3020 // Return the number of bytes written
3021 //
3022 *NumberOfBytesWritten = BytesWritten;
3023 }
3025 {
3026 }
3027 _SEH2_END;
3028 }
3029
3030 //
3031 // Return status
3032 //
3033 return Status;
3034}
3035
3037NTAPI
3040 _In_ SIZE_T FlushSize)
3041{
3045 PAGED_CODE();
3046
3047 /* Is a base address given? */
3048 if (BaseAddress != NULL)
3049 {
3050 /* If the requested size is 0, there is nothing to do */
3051 if (FlushSize == 0)
3052 {
3053 return STATUS_SUCCESS;
3054 }
3055
3056 /* Is this a user mode call? */
3058 {
3059 /* Make sure the base address is in user space */
3061 {
3062 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress);
3064 }
3065 }
3066 }
3067
3068 /* Is another process requested? */
3070 {
3071 /* Reference the process */
3076 (PVOID*)&Process,
3077 NULL);
3078 if (!NT_SUCCESS(Status))
3079 {
3080 DPRINT1("Failed to reference the process %p\n", ProcessHandle);
3081 return Status;
3082 }
3083
3084 /* Attach to the process */
3086 }
3087
3088 /* Forward to Ke */
3089 KeSweepICache(BaseAddress, FlushSize);
3090
3091 /* Check if we attached */
3093 {
3094 /* Detach from the process and dereference it */
3097 }
3098
3099 /* All done, return to caller */
3100 return STATUS_SUCCESS;
3101}
3102
3104NTAPI
3106 IN OUT PVOID *UnsafeBaseAddress,
3107 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
3108 IN ULONG NewAccessProtection,
3109 OUT PULONG UnsafeOldAccessProtection)
3110{
3112 ULONG OldAccessProtection;
3113 ULONG Protection;
3114 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3116 SIZE_T NumberOfBytesToProtect = 0;
3121 PAGED_CODE();
3122
3123 //
3124 // Check for valid protection flags
3125 //
3126 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
3127 if (Protection != PAGE_NOACCESS &&
3128 Protection != PAGE_READONLY &&
3129 Protection != PAGE_READWRITE &&
3130 Protection != PAGE_WRITECOPY &&
3131 Protection != PAGE_EXECUTE &&
3132 Protection != PAGE_EXECUTE_READ &&
3133 Protection != PAGE_EXECUTE_READWRITE &&
3134 Protection != PAGE_EXECUTE_WRITECOPY)
3135 {
3136 //
3137 // Fail
3138 //
3140 }
3141
3142 //
3143 // Check if we came from user mode
3144 //
3145 if (PreviousMode != KernelMode)
3146 {
3147 //
3148 // Enter SEH for probing
3149 //
3150 _SEH2_TRY
3151 {
3152 //
3153 // Validate all outputs
3154 //
3155 ProbeForWritePointer(UnsafeBaseAddress);
3156 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
3157 ProbeForWriteUlong(UnsafeOldAccessProtection);
3158
3159 //
3160 // Capture them
3161 //
3162 BaseAddress = *UnsafeBaseAddress;
3163 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3164 }
3166 {
3167 //
3168 // Get exception code
3169 //
3171 }
3172 _SEH2_END;
3173 }
3174 else
3175 {
3176 //
3177 // Capture directly
3178 //
3179 BaseAddress = *UnsafeBaseAddress;
3180 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3181 }
3182
3183 //
3184 // Catch illegal base address
3185 //
3187
3188 //
3189 // Catch illegal region size
3190 //
3191 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
3192 {
3193 //
3194 // Fail
3195 //
3197 }
3198
3199 //
3200 // 0 is also illegal
3201 //
3202 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
3203
3204 //
3205 // Get a reference to the process
3206 //
3211 (PVOID*)(&Process),
3212 NULL);
3213 if (!NT_SUCCESS(Status)) return Status;
3214
3215 //
3216 // Check if we should attach
3217 //
3218 if (CurrentProcess != Process)
3219 {
3220 //
3221 // Do it
3222 //
3224 Attached = TRUE;
3225 }
3226
3227 //
3228 // Do the actual work
3229 //
3231 &BaseAddress,
3232 &NumberOfBytesToProtect,
3233 NewAccessProtection,
3234 &OldAccessProtection);
3235
3236 //
3237 // Detach if needed
3238 //
3240
3241 //
3242 // Release reference
3243 //
3245
3246 //
3247 // Enter SEH to return data
3248 //
3249 _SEH2_TRY
3250 {
3251 //
3252 // Return data to user
3253 //
3254 *UnsafeOldAccessProtection = OldAccessProtection;
3255 *UnsafeBaseAddress = BaseAddress;
3256 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
3257 }
3259 {
3260 }
3261 _SEH2_END;
3262
3263 //
3264 // Return status
3265 //
3266 return Status;
3267}
3268
3270BOOLEAN
3272 PMMPFN Pfn1,
3274{
3275 // HACK until we have proper WSLIST support
3276 PMMWSLE Wsle = &Pfn1->Wsle;
3277
3278 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
3279 return TRUE;
3280 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
3281 return TRUE;
3282
3283 return FALSE;
3284}
3285
3287VOID
3289 PMMPFN Pfn1,
3291{
3292 // HACK until we have proper WSLIST support
3293 PMMWSLE Wsle = &Pfn1->Wsle;
3294
3295 if (!Wsle->u1.e1.LockedInWs &&
3296 !Wsle->u1.e1.LockedInMemory)
3297 {
3299 }
3300
3301 if (LockType & MAP_PROCESS)
3302 Wsle->u1.e1.LockedInWs = 1;
3303 if (LockType & MAP_SYSTEM)
3304 Wsle->u1.e1.LockedInMemory = 1;
3305}
3306
3308VOID
3310 PMMPFN Pfn1,
3312{
3313 // HACK until we have proper WSLIST support
3314 PMMWSLE Wsle = &Pfn1->Wsle;
3315
3316 if (LockType & MAP_PROCESS)
3317 Wsle->u1.e1.LockedInWs = 0;
3318 if (LockType & MAP_SYSTEM)
3319 Wsle->u1.e1.LockedInMemory = 0;
3320
3321 if (!Wsle->u1.e1.LockedInWs &&
3322 !Wsle->u1.e1.LockedInMemory)
3323 {
3325 }
3326}
3327
3328static
3333 _Inout_ PVOID *EndAddress)
3334
3335{
3336 PMMVAD Vad;
3337 PVOID CurrentVa;
3338
3339 /* Get the base address and align the start address */
3340 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
3341 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
3343
3344 /* First loop and check all VADs */
3345 CurrentVa = *BaseAddress;
3346 while (CurrentVa < *EndAddress)
3347 {
3348 /* Get VAD */
3349 Vad = MiLocateAddress(CurrentVa);
3350 if (Vad == NULL)
3351 {
3354 }
3355
3356 /* Check VAD type */
3357 if ((Vad->u.VadFlags.VadType != VadNone) &&
3358 (Vad->u.VadFlags.VadType != VadImageMap) &&
3359 (Vad->u.VadFlags.VadType != VadWriteWatch))
3360 {
3361 *EndAddress = CurrentVa;
3362 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3364 }
3365
3366 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3367 }
3368
3369 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3370 return STATUS_SUCCESS;
3371}
3372
3373static
3378 IN ULONG MapType)
3379{
3380 PEPROCESS CurrentProcess;
3382 PVOID CurrentVa, EndAddress;
3383 PMMPTE PointerPte, LastPte;
3384 PMMPDE PointerPde;
3385#if (_MI_PAGING_LEVELS >= 3)
3386 PMMPDE PointerPpe;
3387#endif
3388#if (_MI_PAGING_LEVELS == 4)
3389 PMMPDE PointerPxe;
3390#endif
3391 PMMPFN Pfn1;
3392 NTSTATUS Status, TempStatus;
3393
3394 /* Lock the address space */
3397
3398 /* Make sure we still have an address space */
3399 CurrentProcess = PsGetCurrentProcess();
3400 if (CurrentProcess->VmDeleted)
3401 {
3403 goto Cleanup;
3404 }
3405
3406 /* Check the VADs in the requested range */
3408 if (!NT_SUCCESS(Status))
3409 {
3410 goto Cleanup;
3411 }
3412
3413 /* Enter SEH for probing */
3414 _SEH2_TRY
3415 {
3416 /* Loop all pages and probe them */
3417 CurrentVa = *BaseAddress;
3418 while (CurrentVa < EndAddress)
3419 {
3420 (void)(*(volatile CHAR*)CurrentVa);
3421 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3422 }
3423 }
3425 {
3427 goto Cleanup;
3428 }
3429 _SEH2_END;
3430
3431 /* All pages were accessible, since we hold the address space lock, nothing
3432 can be de-committed. Assume success for now. */
3434
3435 /* Get the PTE and PDE */
3436 PointerPte = MiAddressToPte(*BaseAddress);
3437 PointerPde = MiAddressToPde(*BaseAddress);
3438#if (_MI_PAGING_LEVELS >= 3)
3439 PointerPpe = MiAddressToPpe(*BaseAddress);
3440#endif
3441#if (_MI_PAGING_LEVELS == 4)
3442 PointerPxe = MiAddressToPxe(*BaseAddress);
3443#endif
3444
3445 /* Get the last PTE */
3446 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3447
3448 /* Lock the process working set */
3450
3451 /* Loop the pages */
3452 do
3453 {
3454 /* Check for a page that is not accessible */
3455 while (
3456#if (_MI_PAGING_LEVELS == 4)
3457 (PointerPxe->u.Hard.Valid == 0) ||
3458#endif
3459#if (_MI_PAGING_LEVELS >= 3)
3460 (PointerPpe->u.Hard.Valid == 0) ||
3461#endif
3462 (PointerPde->u.Hard.Valid == 0) ||
3463 (PointerPte->u.Hard.Valid == 0))
3464 {
3465 /* Release process working set */
3467
3468 /* Access the page */
3469 CurrentVa = MiPteToAddress(PointerPte);
3470
3471 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3472 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL);
3473 if (!NT_SUCCESS(TempStatus))
3474 {
3475 // This should only happen, when remote backing storage is not accessible
3476 ASSERT(FALSE);
3477 Status = TempStatus;
3478 goto Cleanup;
3479 }
3480
3481 /* Lock the process working set */
3483 }
3484
3485 /* Get the PFN */
3486 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3487 ASSERT(Pfn1 != NULL);
3488
3489 /* Check the previous lock status */
3490 if (MI_IS_LOCKED_VA(Pfn1, MapType))
3491 {
3493 }
3494
3495 /* Lock it */
3496 MI_LOCK_VA(Pfn1, MapType);
3497
3498 /* Go to the next PTE */
3499 PointerPte++;
3500
3501 /* Check if we're on a PDE boundary */
3502 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3503#if (_MI_PAGING_LEVELS >= 3)
3504 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3505#endif
3506#if (_MI_PAGING_LEVELS == 4)
3507 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3508#endif
3509 } while (PointerPte <= LastPte);
3510
3511 /* Release process working set */
3513
3514Cleanup:
3515 /* Unlock address space */
3517
3518 return Status;
3519}
3520
3522NTAPI
3525 IN OUT PSIZE_T NumberOfBytesToLock,
3526 IN ULONG MapType)
3527{
3529 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3534 PVOID CapturedBaseAddress;
3535 SIZE_T CapturedBytesToLock;
3536 PAGED_CODE();
3537
3538 //
3539 // Validate flags
3540 //
3541 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3542 {
3543 //
3544 // Invalid set of flags
3545 //
3547 }
3548
3549 //
3550 // At least one flag must be specified
3551 //
3552 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3553 {
3554 //
3555 // No flag given
3556 //
3558 }
3559
3560 //
3561 // Enter SEH for probing
3562 //
3563 _SEH2_TRY
3564 {
3565 //
3566 // Validate output data
3567 //
3569 ProbeForWriteSize_t(NumberOfBytesToLock);
3570
3571 //
3572 // Capture it
3573 //
3574 CapturedBaseAddress = *BaseAddress;
3575 CapturedBytesToLock = *NumberOfBytesToLock;
3576 }
3578 {
3579 //
3580 // Get exception code
3581 //
3583 }
3584 _SEH2_END;
3585
3586 //
3587 // Catch illegal base address
3588 //
3589 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3590
3591 //
3592 // Catch illegal region size
3593 //
3594 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3595 {
3596 //
3597 // Fail
3598 //
3600 }
3601
3602 //
3603 // 0 is also illegal
3604 //
3605 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3606
3607 //
3608 // Get a reference to the process
3609 //
3614 (PVOID*)(&Process),
3615 NULL);
3616 if (!NT_SUCCESS(Status)) return Status;
3617
3618 //
3619 // Check if this is is system-mapped
3620 //
3621 if (MapType & MAP_SYSTEM)
3622 {
3623 //
3624 // Check for required privilege
3625 //
3627 {
3628 //
3629 // Fail: Don't have it
3630 //
3633 }
3634 }
3635
3636 //
3637 // Check if we should attach
3638 //
3639 if (CurrentProcess != Process)
3640 {
3641 //
3642 // Do it
3643 //
3645 Attached = TRUE;
3646 }
3647
3648 //
3649 // Call the internal function
3650 //
3651 Status = MiLockVirtualMemory(&CapturedBaseAddress,
3652 &CapturedBytesToLock,
3653 MapType);
3654
3655 //
3656 // Detach if needed
3657 //
3659
3660 //
3661 // Release reference
3662 //
3664
3665 //
3666 // Enter SEH to return data
3667 //
3668 _SEH2_TRY
3669 {
3670 //
3671 // Return data to user
3672 //
3673 *BaseAddress = CapturedBaseAddress;
3674 *NumberOfBytesToLock = CapturedBytesToLock;
3675 }
3677 {
3678 //
3679 // Get exception code
3680 //
3682 }
3683 _SEH2_END;
3684
3685 //
3686 // Return status
3687 //
3688 return Status;
3689}
3690
3691
3692static
3697 IN ULONG MapType)
3698{
3699 PEPROCESS CurrentProcess;
3701 PVOID EndAddress;
3702 PMMPTE PointerPte, LastPte;
3703 PMMPDE PointerPde;
3704#if (_MI_PAGING_LEVELS >= 3)
3705 PMMPDE PointerPpe;
3706#endif
3707#if (_MI_PAGING_LEVELS == 4)
3708 PMMPDE PointerPxe;
3709#endif
3710 PMMPFN Pfn1;
3712
3713 /* Lock the address space */
3716
3717 /* Make sure we still have an address space */
3718 CurrentProcess = PsGetCurrentProcess();
3719 if (CurrentProcess->VmDeleted)
3720 {
3722 goto Cleanup;
3723 }
3724
3725 /* Check the VADs in the requested range */
3727
3728 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3729 incompatible VAD we continue, like Windows does */
3731 {
3733 goto Cleanup;
3734 }
3735
3736 /* Get the PTE and PDE */
3737 PointerPte = MiAddressToPte(*BaseAddress);
3738 PointerPde = MiAddressToPde(*BaseAddress);
3739#if (_MI_PAGING_LEVELS >= 3)
3740 PointerPpe = MiAddressToPpe(*BaseAddress);
3741#endif
3742#if (_MI_PAGING_LEVELS == 4)
3743 PointerPxe = MiAddressToPxe(*BaseAddress);
3744#endif
3745
3746 /* Get the last PTE */
3747 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3748
3749 /* Lock the process working set */
3751
3752 /* Loop the pages */
3753 do
3754 {
3755 /* Check for a page that is not present */
3756 if (
3757#if (_MI_PAGING_LEVELS == 4)
3758 (PointerPxe->u.Hard.Valid == 0) ||
3759#endif
3760#if (_MI_PAGING_LEVELS >= 3)
3761 (PointerPpe->u.Hard.Valid == 0) ||
3762#endif
3763 (PointerPde->u.Hard.Valid == 0) ||
3764 (PointerPte->u.Hard.Valid == 0))
3765 {
3766 /* Remember it, but keep going */
3768 }
3769 else
3770 {
3771 /* Get the PFN */
3772 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3773 ASSERT(Pfn1 != NULL);
3774
3775 /* Check if all of the requested locks are present */
3776 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3777 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3778 {
3779 /* Remember it, but keep going */
3781
3782 /* Check if no lock is present */
3784 {
3785 DPRINT1("FIXME: Should remove the page from WS\n");
3786 }
3787 }
3788 }
3789
3790 /* Go to the next PTE */
3791 PointerPte++;
3792
3793 /* Check if we're on a PDE boundary */
3794 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3795#if (_MI_PAGING_LEVELS >= 3)
3796 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3797#endif
3798#if (_MI_PAGING_LEVELS == 4)
3799 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3800#endif
3801 } while (PointerPte <= LastPte);
3802
3803 /* Check if we hit a page that was not locked */
3805 {
3806 goto CleanupWithWsLock;
3807 }
3808
3809 /* All pages in the region were locked, so unlock them all */
3810
3811 /* Get the PTE and PDE */
3812 PointerPte = MiAddressToPte(*BaseAddress);
3813 PointerPde = MiAddressToPde(*BaseAddress);
3814#if (_MI_PAGING_LEVELS >= 3)
3815 PointerPpe = MiAddressToPpe(*BaseAddress);
3816#endif
3817#if (_MI_PAGING_LEVELS == 4)
3818 PointerPxe = MiAddressToPxe(*BaseAddress);
3819#endif
3820
3821 /* Loop the pages */
3822 do
3823 {
3824 /* Unlock it */
3825 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3826 MI_UNLOCK_VA(Pfn1, MapType);
3827
3828 /* Go to the next PTE */
3829 PointerPte++;
3830
3831 /* Check if we're on a PDE boundary */
3832 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3833#if (_MI_PAGING_LEVELS >= 3)
3834 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3835#endif
3836#if (_MI_PAGING_LEVELS == 4)
3837 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3838#endif
3839 } while (PointerPte <= LastPte);
3840
3841 /* Everything is done */
3843
3844CleanupWithWsLock:
3845
3846 /* Release process working set */
3848
3849Cleanup:
3850 /* Unlock address space */
3852
3853 return Status;
3854}
3855
3856
3858NTAPI
3861 IN OUT PSIZE_T NumberOfBytesToUnlock,
3862 IN ULONG MapType)
3863{
3865 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3870 PVOID CapturedBaseAddress;
3871 SIZE_T CapturedBytesToUnlock;
3872 PAGED_CODE();
3873
3874 //
3875 // Validate flags
3876 //
3877 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3878 {
3879 //
3880 // Invalid set of flags
3881 //
3883 }
3884
3885 //
3886 // At least one flag must be specified
3887 //
3888 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3889 {
3890 //
3891 // No flag given
3892 //
3894 }
3895
3896 //
3897 // Enter SEH for probing
3898 //
3899 _SEH2_TRY
3900 {
3901 //
3902 // Validate output data
3903 //
3905 ProbeForWriteSize_t(NumberOfBytesToUnlock);
3906
3907 //
3908 // Capture it
3909 //
3910 CapturedBaseAddress = *BaseAddress;
3911 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3912 }
3914 {
3915 //
3916 // Get exception code
3917 //
3919 }
3920 _SEH2_END;
3921
3922 //
3923 // Catch illegal base address
3924 //
3925 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3926
3927 //
3928 // Catch illegal region size
3929 //
3930 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3931 {
3932 //
3933 // Fail
3934 //
3936 }
3937
3938 //
3939 // 0 is also illegal
3940 //
3941 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3942
3943 //
3944 // Get a reference to the process
3945 //
3950 (PVOID*)(&Process),
3951 NULL);
3952 if (!NT_SUCCESS(Status)) return Status;
3953
3954 //
3955 // Check if this is is system-mapped
3956 //
3957 if (MapType & MAP_SYSTEM)
3958 {
3959 //
3960 // Check for required privilege
3961 //
3963 {
3964 //
3965 // Fail: Don't have it
3966 //
3969 }
3970 }
3971
3972 //
3973 // Check if we should attach
3974 //
3975 if (CurrentProcess != Process)
3976 {
3977 //
3978 // Do it
3979 //
3981 Attached = TRUE;
3982 }
3983
3984 //
3985 // Call the internal function
3986 //
3987 Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3988 &CapturedBytesToUnlock,
3989 MapType);
3990
3991 //
3992 // Detach if needed
3993 //
3995
3996 //
3997 // Release reference
3998 //
4000
4001 //
4002 // Enter SEH to return data
4003 //
4004 _SEH2_TRY
4005 {
4006 //
4007 // Return data to user
4008 //
4009 *BaseAddress = CapturedBaseAddress;
4010 *NumberOfBytesToUnlock = CapturedBytesToUnlock;
4011 }
4013 {
4014 //
4015 // Get exception code
4016 //
4018 }
4019 _SEH2_END;
4020
4021 //
4022 // Return status
4023 //
4024 return STATUS_SUCCESS;
4025}
4026
4028NTAPI
4031 IN OUT PSIZE_T NumberOfBytesToFlush,
4033{
4037 PVOID CapturedBaseAddress;
4038 SIZE_T CapturedBytesToFlush;
4039 IO_STATUS_BLOCK LocalStatusBlock;
4040 PAGED_CODE();
4041
4042 //
4043 // Check if we came from user mode
4044 //
4045 if (PreviousMode != KernelMode)
4046 {
4047 //
4048 // Enter SEH for probing
4049 //
4050 _SEH2_TRY
4051 {
4052 //
4053 // Validate all outputs
4054 //
4056 ProbeForWriteSize_t(NumberOfBytesToFlush);
4058
4059 //
4060 // Capture them
4061 //
4062 CapturedBaseAddress = *BaseAddress;
4063 CapturedBytesToFlush = *NumberOfBytesToFlush;
4064 }
4066 {
4067 //
4068 // Get exception code
4069 //
4071 }
4072 _SEH2_END;
4073 }
4074 else
4075 {
4076 //
4077 // Capture directly
4078 //
4079 CapturedBaseAddress = *BaseAddress;
4080 CapturedBytesToFlush = *NumberOfBytesToFlush;
4081 }
4082
4083 //
4084 // Catch illegal base address
4085 //
4086 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4087
4088 //
4089 // Catch illegal region size
4090 //
4091 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
4092 {
4093 //
4094 // Fail
4095 //
4097 }
4098
4099 //
4100 // Get a reference to the process
4101 //
4106 (PVOID*)(&Process),
4107 NULL);
4108 if (!NT_SUCCESS(Status)) return Status;
4109
4110 //
4111 // Do it
4112 //
4114 &CapturedBaseAddress,
4115 &CapturedBytesToFlush,
4116 &LocalStatusBlock);
4117
4118 //
4119 // Release reference
4120 //
4122
4123 //
4124 // Enter SEH to return data
4125 //
4126 _SEH2_TRY
4127 {
4128 //
4129 // Return data to user
4130 //
4131 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
4132 *NumberOfBytesToFlush = 0;
4133 *IoStatusBlock = LocalStatusBlock;
4134 }
4136 {
4137 }
4138 _SEH2_END;
4139
4140 //
4141 // Return status
4142 //
4143 return Status;
4144}
4145
4146/*
4147 * @unimplemented
4148 */
4150NTAPI
4152 IN ULONG Flags,
4155 IN PVOID *UserAddressArray,
4156 OUT PULONG_PTR EntriesInUserAddressArray,
4157 OUT PULONG Granularity)
4158{
4161 PVOID EndAddress;
4163 ULONG_PTR CapturedEntryCount;
4164 PAGED_CODE();
4165
4166 //
4167 // Check if we came from user mode
4168 //
4169 if (PreviousMode != KernelMode)
4170 {
4171 //
4172 // Enter SEH for probing
4173 //
4174 _SEH2_TRY
4175 {
4176 //
4177 // Catch illegal base address
4178 //
4180
4181 //
4182 // Catch illegal region size
4183 //
4185 {
4186 //
4187 // Fail
4188 //
4190 }
4191
4192 //
4193 // Validate all data
4194 //
4195 ProbeForWriteSize_t(EntriesInUserAddressArray);
4196 ProbeForWriteUlong(Granularity);
4197
4198 //
4199 // Capture them
4200 //
4201 CapturedEntryCount = *EntriesInUserAddressArray;
4202
4203 //
4204 // Must have a count
4205 //
4206 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4207
4208 //
4209 // Can't be larger than the maximum
4210 //
4211 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
4212 {
4213 //
4214 // Fail
4215 //
4217 }
4218
4219 //
4220 // Probe the actual array
4221 //
4222 ProbeForWrite(UserAddressArray,
4223 CapturedEntryCount * sizeof(PVOID),
4224 sizeof(PVOID));
4225 }
4227 {
4228 //
4229 // Get exception code
4230 //
4232 }
4233 _SEH2_END;
4234 }
4235 else
4236 {
4237 //
4238 // Capture directly
4239 //
4240 CapturedEntryCount = *EntriesInUserAddressArray;
4241 ASSERT(CapturedEntryCount != 0);
4242 }
4243
4244 //
4245 // Check if this is a local request
4246 //
4248 {
4249 //
4250 // No need to reference the process
4251 //
4253 }
4254 else
4255 {
4256 //
4257 // Reference the target
4258 //
4263 (PVOID *)&Process,
4264 NULL);
4265 if (!NT_SUCCESS(Status)) return Status;
4266 }
4267
4268 //
4269 // Compute the last address and validate it
4270 //
4271 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4272 if (BaseAddress > EndAddress)
4273 {
4274 //
4275 // Fail
4276 //
4279 }
4280
4281 //
4282 // Oops :(
4283 //
4285
4286 //
4287 // Dereference if needed
4288 //
4290
4291 //
4292 // Enter SEH to return data
4293 //
4294 _SEH2_TRY
4295 {
4296 //
4297 // Return data to user
4298 //
4299 *EntriesInUserAddressArray = 0;
4300 *Granularity = PAGE_SIZE;
4301 }
4303 {
4304 //
4305 // Get exception code
4306 //
4308 }
4309 _SEH2_END;
4310
4311 //
4312 // Return success
4313 //
4314 return STATUS_SUCCESS;
4315}
4316
4317/*
4318 * @unimplemented
4319 */
4321NTAPI
4325{
4326 PVOID EndAddress;
4331
4332 //
4333 // Catch illegal base address
4334 //
4336
4337 //
4338 // Catch illegal region size
4339 //
4341 {
4342 //
4343 // Fail
4344 //
4346 }
4347
4348 //
4349 // Check if this is a local request
4350 //
4352 {
4353 //
4354 // No need to reference the process
4355 //
4357 }
4358 else
4359 {
4360 //
4361 // Reference the target
4362 //
4367 (PVOID *)&Process,
4368 NULL);
4369 if (!NT_SUCCESS(Status)) return Status;
4370 }
4371
4372 //
4373 // Compute the last address and validate it
4374 //
4375 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4376 if (BaseAddress > EndAddress)
4377 {
4378 //
4379 // Fail
4380 //
4383 }
4384
4385 //
4386 // Oops :(
4387 //
4389
4390 //
4391 // Dereference if needed
4392 //
4394
4395 //
4396 // Return success
4397 //
4398 return STATUS_SUCCESS;
4399}
4400
4402NTAPI
4405 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4406 OUT PVOID MemoryInformation,
4407 IN SIZE_T MemoryInformationLength,
4409{
4412
4413 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4414
4415 /* Bail out if the address is invalid */
4417
4418 /* Probe return buffer */
4420 if (PreviousMode != KernelMode)
4421 {
4422 _SEH2_TRY
4423 {
4424 ProbeForWrite(MemoryInformation,
4425 MemoryInformationLength,
4426 sizeof(ULONG_PTR));
4427
4429 }
4431 {
4433 }
4434 _SEH2_END;
4435
4436 if (!NT_SUCCESS(Status))
4437 {
4438 return Status;
4439 }
4440 }
4441
4442 switch(MemoryInformationClass)
4443 {
4445 /* Validate the size information of the class */
4446 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4447 {
4448 /* The size is invalid */
4450 }
4453 MemoryInformation,
4454 MemoryInformationLength,
4455 ReturnLength);
4456 break;
4457
4458 case MemorySectionName:
4459 /* Validate the size information of the class */
4460 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4461 {
4462 /* The size is invalid */
4464 }
4467 MemoryInformation,
4468 MemoryInformationLength,
4469 ReturnLength);
4470 break;
4473 default:
4474 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4475 break;
4476 }
4477
4478 return Status;
4479}
4480
4481/*
4482 * @implemented
4483 */
4485NTAPI
4487 IN OUT PVOID* UBaseAddress,
4489 IN OUT PSIZE_T URegionSize,
4492{
4495 PMMVAD Vad = NULL, FoundVad;
4498 PVOID PBaseAddress;
4499 ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4500 ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4501 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4503 PETHREAD CurrentThread = PsGetCurrentThread();
4505 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4506 BOOLEAN Attached = FALSE, ChangeProtection = FALSE, QuotaCharged = FALSE;
4507 MMPTE TempPte;
4508 PMMPTE PointerPte, LastPte;
4509 PMMPDE PointerPde;
4511 PAGED_CODE();
4512
4513 /* Check for valid Zero bits */
4515 {
4516 DPRINT1("Too many zero bits\n");
4518 }
4519
4520 /* Check for valid Allocation Types */
4523 {
4524 DPRINT1("Invalid Allocation Type\n");
4526 }
4527
4528 /* Check for at least one of these Allocation Types to be set */
4530 {
4531 DPRINT1("No memory allocation base type\n");
4533 }
4534
4535 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4537 {
4538 DPRINT1("Invalid use of MEM_RESET\n");
4540 }
4541
4542 /* Check if large pages are being used */
4544 {
4545 /* Large page allocations MUST be committed */
4546 if (!(AllocationType & MEM_COMMIT))
4547 {
4548 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4550 }
4551
4552 /* These flags are not allowed with large page allocations */
4554 {
4555 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4557 }
4558 }
4559
4560 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4562 {
4563 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4565 }
4566
4567 /* Check for valid MEM_PHYSICAL usage */
4569 {
4570 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4571 if (!(AllocationType & MEM_RESERVE))
4572 {
4573 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4575 }
4576
4577 /* Only these flags are allowed with MEM_PHYSIAL */
4579 {
4580 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4582 }
4583
4584 /* Then make sure PAGE_READWRITE is used */
4585 if (Protect != PAGE_READWRITE)
4586 {
4587 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4589 }
4590 }
4591
4592 /* Calculate the protection mask and make sure it's valid */
4593 ProtectionMask = MiMakeProtectionMask(Protect);
4594 if (ProtectionMask == MM_INVALID_PROTECTION)
4595 {
4596 DPRINT1("Invalid protection mask\n");
4598 }
4599
4600 /* Enter SEH */
4601 _SEH2_TRY
4602 {
4603 /* Check for user-mode parameters */
4604 if (PreviousMode != KernelMode)
4605 {
4606 /* Make sure they are writable */
4607 ProbeForWritePointer(UBaseAddress);
4608 ProbeForWriteSize_t(URegionSize);
4609 }
4610
4611 /* Capture their values */
4612 PBaseAddress = *UBaseAddress;
4613 PRegionSize = *URegionSize;
4614 }
4616 {
4617 /* Return the exception code */
4619 }
4620 _SEH2_END;
4621
4622 /* Make sure the allocation isn't past the VAD area */
4623 if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4624 {
4625 DPRINT1("Virtual allocation base above User Space\n");
4627 }
4628
4629 /* Make sure the allocation wouldn't overflow past the VAD area */
4630 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4631 {
4632 DPRINT1("Region size would overflow into kernel-memory\n");
4634 }
4635
4636 /* Make sure there's a size specified */
4637 if (!PRegionSize)
4638 {
4639 DPRINT1("Region size is invalid (zero)\n");
4641 }
4642
4643 //
4644 // If this is for the current process, just use PsGetCurrentProcess
4645 //
4647 {
4648 Process = CurrentProcess;
4649 }
4650 else
4651 {
4652 //
4653 // Otherwise, reference the process with VM rights and attach to it if
4654 // this isn't the current process. We must attach because we'll be touching
4655 // PTEs and PDEs that belong to user-mode memory, and also touching the
4656 // Working Set which is stored in Hyperspace.
4657 //
4662 (PVOID*)&Process,
4663 NULL);
4664 if (!NT_SUCCESS(Status)) return Status;
4665 if (CurrentProcess != Process)
4666 {
4668 Attached = TRUE;
4669 }
4670 }
4671
4672 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4673 Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect);
4674
4675 //
4676 // Check for large page allocations and make sure that the required privilege
4677 // is being held, before attempting to handle them.
4678 //
4681 {
4682 /* Fail without it */
4683 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4685 goto FailPathNoLock;
4686 }
4687
4688 //
4689 // Fail on the things we don't yet support
4690 //
4692 {
4693 DPRINT1("MEM_LARGE_PAGES not supported\n");
4695 goto FailPathNoLock;
4696 }
4698 {
4699 DPRINT1("MEM_PHYSICAL not supported\n");
4701 goto FailPathNoLock;
4702 }
4704 {
4705 DPRINT1("MEM_WRITE_WATCH not supported\n");
4707 goto FailPathNoLock;
4708 }
4709
4710 //
4711 // Check if the caller is reserving memory, or committing memory and letting
4712 // us pick the base address
4713 //
4714 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4715 {
4716 //
4717 // Do not allow COPY_ON_WRITE through this API
4718 //
4720 {
4721 DPRINT1("Copy on write not allowed through this path\n");
4723 goto FailPathNoLock;
4724 }
4725
4726 //
4727 // Does the caller have an address in mind, or is this a blind commit?
4728 //
4729 if (!PBaseAddress)
4730 {
4731 //
4732 // This is a blind commit, all we need is the region size
4733 //
4734 PRegionSize = ROUND_TO_PAGES(PRegionSize);
4735 EndingAddress = 0;
4736 StartingAddress = 0;
4737
4738 //
4739 // Check if ZeroBits were specified
4740 //
4741 if (ZeroBits != 0)
4742 {
4743 //
4744 // Calculate the highest address and check if it's valid
4745 //
4746 HighestAddress = MAXULONG_PTR >> ZeroBits;
4747 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4748 {
4750 goto FailPathNoLock;
4751 }
4752 }
4753 }
4754 else
4755 {
4756 //
4757 // This is a reservation, so compute the starting address on the
4758 // expected 64KB granularity, and see where the ending address will
4759 // fall based on the aligned address and the passed in region size
4760 //
4761 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4762 PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4763 StartingAddress = (ULONG_PTR)PBaseAddress;
4764 }
4765
4766 // Charge quotas for the VAD
4768 if (!NT_SUCCESS(Status))
4769 {
4770 DPRINT1("Quota exceeded.\n");
4771 goto FailPathNoLock;
4772 }
4773
4775
4776 //
4777 // Allocate and initialize the VAD
4778 //
4779 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4780 if (Vad == NULL)
4781 {
4782 DPRINT1("Failed to allocate a VAD!\n");
4784 goto FailPathNoLock;
4785 }
4786
4787 RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
4789 Vad->u.VadFlags.Protection = ProtectionMask;
4790 Vad->u.VadFlags.PrivateMemory = 1;
4791 Vad->ControlArea = NULL; // For Memory-Area hack
4792
4793 //
4794 // Insert the VAD
4795 //
4796 Status = MiInsertVadEx(Vad,
4797 &StartingAddress,
4798 PRegionSize,
4799 HighestAddress,
4802 if (!NT_SUCCESS(Status))
4803 {
4804 DPRINT1("Failed to insert the VAD!\n");
4805 ExFreePoolWithTag(Vad, 'SdaV');
4806 goto FailPathNoLock;
4807 }
4808
4809 //
4810 // Detach and dereference the target process if
4811 // it was different from the current process
4812 //
4815
4816 //
4817 // Use SEH to write back the base address and the region size. In the case
4818 // of an exception, we do not return back the exception code, as the memory
4819 // *has* been allocated. The caller would now have to call VirtualQuery
4820 // or do some other similar trick to actually find out where its memory
4821 // allocation ended up
4822 //
4823 _SEH2_TRY
4824 {
4825 *URegionSize = PRegionSize;
4826 *UBaseAddress = (PVOID)StartingAddress;
4827 }
4829 {
4830 //
4831 // Ignore exception!
4832 //
4833 }
4834 _SEH2_END;
4835 DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress);
4836 return STATUS_SUCCESS;
4837 }
4838
4839 //
4840 // This is a MEM_COMMIT on top of an existing address which must have been
4841 // MEM_RESERVED already. Compute the start and ending base addresses based
4842 // on the user input, and then compute the actual region size once all the
4843 // alignments have been done.
4844 //
4845 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4846 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4847 PRegionSize = EndingAddress - StartingAddress + 1;
4848
4849 //
4850 // Lock the address space and make sure the process isn't already dead
4851 //
4854 if (Process->VmDeleted)
4855 {
4856 DPRINT1("Process is dying\n");
4858 goto FailPath;
4859 }
4860
4861 //
4862 // Get the VAD for this address range, and make sure it exists
4863 //
4864 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4865 EndingAddress >> PAGE_SHIFT,
4866 &Process->VadRoot,
4867 (PMMADDRESS_NODE*)&FoundVad);
4868 if (Result != TableFoundNode)
4869 {
4870 DPRINT1("Could not find a VAD for this allocation\n");
4872 goto FailPath;
4873 }
4874
4876 {
4878 DPRINT("MEM_RESET not supported\n");
4880 goto FailPath;
4881 }
4882
4883 //
4884 // These kinds of VADs are illegal for this Windows function when trying to
4885 // commit an existing range
4886 //
4887 if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4888 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4889 (FoundVad->u.VadFlags.VadType == VadLargePages))
4890 {
4891 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4893 goto FailPath;
4894 }
4895
4896 //
4897 // Make sure that this address range actually fits within the VAD for it
4898 //
4899 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4900 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4901 {
4902 DPRINT1("Address range does not fit into the VAD\n");
4904 goto FailPath;
4905 }
4906
4907 //
4908 // Make sure this is an ARM3 section
4909 //
4913 {
4914 DPRINT1("Illegal commit of non-ARM3 section!\n");
4916 goto FailPath;
4917 }
4918
4919 // Is this a previously reserved section being committed? If so, enter the
4920 // special section path
4921 //
4922 if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4923 {
4924 //
4925 // You cannot commit large page sections through this API
4926 //
4927 if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4928 {
4929 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4931 goto FailPath;
4932 }
4933
4934 //
4935 // You can only use caching flags on a rotate VAD
4936 //
4938 (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4939 {
4940 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4942 goto FailPath;
4943 }
4944
4945 //
4946 // We should make sure that the section's permissions aren't being
4947 // messed with
4948 //