2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/i386/page.c
5 * PURPOSE: Low level memory managment manipulation
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
10 /* INCLUDES ***************************************************************/
15 #include "../ARM3/miarm.h"
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
19 #pragma alloc_text(INIT, MiInitPageDirectoryMap)
23 /* GLOBALS *****************************************************************/
25 #define PA_BIT_PRESENT (0)
26 #define PA_BIT_READWRITE (1)
27 #define PA_BIT_USER (2)
30 #define PA_BIT_ACCESSED (5)
31 #define PA_BIT_DIRTY (6)
32 #define PA_BIT_GLOBAL (8)
34 #define PA_PRESENT (1 << PA_BIT_PRESENT)
35 #define PA_READWRITE (1 << PA_BIT_READWRITE)
36 #define PA_USER (1 << PA_BIT_USER)
37 #define PA_DIRTY (1 << PA_BIT_DIRTY)
38 #define PA_WT (1 << PA_BIT_WT)
39 #define PA_CD (1 << PA_BIT_CD)
40 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
41 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
43 #define HYPERSPACE (0xc0400000)
44 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000))
46 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
47 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
50 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
52 __inline LARGE_INTEGER
PTE_TO_PAGE(ULONG npage
)
55 dummy
.QuadPart
= (LONGLONG
)(PAGE_MASK(npage
));
62 MmProtectToPteMask
[32] =
65 // These are the base MM_ protection flags
68 PTE_READONLY
| PTE_ENABLE_CACHE
,
69 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
70 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
71 PTE_READWRITE
| PTE_ENABLE_CACHE
,
72 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
73 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
74 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
76 // These OR in the MM_NOCACHE flag
79 PTE_READONLY
| PTE_DISABLE_CACHE
,
80 PTE_EXECUTE
| PTE_DISABLE_CACHE
,
81 PTE_EXECUTE_READ
| PTE_DISABLE_CACHE
,
82 PTE_READWRITE
| PTE_DISABLE_CACHE
,
83 PTE_WRITECOPY
| PTE_DISABLE_CACHE
,
84 PTE_EXECUTE_READWRITE
| PTE_DISABLE_CACHE
,
85 PTE_EXECUTE_WRITECOPY
| PTE_DISABLE_CACHE
,
87 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
90 PTE_READONLY
| PTE_ENABLE_CACHE
,
91 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
92 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
93 PTE_READWRITE
| PTE_ENABLE_CACHE
,
94 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
95 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
96 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
98 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
101 PTE_READONLY
| PTE_WRITECOMBINED_CACHE
,
102 PTE_EXECUTE
| PTE_WRITECOMBINED_CACHE
,
103 PTE_EXECUTE_READ
| PTE_WRITECOMBINED_CACHE
,
104 PTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
105 PTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
106 PTE_EXECUTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
107 PTE_EXECUTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
111 ULONG MmProtectToValue
[32] =
119 PAGE_EXECUTE_READWRITE
,
120 PAGE_EXECUTE_WRITECOPY
,
122 PAGE_NOCACHE
| PAGE_READONLY
,
123 PAGE_NOCACHE
| PAGE_EXECUTE
,
124 PAGE_NOCACHE
| PAGE_EXECUTE_READ
,
125 PAGE_NOCACHE
| PAGE_READWRITE
,
126 PAGE_NOCACHE
| PAGE_WRITECOPY
,
127 PAGE_NOCACHE
| PAGE_EXECUTE_READWRITE
,
128 PAGE_NOCACHE
| PAGE_EXECUTE_WRITECOPY
,
130 PAGE_GUARD
| PAGE_READONLY
,
131 PAGE_GUARD
| PAGE_EXECUTE
,
132 PAGE_GUARD
| PAGE_EXECUTE_READ
,
133 PAGE_GUARD
| PAGE_READWRITE
,
134 PAGE_GUARD
| PAGE_WRITECOPY
,
135 PAGE_GUARD
| PAGE_EXECUTE_READWRITE
,
136 PAGE_GUARD
| PAGE_EXECUTE_WRITECOPY
,
138 PAGE_WRITECOMBINE
| PAGE_READONLY
,
139 PAGE_WRITECOMBINE
| PAGE_EXECUTE
,
140 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READ
,
141 PAGE_WRITECOMBINE
| PAGE_READWRITE
,
142 PAGE_WRITECOMBINE
| PAGE_WRITECOPY
,
143 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READWRITE
,
144 PAGE_WRITECOMBINE
| PAGE_EXECUTE_WRITECOPY
147 /* FUNCTIONS ***************************************************************/
149 BOOLEAN
MmUnmapPageTable(PULONG Pt
);
152 MiFlushTlb(PULONG Pt
, PVOID Address
)
154 if ((Pt
&& MmUnmapPageTable(Pt
)) || Address
>= MmSystemRangeStart
)
156 KeInvalidateTlbEntry(Address
);
161 ProtectToPTE(ULONG flProtect
)
163 ULONG Attributes
= 0;
165 if (flProtect
& (PAGE_NOACCESS
|PAGE_GUARD
))
169 else if (flProtect
& PAGE_IS_WRITABLE
)
171 Attributes
= PA_PRESENT
| PA_READWRITE
;
173 else if (flProtect
& (PAGE_IS_READABLE
| PAGE_IS_EXECUTABLE
))
175 Attributes
= PA_PRESENT
;
179 DPRINT1("Unknown main protection type.\n");
180 KeBugCheck(MEMORY_MANAGEMENT
);
183 if (flProtect
& PAGE_SYSTEM
)
188 Attributes
= Attributes
| PA_USER
;
190 if (flProtect
& PAGE_NOCACHE
)
192 Attributes
= Attributes
| PA_CD
;
194 if (flProtect
& PAGE_WRITETHROUGH
)
196 Attributes
= Attributes
| PA_WT
;
201 /* Taken from ARM3/pagfault.c */
204 MiSynchronizeSystemPde(PMMPDE PointerPde
)
209 /* Get the Index from the PDE */
210 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
212 /* Copy the PDE from the double-mapped system page directory */
213 SystemPde
= MmSystemPagePtes
[Index
];
214 *PointerPde
= SystemPde
;
216 /* Make sure we re-read the PDE and PTE */
217 KeMemoryBarrierWithoutFence();
219 /* Return, if we had success */
220 return SystemPde
.u
.Hard
.Valid
!= 0;
225 MiDispatchFault(IN BOOLEAN StoreInstruction
,
227 IN PMMPTE PointerPte
,
228 IN PMMPTE PointerProtoPte
,
229 IN BOOLEAN Recursive
,
230 IN PEPROCESS Process
,
231 IN PVOID TrapInformation
,
236 MiFillSystemPageDirectory(IN PVOID Base
,
237 IN SIZE_T NumberOfBytes
);
240 MmGetPageTableForProcess(PEPROCESS Process
, PVOID Address
, BOOLEAN Create
)
246 if (Address
< MmSystemRangeStart
)
248 /* We should have a process for user land addresses */
249 ASSERT(Process
!= NULL
);
251 if(Process
!= PsGetCurrentProcess())
254 ULONG PdeOffset
= MiGetPdeOffset(Address
);
256 /* Nobody but page fault should ask for creating the PDE,
257 * Which imples that Process is the current one */
258 ASSERT(Create
== FALSE
);
260 PdeBase
= MmCreateHyperspaceMapping(PTE_TO_PFN(Process
->Pcb
.DirectoryTableBase
[0]));
263 KeBugCheck(MEMORY_MANAGEMENT
);
265 PointerPde
= PdeBase
+ PdeOffset
;
266 if (PointerPde
->u
.Hard
.Valid
== 0)
268 MmDeleteHyperspaceMapping(PdeBase
);
273 Pfn
= PointerPde
->u
.Hard
.PageFrameNumber
;
275 MmDeleteHyperspaceMapping(PdeBase
);
276 Pt
= MmCreateHyperspaceMapping(Pfn
);
279 KeBugCheck(MEMORY_MANAGEMENT
);
281 return Pt
+ MiAddressToPteOffset(Address
);
283 /* This is for our process */
284 PointerPde
= MiAddressToPde(Address
);
285 Pt
= (PULONG
)MiAddressToPte(Address
);
286 if (PointerPde
->u
.Hard
.Valid
== 0)
293 ASSERT(PointerPde
->u
.Long
== 0);
295 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
296 Status
= MiDispatchFault(TRUE
,
301 PsGetCurrentProcess(),
304 ASSERT(KeAreAllApcsDisabled() == TRUE
);
305 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
307 return (PULONG
)MiAddressToPte(Address
);
310 /* This is for kernel land address */
311 ASSERT(Process
== NULL
);
312 PointerPde
= MiAddressToPde(Address
);
313 Pt
= (PULONG
)MiAddressToPte(Address
);
314 if (PointerPde
->u
.Hard
.Valid
== 0)
316 /* Let ARM3 synchronize the PDE */
317 if(!MiSynchronizeSystemPde(PointerPde
))
319 /* PDE (still) not valid, let ARM3 allocate one if asked */
322 MiFillSystemPageDirectory(Address
, PAGE_SIZE
);
328 BOOLEAN
MmUnmapPageTable(PULONG Pt
)
330 if (!IS_HYPERSPACE(Pt
))
337 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pt
));
342 static ULONG
MmGetPageEntryForProcess(PEPROCESS Process
, PVOID Address
)
347 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
351 MmUnmapPageTable(Pt
);
359 MmGetPfnForProcess(PEPROCESS Process
,
363 Entry
= MmGetPageEntryForProcess(Process
, Address
);
364 if (!(Entry
& PA_PRESENT
))
368 return(PTE_TO_PFN(Entry
));
373 MmDisableVirtualMapping(PEPROCESS Process
, PVOID Address
, BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
375 * FUNCTION: Delete a virtual mapping
382 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
385 KeBugCheck(MEMORY_MANAGEMENT
);
389 * Atomically disable the present bit and get the old value.
394 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
& ~PA_PRESENT
, Pte
));
396 MiFlushTlb(Pt
, Address
);
398 WasValid
= (Pte
& PA_PRESENT
);
401 KeBugCheck(MEMORY_MANAGEMENT
);
405 * Return some information to the caller
407 if (WasDirty
!= NULL
)
409 *WasDirty
= Pte
& PA_DIRTY
;
413 *Page
= PTE_TO_PFN(Pte
);
419 MmRawDeleteVirtualMapping(PVOID Address
)
423 Pt
= MmGetPageTableForProcess(NULL
, Address
, FALSE
);
427 * Set the entry to zero
429 InterlockedExchangePte(Pt
, 0);
430 MiFlushTlb(Pt
, Address
);
436 MmDeleteVirtualMapping(PEPROCESS Process
, PVOID Address
, BOOLEAN FreePage
,
437 BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
439 * FUNCTION: Delete a virtual mapping
442 BOOLEAN WasValid
= FALSE
;
447 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
448 Process
, Address
, FreePage
, WasDirty
, Page
);
450 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
454 if (WasDirty
!= NULL
)
466 * Atomically set the entry to zero and get the old value.
468 Pte
= InterlockedExchangePte(Pt
, 0);
470 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
471 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
472 WasValid
= (Pte
& PA_PRESENT
) || ((Pte
>> PAGE_SHIFT
) && !(Pte
& 0x800));
475 /* Flush the TLB since we transitioned this PTE
476 * from valid to invalid so any stale translations
477 * are removed from the cache */
478 MiFlushTlb(Pt
, Address
);
480 if (Address
< MmSystemRangeStart
)
482 /* Remove PDE reference */
483 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
484 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
487 Pfn
= PTE_TO_PFN(Pte
);
491 MmReleasePageMemoryConsumer(MC_SYSTEM
, Pfn
);
497 MmUnmapPageTable(Pt
);
502 * Return some information to the caller
504 if (WasDirty
!= NULL
)
506 *WasDirty
= ((Pte
& PA_DIRTY
) && (Pte
& PA_PRESENT
)) ? TRUE
: FALSE
;
516 MmGetPageFileMapping(PEPROCESS Process
, PVOID Address
,
517 SWAPENTRY
* SwapEntry
)
519 * FUNCTION: Get a page file mapping
522 ULONG Entry
= MmGetPageEntryForProcess(Process
, Address
);
523 *SwapEntry
= Entry
>> 1;
528 MmDeletePageFileMapping(PEPROCESS Process
, PVOID Address
,
529 SWAPENTRY
* SwapEntry
)
531 * FUNCTION: Delete a virtual mapping
537 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
546 * Atomically set the entry to zero and get the old value.
548 Pte
= InterlockedExchangePte(Pt
, 0);
550 if (Address
< MmSystemRangeStart
)
552 /* Remove PDE reference */
553 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
554 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
557 /* We don't need to flush here because page file entries
558 * are invalid translations, so the processor won't cache them */
559 MmUnmapPageTable(Pt
);
561 if ((Pte
& PA_PRESENT
) || !(Pte
& 0x800))
563 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte
);
564 KeBugCheck(MEMORY_MANAGEMENT
);
568 * Return some information to the caller
570 *SwapEntry
= Pte
>> 1;
574 Mmi386MakeKernelPageTableGlobal(PVOID Address
)
576 PMMPDE PointerPde
= MiAddressToPde(Address
);
577 PMMPTE PointerPte
= MiAddressToPte(Address
);
579 if (PointerPde
->u
.Hard
.Valid
== 0)
581 if(!MiSynchronizeSystemPde(PointerPde
))
583 return PointerPte
->u
.Hard
.Valid
!= 0;
590 MmIsDirtyPage(PEPROCESS Process
, PVOID Address
)
592 return MmGetPageEntryForProcess(Process
, Address
) & PA_DIRTY
? TRUE
: FALSE
;
597 MmSetCleanPage(PEPROCESS Process
, PVOID Address
)
602 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
604 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
605 KeBugCheck(MEMORY_MANAGEMENT
);
608 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
611 KeBugCheck(MEMORY_MANAGEMENT
);
617 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
& ~PA_DIRTY
, Pte
));
619 if (!(Pte
& PA_PRESENT
))
621 KeBugCheck(MEMORY_MANAGEMENT
);
623 else if (Pte
& PA_DIRTY
)
625 MiFlushTlb(Pt
, Address
);
629 MmUnmapPageTable(Pt
);
635 MmSetDirtyPage(PEPROCESS Process
, PVOID Address
)
640 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
642 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
643 KeBugCheck(MEMORY_MANAGEMENT
);
646 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
649 KeBugCheck(MEMORY_MANAGEMENT
);
655 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
| PA_DIRTY
, Pte
));
657 if (!(Pte
& PA_PRESENT
))
659 KeBugCheck(MEMORY_MANAGEMENT
);
663 /* The processor will never clear this bit itself, therefore
664 * we do not need to flush the TLB here when setting it */
665 MmUnmapPageTable(Pt
);
671 MmEnableVirtualMapping(PEPROCESS Process
, PVOID Address
)
676 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
679 //HACK to get DPH working, waiting for MM rewrite :-/
680 //KeBugCheck(MEMORY_MANAGEMENT);
684 /* Do not mark a 0 page as present */
685 if(0 == InterlockedCompareExchangePte(Pt
, 0, 0))
691 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
| PA_PRESENT
, Pte
));
693 /* We don't need to flush the TLB here because it
694 * won't cache translations for non-present pages */
695 MmUnmapPageTable(Pt
);
700 MmIsPagePresent(PEPROCESS Process
, PVOID Address
)
702 return MmGetPageEntryForProcess(Process
, Address
) & PA_PRESENT
;
707 MmIsDisabledPage(PEPROCESS Process
, PVOID Address
)
709 ULONG_PTR Entry
= MmGetPageEntryForProcess(Process
, Address
);
710 return !(Entry
& PA_PRESENT
) && !(Entry
& 0x800) && (Entry
>> PAGE_SHIFT
);
715 MmIsPageSwapEntry(PEPROCESS Process
, PVOID Address
)
718 Entry
= MmGetPageEntryForProcess(Process
, Address
);
719 return !(Entry
& PA_PRESENT
) && (Entry
& 0x800);
724 MmCreatePageFileMapping(PEPROCESS Process
,
731 if (Process
== NULL
&& Address
< MmSystemRangeStart
)
733 DPRINT1("No process\n");
734 KeBugCheck(MEMORY_MANAGEMENT
);
736 if (Process
!= NULL
&& Address
>= MmSystemRangeStart
)
738 DPRINT1("Setting kernel address with process context\n");
739 KeBugCheck(MEMORY_MANAGEMENT
);
742 if (SwapEntry
& (1 << 31))
744 KeBugCheck(MEMORY_MANAGEMENT
);
747 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
750 /* Nobody should page out an address that hasn't even been mapped */
751 /* But we might place a wait entry first, requiring the page table */
752 if (SwapEntry
!= MM_WAIT_ENTRY
)
754 KeBugCheck(MEMORY_MANAGEMENT
);
756 Pt
= MmGetPageTableForProcess(Process
, Address
, TRUE
);
758 Pte
= InterlockedExchangePte(Pt
, SwapEntry
<< 1);
761 KeBugCheckEx(MEMORY_MANAGEMENT
, SwapEntry
, (ULONG_PTR
)Process
, (ULONG_PTR
)Address
, 0);
764 if (Address
< MmSystemRangeStart
)
766 /* Add PDE reference */
767 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
768 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
771 /* We don't need to flush the TLB here because it
772 * only caches valid translations and a zero PTE
773 * is not a valid translation */
774 MmUnmapPageTable(Pt
);
776 return(STATUS_SUCCESS
);
782 MmCreateVirtualMappingUnsafe(PEPROCESS Process
,
791 ULONG oldPdeOffset
, PdeOffset
;
794 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
795 Process
, Address
, flProtect
, Pages
, *Pages
, PageCount
);
797 ASSERT(((ULONG_PTR
)Address
% PAGE_SIZE
) == 0);
801 if (Address
< MmSystemRangeStart
)
803 DPRINT1("No process\n");
804 KeBugCheck(MEMORY_MANAGEMENT
);
806 if (PageCount
> 0x10000 ||
807 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
> 0x100000)
809 DPRINT1("Page count too large\n");
810 KeBugCheck(MEMORY_MANAGEMENT
);
815 if (Address
>= MmSystemRangeStart
)
817 DPRINT1("Setting kernel address with process context\n");
818 KeBugCheck(MEMORY_MANAGEMENT
);
820 if (PageCount
> (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
||
821 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
>
822 (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
)
824 DPRINT1("Page Count too large\n");
825 KeBugCheck(MEMORY_MANAGEMENT
);
829 Attributes
= ProtectToPTE(flProtect
);
831 if (Address
>= MmSystemRangeStart
)
833 Attributes
&= ~PA_USER
;
837 Attributes
|= PA_USER
;
841 /* MmGetPageTableForProcess should be called on the first run, so
842 * let this trigger it */
843 oldPdeOffset
= ADDR_TO_PDE_OFFSET(Addr
) + 1;
844 for (i
= 0; i
< PageCount
; i
++, Addr
= (PVOID
)((ULONG_PTR
)Addr
+ PAGE_SIZE
))
846 if (!(Attributes
& PA_PRESENT
) && Pages
[i
] != 0)
848 DPRINT1("Setting physical address but not allowing access at address "
849 "0x%.8X with attributes %x/%x.\n",
850 Addr
, Attributes
, flProtect
);
851 KeBugCheck(MEMORY_MANAGEMENT
);
853 PdeOffset
= ADDR_TO_PDE_OFFSET(Addr
);
854 if (oldPdeOffset
!= PdeOffset
)
856 if(Pt
) MmUnmapPageTable(Pt
);
857 Pt
= MmGetPageTableForProcess(Process
, Addr
, TRUE
);
860 KeBugCheck(MEMORY_MANAGEMENT
);
867 oldPdeOffset
= PdeOffset
;
869 Pte
= InterlockedExchangePte(Pt
, PFN_TO_PTE(Pages
[i
]) | Attributes
);
871 /* There should not be anything valid here */
874 DPRINT1("Bad PTE %lx\n", Pte
);
875 KeBugCheck(MEMORY_MANAGEMENT
);
878 /* We don't need to flush the TLB here because it only caches valid translations
879 * and we're moving this PTE from invalid to valid so it can't be cached right now */
881 if (Addr
< MmSystemRangeStart
)
883 /* Add PDE reference */
884 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)]++;
885 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)] <= PTE_COUNT
);
889 ASSERT(Addr
> Address
);
890 MmUnmapPageTable(Pt
);
892 return(STATUS_SUCCESS
);
897 MmCreateVirtualMapping(PEPROCESS Process
,
905 ASSERT((ULONG_PTR
)Address
% PAGE_SIZE
== 0);
906 for (i
= 0; i
< PageCount
; i
++)
908 if (!MmIsPageInUse(Pages
[i
]))
910 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages
[i
]));
911 KeBugCheck(MEMORY_MANAGEMENT
);
915 return(MmCreateVirtualMappingUnsafe(Process
,
924 MmGetPageProtect(PEPROCESS Process
, PVOID Address
)
929 Entry
= MmGetPageEntryForProcess(Process
, Address
);
932 if (!(Entry
& PA_PRESENT
))
934 Protect
= PAGE_NOACCESS
;
938 if (Entry
& PA_READWRITE
)
940 Protect
= PAGE_READWRITE
;
944 Protect
= PAGE_EXECUTE_READ
;
948 Protect
|= PAGE_NOCACHE
;
952 Protect
|= PAGE_WRITETHROUGH
;
954 if (!(Entry
& PA_USER
))
956 Protect
|= PAGE_SYSTEM
;
965 MmSetPageProtect(PEPROCESS Process
, PVOID Address
, ULONG flProtect
)
967 ULONG Attributes
= 0;
971 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
972 Process
, Address
, flProtect
);
974 Attributes
= ProtectToPTE(flProtect
);
977 if (Address
>= MmSystemRangeStart
)
979 Attributes
&= ~PA_USER
;
983 Attributes
|= PA_USER
;
986 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
989 KeBugCheck(MEMORY_MANAGEMENT
);
991 Pte
= InterlockedExchangePte(Pt
, PAGE_MASK(*Pt
) | Attributes
| (*Pt
& (PA_ACCESSED
|PA_DIRTY
)));
993 // We should be able to bring a page back from PAGE_NOACCESS
994 if ((Pte
& 0x800) || !(Pte
>> PAGE_SHIFT
))
996 DPRINT1("Invalid Pte %lx\n", Pte
);
997 KeBugCheck(MEMORY_MANAGEMENT
);
1000 if((Pte
& Attributes
) != Attributes
)
1001 MiFlushTlb(Pt
, Address
);
1003 MmUnmapPageTable(Pt
);
1009 PHYSICAL_ADDRESS NTAPI
1010 MmGetPhysicalAddress(PVOID vaddr
)
1012 * FUNCTION: Returns the physical address corresponding to a virtual address
1018 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr
);
1019 Pte
= MmGetPageEntryForProcess(NULL
, vaddr
);
1020 if (Pte
!= 0 && (Pte
& PA_PRESENT
))
1022 p
.QuadPart
= PAGE_MASK(Pte
);
1023 p
.u
.LowPart
|= (ULONG_PTR
)vaddr
& (PAGE_SIZE
- 1);
1035 MmInitGlobalKernelPageDirectory(VOID
)
1037 /* Nothing to do here */