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 reserved memory 5 in the entry point. The functionality is to interpret and restore the S3 boot script 6 7 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> 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 EFI_GUID mBootScriptExecutorImageGuid = { 22 0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b } 23 }; 24 25 BOOLEAN mPage1GSupport = FALSE; 26 27 /** 28 Entry function of Boot script exector. This function will be executed in 29 S3 boot path. 30 This function should not return, because it is invoked by switch stack. 31 32 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT 33 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE 34 35 @retval EFI_INVALID_PARAMETER - OS waking vector not found 36 @retval EFI_UNSUPPORTED - something wrong when we resume to OS 37 **/ 38 EFI_STATUS 39 EFIAPI 40 S3BootScriptExecutorEntryFunction ( 41 IN ACPI_S3_CONTEXT *AcpiS3Context, 42 IN PEI_S3_RESUME_STATE *PeiS3ResumeState 43 ) 44 { 45 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; 46 EFI_STATUS Status; 47 UINTN TempStackTop; 48 UINTN TempStack[0x10]; 49 UINTN AsmTransferControl16Address; 50 IA32_DESCRIPTOR IdtDescriptor; 51 52 // 53 // Disable interrupt of Debug timer, since new IDT table cannot handle it. 54 // 55 SaveAndSetDebugTimerInterrupt (FALSE); 56 57 AsmReadIdtr (&IdtDescriptor); 58 // 59 // Restore IDT for debug 60 // 61 SetIdtEntry (AcpiS3Context); 62 63 // 64 // Initialize Debug Agent to support source level debug in S3 path, it will disable interrupt and Debug Timer. 65 // 66 InitializeDebugAgent (DEBUG_AGENT_INIT_S3, (VOID *)&IdtDescriptor, NULL); 67 68 // 69 // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL 70 // for that parameter. 71 // 72 Status = S3BootScriptExecute (); 73 74 // 75 // If invalid script table or opcode in S3 boot script table. 76 // 77 ASSERT_EFI_ERROR (Status); 78 79 if (EFI_ERROR (Status)) { 80 CpuDeadLoop (); 81 return Status; 82 } 83 84 AsmWbinvd (); 85 86 // 87 // Get ACPI Table Address 88 // 89 Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)); 90 91 // 92 // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume. 93 // 94 if (PeiS3ResumeState != 0) { 95 // 96 // Need report status back to S3ResumePeim. 97 // If boot script execution is failed, S3ResumePeim wil report the error status code. 98 // 99 PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status; 100 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 101 // 102 // X64 S3 Resume 103 // 104 DEBUG ((EFI_D_ERROR, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n")); 105 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl32; 106 107 if ((Facs != NULL) && 108 (Facs->Signature == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) && 109 (Facs->FirmwareWakingVector != 0) ) { 110 // 111 // more step needed - because relative address is handled differently between X64 and IA32. 112 // 113 AsmTransferControl16Address = (UINTN)AsmTransferControl16; 114 AsmFixAddress16 = (UINT32)AsmTransferControl16Address; 115 AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12)); 116 } 117 118 AsmDisablePaging64 ( 119 PeiS3ResumeState->ReturnCs, 120 (UINT32)PeiS3ResumeState->ReturnEntryPoint, 121 (UINT32)(UINTN)AcpiS3Context, 122 (UINT32)(UINTN)PeiS3ResumeState, 123 (UINT32)PeiS3ResumeState->ReturnStackPointer 124 ); 125 } else { 126 // 127 // IA32 S3 Resume 128 // 129 DEBUG ((EFI_D_ERROR, "Call SwitchStack() to return to S3 Resume in PEI Phase\n")); 130 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl; 131 132 SwitchStack ( 133 (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint, 134 (VOID *)(UINTN)AcpiS3Context, 135 (VOID *)(UINTN)PeiS3ResumeState, 136 (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer 137 ); 138 } 139 140 // 141 // Never run to here 142 // 143 CpuDeadLoop(); 144 return EFI_UNSUPPORTED; 145 } 146 147 // 148 // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly 149 // 150 if (Facs->XFirmwareWakingVector != 0) { 151 // 152 // Switch to native waking vector 153 // 154 TempStackTop = (UINTN)&TempStack + sizeof(TempStack); 155 if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && 156 ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) && 157 ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) { 158 // 159 // X64 long mode waking vector 160 // 161 DEBUG (( EFI_D_ERROR, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); 162 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 163 SwitchStack ( 164 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector, 165 NULL, 166 NULL, 167 (VOID *)(UINTN)TempStackTop 168 ); 169 } else { 170 // Unsupported for 32bit DXE, 64bit OS vector 171 DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n")); 172 ASSERT (FALSE); 173 } 174 } else { 175 // 176 // IA32 protected mode waking vector (Page disabled) 177 // 178 DEBUG (( EFI_D_ERROR, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); 179 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 180 AsmDisablePaging64 ( 181 0x10, 182 (UINT32)Facs->XFirmwareWakingVector, 183 0, 184 0, 185 (UINT32)TempStackTop 186 ); 187 } else { 188 SwitchStack ( 189 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector, 190 NULL, 191 NULL, 192 (VOID *)(UINTN)TempStackTop 193 ); 194 } 195 } 196 } else { 197 // 198 // 16bit Realmode waking vector 199 // 200 DEBUG (( EFI_D_ERROR, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector)); 201 AsmTransferControl (Facs->FirmwareWakingVector, 0x0); 202 } 203 204 // 205 // Never run to here 206 // 207 CpuDeadLoop(); 208 return EFI_UNSUPPORTED; 209 } 210 211 /** 212 Register image to memory profile. 213 214 @param FileName File name of the image. 215 @param ImageBase Image base address. 216 @param ImageSize Image size. 217 @param FileType File type of the image. 218 219 **/ 220 VOID 221 RegisterMemoryProfileImage ( 222 IN EFI_GUID *FileName, 223 IN PHYSICAL_ADDRESS ImageBase, 224 IN UINT64 ImageSize, 225 IN EFI_FV_FILETYPE FileType 226 ) 227 { 228 EFI_STATUS Status; 229 EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol; 230 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; 231 UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)]; 232 233 if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) { 234 235 FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer; 236 Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **) &ProfileProtocol); 237 if (!EFI_ERROR (Status)) { 238 EfiInitializeFwVolDevicepathNode (FilePath, FileName); 239 SetDevicePathEndNode (FilePath + 1); 240 241 Status = ProfileProtocol->RegisterImage ( 242 ProfileProtocol, 243 (EFI_DEVICE_PATH_PROTOCOL *) FilePath, 244 ImageBase, 245 ImageSize, 246 FileType 247 ); 248 } 249 } 250 } 251 252 /** 253 This is the Event notification function to reload BootScriptExecutor image 254 to RESERVED mem and save it to LockBox. 255 256 @param Event Pointer to this event 257 @param Context Event handler private data 258 **/ 259 VOID 260 EFIAPI 261 ReadyToLockEventNotify ( 262 IN EFI_EVENT Event, 263 IN VOID *Context 264 ) 265 { 266 EFI_STATUS Status; 267 VOID *Interface; 268 UINT8 *Buffer; 269 UINTN BufferSize; 270 EFI_HANDLE NewImageHandle; 271 UINTN Pages; 272 EFI_PHYSICAL_ADDRESS FfsBuffer; 273 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; 274 275 Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); 276 if (EFI_ERROR (Status)) { 277 return; 278 } 279 280 // 281 // A workaround: Here we install a dummy handle 282 // 283 NewImageHandle = NULL; 284 Status = gBS->InstallProtocolInterface ( 285 &NewImageHandle, 286 &gEfiCallerIdGuid, 287 EFI_NATIVE_INTERFACE, 288 NULL 289 ); 290 ASSERT_EFI_ERROR (Status); 291 292 // 293 // Reload BootScriptExecutor image itself to RESERVED mem 294 // 295 Status = GetSectionFromAnyFv ( 296 &gEfiCallerIdGuid, 297 EFI_SECTION_PE32, 298 0, 299 (VOID **) &Buffer, 300 &BufferSize 301 ); 302 ASSERT_EFI_ERROR (Status); 303 ImageContext.Handle = Buffer; 304 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; 305 // 306 // Get information about the image being loaded 307 // 308 Status = PeCoffLoaderGetImageInfo (&ImageContext); 309 ASSERT_EFI_ERROR (Status); 310 if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { 311 Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); 312 } else { 313 Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); 314 } 315 FfsBuffer = 0xFFFFFFFF; 316 Status = gBS->AllocatePages ( 317 AllocateMaxAddress, 318 EfiReservedMemoryType, 319 Pages, 320 &FfsBuffer 321 ); 322 ASSERT_EFI_ERROR (Status); 323 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; 324 // 325 // Align buffer on section boundry 326 // 327 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; 328 ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); 329 // 330 // Load the image to our new buffer 331 // 332 Status = PeCoffLoaderLoadImage (&ImageContext); 333 ASSERT_EFI_ERROR (Status); 334 335 // 336 // Relocate the image in our new buffer 337 // 338 Status = PeCoffLoaderRelocateImage (&ImageContext); 339 ASSERT_EFI_ERROR (Status); 340 341 // 342 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer 343 // 344 gBS->FreePool (Buffer); 345 346 // 347 // Flush the instruction cache so the image data is written before we execute it 348 // 349 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); 350 351 RegisterMemoryProfileImage ( 352 &gEfiCallerIdGuid, 353 ImageContext.ImageAddress, 354 ImageContext.ImageSize, 355 EFI_FV_FILETYPE_DRIVER 356 ); 357 358 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); 359 ASSERT_EFI_ERROR (Status); 360 361 // 362 // Additional step for BootScript integrity 363 // Save BootScriptExecutor image 364 // 365 Status = SaveLockBox ( 366 &mBootScriptExecutorImageGuid, 367 (VOID *)(UINTN)ImageContext.ImageAddress, 368 (UINTN)ImageContext.ImageSize 369 ); 370 ASSERT_EFI_ERROR (Status); 371 372 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 373 ASSERT_EFI_ERROR (Status); 374 375 gBS->CloseEvent (Event); 376 } 377 378 /** 379 Entrypoint of Boot script exector driver, this function will be executed in 380 normal boot phase and invoked by DXE dispatch. 381 382 @param[in] ImageHandle The firmware allocated handle for the EFI image. 383 @param[in] SystemTable A pointer to the EFI System Table. 384 385 @retval EFI_SUCCESS The entry point is executed successfully. 386 @retval other Some error occurs when executing this entry point. 387 **/ 388 EFI_STATUS 389 EFIAPI 390 BootScriptExecutorEntryPoint ( 391 IN EFI_HANDLE ImageHandle, 392 IN EFI_SYSTEM_TABLE *SystemTable 393 ) 394 { 395 UINTN BufferSize; 396 UINTN Pages; 397 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; 398 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; 399 EFI_STATUS Status; 400 VOID *DevicePath; 401 EFI_EVENT ReadyToLockEvent; 402 VOID *Registration; 403 UINT32 RegEax; 404 UINT32 RegEdx; 405 406 // 407 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry 408 // point is loaded by DXE code which is the first time loaded. or else, it is already 409 // be reloaded be itself.This is a work-around 410 // 411 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); 412 if (EFI_ERROR (Status)) { 413 // 414 // Create ReadyToLock event to reload BootScriptExecutor image 415 // to RESERVED mem and save it to LockBox. 416 // 417 ReadyToLockEvent = EfiCreateProtocolNotifyEvent ( 418 &gEfiDxeSmmReadyToLockProtocolGuid, 419 TPL_NOTIFY, 420 ReadyToLockEventNotify, 421 NULL, 422 &Registration 423 ); 424 ASSERT (ReadyToLockEvent != NULL); 425 } else { 426 // 427 // the entry point is invoked after reloading. following code only run in RESERVED mem 428 // 429 if (PcdGetBool(PcdUse1GPageTable)) { 430 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 431 if (RegEax >= 0x80000001) { 432 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); 433 if ((RegEdx & BIT26) != 0) { 434 mPage1GSupport = TRUE; 435 } 436 } 437 } 438 439 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); 440 441 BootScriptExecutorBuffer = 0xFFFFFFFF; 442 Pages = EFI_SIZE_TO_PAGES(BufferSize); 443 Status = gBS->AllocatePages ( 444 AllocateMaxAddress, 445 EfiReservedMemoryType, 446 Pages, 447 &BootScriptExecutorBuffer 448 ); 449 ASSERT_EFI_ERROR (Status); 450 451 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer; 452 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ; 453 454 Status = SaveLockBox ( 455 &gEfiBootScriptExecutorVariableGuid, 456 &BootScriptExecutorBuffer, 457 sizeof(BootScriptExecutorBuffer) 458 ); 459 ASSERT_EFI_ERROR (Status); 460 461 // 462 // Additional step for BootScript integrity 463 // Save BootScriptExecutor context 464 // 465 Status = SaveLockBox ( 466 &gEfiBootScriptExecutorContextGuid, 467 EfiBootScriptExecutorVariable, 468 sizeof(*EfiBootScriptExecutorVariable) 469 ); 470 ASSERT_EFI_ERROR (Status); 471 472 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 473 ASSERT_EFI_ERROR (Status); 474 } 475 476 return EFI_SUCCESS; 477 } 478 479