2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/patpge.c
5 * PURPOSE: Support for PAT and PGE (Large Pages)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
18 /* FUNCTIONS *****************************************************************/
23 Ki386EnableGlobalPage(IN
volatile ULONG_PTR Context
)
25 volatile PLONG Count
= (PLONG
)Context
;
28 /* Disable interrupts */
31 /* Decrease CPU Count and loop until it's reached 0 */
32 do {InterlockedDecrement(Count
);} while (!*Count
);
34 /* Now check if this is the Boot CPU */
35 if (!KeGetPcr()->Number
)
37 /* It is.FIXME: Patch KeFlushCurrentTb */
40 /* Now get CR4 and make sure PGE is masked out */
42 __writecr4(Cr4
& ~CR4_PGE
);
49 DPRINT("Global page support detected but not yet taken advantage of\n");
50 //__writecr4(Cr4 | CR4_PGE);
52 /* Restore interrupts */
62 /* FIXME: Support this */
63 DPRINT("PAT support detected but not yet taken advantage of\n");
69 Ki386EnableTargetLargePage(IN ULONG_PTR Context
)
71 PLARGE_IDENTITY_MAP IdentityMap
= (PLARGE_IDENTITY_MAP
)Context
;
73 /* Call helper function with the start address and temporary page table pointer */
74 Ki386EnableCurrentLargePage(IdentityMap
->StartAddress
, IdentityMap
->Cr3
);
81 Ki386AllocateContiguousMemory(PLARGE_IDENTITY_MAP IdentityMap
,
85 PHYSICAL_ADDRESS AddressMask
;
87 ULONG SizeInBytes
= PagesCount
* PAGE_SIZE
;
89 /* Initialize acceptable address mask to any possible address */
90 AddressMask
.LowPart
= 0xFFFFFFFF;
91 AddressMask
.HighPart
= 0xFFFFFFFF;
93 /* Mark out higher 4Gb if caller asked so */
94 if (InLower4Gb
) AddressMask
.HighPart
= 0;
96 /* Try to allocate the memory */
97 Result
= MmAllocateContiguousMemory(SizeInBytes
, AddressMask
);
98 if (!Result
) return NULL
;
101 RtlZeroMemory(Result
, SizeInBytes
);
103 /* Track allocated pages in the IdentityMap structure */
104 IdentityMap
->PagesList
[IdentityMap
->PagesCount
] = Result
;
105 IdentityMap
->PagesCount
++;
112 Ki386BuildIdentityBuffer(PLARGE_IDENTITY_MAP IdentityMap
,
116 // TODO: Check whether all pages are below 4GB boundary
117 return MmGetPhysicalAddress(StartPtr
);
122 Ki386IdentityMapMakeValid(PLARGE_IDENTITY_MAP IdentityMap
,
124 PHARDWARE_PTE
*PageTable
)
130 /* Invalid, so allocate a new page for it */
131 NewPage
= (ULONG
)Ki386AllocateContiguousMemory(IdentityMap
, 1, FALSE
);
132 if (!NewPage
) return FALSE
;
134 /* Set PFN to its virtual address and mark it as valid */
135 Pde
->PageFrameNumber
= NewPage
>> PAGE_SHIFT
;
138 /* Pass page table address to the caller */
139 if (PageTable
) *PageTable
= (PHARDWARE_PTE
)NewPage
;
143 /* Valid entry, just pass the page table address to the caller */
145 *PageTable
= (PHARDWARE_PTE
)(Pde
->PageFrameNumber
<< PAGE_SHIFT
);
153 Ki386MapAddress(PLARGE_IDENTITY_MAP IdentityMap
,
154 ULONG_PTR VirtualPtr
,
155 PHYSICAL_ADDRESS PhysicalPtr
)
157 PHARDWARE_PTE Pde
, Pte
;
158 PHARDWARE_PTE PageTable
;
160 /* Allocate page directory on demand */
161 if (!IdentityMap
->TopLevelDirectory
)
163 IdentityMap
->TopLevelDirectory
= Ki386AllocateContiguousMemory(IdentityMap
, 1, 1);
164 if (!IdentityMap
->TopLevelDirectory
) return FALSE
;
167 /* Get PDE of VirtualPtr and make it writable and valid */
168 Pde
= &IdentityMap
->TopLevelDirectory
[(VirtualPtr
>> 22) & ((1 << PDE_BITS
) - 1)];
169 if (!Ki386IdentityMapMakeValid(IdentityMap
, Pde
, &PageTable
)) return FALSE
;
172 /* Get PTE of VirtualPtr, make it valid, and map PhysicalPtr there */
173 Pte
= &PageTable
[(VirtualPtr
>> 12) & ((1 << PTE_BITS
) - 1)];
175 Pte
->PageFrameNumber
= PhysicalPtr
.QuadPart
>> PAGE_SHIFT
;
182 Ki386ConvertPte(PHARDWARE_PTE Pte
)
185 PHYSICAL_ADDRESS PhysicalPtr
;
187 /* Get virtual and physical addresses */
188 VirtualPtr
= (PVOID
)(Pte
->PageFrameNumber
<< PAGE_SHIFT
);
189 PhysicalPtr
= MmGetPhysicalAddress(VirtualPtr
);
191 /* Map its physical address in the page table provided by the caller */
192 Pte
->PageFrameNumber
= PhysicalPtr
.QuadPart
>> PAGE_SHIFT
;
197 Ki386CreateIdentityMap(PLARGE_IDENTITY_MAP IdentityMap
, PVOID StartPtr
, ULONG PagesCount
)
201 PHYSICAL_ADDRESS IdentityPtr
;
203 /* Zero out the IdentityMap contents */
204 RtlZeroMemory(IdentityMap
, sizeof(LARGE_IDENTITY_MAP
));
206 /* Get the pointer to the physical address and save it in the struct */
207 IdentityPtr
= Ki386BuildIdentityBuffer(IdentityMap
, StartPtr
, PagesCount
);
208 IdentityMap
->StartAddress
= IdentityPtr
.LowPart
;
209 if (IdentityMap
->StartAddress
== 0)
211 DPRINT1("Failed to get buffer for large pages identity mapping\n");
214 DPRINT("IdentityMap->StartAddress %p\n", IdentityMap
->StartAddress
);
217 for (Ptr
= (ULONG_PTR
)StartPtr
;
218 Ptr
< (ULONG_PTR
)StartPtr
+ PagesCount
* PAGE_SIZE
;
219 Ptr
+= PAGE_SIZE
, IdentityPtr
.QuadPart
+= PAGE_SIZE
)
221 /* Map virtual address */
222 if (!Ki386MapAddress(IdentityMap
, Ptr
, IdentityPtr
)) return FALSE
;
224 /* Map physical address */
225 if (!Ki386MapAddress(IdentityMap
, IdentityPtr
.LowPart
, IdentityPtr
)) return FALSE
;
228 /* Convert all PTEs in the page directory from virtual to physical,
229 because Ki386IdentityMapMakeValid mapped only virtual addresses */
230 for (PteIndex
= 0; PteIndex
< (PAGE_SIZE
/ sizeof(HARDWARE_PTE
)); PteIndex
++)
232 if (IdentityMap
->TopLevelDirectory
[PteIndex
].Valid
!= 0)
233 Ki386ConvertPte(&IdentityMap
->TopLevelDirectory
[PteIndex
]);
236 /* Save the page directory address (allocated by Ki386MapAddress) */
237 IdentityMap
->Cr3
= MmGetPhysicalAddress(IdentityMap
->TopLevelDirectory
).LowPart
;
239 DPRINT("IdentityMap->Cr3 0x%x\n", IdentityMap
->Cr3
);
246 Ki386FreeIdentityMap(PLARGE_IDENTITY_MAP IdentityMap
)
250 DPRINT("Freeing %lu pages allocated for identity mapping\n", IdentityMap
->PagesCount
);
252 /* Free all allocated pages, if any */
253 for (Page
= 0; Page
< IdentityMap
->PagesCount
; Page
++)
254 MmFreeContiguousMemory(IdentityMap
->PagesList
[Page
]);