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