1 /** @file 2 This is the code for Boot Script Executer module. 3 4 This driver is dispatched by Dxe core and the driver will reload itself to ACPI NVS memory 5 in the entry point. The functionality is to interpret and restore the S3 boot script 6 7 Copyright (c) 2013-2016 Intel Corporation. 8 9 This program and the accompanying materials 10 are licensed and made available under the terms and conditions of the BSD License 11 which accompanies this distribution. The full text of the license may be found at 12 http://opensource.org/licenses/bsd-license.php 13 14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 16 17 **/ 18 19 #include "ScriptExecute.h" 20 21 #pragma pack(1) 22 typedef union { 23 struct { 24 UINT32 LimitLow : 16; 25 UINT32 BaseLow : 16; 26 UINT32 BaseMid : 8; 27 UINT32 Type : 4; 28 UINT32 System : 1; 29 UINT32 Dpl : 2; 30 UINT32 Present : 1; 31 UINT32 LimitHigh : 4; 32 UINT32 Software : 1; 33 UINT32 Reserved : 1; 34 UINT32 DefaultSize : 1; 35 UINT32 Granularity : 1; 36 UINT32 BaseHigh : 8; 37 } Bits; 38 UINT64 Uint64; 39 } IA32_GDT; 40 41 #pragma pack() 42 43 EFI_GUID mBootScriptExecutorImageGuid = { 44 0x9a8d3433, 0x9fe8, 0x42b6, {0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b} 45 }; 46 47 // 48 // Global Descriptor Table (GDT) 49 // 50 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = { 51 /* selector { Global Segment Descriptor } */ 52 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 53 /* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 54 /* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, 55 /* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, 56 /* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 57 /* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, 58 /* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, 59 /* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}}, 60 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 61 }; 62 63 // 64 // IA32 Gdt register 65 // 66 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = { 67 sizeof (mGdtEntries) - 1, 68 (UINTN) mGdtEntries 69 }; 70 71 /** 72 Entry function of Boot script exector. This function will be executed in 73 S3 boot path. 74 This function should not return, because it is invoked by switch stack. 75 76 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT 77 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE 78 79 @retval EFI_INVALID_PARAMETER - OS waking vector not found 80 @retval EFI_UNSUPPORTED - something wrong when we resume to OS 81 **/ 82 EFI_STATUS 83 EFIAPI 84 S3BootScriptExecutorEntryFunction ( 85 IN ACPI_S3_CONTEXT *AcpiS3Context, 86 IN PEI_S3_RESUME_STATE *PeiS3ResumeState 87 ) 88 { 89 EFI_STATUS Status; 90 91 // 92 // Disable interrupt of Debug timer, since new IDT table cannot handle it. 93 // 94 SaveAndSetDebugTimerInterrupt (FALSE); 95 96 // 97 // Restore IDT for debug 98 // 99 SetIdtEntry (AcpiS3Context); 100 101 // 102 // Initialize Debug Agent to support source level debug in S3 path. 103 // 104 InitializeDebugAgent (DEBUG_AGENT_INIT_S3, NULL, NULL); 105 106 // 107 // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL 108 // for that parameter. 109 // 110 Status = S3BootScriptExecute (); 111 112 AsmWbinvd (); 113 114 // 115 // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume. 116 // 117 if (PeiS3ResumeState != 0) { 118 // 119 // Need report status back to S3ResumePeim. 120 // If boot script execution is failed, S3ResumePeim wil report the error status code. 121 // 122 PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status; 123 // 124 // IA32 S3 Resume 125 // 126 DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n")); 127 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)PlatformTransferControl16; 128 129 SwitchStack ( 130 (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint, 131 (VOID *)(UINTN)AcpiS3Context, 132 (VOID *)(UINTN)PeiS3ResumeState, 133 (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer 134 ); 135 136 // 137 // Never run to here 138 // 139 CpuDeadLoop(); 140 return EFI_UNSUPPORTED; 141 } 142 143 // 144 // Never run to here 145 // 146 CpuDeadLoop(); 147 return EFI_UNSUPPORTED; 148 } 149 /** 150 Entrypoint of Boot script exector driver, this function will be executed in 151 normal boot phase and invoked by DXE dispatch. 152 153 @param[in] ImageHandle The firmware allocated handle for the EFI image. 154 @param[in] SystemTable A pointer to the EFI System Table. 155 156 @retval EFI_SUCCESS The entry point is executed successfully. 157 @retval other Some error occurs when executing this entry point. 158 **/ 159 EFI_STATUS 160 EFIAPI 161 BootScriptExecutorEntryPoint ( 162 IN EFI_HANDLE ImageHandle, 163 IN EFI_SYSTEM_TABLE *SystemTable 164 ) 165 { 166 UINT8 *Buffer; 167 UINTN BufferSize; 168 UINTN Pages; 169 EFI_PHYSICAL_ADDRESS FfsBuffer; 170 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; 171 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; 172 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; 173 EFI_STATUS Status; 174 VOID *DevicePath; 175 EFI_HANDLE NewImageHandle; 176 177 // 178 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry 179 // point is loaded by DXE code which is the first time loaded. or else, it is already 180 // be reloaded be itself.This is a work-around 181 // 182 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); 183 if (EFI_ERROR (Status)) { 184 185 // 186 // This is the first-time loaded by DXE core. reload itself to NVS mem 187 // 188 // 189 // A workarouond: Here we install a dummy handle 190 // 191 NewImageHandle = NULL; 192 Status = gBS->InstallProtocolInterface ( 193 &NewImageHandle, 194 &gEfiCallerIdGuid, 195 EFI_NATIVE_INTERFACE, 196 NULL 197 ); 198 199 Status = GetSectionFromAnyFv ( 200 &gEfiCallerIdGuid, 201 EFI_SECTION_PE32, 202 0, 203 (VOID **) &Buffer, 204 &BufferSize 205 ); 206 ImageContext.Handle = Buffer; 207 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; 208 // 209 // Get information about the image being loaded 210 // 211 Status = PeCoffLoaderGetImageInfo (&ImageContext); 212 if (EFI_ERROR (Status)) { 213 return Status; 214 } 215 Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment); 216 FfsBuffer = 0xFFFFFFFF; 217 Status = gBS->AllocatePages ( 218 AllocateMaxAddress, 219 EfiACPIMemoryNVS, 220 Pages, 221 &FfsBuffer 222 ); 223 if (EFI_ERROR (Status)) { 224 return EFI_OUT_OF_RESOURCES; 225 } 226 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; 227 // 228 // Align buffer on section boundary 229 // 230 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; 231 ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); 232 // 233 // Load the image to our new buffer 234 // 235 Status = PeCoffLoaderLoadImage (&ImageContext); 236 if (EFI_ERROR (Status)) { 237 gBS->FreePages (FfsBuffer, Pages); 238 return Status; 239 } 240 241 // 242 // Relocate the image in our new buffer 243 // 244 Status = PeCoffLoaderRelocateImage (&ImageContext); 245 246 if (EFI_ERROR (Status)) { 247 PeCoffLoaderUnloadImage (&ImageContext); 248 gBS->FreePages (FfsBuffer, Pages); 249 return Status; 250 } 251 // 252 // Flush the instruction cache so the image data is written before we execute it 253 // 254 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); 255 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); 256 if (EFI_ERROR (Status)) { 257 gBS->FreePages (FfsBuffer, Pages); 258 return Status; 259 } 260 // 261 // Additional step for BootScript integrity 262 // Save BootScriptExecutor image 263 // 264 Status = SaveLockBox ( 265 &mBootScriptExecutorImageGuid, 266 (VOID *)(UINTN)ImageContext.ImageAddress, 267 (UINTN)ImageContext.ImageSize 268 ); 269 ASSERT_EFI_ERROR (Status); 270 271 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 272 ASSERT_EFI_ERROR (Status); 273 274 } else { 275 // 276 // the entry point is invoked after reloading. following code only run in ACPI NVS 277 // 278 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); 279 280 BootScriptExecutorBuffer = 0xFFFFFFFF; 281 Pages = EFI_SIZE_TO_PAGES(BufferSize); 282 Status = gBS->AllocatePages ( 283 AllocateMaxAddress, 284 EfiACPIMemoryNVS, 285 Pages, 286 &BootScriptExecutorBuffer 287 ); 288 if (EFI_ERROR (Status)) { 289 return EFI_OUT_OF_RESOURCES; 290 } 291 292 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer; 293 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ; 294 295 Status = SaveLockBox ( 296 &gEfiBootScriptExecutorVariableGuid, 297 &BootScriptExecutorBuffer, 298 sizeof(BootScriptExecutorBuffer) 299 ); 300 ASSERT_EFI_ERROR (Status); 301 302 // 303 // Additional step for BootScript integrity 304 // Save BootScriptExecutor context 305 // 306 Status = SaveLockBox ( 307 &gEfiBootScriptExecutorContextGuid, 308 EfiBootScriptExecutorVariable, 309 sizeof(*EfiBootScriptExecutorVariable) 310 ); 311 ASSERT_EFI_ERROR (Status); 312 313 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 314 ASSERT_EFI_ERROR (Status); 315 316 } 317 318 return EFI_SUCCESS; 319 } 320 321 /** 322 Platform specific mechanism to transfer control to 16bit OS waking vector 323 324 @param[in] AcpiWakingVector The 16bit OS waking vector 325 @param[in] AcpiLowMemoryBase A buffer under 1M which could be used during the transfer 326 327 **/ 328 VOID 329 PlatformTransferControl16 ( 330 IN UINT32 AcpiWakingVector, 331 IN UINT32 AcpiLowMemoryBase 332 ) 333 { 334 UINT32 NewValue; 335 UINT64 BaseAddress; 336 UINT64 SmramLength; 337 UINTN Index; 338 339 DEBUG (( EFI_D_INFO, "PlatformTransferControl - Entry\r\n")); 340 341 // 342 // Need to make sure the GDT is loaded with values that support long mode and real mode. 343 // 344 AsmWriteGdtr (&mGdt); 345 346 // 347 // Disable eSram block (this will also clear/zero eSRAM) 348 // We only use eSRAM in the PEI phase. Disable now that we are resuming the OS 349 // 350 NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK); 351 NewValue |= BLOCK_DISABLE_PG; 352 QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK, NewValue); 353 354 // 355 // Update HMBOUND to top of DDR3 memory and LOCK 356 // We disabled eSRAM so now we move HMBOUND down to top of DDR3 357 // 358 QNCGetTSEGMemoryRange (&BaseAddress, &SmramLength); 359 NewValue = (UINT32)(BaseAddress + SmramLength); 360 DEBUG ((EFI_D_INFO,"Locking HMBOUND at: = 0x%8x\n",NewValue)); 361 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HMBOUND_REG, (NewValue | HMBOUND_LOCK)); 362 363 // 364 // Lock all IMR regions now that HMBOUND is locked 365 // 366 for (Index = (QUARK_NC_MEMORY_MANAGER_IMR0+QUARK_NC_MEMORY_MANAGER_IMRXL); Index <= (QUARK_NC_MEMORY_MANAGER_IMR7+QUARK_NC_MEMORY_MANAGER_IMRXL); Index += 4) { 367 NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index); 368 NewValue |= IMR_LOCK; 369 QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index, NewValue); 370 } 371 372 // 373 // Call ASM routine to switch to real mode and jump to 16bit OS waking vector 374 // 375 AsmTransferControl(AcpiWakingVector, 0); 376 377 // 378 // Never run to here 379 // 380 CpuDeadLoop(); 381 } 382 383 384 385 386