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
, BOOLEAN FreePage
,
362 BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
364 * FUNCTION: Delete a virtual mapping
367 BOOLEAN WasValid
= FALSE
;
372 DPRINT("MmDeleteVirtualMapping(%p, %p, %u, %p, %p)\n",
373 Process
, Address
, FreePage
, 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 MmReleasePageMemoryConsumer(MC_SYSTEM
, Pfn
);
422 MmUnmapPageTable(Pt
);
427 * Return some information to the caller
429 if (WasDirty
!= NULL
)
431 *WasDirty
= ((Pte
& PA_DIRTY
) && (Pte
& PA_PRESENT
)) ? TRUE
: FALSE
;
441 MmGetPageFileMapping(PEPROCESS Process
, PVOID Address
,
442 SWAPENTRY
* SwapEntry
)
444 * FUNCTION: Get a page file mapping
447 ULONG Entry
= MmGetPageEntryForProcess(Process
, Address
);
448 *SwapEntry
= Entry
>> 1;
453 MmDeletePageFileMapping(PEPROCESS Process
, PVOID Address
,
454 SWAPENTRY
* SwapEntry
)
456 * FUNCTION: Delete a virtual mapping
462 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
471 * Atomically set the entry to zero and get the old value.
473 Pte
= InterlockedExchangePte(Pt
, 0);
475 if (Address
< MmSystemRangeStart
)
477 /* Remove PDE reference */
478 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
479 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
482 /* We don't need to flush here because page file entries
483 * are invalid translations, so the processor won't cache them */
484 MmUnmapPageTable(Pt
);
486 if ((Pte
& PA_PRESENT
) || !(Pte
& 0x800))
488 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte
);
489 KeBugCheck(MEMORY_MANAGEMENT
);
493 * Return some information to the caller
495 *SwapEntry
= Pte
>> 1;
499 Mmi386MakeKernelPageTableGlobal(PVOID Address
)
501 PMMPDE PointerPde
= MiAddressToPde(Address
);
502 PMMPTE PointerPte
= MiAddressToPte(Address
);
504 if (PointerPde
->u
.Hard
.Valid
== 0)
506 if(!MiSynchronizeSystemPde(PointerPde
))
508 return PointerPte
->u
.Hard
.Valid
!= 0;
515 MmIsDirtyPage(PEPROCESS Process
, PVOID Address
)
517 return MmGetPageEntryForProcess(Process
, Address
) & PA_DIRTY
? TRUE
: FALSE
;
522 MmSetCleanPage(PEPROCESS Process
, PVOID Address
)
527 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
529 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
530 KeBugCheck(MEMORY_MANAGEMENT
);
533 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
536 KeBugCheck(MEMORY_MANAGEMENT
);
542 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
& ~PA_DIRTY
, Pte
));
544 if (!(Pte
& PA_PRESENT
))
546 KeBugCheck(MEMORY_MANAGEMENT
);
548 else if (Pte
& PA_DIRTY
)
550 MiFlushTlb(Pt
, Address
);
554 MmUnmapPageTable(Pt
);
560 MmSetDirtyPage(PEPROCESS Process
, PVOID Address
)
565 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
567 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
568 KeBugCheck(MEMORY_MANAGEMENT
);
571 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
574 KeBugCheck(MEMORY_MANAGEMENT
);
580 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
| PA_DIRTY
, Pte
));
582 if (!(Pte
& PA_PRESENT
))
584 KeBugCheck(MEMORY_MANAGEMENT
);
588 /* The processor will never clear this bit itself, therefore
589 * we do not need to flush the TLB here when setting it */
590 MmUnmapPageTable(Pt
);
596 MmIsPagePresent(PEPROCESS Process
, PVOID Address
)
598 return MmGetPageEntryForProcess(Process
, Address
) & PA_PRESENT
;
603 MmIsDisabledPage(PEPROCESS Process
, PVOID Address
)
605 ULONG_PTR Entry
= MmGetPageEntryForProcess(Process
, Address
);
606 return !(Entry
& PA_PRESENT
) && !(Entry
& 0x800) && (Entry
>> PAGE_SHIFT
);
611 MmIsPageSwapEntry(PEPROCESS Process
, PVOID Address
)
614 Entry
= MmGetPageEntryForProcess(Process
, Address
);
615 return !(Entry
& PA_PRESENT
) && (Entry
& 0x800);
620 MmCreatePageFileMapping(PEPROCESS Process
,
627 if (Process
== NULL
&& Address
< MmSystemRangeStart
)
629 DPRINT1("No process\n");
630 KeBugCheck(MEMORY_MANAGEMENT
);
632 if (Process
!= NULL
&& Address
>= MmSystemRangeStart
)
634 DPRINT1("Setting kernel address with process context\n");
635 KeBugCheck(MEMORY_MANAGEMENT
);
638 if (SwapEntry
& (1 << 31))
640 KeBugCheck(MEMORY_MANAGEMENT
);
643 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
646 /* Nobody should page out an address that hasn't even been mapped */
647 /* But we might place a wait entry first, requiring the page table */
648 if (SwapEntry
!= MM_WAIT_ENTRY
)
650 KeBugCheck(MEMORY_MANAGEMENT
);
652 Pt
= MmGetPageTableForProcess(Process
, Address
, TRUE
);
654 Pte
= InterlockedExchangePte(Pt
, SwapEntry
<< 1);
657 KeBugCheckEx(MEMORY_MANAGEMENT
, SwapEntry
, (ULONG_PTR
)Process
, (ULONG_PTR
)Address
, 0);
660 if (Address
< MmSystemRangeStart
)
662 /* Add PDE reference */
663 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
664 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
667 /* We don't need to flush the TLB here because it
668 * only caches valid translations and a zero PTE
669 * is not a valid translation */
670 MmUnmapPageTable(Pt
);
672 return(STATUS_SUCCESS
);
678 MmCreateVirtualMappingUnsafe(PEPROCESS Process
,
687 ULONG oldPdeOffset
, PdeOffset
;
690 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
691 Process
, Address
, flProtect
, Pages
, *Pages
, PageCount
);
693 ASSERT(((ULONG_PTR
)Address
% PAGE_SIZE
) == 0);
697 if (Address
< MmSystemRangeStart
)
699 DPRINT1("No process\n");
700 KeBugCheck(MEMORY_MANAGEMENT
);
702 if (PageCount
> 0x10000 ||
703 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
> 0x100000)
705 DPRINT1("Page count too large\n");
706 KeBugCheck(MEMORY_MANAGEMENT
);
711 if (Address
>= MmSystemRangeStart
)
713 DPRINT1("Setting kernel address with process context\n");
714 KeBugCheck(MEMORY_MANAGEMENT
);
716 if (PageCount
> (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
||
717 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
>
718 (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
)
720 DPRINT1("Page Count too large\n");
721 KeBugCheck(MEMORY_MANAGEMENT
);
725 Attributes
= ProtectToPTE(flProtect
);
727 if (Address
>= MmSystemRangeStart
)
729 Attributes
&= ~PA_USER
;
733 Attributes
|= PA_USER
;
737 /* MmGetPageTableForProcess should be called on the first run, so
738 * let this trigger it */
739 oldPdeOffset
= ADDR_TO_PDE_OFFSET(Addr
) + 1;
740 for (i
= 0; i
< PageCount
; i
++, Addr
= (PVOID
)((ULONG_PTR
)Addr
+ PAGE_SIZE
))
742 if (!(Attributes
& PA_PRESENT
) && Pages
[i
] != 0)
744 DPRINT1("Setting physical address but not allowing access at address "
745 "0x%p with attributes %x/%x.\n",
746 Addr
, Attributes
, flProtect
);
747 KeBugCheck(MEMORY_MANAGEMENT
);
749 PdeOffset
= ADDR_TO_PDE_OFFSET(Addr
);
750 if (oldPdeOffset
!= PdeOffset
)
752 if(Pt
) MmUnmapPageTable(Pt
);
753 Pt
= MmGetPageTableForProcess(Process
, Addr
, TRUE
);
756 KeBugCheck(MEMORY_MANAGEMENT
);
763 oldPdeOffset
= PdeOffset
;
765 Pte
= InterlockedExchangePte(Pt
, PFN_TO_PTE(Pages
[i
]) | Attributes
);
767 /* There should not be anything valid here */
770 DPRINT1("Bad PTE %lx\n", Pte
);
771 KeBugCheck(MEMORY_MANAGEMENT
);
774 /* We don't need to flush the TLB here because it only caches valid translations
775 * and we're moving this PTE from invalid to valid so it can't be cached right now */
777 if (Addr
< MmSystemRangeStart
)
779 /* Add PDE reference */
780 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)]++;
781 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)] <= PTE_COUNT
);
785 ASSERT(Addr
> Address
);
786 MmUnmapPageTable(Pt
);
788 return(STATUS_SUCCESS
);
793 MmCreateVirtualMapping(PEPROCESS Process
,
801 ASSERT((ULONG_PTR
)Address
% PAGE_SIZE
== 0);
802 for (i
= 0; i
< PageCount
; i
++)
804 if (!MmIsPageInUse(Pages
[i
]))
806 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages
[i
]));
807 KeBugCheck(MEMORY_MANAGEMENT
);
811 return(MmCreateVirtualMappingUnsafe(Process
,
820 MmGetPageProtect(PEPROCESS Process
, PVOID Address
)
825 Entry
= MmGetPageEntryForProcess(Process
, Address
);
828 if (!(Entry
& PA_PRESENT
))
830 Protect
= PAGE_NOACCESS
;
834 if (Entry
& PA_READWRITE
)
836 Protect
= PAGE_READWRITE
;
840 Protect
= PAGE_EXECUTE_READ
;
844 Protect
|= PAGE_NOCACHE
;
848 Protect
|= PAGE_WRITETHROUGH
;
850 if (!(Entry
& PA_USER
))
852 Protect
|= PAGE_SYSTEM
;
861 MmSetPageProtect(PEPROCESS Process
, PVOID Address
, ULONG flProtect
)
863 ULONG Attributes
= 0;
867 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
868 Process
, Address
, flProtect
);
870 Attributes
= ProtectToPTE(flProtect
);
873 if (Address
>= MmSystemRangeStart
)
875 Attributes
&= ~PA_USER
;
879 Attributes
|= PA_USER
;
882 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
885 KeBugCheck(MEMORY_MANAGEMENT
);
887 Pte
= InterlockedExchangePte(Pt
, PAGE_MASK(*Pt
) | Attributes
| (*Pt
& (PA_ACCESSED
|PA_DIRTY
)));
889 // We should be able to bring a page back from PAGE_NOACCESS
890 if ((Pte
& 0x800) || !(Pte
>> PAGE_SHIFT
))
892 DPRINT1("Invalid Pte %lx\n", Pte
);
893 KeBugCheck(MEMORY_MANAGEMENT
);
896 if((Pte
& Attributes
) != Attributes
)
897 MiFlushTlb(Pt
, Address
);
899 MmUnmapPageTable(Pt
);
905 MmInitGlobalKernelPageDirectory(VOID
)
907 /* Nothing to do here */