[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / i386 / patpge.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define PDE_BITS 10
16 #define PTE_BITS 10
17
18 /* FUNCTIONS *****************************************************************/
19
20 ULONG_PTR
21 NTAPI
22 INIT_FUNCTION
23 Ki386EnableGlobalPage(IN volatile ULONG_PTR Context)
24 {
25 volatile PLONG Count = (PLONG)Context;
26 ULONG Cr4, Cr3;
27
28 /* Disable interrupts */
29 _disable();
30
31 /* Decrease CPU Count and loop until it's reached 0 */
32 do {InterlockedDecrement(Count);} while (!*Count);
33
34 /* Now check if this is the Boot CPU */
35 if (!KeGetPcr()->Number)
36 {
37 /* It is.FIXME: Patch KeFlushCurrentTb */
38 }
39
40 /* Now get CR4 and make sure PGE is masked out */
41 Cr4 = __readcr4();
42 __writecr4(Cr4 & ~CR4_PGE);
43
44 /* Flush the TLB */
45 Cr3 = __readcr3();
46 __writecr3(Cr3);
47
48 /* Now enable PGE */
49 DPRINT("Global page support detected but not yet taken advantage of\n");
50 //__writecr4(Cr4 | CR4_PGE);
51
52 /* Restore interrupts */
53 _enable();
54 return 0;
55 }
56
57 VOID
58 NTAPI
59 INIT_FUNCTION
60 KiInitializePAT(VOID)
61 {
62 /* FIXME: Support this */
63 DPRINT("PAT support detected but not yet taken advantage of\n");
64 }
65
66 ULONG_PTR
67 NTAPI
68 INIT_FUNCTION
69 Ki386EnableTargetLargePage(IN ULONG_PTR Context)
70 {
71 PLARGE_IDENTITY_MAP IdentityMap = (PLARGE_IDENTITY_MAP)Context;
72
73 /* Call helper function with the start address and temporary page table pointer */
74 Ki386EnableCurrentLargePage(IdentityMap->StartAddress, IdentityMap->Cr3);
75
76 return 0;
77 }
78
79 PVOID
80 NTAPI
81 Ki386AllocateContiguousMemory(PLARGE_IDENTITY_MAP IdentityMap,
82 ULONG PagesCount,
83 BOOLEAN InLower4Gb)
84 {
85 PHYSICAL_ADDRESS AddressMask;
86 PVOID Result;
87 ULONG SizeInBytes = PagesCount * PAGE_SIZE;
88
89 /* Initialize acceptable address mask to any possible address */
90 AddressMask.LowPart = 0xFFFFFFFF;
91 AddressMask.HighPart = 0xFFFFFFFF;
92
93 /* Mark out higher 4Gb if caller asked so */
94 if (InLower4Gb) AddressMask.HighPart = 0;
95
96 /* Try to allocate the memory */
97 Result = MmAllocateContiguousMemory(SizeInBytes, AddressMask);
98 if (!Result) return NULL;
99
100 /* Zero it out */
101 RtlZeroMemory(Result, SizeInBytes);
102
103 /* Track allocated pages in the IdentityMap structure */
104 IdentityMap->PagesList[IdentityMap->PagesCount] = Result;
105 IdentityMap->PagesCount++;
106
107 return Result;
108 }
109
110 PHYSICAL_ADDRESS
111 NTAPI
112 Ki386BuildIdentityBuffer(PLARGE_IDENTITY_MAP IdentityMap,
113 PVOID StartPtr,
114 ULONG Length)
115 {
116 // TODO: Check whether all pages are below 4GB boundary
117 return MmGetPhysicalAddress(StartPtr);
118 }
119
120 BOOLEAN
121 NTAPI
122 Ki386IdentityMapMakeValid(PLARGE_IDENTITY_MAP IdentityMap,
123 PHARDWARE_PTE Pde,
124 PHARDWARE_PTE *PageTable)
125 {
126 ULONG NewPage;
127
128 if (Pde->Valid == 0)
129 {
130 /* Invalid, so allocate a new page for it */
131 NewPage = (ULONG)Ki386AllocateContiguousMemory(IdentityMap, 1, FALSE);
132 if (!NewPage) return FALSE;
133
134 /* Set PFN to its virtual address and mark it as valid */
135 Pde->PageFrameNumber = NewPage >> PAGE_SHIFT;
136 Pde->Valid = 1;
137
138 /* Pass page table address to the caller */
139 if (PageTable) *PageTable = (PHARDWARE_PTE)NewPage;
140 }
141 else
142 {
143 /* Valid entry, just pass the page table address to the caller */
144 if (PageTable)
145 *PageTable = (PHARDWARE_PTE)(Pde->PageFrameNumber << PAGE_SHIFT);
146 }
147
148 return TRUE;
149 }
150
151 BOOLEAN
152 NTAPI
153 Ki386MapAddress(PLARGE_IDENTITY_MAP IdentityMap,
154 ULONG_PTR VirtualPtr,
155 PHYSICAL_ADDRESS PhysicalPtr)
156 {
157 PHARDWARE_PTE Pde, Pte;
158 PHARDWARE_PTE PageTable;
159
160 /* Allocate page directory on demand */
161 if (!IdentityMap->TopLevelDirectory)
162 {
163 IdentityMap->TopLevelDirectory = Ki386AllocateContiguousMemory(IdentityMap, 1, 1);
164 if (!IdentityMap->TopLevelDirectory) return FALSE;
165 }
166
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;
170 Pde->Write = 1;
171
172 /* Get PTE of VirtualPtr, make it valid, and map PhysicalPtr there */
173 Pte = &PageTable[(VirtualPtr >> 12) & ((1 << PTE_BITS) - 1)];
174 Pte->Valid = 1;
175 Pte->PageFrameNumber = PhysicalPtr.QuadPart >> PAGE_SHIFT;
176
177 return TRUE;
178 }
179
180 VOID
181 NTAPI
182 Ki386ConvertPte(PHARDWARE_PTE Pte)
183 {
184 PVOID VirtualPtr;
185 PHYSICAL_ADDRESS PhysicalPtr;
186
187 /* Get virtual and physical addresses */
188 VirtualPtr = (PVOID)(Pte->PageFrameNumber << PAGE_SHIFT);
189 PhysicalPtr = MmGetPhysicalAddress(VirtualPtr);
190
191 /* Map its physical address in the page table provided by the caller */
192 Pte->PageFrameNumber = PhysicalPtr.QuadPart >> PAGE_SHIFT;
193 }
194
195 BOOLEAN
196 NTAPI
197 Ki386CreateIdentityMap(PLARGE_IDENTITY_MAP IdentityMap, PVOID StartPtr, ULONG PagesCount)
198 {
199 ULONG_PTR Ptr;
200 ULONG PteIndex;
201 PHYSICAL_ADDRESS IdentityPtr;
202
203 /* Zero out the IdentityMap contents */
204 RtlZeroMemory(IdentityMap, sizeof(LARGE_IDENTITY_MAP));
205
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)
210 {
211 DPRINT1("Failed to get buffer for large pages identity mapping\n");
212 return FALSE;
213 }
214 DPRINT("IdentityMap->StartAddress %p\n", IdentityMap->StartAddress);
215
216 /* Map all pages */
217 for (Ptr = (ULONG_PTR)StartPtr;
218 Ptr < (ULONG_PTR)StartPtr + PagesCount * PAGE_SIZE;
219 Ptr += PAGE_SIZE, IdentityPtr.QuadPart += PAGE_SIZE)
220 {
221 /* Map virtual address */
222 if (!Ki386MapAddress(IdentityMap, Ptr, IdentityPtr)) return FALSE;
223
224 /* Map physical address */
225 if (!Ki386MapAddress(IdentityMap, IdentityPtr.LowPart, IdentityPtr)) return FALSE;
226 }
227
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++)
231 {
232 if (IdentityMap->TopLevelDirectory[PteIndex].Valid != 0)
233 Ki386ConvertPte(&IdentityMap->TopLevelDirectory[PteIndex]);
234 }
235
236 /* Save the page directory address (allocated by Ki386MapAddress) */
237 IdentityMap->Cr3 = MmGetPhysicalAddress(IdentityMap->TopLevelDirectory).LowPart;
238
239 DPRINT("IdentityMap->Cr3 0x%x\n", IdentityMap->Cr3);
240
241 return TRUE;
242 }
243
244 VOID
245 NTAPI
246 Ki386FreeIdentityMap(PLARGE_IDENTITY_MAP IdentityMap)
247 {
248 ULONG Page;
249
250 DPRINT("Freeing %lu pages allocated for identity mapping\n", IdentityMap->PagesCount);
251
252 /* Free all allocated pages, if any */
253 for (Page = 0; Page < IdentityMap->PagesCount; Page++)
254 MmFreeContiguousMemory(IdentityMap->PagesList[Page]);
255 }