2 * COPYRIGHT: GPL, See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/amd64/page.c
5 * PURPOSE: Low level memory managment manipulation
7 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
8 * ReactOS Portable Systems Group
11 /* INCLUDES ***************************************************************/
16 #include "../ARM3/miarm.h"
18 #undef InterlockedExchangePte
19 #define InterlockedExchangePte(pte1, pte2) \
20 InterlockedExchange64((LONG64*)&pte1->u.Long, pte2.u.Long)
22 #define PAGE_EXECUTE_ANY (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)
23 #define PAGE_WRITE_ANY (PAGE_EXECUTE_READWRITE|PAGE_READWRITE|PAGE_EXECUTE_WRITECOPY|PAGE_WRITECOPY)
24 #define PAGE_WRITECOPY_ANY (PAGE_EXECUTE_WRITECOPY|PAGE_WRITECOPY)
26 extern MMPTE HyperTemplatePte
;
28 /* GLOBALS *****************************************************************/
32 MmProtectToPteMask
[32] =
35 // These are the base MM_ protection flags
38 PTE_READONLY
| PTE_ENABLE_CACHE
,
39 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
40 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
41 PTE_READWRITE
| PTE_ENABLE_CACHE
,
42 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
43 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
44 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
46 // These OR in the MM_NOCACHE flag
49 PTE_READONLY
| PTE_DISABLE_CACHE
,
50 PTE_EXECUTE
| PTE_DISABLE_CACHE
,
51 PTE_EXECUTE_READ
| PTE_DISABLE_CACHE
,
52 PTE_READWRITE
| PTE_DISABLE_CACHE
,
53 PTE_WRITECOPY
| PTE_DISABLE_CACHE
,
54 PTE_EXECUTE_READWRITE
| PTE_DISABLE_CACHE
,
55 PTE_EXECUTE_WRITECOPY
| PTE_DISABLE_CACHE
,
57 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
60 PTE_READONLY
| PTE_ENABLE_CACHE
,
61 PTE_EXECUTE
| PTE_ENABLE_CACHE
,
62 PTE_EXECUTE_READ
| PTE_ENABLE_CACHE
,
63 PTE_READWRITE
| PTE_ENABLE_CACHE
,
64 PTE_WRITECOPY
| PTE_ENABLE_CACHE
,
65 PTE_EXECUTE_READWRITE
| PTE_ENABLE_CACHE
,
66 PTE_EXECUTE_WRITECOPY
| PTE_ENABLE_CACHE
,
68 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
71 PTE_READONLY
| PTE_WRITECOMBINED_CACHE
,
72 PTE_EXECUTE
| PTE_WRITECOMBINED_CACHE
,
73 PTE_EXECUTE_READ
| PTE_WRITECOMBINED_CACHE
,
74 PTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
75 PTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
76 PTE_EXECUTE_READWRITE
| PTE_WRITECOMBINED_CACHE
,
77 PTE_EXECUTE_WRITECOPY
| PTE_WRITECOMBINED_CACHE
,
80 /* PRIVATE FUNCTIONS *******************************************************/
84 MiIsHyperspaceAddress(PVOID Address
)
86 return ((ULONG64
)Address
>= HYPER_SPACE
&&
87 (ULONG64
)Address
<= HYPER_SPACE_END
);
91 MiFlushTlb(PMMPTE Pte
, PVOID Address
)
93 if (MiIsHyperspaceAddress(Pte
))
95 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pte
));
112 /* Check if we need hypersapce mapping */
113 if (Address
< MmSystemRangeStart
&&
114 Process
&& Process
!= PsGetCurrentProcess())
122 TmplPte
.u
.Flush
.Valid
= 1;
123 TmplPte
.u
.Flush
.Write
= 1;
126 Pte
= MiAddressToPxe(Address
);
127 if (!Pte
->u
.Hard
.Valid
)
129 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
130 InterlockedExchangePte(Pte
, TmplPte
);
134 Pte
= MiAddressToPpe(Address
);
135 if (!Pte
->u
.Hard
.Valid
)
137 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
138 InterlockedExchangePte(Pte
, TmplPte
);
142 Pte
= MiAddressToPde(Address
);
143 if (!Pte
->u
.Hard
.Valid
)
145 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
146 InterlockedExchangePte(Pte
, TmplPte
);
152 Pte
= MiAddressToPxe(Address
);
153 if (!Pte
->u
.Hard
.Valid
)
157 Pte
= MiAddressToPpe(Address
);
158 if (!Pte
->u
.Hard
.Valid
)
162 Pte
= MiAddressToPde(Address
);
163 if (!Pte
->u
.Hard
.Valid
)
167 return MiAddressToPte(Address
);
172 MiGetPteValueForProcess(
179 Pte
= MiGetPteForProcess(Process
, Address
, FALSE
);
180 PteValue
= Pte
? Pte
->u
.Long
: 0;
182 if (MiIsHyperspaceAddress(Pte
))
183 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pte
));
190 MiGetPteProtection(MMPTE Pte
)
194 if (!Pte
.u
.Flush
.Valid
)
196 Protect
= PAGE_NOACCESS
;
198 else if (Pte
.u
.Flush
.NoExecute
)
200 if (Pte
.u
.Flush
.CopyOnWrite
)
201 Protect
= PAGE_WRITECOPY
;
202 else if (Pte
.u
.Flush
.Write
)
203 Protect
= PAGE_READWRITE
;
205 Protect
= PAGE_READONLY
;
209 if (Pte
.u
.Flush
.CopyOnWrite
)
210 Protect
= PAGE_EXECUTE_WRITECOPY
;
211 else if (Pte
.u
.Flush
.Write
)
212 Protect
= PAGE_EXECUTE_READWRITE
;
214 Protect
= PAGE_EXECUTE_READ
;
217 if (Pte
.u
.Flush
.CacheDisable
)
218 Protect
|= PAGE_NOCACHE
;
220 if (Pte
.u
.Flush
.WriteThrough
)
221 Protect
|= PAGE_WRITETHROUGH
;
229 MiSetPteProtection(PMMPTE Pte
, ULONG Protection
)
231 Pte
->u
.Flush
.CopyOnWrite
= (Protection
& PAGE_WRITECOPY_ANY
) ? 1 : 0;
232 Pte
->u
.Flush
.Write
= (Protection
& PAGE_WRITE_ANY
) ? 1 : 0;
233 Pte
->u
.Flush
.CacheDisable
= (Protection
& PAGE_NOCACHE
) ? 1 : 0;
234 Pte
->u
.Flush
.WriteThrough
= (Protection
& PAGE_WRITETHROUGH
) ? 1 : 0;
236 // FIXME: This doesn't work. Why?
237 // Pte->u.Flush.NoExecute = (Protection & PAGE_EXECUTE_ANY) ? 0 : 1;
240 /* FUNCTIONS ***************************************************************/
244 MmGetPfnForProcess(PEPROCESS Process
,
248 Pte
.u
.Long
= MiGetPteValueForProcess(Process
, Address
);
249 return Pte
.u
.Hard
.Valid
? Pte
.u
.Hard
.PageFrameNumber
: 0;
254 MmGetPhysicalAddress(PVOID Address
)
259 Pte
.u
.Long
= MiGetPteValueForProcess(NULL
, Address
);
260 if (Pte
.u
.Hard
.Valid
)
262 p
.QuadPart
= Pte
.u
.Hard
.PageFrameNumber
* PAGE_SIZE
;
263 p
.u
.LowPart
|= (ULONG_PTR
)Address
& (PAGE_SIZE
- 1);
275 MmIsPagePresent(PEPROCESS Process
, PVOID Address
)
278 Pte
.u
.Long
= MiGetPteValueForProcess(Process
, Address
);
279 return Pte
.u
.Hard
.Valid
;
284 MmIsPageSwapEntry(PEPROCESS Process
, PVOID Address
)
287 Pte
.u
.Long
= MiGetPteValueForProcess(Process
, Address
);
288 return Pte
.u
.Hard
.Valid
&& Pte
.u
.Soft
.Transition
;
293 MmIsDirtyPage(PEPROCESS Process
, PVOID Address
)
296 Pte
.u
.Long
= MiGetPteValueForProcess(Process
, Address
);
297 return Pte
.u
.Hard
.Valid
&& Pte
.u
.Hard
.Dirty
;
302 MmGetPageProtect(PEPROCESS Process
, PVOID Address
)
306 Pte
.u
.Long
= MiGetPteValueForProcess(Process
, Address
);
308 return MiGetPteProtection(Pte
);
313 MmSetPageProtect(PEPROCESS Process
, PVOID Address
, ULONG flProtect
)
318 Pte
= MiGetPteForProcess(Process
, Address
, FALSE
);
323 MiSetPteProtection(&NewPte
, flProtect
);
325 InterlockedExchangePte(Pte
, NewPte
);
327 MiFlushTlb(Pte
, Address
);
332 MmSetCleanPage(PEPROCESS Process
, PVOID Address
)
336 Pte
= MiGetPteForProcess(Process
, Address
, FALSE
);
339 KeBugCheckEx(MEMORY_MANAGEMENT
, 0x1234, (ULONG64
)Address
, 0, 0);
342 /* Ckear the dirty bit */
343 if (InterlockedBitTestAndReset64((PVOID
)Pte
, 6))
345 if (!MiIsHyperspaceAddress(Pte
))
349 MiFlushTlb(Pte
, Address
);
354 MmSetDirtyPage(PEPROCESS Process
, PVOID Address
)
358 Pte
= MiGetPteForProcess(Process
, Address
, FALSE
);
361 KeBugCheckEx(MEMORY_MANAGEMENT
, 0x1234, (ULONG64
)Address
, 0, 0);
364 /* Ckear the dirty bit */
365 if (InterlockedBitTestAndSet64((PVOID
)Pte
, 6))
367 if (!MiIsHyperspaceAddress(Pte
))
371 MiFlushTlb(Pte
, Address
);
377 Mmi386ReleaseMmInfo(PEPROCESS Process
)
380 return STATUS_UNSUCCESSFUL
;
385 MmDisableVirtualMapping(PEPROCESS Process
, PVOID Address
, BOOLEAN
* WasDirty
, PPFN_NUMBER Page
)
392 MmRawDeleteVirtualMapping(PVOID Address
)
399 MmDeleteVirtualMapping(
410 Pte
= MiGetPteForProcess(Process
, Address
, FALSE
);
414 /* Atomically set the entry to zero and get the old value. */
415 OldPte
.u
.Long
= InterlockedExchange64((LONG64
*)&Pte
->u
.Long
, 0);
417 if (OldPte
.u
.Hard
.Valid
)
419 Pfn
= OldPte
.u
.Hard
.PageFrameNumber
;
422 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pfn
);
433 /* Return information to the caller */
435 *WasDirty
= OldPte
.u
.Hard
.Dirty
;;
440 MiFlushTlb(Pte
, Address
);
445 MmDeletePageFileMapping(PEPROCESS Process
, PVOID Address
,
446 SWAPENTRY
* SwapEntry
)
454 MmEnableVirtualMapping(PEPROCESS Process
, PVOID Address
)
462 MmCreatePageFileMapping(PEPROCESS Process
,
467 return STATUS_UNSUCCESSFUL
;
473 MmCreateVirtualMappingUnsafe(
476 ULONG PageProtection
,
483 /* Check if the range is valid */
484 if ((Process
== NULL
&& Address
< MmSystemRangeStart
) ||
485 (Process
!= NULL
&& Address
> MmHighestUserAddress
))
487 DPRINT1("Address 0x%p is invalid for process %p\n", Address
, Process
);
492 TmplPte
.u
.Hard
.Valid
= 1;
493 MiSetPteProtection(&TmplPte
, PageProtection
);
497 for (i
= 0; i
< PageCount
; i
++)
499 TmplPte
.u
.Hard
.PageFrameNumber
= Pages
[i
];
501 Pte
= MiGetPteForProcess(Process
, Address
, TRUE
);
503 DPRINT1("MmCreateVirtualMappingUnsafe, Address=%p, TmplPte=%p, Pte=%p\n",
504 Address
, TmplPte
.u
.Long
, Pte
);
506 if (InterlockedExchangePte(Pte
, TmplPte
))
508 KeInvalidateTlbEntry(Address
);
511 if (MiIsHyperspaceAddress(Pte
))
512 MmDeleteHyperspaceMapping((PVOID
)PAGE_ROUND_DOWN(Pte
));
514 Address
= (PVOID
)((ULONG64
)Address
+ PAGE_SIZE
);
518 return STATUS_SUCCESS
;
523 MmCreateVirtualMapping(PEPROCESS Process
,
531 for (i
= 0; i
< PageCount
; i
++)
533 if (!MmIsPageInUse(Pages
[i
]))
535 DPRINT1("Page %x not in use\n", Pages
[i
]);
536 KeBugCheck(MEMORY_MANAGEMENT
);
540 return MmCreateVirtualMappingUnsafe(Process
, Address
, Protect
, Pages
, PageCount
);
545 MmCreateProcessAddressSpace(IN ULONG MinWs
,
546 IN PEPROCESS Process
,
547 OUT PULONG_PTR DirectoryTableBase
)
550 PFN_NUMBER TableBasePfn
, HyperPfn
;
552 MMPTE TempPte
, PdePte
;
556 /* No page colors yet */
557 Process
->NextPageColor
= 0;
559 /* Setup the hyperspace lock */
560 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
562 /* Lock PFN database */
563 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
565 /* Get a page for the table base and for hyperspace */
566 TableBasePfn
= MiRemoveAnyPage(0);
567 HyperPfn
= MiRemoveAnyPage(0);
569 /* Release PFN lock */
570 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
572 /* Zero both pages */
573 MiZeroPhysicalPage(TableBasePfn
);
574 MiZeroPhysicalPage(HyperPfn
);
576 /* Set the base directory pointers */
577 DirectoryTableBase
[0] = TableBasePfn
<< PAGE_SHIFT
;
578 DirectoryTableBase
[1] = HyperPfn
<< PAGE_SHIFT
;
580 /* Make sure we don't already have a page directory setup */
581 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == 0);
583 /* Insert us into the Mm process list */
584 InsertTailList(&MmProcessList
, &Process
->MmProcessLinks
);
586 /* Get a PTE to map the page directory */
587 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
588 ASSERT(PointerPte
!= NULL
);
591 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
596 /* Set it dirty and map it */
597 PdePte
.u
.Hard
.Dirty
= TRUE
;
598 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
600 /* Now get the page directory (which we'll double map, so call it a page table */
601 SystemTable
= MiPteToAddress(PointerPte
);
603 /* Copy all the kernel mappings */
604 TableIndex
= MiAddressToPxi(MmSystemRangeStart
);
606 RtlCopyMemory(&SystemTable
[TableIndex
],
607 MiAddressToPxe(MmSystemRangeStart
),
608 PAGE_SIZE
- TableIndex
* sizeof(MMPTE
));
610 /* Now write the PTE/PDE entry for hyperspace itself */
611 TempPte
= ValidKernelPte
;
612 TempPte
.u
.Hard
.PageFrameNumber
= HyperPfn
;
613 TableIndex
= MiAddressToPxi(HYPER_SPACE
);
614 SystemTable
[TableIndex
] = TempPte
;
617 ASSERT(MiAddressToPxi(MmHyperSpaceEnd
) > TableIndex
);
619 /* Now do the x86 trick of making the PDE a page table itself */
620 TableIndex
= MiAddressToPxi(PTE_BASE
);
621 TempPte
.u
.Hard
.PageFrameNumber
= TableBasePfn
;
622 SystemTable
[TableIndex
] = TempPte
;
624 /* Let go of the system PTE */
625 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
627 /* Switch to phase 1 initialization */
628 ASSERT(Process
->AddressSpaceInitialized
== 0);
629 Process
->AddressSpaceInitialized
= 1;