2 This is the code for Boot Script Executer module.
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
7 Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
14 #include "ScriptExecute.h"
16 EFI_GUID mBootScriptExecutorImageGuid
= {
17 0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b }
20 BOOLEAN mPage1GSupport
= FALSE
;
21 UINT64 mAddressEncMask
= 0;
24 Entry function of Boot script exector. This function will be executed in
26 This function should not return, because it is invoked by switch stack.
28 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
29 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
31 @retval EFI_INVALID_PARAMETER - OS waking vector not found
32 @retval EFI_UNSUPPORTED - something wrong when we resume to OS
36 S3BootScriptExecutorEntryFunction (
37 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
38 IN PEI_S3_RESUME_STATE
*PeiS3ResumeState
41 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
44 UINTN TempStack
[0x10];
45 UINTN AsmTransferControl16Address
;
46 IA32_DESCRIPTOR IdtDescriptor
;
49 // Disable interrupt of Debug timer, since new IDT table cannot handle it.
51 SaveAndSetDebugTimerInterrupt (FALSE
);
53 AsmReadIdtr (&IdtDescriptor
);
55 // Restore IDT for debug
57 SetIdtEntry (AcpiS3Context
);
60 // Initialize Debug Agent to support source level debug in S3 path, it will disable interrupt and Debug Timer.
62 InitializeDebugAgent (DEBUG_AGENT_INIT_S3
, (VOID
*)&IdtDescriptor
, NULL
);
65 // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL
66 // for that parameter.
68 Status
= S3BootScriptExecute ();
71 // If invalid script table or opcode in S3 boot script table.
73 ASSERT_EFI_ERROR (Status
);
75 if (EFI_ERROR (Status
)) {
83 // Get ACPI Table Address
85 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)((UINTN
)(AcpiS3Context
->AcpiFacsTable
));
88 // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.
90 if (PeiS3ResumeState
!= 0) {
92 // Need report status back to S3ResumePeim.
93 // If boot script execution is failed, S3ResumePeim wil report the error status code.
95 PeiS3ResumeState
->ReturnStatus
= (UINT64
)(UINTN
)Status
;
96 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
98 // X64 DXE to IA32 PEI S3 Resume
100 DEBUG ((DEBUG_INFO
, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));
101 PeiS3ResumeState
->AsmTransferControl
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AsmTransferControl32
;
103 if ((Facs
!= NULL
) &&
104 (Facs
->Signature
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) &&
105 (Facs
->FirmwareWakingVector
!= 0))
108 // more step needed - because relative address is handled differently between X64 and IA32.
110 AsmTransferControl16Address
= (UINTN
)AsmTransferControl16
;
111 AsmFixAddress16
= (UINT32
)AsmTransferControl16Address
;
112 AsmJmpAddr32
= (UINT32
)((Facs
->FirmwareWakingVector
& 0xF) | ((Facs
->FirmwareWakingVector
& 0xFFFF0) << 12));
116 PeiS3ResumeState
->ReturnCs
,
117 (UINT32
)PeiS3ResumeState
->ReturnEntryPoint
,
118 (UINT32
)(UINTN
)AcpiS3Context
,
119 (UINT32
)(UINTN
)PeiS3ResumeState
,
120 (UINT32
)PeiS3ResumeState
->ReturnStackPointer
124 // IA32 DXE to IA32 PEI S3 Resume / X64 DXE to X64 PEI S3 Resume
126 DEBUG ((DEBUG_INFO
, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
127 PeiS3ResumeState
->AsmTransferControl
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AsmTransferControl
;
130 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)PeiS3ResumeState
->ReturnEntryPoint
,
131 (VOID
*)(UINTN
)AcpiS3Context
,
132 (VOID
*)(UINTN
)PeiS3ResumeState
,
133 (VOID
*)(UINTN
)PeiS3ResumeState
->ReturnStackPointer
141 return EFI_UNSUPPORTED
;
145 // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly
147 if (Facs
->XFirmwareWakingVector
!= 0) {
149 // Switch to native waking vector
151 TempStackTop
= (UINTN
)&TempStack
+ sizeof (TempStack
);
152 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
153 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
154 ((Facs
->OspmFlags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0))
157 // X64 long mode waking vector
159 DEBUG ((DEBUG_INFO
, "Transfer from 64bit DXE to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
160 if (sizeof (UINTN
) == sizeof (UINT64
)) {
162 // 64bit DXE calls to 64bit OS S3 waking vector
165 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
168 (VOID
*)(UINTN
)TempStackTop
171 // Unsupported for 32bit DXE, 64bit OS vector
172 DEBUG ((DEBUG_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
177 // IA32 protected mode waking vector (Page disabled)
179 DEBUG ((DEBUG_INFO
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
180 if (sizeof (UINTN
) == sizeof (UINT64
)) {
182 // 64bit DXE calls to 32bit OS S3 waking vector
186 (UINT32
)Facs
->XFirmwareWakingVector
,
193 // 32bit DXE calls to 32bit OS S3 waking vector
196 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
199 (VOID
*)(UINTN
)TempStackTop
205 // 16bit Realmode waking vector
207 DEBUG ((DEBUG_INFO
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
208 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
215 return EFI_UNSUPPORTED
;
219 Register image to memory profile.
221 @param FileName File name of the image.
222 @param ImageBase Image base address.
223 @param ImageSize Image size.
224 @param FileType File type of the image.
228 RegisterMemoryProfileImage (
229 IN EFI_GUID
*FileName
,
230 IN PHYSICAL_ADDRESS ImageBase
,
232 IN EFI_FV_FILETYPE FileType
236 EDKII_MEMORY_PROFILE_PROTOCOL
*ProfileProtocol
;
237 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*FilePath
;
238 UINT8 TempBuffer
[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
) + sizeof (EFI_DEVICE_PATH_PROTOCOL
)];
240 if ((PcdGet8 (PcdMemoryProfilePropertyMask
) & BIT0
) != 0) {
241 FilePath
= (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*)TempBuffer
;
242 Status
= gBS
->LocateProtocol (&gEdkiiMemoryProfileGuid
, NULL
, (VOID
**)&ProfileProtocol
);
243 if (!EFI_ERROR (Status
)) {
244 EfiInitializeFwVolDevicepathNode (FilePath
, FileName
);
245 SetDevicePathEndNode (FilePath
+ 1);
247 Status
= ProfileProtocol
->RegisterImage (
249 (EFI_DEVICE_PATH_PROTOCOL
*)FilePath
,
259 This is the Event notification function to reload BootScriptExecutor image
260 to RESERVED mem and save it to LockBox.
262 @param Event Pointer to this event
263 @param Context Event handler private data
267 ReadyToLockEventNotify (
276 EFI_HANDLE NewImageHandle
;
278 EFI_PHYSICAL_ADDRESS FfsBuffer
;
279 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext
;
280 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc
;
282 Status
= gBS
->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid
, NULL
, &Interface
);
283 if (EFI_ERROR (Status
)) {
288 // A workaround: Here we install a dummy handle
290 NewImageHandle
= NULL
;
291 Status
= gBS
->InstallProtocolInterface (
294 EFI_NATIVE_INTERFACE
,
297 ASSERT_EFI_ERROR (Status
);
300 // Reload BootScriptExecutor image itself to RESERVED mem
302 Status
= GetSectionFromAnyFv (
309 ASSERT_EFI_ERROR (Status
);
310 ImageContext
.Handle
= Buffer
;
311 ImageContext
.ImageRead
= PeCoffLoaderImageReadFromMemory
;
313 // Get information about the image being loaded
315 Status
= PeCoffLoaderGetImageInfo (&ImageContext
);
316 ASSERT_EFI_ERROR (Status
);
317 if (ImageContext
.SectionAlignment
> EFI_PAGE_SIZE
) {
318 Pages
= EFI_SIZE_TO_PAGES ((UINTN
)(ImageContext
.ImageSize
+ ImageContext
.SectionAlignment
));
320 Pages
= EFI_SIZE_TO_PAGES ((UINTN
)ImageContext
.ImageSize
);
323 FfsBuffer
= 0xFFFFFFFF;
324 Status
= gBS
->AllocatePages (
326 EfiReservedMemoryType
,
330 ASSERT_EFI_ERROR (Status
);
333 // Make sure that the buffer can be used to store code.
335 Status
= gDS
->GetMemorySpaceDescriptor (FfsBuffer
, &MemDesc
);
336 if (!EFI_ERROR (Status
) && ((MemDesc
.Attributes
& EFI_MEMORY_XP
) != 0)) {
337 gDS
->SetMemorySpaceAttributes (
339 EFI_PAGES_TO_SIZE (Pages
),
340 MemDesc
.Attributes
& (~EFI_MEMORY_XP
)
344 ImageContext
.ImageAddress
= (PHYSICAL_ADDRESS
)(UINTN
)FfsBuffer
;
346 // Align buffer on section boundary
348 ImageContext
.ImageAddress
+= ImageContext
.SectionAlignment
- 1;
349 ImageContext
.ImageAddress
&= ~((EFI_PHYSICAL_ADDRESS
)ImageContext
.SectionAlignment
- 1);
351 // Load the image to our new buffer
353 Status
= PeCoffLoaderLoadImage (&ImageContext
);
354 ASSERT_EFI_ERROR (Status
);
357 // Relocate the image in our new buffer
359 Status
= PeCoffLoaderRelocateImage (&ImageContext
);
360 ASSERT_EFI_ERROR (Status
);
363 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
365 gBS
->FreePool (Buffer
);
368 // Flush the instruction cache so the image data is written before we execute it
370 InvalidateInstructionCacheRange ((VOID
*)(UINTN
)ImageContext
.ImageAddress
, (UINTN
)ImageContext
.ImageSize
);
372 RegisterMemoryProfileImage (
374 ImageContext
.ImageAddress
,
375 ImageContext
.ImageSize
,
376 EFI_FV_FILETYPE_DRIVER
379 Status
= ((EFI_IMAGE_ENTRY_POINT
)(UINTN
)(ImageContext
.EntryPoint
))(NewImageHandle
, gST
);
380 ASSERT_EFI_ERROR (Status
);
383 // Additional step for BootScript integrity
384 // Save BootScriptExecutor image
386 Status
= SaveLockBox (
387 &mBootScriptExecutorImageGuid
,
388 (VOID
*)(UINTN
)ImageContext
.ImageAddress
,
389 (UINTN
)ImageContext
.ImageSize
391 ASSERT_EFI_ERROR (Status
);
393 Status
= SetLockBoxAttributes (&mBootScriptExecutorImageGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
394 ASSERT_EFI_ERROR (Status
);
396 gBS
->CloseEvent (Event
);
400 Entrypoint of Boot script exector driver, this function will be executed in
401 normal boot phase and invoked by DXE dispatch.
403 @param[in] ImageHandle The firmware allocated handle for the EFI image.
404 @param[in] SystemTable A pointer to the EFI System Table.
406 @retval EFI_SUCCESS The entry point is executed successfully.
407 @retval other Some error occurs when executing this entry point.
411 BootScriptExecutorEntryPoint (
412 IN EFI_HANDLE ImageHandle
,
413 IN EFI_SYSTEM_TABLE
*SystemTable
418 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
419 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer
;
422 EFI_EVENT ReadyToLockEvent
;
427 if (!PcdGetBool (PcdAcpiS3Enable
)) {
428 return EFI_UNSUPPORTED
;
432 // Make sure AddressEncMask is contained to smallest supported address field.
434 mAddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
437 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
438 // point is loaded by DXE code which is the first time loaded. or else, it is already
439 // be reloaded be itself.This is a work-around
441 Status
= gBS
->LocateProtocol (&gEfiCallerIdGuid
, NULL
, &DevicePath
);
442 if (EFI_ERROR (Status
)) {
444 // Create ReadyToLock event to reload BootScriptExecutor image
445 // to RESERVED mem and save it to LockBox.
447 ReadyToLockEvent
= EfiCreateProtocolNotifyEvent (
448 &gEfiDxeSmmReadyToLockProtocolGuid
,
450 ReadyToLockEventNotify
,
454 ASSERT (ReadyToLockEvent
!= NULL
);
457 // the entry point is invoked after reloading. following code only run in RESERVED mem
459 if (PcdGetBool (PcdUse1GPageTable
)) {
460 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
461 if (RegEax
>= 0x80000001) {
462 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
463 if ((RegEdx
& BIT26
) != 0) {
464 mPage1GSupport
= TRUE
;
469 BufferSize
= sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE
);
471 BootScriptExecutorBuffer
= 0xFFFFFFFF;
472 Pages
= EFI_SIZE_TO_PAGES (BufferSize
);
473 Status
= gBS
->AllocatePages (
475 EfiReservedMemoryType
,
477 &BootScriptExecutorBuffer
479 ASSERT_EFI_ERROR (Status
);
481 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*)(UINTN
)BootScriptExecutorBuffer
;
482 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
= (UINTN
)S3BootScriptExecutorEntryFunction
;
484 Status
= SaveLockBox (
485 &gEfiBootScriptExecutorVariableGuid
,
486 &BootScriptExecutorBuffer
,
487 sizeof (BootScriptExecutorBuffer
)
489 ASSERT_EFI_ERROR (Status
);
492 // Additional step for BootScript integrity
493 // Save BootScriptExecutor context
495 Status
= SaveLockBox (
496 &gEfiBootScriptExecutorContextGuid
,
497 EfiBootScriptExecutorVariable
,
498 sizeof (*EfiBootScriptExecutorVariable
)
500 ASSERT_EFI_ERROR (Status
);
502 Status
= SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
503 ASSERT_EFI_ERROR (Status
);