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)
22 /* GLOBALS *****************************************************************/
24 #define PA_BIT_PRESENT (0)
25 #define PA_BIT_READWRITE (1)
26 #define PA_BIT_USER (2)
29 #define PA_BIT_ACCESSED (5)
30 #define PA_BIT_DIRTY (6)
31 #define PA_BIT_GLOBAL (8)
33 #define PA_PRESENT (1 << PA_BIT_PRESENT)
34 #define PA_READWRITE (1 << PA_BIT_READWRITE)
35 #define PA_USER (1 << PA_BIT_USER)
36 #define PA_DIRTY (1 << PA_BIT_DIRTY)
37 #define PA_WT (1 << PA_BIT_WT)
38 #define PA_CD (1 << PA_BIT_CD)
39 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
40 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
42 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END))
44 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
45 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
49 MmProtectToPteMask
[32] =
52 // These are the base MM_ protection flags
55 PTE_READONLY
| PTE_ENABLE_CACHE
,
56 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
57 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
58 PTE_READWRITE
| PTE_ENABLE_CACHE
,
59 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
60 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
61 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
63 // These OR in the MM_NOCACHE flag
66 PTE_READONLY
| PTE_DISABLE_CACHE
,
67 PTE_EXECUTE
| PTE_DISABLE_CACHE
,
68 PTE_EXECUTE_READ
| PTE_DISABLE_CACHE
,
69 PTE_READWRITE
| PTE_DISABLE_CACHE
,
70 PTE_WRITECOPY
| PTE_DISABLE_CACHE
,
71 PTE_EXECUTE_READWRITE
| PTE_DISABLE_CACHE
,
72 PTE_EXECUTE_WRITECOPY
| PTE_DISABLE_CACHE
,
74 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
77 PTE_READONLY
| PTE_ENABLE_CACHE
,
78 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
79 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
80 PTE_READWRITE
| PTE_ENABLE_CACHE
,
81 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
82 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
83 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
85 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
88 PTE_READONLY
| PTE_WRITECOMBINED_CACHE
,
89 PTE_EXECUTE
| PTE_WRITECOMBINED_CACHE
,
90 PTE_EXECUTE_READ
| PTE_WRITECOMBINED_CACHE
,
91 PTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
92 PTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
93 PTE_EXECUTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
94 PTE_EXECUTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
98 ULONG MmProtectToValue
[32] =
106 PAGE_EXECUTE_READWRITE
,
107 PAGE_EXECUTE_WRITECOPY
,
109 PAGE_NOCACHE
| PAGE_READONLY
,
110 PAGE_NOCACHE
| PAGE_EXECUTE
,
111 PAGE_NOCACHE
| PAGE_EXECUTE_READ
,
112 PAGE_NOCACHE
| PAGE_READWRITE
,
113 PAGE_NOCACHE
| PAGE_WRITECOPY
,
114 PAGE_NOCACHE
| PAGE_EXECUTE_READWRITE
,
115 PAGE_NOCACHE
| PAGE_EXECUTE_WRITECOPY
,
117 PAGE_GUARD
| PAGE_READONLY
,
118 PAGE_GUARD
| PAGE_EXECUTE
,
119 PAGE_GUARD
| PAGE_EXECUTE_READ
,
120 PAGE_GUARD
| PAGE_READWRITE
,
121 PAGE_GUARD
| PAGE_WRITECOPY
,
122 PAGE_GUARD
| PAGE_EXECUTE_READWRITE
,
123 PAGE_GUARD
| PAGE_EXECUTE_WRITECOPY
,
125 PAGE_WRITECOMBINE
| PAGE_READONLY
,
126 PAGE_WRITECOMBINE
| PAGE_EXECUTE
,
127 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READ
,
128 PAGE_WRITECOMBINE
| PAGE_READWRITE
,
129 PAGE_WRITECOMBINE
| PAGE_WRITECOPY
,
130 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READWRITE
,
131 PAGE_WRITECOMBINE
| PAGE_EXECUTE_WRITECOPY
134 /* FUNCTIONS ***************************************************************/
136 static BOOLEAN
MmUnmapPageTable(PULONG Pt
);
139 MiFlushTlb(PULONG Pt
, PVOID Address
)
141 if ((Pt
&& MmUnmapPageTable(Pt
)) || Address
>= MmSystemRangeStart
)
143 KeInvalidateTlbEntry(Address
);
148 ProtectToPTE(ULONG flProtect
)
150 ULONG Attributes
= 0;
152 if (flProtect
& (PAGE_NOACCESS
|PAGE_GUARD
))
156 else if (flProtect
& PAGE_IS_WRITABLE
)
158 Attributes
= PA_PRESENT
| PA_READWRITE
;
160 else if (flProtect
& (PAGE_IS_READABLE
| PAGE_IS_EXECUTABLE
))
162 Attributes
= PA_PRESENT
;
166 DPRINT1("Unknown main protection type.\n");
167 KeBugCheck(MEMORY_MANAGEMENT
);
170 if (flProtect
& PAGE_SYSTEM
)
175 Attributes
= Attributes
| PA_USER
;
177 if (flProtect
& PAGE_NOCACHE
)
179 Attributes
= Attributes
| PA_CD
;
181 if (flProtect
& PAGE_WRITETHROUGH
)
183 Attributes
= Attributes
| PA_WT
;
188 /* Taken from ARM3/pagfault.c */
191 MiSynchronizeSystemPde(PMMPDE PointerPde
)
196 /* Get the Index from the PDE */
197 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
199 /* Copy the PDE from the double-mapped system page directory */
200 SystemPde
= MmSystemPagePtes
[Index
];
201 *PointerPde
= SystemPde
;
203 /* Make sure we re-read the PDE and PTE */
204 KeMemoryBarrierWithoutFence();
206 /* Return, if we had success */
207 return SystemPde
.u
.Hard
.Valid
!= 0;
212 MiDispatchFault(IN BOOLEAN StoreInstruction
,
214 IN PMMPTE PointerPte
,
215 IN PMMPTE PointerProtoPte
,
216 IN BOOLEAN Recursive
,
217 IN PEPROCESS Process
,
218 IN PVOID TrapInformation
,
223 MiFillSystemPageDirectory(IN PVOID Base
,
224 IN SIZE_T NumberOfBytes
);
227 MmGetPageTableForProcess(PEPROCESS Process
, PVOID Address
, BOOLEAN Create
)
233 if (Address
< MmSystemRangeStart
)
235 /* We should have a process for user land addresses */
236 ASSERT(Process
!= NULL
);
238 if(Process
!= PsGetCurrentProcess())
241 ULONG PdeOffset
= MiGetPdeOffset(Address
);
243 /* Nobody but page fault should ask for creating the PDE,
244 * Which imples that Process is the current one */
245 ASSERT(Create
== FALSE
);
247 PdeBase
= MmCreateHyperspaceMapping(PTE_TO_PFN(Process
->Pcb
.DirectoryTableBase
[0]));
250 KeBugCheck(MEMORY_MANAGEMENT
);
252 PointerPde
= PdeBase
+ PdeOffset
;
253 if (PointerPde
->u
.Hard
.Valid
== 0)
255 MmDeleteHyperspaceMapping(PdeBase
);
260 Pfn
= PointerPde
->u
.Hard
.PageFrameNumber
;
262 MmDeleteHyperspaceMapping(PdeBase
);
263 Pt
= MmCreateHyperspaceMapping(Pfn
);
266 KeBugCheck(MEMORY_MANAGEMENT
);
268 return Pt
+ MiAddressToPteOffset(Address
);
270 /* This is for our process */
271 PointerPde
= MiAddressToPde(Address
);
272 Pt
= (PULONG
)MiAddressToPte(Address
);
273 if (PointerPde
->u
.Hard
.Valid
== 0)
280 ASSERT(PointerPde
->u
.Long
== 0);
282 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
283 Status
= MiDispatchFault(TRUE
,
288 PsGetCurrentProcess(),
291 DBG_UNREFERENCED_LOCAL_VARIABLE(Status
);
292 ASSERT(KeAreAllApcsDisabled() == TRUE
);
293 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
295 return (PULONG
)MiAddressToPte(Address
);
298 /* This is for kernel land address */
299 ASSERT(Process
== NULL
);
300 PointerPde
= MiAddressToPde(Address
);
301 Pt
= (PULONG
)MiAddressToPte(Address
);
302 if (PointerPde
->u
.Hard
.Valid
== 0)
304 /* Let ARM3 synchronize the PDE */
305 if(!MiSynchronizeSystemPde(PointerPde
))
307 /* PDE (still) not valid, let ARM3 allocate one if asked */
310 MiFillSystemPageDirectory(Address
, PAGE_SIZE
);
316 static BOOLEAN
MmUnmapPageTable(PULONG Pt
)
318 if (!IS_HYPERSPACE(Pt
))
325 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pt
));
330 static ULONG
MmGetPageEntryForProcess(PEPROCESS Process
, PVOID Address
)
335 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
339 MmUnmapPageTable(Pt
);
347 MmGetPfnForProcess(PEPROCESS Process
,
351 Entry
= MmGetPageEntryForProcess(Process
, Address
);
352 if (!(Entry
& PA_PRESENT
))
356 return(PTE_TO_PFN(Entry
));
361 MmDeleteVirtualMapping(PEPROCESS Process
, PVOID Address
,
362 BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
364 * FUNCTION: Delete a virtual mapping
367 BOOLEAN WasValid
= FALSE
;
372 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n",
373 Process
, Address
, WasDirty
, Page
);
375 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
379 if (WasDirty
!= NULL
)
391 * Atomically set the entry to zero and get the old value.
393 Pte
= InterlockedExchangePte(Pt
, 0);
395 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
396 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
397 WasValid
= (Pte
& PA_PRESENT
) || ((Pte
>> PAGE_SHIFT
) && !(Pte
& 0x800));
400 /* Flush the TLB since we transitioned this PTE
401 * from valid to invalid so any stale translations
402 * are removed from the cache */
403 MiFlushTlb(Pt
, Address
);
405 if (Address
< MmSystemRangeStart
)
407 /* Remove PDE reference */
408 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
409 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
412 Pfn
= PTE_TO_PFN(Pte
);
416 MmUnmapPageTable(Pt
);
421 * Return some information to the caller
423 if (WasDirty
!= NULL
)
425 *WasDirty
= ((Pte
& PA_DIRTY
) && (Pte
& PA_PRESENT
)) ? TRUE
: FALSE
;
435 MmGetPageFileMapping(PEPROCESS Process
, PVOID Address
,
436 SWAPENTRY
* SwapEntry
)
438 * FUNCTION: Get a page file mapping
441 ULONG Entry
= MmGetPageEntryForProcess(Process
, Address
);
442 *SwapEntry
= Entry
>> 1;
447 MmDeletePageFileMapping(PEPROCESS Process
, PVOID Address
,
448 SWAPENTRY
* SwapEntry
)
450 * FUNCTION: Delete a virtual mapping
456 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
465 * Atomically set the entry to zero and get the old value.
467 Pte
= InterlockedExchangePte(Pt
, 0);
469 if (Address
< MmSystemRangeStart
)
471 /* Remove PDE reference */
472 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
473 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
476 /* We don't need to flush here because page file entries
477 * are invalid translations, so the processor won't cache them */
478 MmUnmapPageTable(Pt
);
480 if ((Pte
& PA_PRESENT
) || !(Pte
& 0x800))
482 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte
);
483 KeBugCheck(MEMORY_MANAGEMENT
);
487 * Return some information to the caller
489 *SwapEntry
= Pte
>> 1;
493 Mmi386MakeKernelPageTableGlobal(PVOID Address
)
495 PMMPDE PointerPde
= MiAddressToPde(Address
);
496 PMMPTE PointerPte
= MiAddressToPte(Address
);
498 if (PointerPde
->u
.Hard
.Valid
== 0)
500 if(!MiSynchronizeSystemPde(PointerPde
))
502 return PointerPte
->u
.Hard
.Valid
!= 0;
509 MmIsDirtyPage(PEPROCESS Process
, PVOID Address
)
511 return MmGetPageEntryForProcess(Process
, Address
) & PA_DIRTY
? TRUE
: FALSE
;
516 MmSetCleanPage(PEPROCESS Process
, PVOID Address
)
521 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
523 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
524 KeBugCheck(MEMORY_MANAGEMENT
);
527 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
530 KeBugCheck(MEMORY_MANAGEMENT
);
536 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
& ~PA_DIRTY
, Pte
));
538 if (!(Pte
& PA_PRESENT
))
540 KeBugCheck(MEMORY_MANAGEMENT
);
542 else if (Pte
& PA_DIRTY
)
544 MiFlushTlb(Pt
, Address
);
548 MmUnmapPageTable(Pt
);
554 MmSetDirtyPage(PEPROCESS Process
, PVOID Address
)
559 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
561 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
562 KeBugCheck(MEMORY_MANAGEMENT
);
565 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
568 KeBugCheck(MEMORY_MANAGEMENT
);
574 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
| PA_DIRTY
, Pte
));
576 if (!(Pte
& PA_PRESENT
))
578 KeBugCheck(MEMORY_MANAGEMENT
);
582 /* The processor will never clear this bit itself, therefore
583 * we do not need to flush the TLB here when setting it */
584 MmUnmapPageTable(Pt
);
590 MmIsPagePresent(PEPROCESS Process
, PVOID Address
)
592 return MmGetPageEntryForProcess(Process
, Address
) & PA_PRESENT
;
597 MmIsDisabledPage(PEPROCESS Process
, PVOID Address
)
599 ULONG_PTR Entry
= MmGetPageEntryForProcess(Process
, Address
);
600 return !(Entry
& PA_PRESENT
) && !(Entry
& 0x800) && (Entry
>> PAGE_SHIFT
);
605 MmIsPageSwapEntry(PEPROCESS Process
, PVOID Address
)
608 Entry
= MmGetPageEntryForProcess(Process
, Address
);
609 return !(Entry
& PA_PRESENT
) && (Entry
& 0x800);
614 MmCreatePageFileMapping(PEPROCESS Process
,
621 if (Process
== NULL
&& Address
< MmSystemRangeStart
)
623 DPRINT1("No process\n");
624 KeBugCheck(MEMORY_MANAGEMENT
);
626 if (Process
!= NULL
&& Address
>= MmSystemRangeStart
)
628 DPRINT1("Setting kernel address with process context\n");
629 KeBugCheck(MEMORY_MANAGEMENT
);
632 if (SwapEntry
& (1 << 31))
634 KeBugCheck(MEMORY_MANAGEMENT
);
637 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
640 /* Nobody should page out an address that hasn't even been mapped */
641 /* But we might place a wait entry first, requiring the page table */
642 if (SwapEntry
!= MM_WAIT_ENTRY
)
644 KeBugCheck(MEMORY_MANAGEMENT
);
646 Pt
= MmGetPageTableForProcess(Process
, Address
, TRUE
);
648 Pte
= InterlockedExchangePte(Pt
, SwapEntry
<< 1);
651 KeBugCheckEx(MEMORY_MANAGEMENT
, SwapEntry
, (ULONG_PTR
)Process
, (ULONG_PTR
)Address
, 0);
654 if (Address
< MmSystemRangeStart
)
656 /* Add PDE reference */
657 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
658 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
661 /* We don't need to flush the TLB here because it
662 * only caches valid translations and a zero PTE
663 * is not a valid translation */
664 MmUnmapPageTable(Pt
);
666 return(STATUS_SUCCESS
);
672 MmCreateVirtualMappingUnsafe(PEPROCESS Process
,
681 ULONG oldPdeOffset
, PdeOffset
;
684 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
685 Process
, Address
, flProtect
, Pages
, *Pages
, PageCount
);
687 ASSERT(((ULONG_PTR
)Address
% PAGE_SIZE
) == 0);
691 if (Address
< MmSystemRangeStart
)
693 DPRINT1("No process\n");
694 KeBugCheck(MEMORY_MANAGEMENT
);
696 if (PageCount
> 0x10000 ||
697 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
> 0x100000)
699 DPRINT1("Page count too large\n");
700 KeBugCheck(MEMORY_MANAGEMENT
);
705 if (Address
>= MmSystemRangeStart
)
707 DPRINT1("Setting kernel address with process context\n");
708 KeBugCheck(MEMORY_MANAGEMENT
);
710 if (PageCount
> (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
||
711 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
>
712 (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
)
714 DPRINT1("Page Count too large\n");
715 KeBugCheck(MEMORY_MANAGEMENT
);
719 Attributes
= ProtectToPTE(flProtect
);
721 if (Address
>= MmSystemRangeStart
)
723 Attributes
&= ~PA_USER
;
727 Attributes
|= PA_USER
;
731 /* MmGetPageTableForProcess should be called on the first run, so
732 * let this trigger it */
733 oldPdeOffset
= ADDR_TO_PDE_OFFSET(Addr
) + 1;
734 for (i
= 0; i
< PageCount
; i
++, Addr
= (PVOID
)((ULONG_PTR
)Addr
+ PAGE_SIZE
))
736 if (!(Attributes
& PA_PRESENT
) && Pages
[i
] != 0)
738 DPRINT1("Setting physical address but not allowing access at address "
739 "0x%p with attributes %x/%x.\n",
740 Addr
, Attributes
, flProtect
);
741 KeBugCheck(MEMORY_MANAGEMENT
);
743 PdeOffset
= ADDR_TO_PDE_OFFSET(Addr
);
744 if (oldPdeOffset
!= PdeOffset
)
746 if(Pt
) MmUnmapPageTable(Pt
);
747 Pt
= MmGetPageTableForProcess(Process
, Addr
, TRUE
);
750 KeBugCheck(MEMORY_MANAGEMENT
);
757 oldPdeOffset
= PdeOffset
;
759 Pte
= InterlockedExchangePte(Pt
, PFN_TO_PTE(Pages
[i
]) | Attributes
);
761 /* There should not be anything valid here */
764 DPRINT1("Bad PTE %lx\n", Pte
);
765 KeBugCheck(MEMORY_MANAGEMENT
);
768 /* We don't need to flush the TLB here because it only caches valid translations
769 * and we're moving this PTE from invalid to valid so it can't be cached right now */
771 if (Addr
< MmSystemRangeStart
)
773 /* Add PDE reference */
774 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)]++;
775 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)] <= PTE_COUNT
);
779 ASSERT(Addr
> Address
);
780 MmUnmapPageTable(Pt
);
782 return(STATUS_SUCCESS
);
787 MmCreateVirtualMapping(PEPROCESS Process
,
795 ASSERT((ULONG_PTR
)Address
% PAGE_SIZE
== 0);
796 for (i
= 0; i
< PageCount
; i
++)
798 if (!MmIsPageInUse(Pages
[i
]))
800 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages
[i
]));
801 KeBugCheck(MEMORY_MANAGEMENT
);
805 return(MmCreateVirtualMappingUnsafe(Process
,
814 MmGetPageProtect(PEPROCESS Process
, PVOID Address
)
819 Entry
= MmGetPageEntryForProcess(Process
, Address
);
822 if (!(Entry
& PA_PRESENT
))
824 Protect
= PAGE_NOACCESS
;
828 if (Entry
& PA_READWRITE
)
830 Protect
= PAGE_READWRITE
;
834 Protect
= PAGE_EXECUTE_READ
;
838 Protect
|= PAGE_NOCACHE
;
842 Protect
|= PAGE_WRITETHROUGH
;
844 if (!(Entry
& PA_USER
))
846 Protect
|= PAGE_SYSTEM
;
855 MmSetPageProtect(PEPROCESS Process
, PVOID Address
, ULONG flProtect
)
857 ULONG Attributes
= 0;
861 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
862 Process
, Address
, flProtect
);
864 Attributes
= ProtectToPTE(flProtect
);
867 if (Address
>= MmSystemRangeStart
)
869 Attributes
&= ~PA_USER
;
873 Attributes
|= PA_USER
;
876 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
879 KeBugCheck(MEMORY_MANAGEMENT
);
881 Pte
= InterlockedExchangePte(Pt
, PAGE_MASK(*Pt
) | Attributes
| (*Pt
& (PA_ACCESSED
|PA_DIRTY
)));
883 // We should be able to bring a page back from PAGE_NOACCESS
884 if ((Pte
& 0x800) || !(Pte
>> PAGE_SHIFT
))
886 DPRINT1("Invalid Pte %lx\n", Pte
);
887 KeBugCheck(MEMORY_MANAGEMENT
);
890 if((Pte
& Attributes
) != Attributes
)
891 MiFlushTlb(Pt
, Address
);
893 MmUnmapPageTable(Pt
);
899 MmInitGlobalKernelPageDirectory(VOID
)
901 /* Nothing to do here */