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 INIT_FUNCTION 21 ULONG_PTR 22 NTAPI 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 INIT_FUNCTION 59 VOID 60 NTAPI 61 KiInitializePAT(VOID) 62 { 63 /* FIXME: Support this */ 64 DPRINT("PAT support detected but not yet taken advantage of\n"); 65 } 66 67 INIT_FUNCTION 68 ULONG_PTR 69 NTAPI 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 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 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 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 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 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 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 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