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 <mm/ARM3/miarm.h>
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
21 #define ADDR_TO_PDE_OFFSET MiAddressToPdeOffset
22 #define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE))
24 /* GLOBALS *****************************************************************/
26 #define PA_BIT_PRESENT (0)
27 #define PA_BIT_READWRITE (1)
28 #define PA_BIT_USER (2)
31 #define PA_BIT_ACCESSED (5)
32 #define PA_BIT_DIRTY (6)
33 #define PA_BIT_GLOBAL (8)
35 #define PA_PRESENT (1 << PA_BIT_PRESENT)
36 #define PA_READWRITE (1 << PA_BIT_READWRITE)
37 #define PA_USER (1 << PA_BIT_USER)
38 #define PA_DIRTY (1 << PA_BIT_DIRTY)
39 #define PA_WT (1 << PA_BIT_WT)
40 #define PA_CD (1 << PA_BIT_CD)
41 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
42 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
44 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END))
46 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
47 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
49 #define PAGE_MASK(x) ((x)&(~0xfff))
53 MmProtectToPteMask
[32] =
56 // These are the base MM_ protection flags
59 PTE_READONLY
| PTE_ENABLE_CACHE
,
60 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
61 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
62 PTE_READWRITE
| PTE_ENABLE_CACHE
,
63 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
64 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
65 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
67 // These OR in the MM_NOCACHE flag
70 PTE_READONLY
| PTE_DISABLE_CACHE
,
71 PTE_EXECUTE
| PTE_DISABLE_CACHE
,
72 PTE_EXECUTE_READ
| PTE_DISABLE_CACHE
,
73 PTE_READWRITE
| PTE_DISABLE_CACHE
,
74 PTE_WRITECOPY
| PTE_DISABLE_CACHE
,
75 PTE_EXECUTE_READWRITE
| PTE_DISABLE_CACHE
,
76 PTE_EXECUTE_WRITECOPY
| PTE_DISABLE_CACHE
,
78 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
81 PTE_READONLY
| PTE_ENABLE_CACHE
,
82 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
83 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
84 PTE_READWRITE
| PTE_ENABLE_CACHE
,
85 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
86 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
87 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
89 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
92 PTE_READONLY
| PTE_WRITECOMBINED_CACHE
,
93 PTE_EXECUTE
| PTE_WRITECOMBINED_CACHE
,
94 PTE_EXECUTE_READ
| PTE_WRITECOMBINED_CACHE
,
95 PTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
96 PTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
97 PTE_EXECUTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
98 PTE_EXECUTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
102 ULONG MmProtectToValue
[32] =
110 PAGE_EXECUTE_READWRITE
,
111 PAGE_EXECUTE_WRITECOPY
,
113 PAGE_NOCACHE
| PAGE_READONLY
,
114 PAGE_NOCACHE
| PAGE_EXECUTE
,
115 PAGE_NOCACHE
| PAGE_EXECUTE_READ
,
116 PAGE_NOCACHE
| PAGE_READWRITE
,
117 PAGE_NOCACHE
| PAGE_WRITECOPY
,
118 PAGE_NOCACHE
| PAGE_EXECUTE_READWRITE
,
119 PAGE_NOCACHE
| PAGE_EXECUTE_WRITECOPY
,
121 PAGE_GUARD
| PAGE_READONLY
,
122 PAGE_GUARD
| PAGE_EXECUTE
,
123 PAGE_GUARD
| PAGE_EXECUTE_READ
,
124 PAGE_GUARD
| PAGE_READWRITE
,
125 PAGE_GUARD
| PAGE_WRITECOPY
,
126 PAGE_GUARD
| PAGE_EXECUTE_READWRITE
,
127 PAGE_GUARD
| PAGE_EXECUTE_WRITECOPY
,
129 PAGE_WRITECOMBINE
| PAGE_READONLY
,
130 PAGE_WRITECOMBINE
| PAGE_EXECUTE
,
131 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READ
,
132 PAGE_WRITECOMBINE
| PAGE_READWRITE
,
133 PAGE_WRITECOMBINE
| PAGE_WRITECOPY
,
134 PAGE_WRITECOMBINE
| PAGE_EXECUTE_READWRITE
,
135 PAGE_WRITECOMBINE
| PAGE_EXECUTE_WRITECOPY
138 /* FUNCTIONS ***************************************************************/
140 static BOOLEAN
MmUnmapPageTable(PULONG Pt
);
143 MiFlushTlb(PULONG Pt
, PVOID Address
)
145 if ((Pt
&& MmUnmapPageTable(Pt
)) || Address
>= MmSystemRangeStart
)
147 KeInvalidateTlbEntry(Address
);
152 ProtectToPTE(ULONG flProtect
)
154 ULONG Attributes
= 0;
156 if (flProtect
& (PAGE_NOACCESS
|PAGE_GUARD
))
160 else if (flProtect
& PAGE_IS_WRITABLE
)
162 Attributes
= PA_PRESENT
| PA_READWRITE
;
164 else if (flProtect
& (PAGE_IS_READABLE
| PAGE_IS_EXECUTABLE
))
166 Attributes
= PA_PRESENT
;
170 DPRINT1("Unknown main protection type.\n");
171 KeBugCheck(MEMORY_MANAGEMENT
);
174 if (flProtect
& PAGE_SYSTEM
)
179 Attributes
= Attributes
| PA_USER
;
181 if (flProtect
& PAGE_NOCACHE
)
183 Attributes
= Attributes
| PA_CD
;
185 if (flProtect
& PAGE_WRITETHROUGH
)
187 Attributes
= Attributes
| PA_WT
;
192 /* Taken from ARM3/pagfault.c */
195 MiSynchronizeSystemPde(PMMPDE PointerPde
)
200 /* Get the Index from the PDE */
201 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
203 /* Copy the PDE from the double-mapped system page directory */
204 SystemPde
= MmSystemPagePtes
[Index
];
205 *PointerPde
= SystemPde
;
207 /* Make sure we re-read the PDE and PTE */
208 KeMemoryBarrierWithoutFence();
210 /* Return, if we had success */
211 return SystemPde
.u
.Hard
.Valid
!= 0;
216 MiDispatchFault(IN BOOLEAN StoreInstruction
,
218 IN PMMPTE PointerPte
,
219 IN PMMPTE PointerProtoPte
,
220 IN BOOLEAN Recursive
,
221 IN PEPROCESS Process
,
222 IN PVOID TrapInformation
,
227 MiFillSystemPageDirectory(IN PVOID Base
,
228 IN SIZE_T NumberOfBytes
);
231 MmGetPageTableForProcess(PEPROCESS Process
, PVOID Address
, BOOLEAN Create
)
237 if (Address
< MmSystemRangeStart
)
239 /* We should have a process for user land addresses */
240 ASSERT(Process
!= NULL
);
242 if(Process
!= PsGetCurrentProcess())
245 ULONG PdeOffset
= MiGetPdeOffset(Address
);
247 /* Nobody but page fault should ask for creating the PDE,
248 * Which imples that Process is the current one */
249 ASSERT(Create
== FALSE
);
251 PdeBase
= MmCreateHyperspaceMapping(PTE_TO_PFN(Process
->Pcb
.DirectoryTableBase
[0]));
254 KeBugCheck(MEMORY_MANAGEMENT
);
256 PointerPde
= PdeBase
+ PdeOffset
;
257 if (PointerPde
->u
.Hard
.Valid
== 0)
259 MmDeleteHyperspaceMapping(PdeBase
);
264 Pfn
= PointerPde
->u
.Hard
.PageFrameNumber
;
266 MmDeleteHyperspaceMapping(PdeBase
);
267 Pt
= MmCreateHyperspaceMapping(Pfn
);
270 KeBugCheck(MEMORY_MANAGEMENT
);
272 return Pt
+ MiAddressToPteOffset(Address
);
274 /* This is for our process */
275 PointerPde
= MiAddressToPde(Address
);
276 Pt
= (PULONG
)MiAddressToPte(Address
);
277 if (PointerPde
->u
.Hard
.Valid
== 0)
284 ASSERT(PointerPde
->u
.Long
== 0);
286 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
287 Status
= MiDispatchFault(TRUE
,
292 PsGetCurrentProcess(),
295 DBG_UNREFERENCED_LOCAL_VARIABLE(Status
);
296 ASSERT(KeAreAllApcsDisabled() == TRUE
);
297 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
299 return (PULONG
)MiAddressToPte(Address
);
302 /* This is for kernel land address */
303 ASSERT(Process
== NULL
);
304 PointerPde
= MiAddressToPde(Address
);
305 Pt
= (PULONG
)MiAddressToPte(Address
);
306 if (PointerPde
->u
.Hard
.Valid
== 0)
308 /* Let ARM3 synchronize the PDE */
309 if(!MiSynchronizeSystemPde(PointerPde
))
311 /* PDE (still) not valid, let ARM3 allocate one if asked */
314 MiFillSystemPageDirectory(Address
, PAGE_SIZE
);
320 static BOOLEAN
MmUnmapPageTable(PULONG Pt
)
322 if (!IS_HYPERSPACE(Pt
))
329 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pt
));
334 static ULONG
MmGetPageEntryForProcess(PEPROCESS Process
, PVOID Address
)
339 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
343 MmUnmapPageTable(Pt
);
351 MmGetPfnForProcess(PEPROCESS Process
,
355 Entry
= MmGetPageEntryForProcess(Process
, Address
);
356 if (!(Entry
& PA_PRESENT
))
360 return(PTE_TO_PFN(Entry
));
365 MmDeleteVirtualMapping(PEPROCESS Process
, PVOID Address
,
366 BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
368 * FUNCTION: Delete a virtual mapping
371 BOOLEAN WasValid
= FALSE
;
376 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n",
377 Process
, Address
, WasDirty
, Page
);
379 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
383 if (WasDirty
!= NULL
)
395 * Atomically set the entry to zero and get the old value.
397 Pte
= InterlockedExchangePte(Pt
, 0);
399 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
400 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
401 WasValid
= (Pte
& PA_PRESENT
) || ((Pte
>> PAGE_SHIFT
) && !(Pte
& 0x800));
404 /* Flush the TLB since we transitioned this PTE
405 * from valid to invalid so any stale translations
406 * are removed from the cache */
407 MiFlushTlb(Pt
, Address
);
409 if (Address
< MmSystemRangeStart
)
411 /* Remove PDE reference */
412 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
413 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
416 Pfn
= PTE_TO_PFN(Pte
);
420 MmUnmapPageTable(Pt
);
425 * Return some information to the caller
427 if (WasDirty
!= NULL
)
429 *WasDirty
= ((Pte
& PA_DIRTY
) && (Pte
& PA_PRESENT
)) ? TRUE
: FALSE
;
439 MmGetPageFileMapping(PEPROCESS Process
, PVOID Address
,
440 SWAPENTRY
* SwapEntry
)
442 * FUNCTION: Get a page file mapping
445 ULONG Entry
= MmGetPageEntryForProcess(Process
, Address
);
446 *SwapEntry
= Entry
>> 1;
451 MmDeletePageFileMapping(PEPROCESS Process
, PVOID Address
,
452 SWAPENTRY
* SwapEntry
)
454 * FUNCTION: Delete a virtual mapping
460 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
469 * Atomically set the entry to zero and get the old value.
471 Pte
= InterlockedExchangePte(Pt
, 0);
473 if (Address
< MmSystemRangeStart
)
475 /* Remove PDE reference */
476 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]--;
477 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] < PTE_COUNT
);
480 /* We don't need to flush here because page file entries
481 * are invalid translations, so the processor won't cache them */
482 MmUnmapPageTable(Pt
);
484 if ((Pte
& PA_PRESENT
) || !(Pte
& 0x800))
486 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte
);
487 KeBugCheck(MEMORY_MANAGEMENT
);
491 * Return some information to the caller
493 *SwapEntry
= Pte
>> 1;
497 Mmi386MakeKernelPageTableGlobal(PVOID Address
)
499 PMMPDE PointerPde
= MiAddressToPde(Address
);
500 PMMPTE PointerPte
= MiAddressToPte(Address
);
502 if (PointerPde
->u
.Hard
.Valid
== 0)
504 if(!MiSynchronizeSystemPde(PointerPde
))
506 return PointerPte
->u
.Hard
.Valid
!= 0;
513 MmIsDirtyPage(PEPROCESS Process
, PVOID Address
)
515 return MmGetPageEntryForProcess(Process
, Address
) & PA_DIRTY
? TRUE
: FALSE
;
520 MmSetCleanPage(PEPROCESS Process
, PVOID Address
)
525 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
527 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
528 KeBugCheck(MEMORY_MANAGEMENT
);
531 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
534 KeBugCheck(MEMORY_MANAGEMENT
);
540 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
& ~PA_DIRTY
, Pte
));
542 if (!(Pte
& PA_PRESENT
))
544 KeBugCheck(MEMORY_MANAGEMENT
);
546 else if (Pte
& PA_DIRTY
)
548 MiFlushTlb(Pt
, Address
);
552 MmUnmapPageTable(Pt
);
558 MmSetDirtyPage(PEPROCESS Process
, PVOID Address
)
563 if (Address
< MmSystemRangeStart
&& Process
== NULL
)
565 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
566 KeBugCheck(MEMORY_MANAGEMENT
);
569 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
572 KeBugCheck(MEMORY_MANAGEMENT
);
578 } while (Pte
!= InterlockedCompareExchangePte(Pt
, Pte
| PA_DIRTY
, Pte
));
580 if (!(Pte
& PA_PRESENT
))
582 KeBugCheck(MEMORY_MANAGEMENT
);
586 /* The processor will never clear this bit itself, therefore
587 * we do not need to flush the TLB here when setting it */
588 MmUnmapPageTable(Pt
);
594 MmIsPagePresent(PEPROCESS Process
, PVOID Address
)
596 return MmGetPageEntryForProcess(Process
, Address
) & PA_PRESENT
;
601 MmIsDisabledPage(PEPROCESS Process
, PVOID Address
)
603 ULONG_PTR Entry
= MmGetPageEntryForProcess(Process
, Address
);
604 return !(Entry
& PA_PRESENT
) && !(Entry
& 0x800) && (Entry
>> PAGE_SHIFT
);
609 MmIsPageSwapEntry(PEPROCESS Process
, PVOID Address
)
612 Entry
= MmGetPageEntryForProcess(Process
, Address
);
613 return !(Entry
& PA_PRESENT
) && (Entry
& 0x800);
618 MmCreatePageFileMapping(PEPROCESS Process
,
625 if (Process
== NULL
&& Address
< MmSystemRangeStart
)
627 DPRINT1("No process\n");
628 KeBugCheck(MEMORY_MANAGEMENT
);
630 if (Process
!= NULL
&& Address
>= MmSystemRangeStart
)
632 DPRINT1("Setting kernel address with process context\n");
633 KeBugCheck(MEMORY_MANAGEMENT
);
636 if (SwapEntry
& (1 << 31))
638 KeBugCheck(MEMORY_MANAGEMENT
);
641 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
644 /* Nobody should page out an address that hasn't even been mapped */
645 /* But we might place a wait entry first, requiring the page table */
646 if (SwapEntry
!= MM_WAIT_ENTRY
)
648 KeBugCheck(MEMORY_MANAGEMENT
);
650 Pt
= MmGetPageTableForProcess(Process
, Address
, TRUE
);
652 Pte
= InterlockedExchangePte(Pt
, SwapEntry
<< 1);
655 KeBugCheckEx(MEMORY_MANAGEMENT
, SwapEntry
, (ULONG_PTR
)Process
, (ULONG_PTR
)Address
, 0);
658 if (Address
< MmSystemRangeStart
)
660 /* Add PDE reference */
661 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
662 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
665 /* We don't need to flush the TLB here because it
666 * only caches valid translations and a zero PTE
667 * is not a valid translation */
668 MmUnmapPageTable(Pt
);
670 return(STATUS_SUCCESS
);
676 MmCreateVirtualMappingUnsafe(PEPROCESS Process
,
685 ULONG oldPdeOffset
, PdeOffset
;
688 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
689 Process
, Address
, flProtect
, Pages
, *Pages
, PageCount
);
691 ASSERT(((ULONG_PTR
)Address
% PAGE_SIZE
) == 0);
695 if (Address
< MmSystemRangeStart
)
697 DPRINT1("No process\n");
698 KeBugCheck(MEMORY_MANAGEMENT
);
700 if (PageCount
> 0x10000 ||
701 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
> 0x100000)
703 DPRINT1("Page count too large\n");
704 KeBugCheck(MEMORY_MANAGEMENT
);
709 if (Address
>= MmSystemRangeStart
)
711 DPRINT1("Setting kernel address with process context\n");
712 KeBugCheck(MEMORY_MANAGEMENT
);
714 if (PageCount
> (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
||
715 (ULONG_PTR
) Address
/ PAGE_SIZE
+ PageCount
>
716 (ULONG_PTR
)MmSystemRangeStart
/ PAGE_SIZE
)
718 DPRINT1("Page Count too large\n");
719 KeBugCheck(MEMORY_MANAGEMENT
);
723 Attributes
= ProtectToPTE(flProtect
);
725 if (Address
>= MmSystemRangeStart
)
727 Attributes
&= ~PA_USER
;
731 Attributes
|= PA_USER
;
735 /* MmGetPageTableForProcess should be called on the first run, so
736 * let this trigger it */
737 oldPdeOffset
= ADDR_TO_PDE_OFFSET(Addr
) + 1;
738 for (i
= 0; i
< PageCount
; i
++, Addr
= (PVOID
)((ULONG_PTR
)Addr
+ PAGE_SIZE
))
740 if (!(Attributes
& PA_PRESENT
) && Pages
[i
] != 0)
742 DPRINT1("Setting physical address but not allowing access at address "
743 "0x%p with attributes %x/%x.\n",
744 Addr
, Attributes
, flProtect
);
745 KeBugCheck(MEMORY_MANAGEMENT
);
747 PdeOffset
= ADDR_TO_PDE_OFFSET(Addr
);
748 if (oldPdeOffset
!= PdeOffset
)
750 if(Pt
) MmUnmapPageTable(Pt
);
751 Pt
= MmGetPageTableForProcess(Process
, Addr
, TRUE
);
754 KeBugCheck(MEMORY_MANAGEMENT
);
761 oldPdeOffset
= PdeOffset
;
763 Pte
= InterlockedExchangePte(Pt
, PFN_TO_PTE(Pages
[i
]) | Attributes
);
765 /* There should not be anything valid here */
768 DPRINT1("Bad PTE %lx\n", Pte
);
769 KeBugCheck(MEMORY_MANAGEMENT
);
772 /* We don't need to flush the TLB here because it only caches valid translations
773 * and we're moving this PTE from invalid to valid so it can't be cached right now */
775 if (Addr
< MmSystemRangeStart
)
777 /* Add PDE reference */
778 Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)]++;
779 ASSERT(Process
->Vm
.VmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Addr
)] <= PTE_COUNT
);
783 ASSERT(Addr
> Address
);
784 MmUnmapPageTable(Pt
);
786 return(STATUS_SUCCESS
);
791 MmCreateVirtualMapping(PEPROCESS Process
,
799 ASSERT((ULONG_PTR
)Address
% PAGE_SIZE
== 0);
800 for (i
= 0; i
< PageCount
; i
++)
802 if (!MmIsPageInUse(Pages
[i
]))
804 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages
[i
]));
805 KeBugCheck(MEMORY_MANAGEMENT
);
809 return(MmCreateVirtualMappingUnsafe(Process
,
818 MmGetPageProtect(PEPROCESS Process
, PVOID Address
)
823 Entry
= MmGetPageEntryForProcess(Process
, Address
);
826 if (!(Entry
& PA_PRESENT
))
828 Protect
= PAGE_NOACCESS
;
832 if (Entry
& PA_READWRITE
)
834 Protect
= PAGE_READWRITE
;
838 Protect
= PAGE_EXECUTE_READ
;
842 Protect
|= PAGE_NOCACHE
;
846 Protect
|= PAGE_WRITETHROUGH
;
848 if (!(Entry
& PA_USER
))
850 Protect
|= PAGE_SYSTEM
;
859 MmSetPageProtect(PEPROCESS Process
, PVOID Address
, ULONG flProtect
)
861 ULONG Attributes
= 0;
865 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
866 Process
, Address
, flProtect
);
868 Attributes
= ProtectToPTE(flProtect
);
871 if (Address
>= MmSystemRangeStart
)
873 Attributes
&= ~PA_USER
;
877 Attributes
|= PA_USER
;
880 Pt
= MmGetPageTableForProcess(Process
, Address
, FALSE
);
883 KeBugCheck(MEMORY_MANAGEMENT
);
885 Pte
= InterlockedExchangePte(Pt
, PAGE_MASK(*Pt
) | Attributes
| (*Pt
& (PA_ACCESSED
|PA_DIRTY
)));
887 // We should be able to bring a page back from PAGE_NOACCESS
888 if ((Pte
& 0x800) || !(Pte
>> PAGE_SHIFT
))
890 DPRINT1("Invalid Pte %lx\n", Pte
);
891 KeBugCheck(MEMORY_MANAGEMENT
);
894 if((Pte
& Attributes
) != Attributes
)
895 MiFlushTlb(Pt
, Address
);
897 MmUnmapPageTable(Pt
);
903 MmInitGlobalKernelPageDirectory(VOID
)
905 /* Nothing to do here */