1 /** @file 2 The X64 entrypoint is used to process capsule in long mode. 3 4 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include <Library/DebugLib.h> 16 #include <Library/BaseMemoryLib.h> 17 #include <Library/CpuExceptionHandlerLib.h> 18 #include <Library/DebugAgentLib.h> 19 #include "CommonHeader.h" 20 21 #define EXCEPTION_VECTOR_NUMBER 0x22 22 23 #define IA32_PG_P BIT0 24 #define IA32_PG_RW BIT1 25 #define IA32_PG_PS BIT7 26 27 typedef struct _PAGE_FAULT_CONTEXT { 28 BOOLEAN Page1GSupport; 29 UINT64 PhyMask; 30 UINTN PageFaultBuffer; 31 UINTN PageFaultIndex; 32 // 33 // Store the uplink information for each page being used. 34 // 35 UINT64 *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES]; 36 VOID *OriginalHandler; 37 } PAGE_FAULT_CONTEXT; 38 39 typedef struct _PAGE_FAULT_IDT_TABLE { 40 PAGE_FAULT_CONTEXT PageFaultContext; 41 IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER]; 42 } PAGE_FAULT_IDT_TABLE; 43 44 /** 45 Page fault handler. 46 47 **/ 48 VOID 49 EFIAPI 50 PageFaultHandlerHook ( 51 VOID 52 ); 53 54 /** 55 Hook IDT with our page fault handler so that the on-demand paging works on page fault. 56 57 @param[in, out] IdtEntry Pointer to IDT entry. 58 @param[in, out] PageFaultContext Pointer to page fault context. 59 60 **/ 61 VOID 62 HookPageFaultHandler ( 63 IN OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry, 64 IN OUT PAGE_FAULT_CONTEXT *PageFaultContext 65 ) 66 { 67 UINT32 RegEax; 68 UINT8 PhysicalAddressBits; 69 UINTN PageFaultHandlerHookAddress; 70 71 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 72 if (RegEax >= 0x80000008) { 73 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); 74 PhysicalAddressBits = (UINT8) RegEax; 75 } else { 76 PhysicalAddressBits = 36; 77 } 78 PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1; 79 PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB; 80 81 // 82 // Set Page Fault entry to catch >4G access 83 // 84 PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook; 85 PageFaultContext->OriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16)); 86 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress; 87 IdtEntry->Bits.Selector = (UINT16)AsmReadCs (); 88 IdtEntry->Bits.Reserved_0 = 0; 89 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; 90 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16); 91 IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32); 92 IdtEntry->Bits.Reserved_1 = 0; 93 94 if (PageFaultContext->Page1GSupport) { 95 PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2); 96 }else { 97 PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6); 98 } 99 PageFaultContext->PageFaultIndex = 0; 100 ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink)); 101 } 102 103 /** 104 Acquire page for page fault. 105 106 @param[in, out] PageFaultContext Pointer to page fault context. 107 @param[in, out] Uplink Pointer to up page table entry. 108 109 **/ 110 VOID 111 AcquirePage ( 112 IN OUT PAGE_FAULT_CONTEXT *PageFaultContext, 113 IN OUT UINT64 *Uplink 114 ) 115 { 116 UINTN Address; 117 118 Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex); 119 ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1)); 120 121 // 122 // Cut the previous uplink if it exists and wasn't overwritten. 123 // 124 if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) && ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & PageFaultContext->PhyMask) == Address)) { 125 *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0; 126 } 127 128 // 129 // Link & Record the current uplink. 130 // 131 *Uplink = Address | IA32_PG_P | IA32_PG_RW; 132 PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink; 133 134 PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES; 135 } 136 137 /** 138 The page fault handler that on-demand read >4G memory/MMIO. 139 140 @retval NULL The page fault is correctly handled. 141 @retval OriginalHandler The page fault is not handled and is passed through to original handler. 142 143 **/ 144 VOID * 145 EFIAPI 146 PageFaultHandler ( 147 VOID 148 ) 149 { 150 IA32_DESCRIPTOR Idtr; 151 PAGE_FAULT_CONTEXT *PageFaultContext; 152 UINT64 PhyMask; 153 UINT64 *PageTable; 154 UINT64 PFAddress; 155 UINTN PTIndex; 156 157 // 158 // Get the IDT Descriptor. 159 // 160 AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); 161 // 162 // Then get page fault context by IDT Descriptor. 163 // 164 PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT)); 165 PhyMask = PageFaultContext->PhyMask; 166 167 PFAddress = AsmReadCr2 (); 168 DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress)); 169 170 if (PFAddress >= PhyMask + SIZE_4KB) { 171 return PageFaultContext->OriginalHandler; 172 } 173 PFAddress &= PhyMask; 174 175 PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask); 176 177 PTIndex = BitFieldRead64 (PFAddress, 39, 47); 178 // PML4E 179 if ((PageTable[PTIndex] & IA32_PG_P) == 0) { 180 AcquirePage (PageFaultContext, &PageTable[PTIndex]); 181 } 182 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PhyMask); 183 PTIndex = BitFieldRead64 (PFAddress, 30, 38); 184 // PDPTE 185 if (PageFaultContext->Page1GSupport) { 186 PageTable[PTIndex] = (PFAddress & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; 187 } else { 188 if ((PageTable[PTIndex] & IA32_PG_P) == 0) { 189 AcquirePage (PageFaultContext, &PageTable[PTIndex]); 190 } 191 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PhyMask); 192 PTIndex = BitFieldRead64 (PFAddress, 21, 29); 193 // PD 194 PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; 195 } 196 197 return NULL; 198 } 199 200 201 /** 202 The X64 entrypoint is used to process capsule in long mode then 203 return to 32-bit protected mode. 204 205 @param EntrypointContext Pointer to the context of long mode. 206 @param ReturnContext Pointer to the context of 32-bit protected mode. 207 208 @retval This function should never return actually. 209 210 **/ 211 EFI_STATUS 212 EFIAPI 213 _ModuleEntryPoint ( 214 SWITCH_32_TO_64_CONTEXT *EntrypointContext, 215 SWITCH_64_TO_32_CONTEXT *ReturnContext 216 ) 217 { 218 EFI_STATUS Status; 219 IA32_DESCRIPTOR Ia32Idtr; 220 IA32_DESCRIPTOR X64Idtr; 221 PAGE_FAULT_IDT_TABLE PageFaultIdtTable; 222 IA32_IDT_GATE_DESCRIPTOR *IdtEntry; 223 224 // 225 // Save the IA32 IDT Descriptor 226 // 227 AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); 228 229 // 230 // Setup X64 IDT table 231 // 232 ZeroMem (PageFaultIdtTable.IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER); 233 X64Idtr.Base = (UINTN) PageFaultIdtTable.IdtEntryTable; 234 X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER - 1); 235 AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr); 236 237 // 238 // Setup the default CPU exception handlers 239 // 240 Status = InitializeCpuExceptionHandlers (NULL); 241 ASSERT_EFI_ERROR (Status); 242 243 // 244 // Hook page fault handler to handle >4G request. 245 // 246 PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport; 247 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) (X64Idtr.Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR))); 248 HookPageFaultHandler (IdtEntry, &(PageFaultIdtTable.PageFaultContext)); 249 250 // 251 // Initialize Debug Agent to support source level debug 252 // 253 InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL); 254 255 // 256 // Call CapsuleDataCoalesce to process capsule. 257 // 258 Status = CapsuleDataCoalesce ( 259 NULL, 260 (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr, 261 (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr, 262 (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr 263 ); 264 265 ReturnContext->ReturnStatus = Status; 266 267 // 268 // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode 269 // 270 SaveAndSetDebugTimerInterrupt (FALSE); 271 // 272 // Restore IA32 IDT table 273 // 274 AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); 275 276 // 277 // Finish to coalesce capsule, and return to 32-bit mode. 278 // 279 AsmDisablePaging64 ( 280 ReturnContext->ReturnCs, 281 (UINT32) ReturnContext->ReturnEntryPoint, 282 (UINT32) (UINTN) EntrypointContext, 283 (UINT32) (UINTN) ReturnContext, 284 (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength) 285 ); 286 287 // 288 // Should never be here. 289 // 290 ASSERT (FALSE); 291 return EFI_SUCCESS; 292 }