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 - 2019, 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
)) {
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) ) {
107 // more step needed - because relative address is handled differently between X64 and IA32.
109 AsmTransferControl16Address
= (UINTN
)AsmTransferControl16
;
110 AsmFixAddress16
= (UINT32
)AsmTransferControl16Address
;
111 AsmJmpAddr32
= (UINT32
)((Facs
->FirmwareWakingVector
& 0xF) | ((Facs
->FirmwareWakingVector
& 0xFFFF0) << 12));
115 PeiS3ResumeState
->ReturnCs
,
116 (UINT32
)PeiS3ResumeState
->ReturnEntryPoint
,
117 (UINT32
)(UINTN
)AcpiS3Context
,
118 (UINT32
)(UINTN
)PeiS3ResumeState
,
119 (UINT32
)PeiS3ResumeState
->ReturnStackPointer
125 DEBUG ((DEBUG_INFO
, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
126 PeiS3ResumeState
->AsmTransferControl
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AsmTransferControl
;
129 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)PeiS3ResumeState
->ReturnEntryPoint
,
130 (VOID
*)(UINTN
)AcpiS3Context
,
131 (VOID
*)(UINTN
)PeiS3ResumeState
,
132 (VOID
*)(UINTN
)PeiS3ResumeState
->ReturnStackPointer
140 return EFI_UNSUPPORTED
;
144 // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly
146 if (Facs
->XFirmwareWakingVector
!= 0) {
148 // Switch to native waking vector
150 TempStackTop
= (UINTN
)&TempStack
+ sizeof(TempStack
);
151 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
152 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
153 ((Facs
->OspmFlags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
155 // X64 long mode waking vector
157 DEBUG ((DEBUG_INFO
, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
158 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
160 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
163 (VOID
*)(UINTN
)TempStackTop
166 // Unsupported for 32bit DXE, 64bit OS vector
167 DEBUG (( EFI_D_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
172 // IA32 protected mode waking vector (Page disabled)
174 DEBUG ((DEBUG_INFO
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
175 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
178 (UINT32
)Facs
->XFirmwareWakingVector
,
185 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
188 (VOID
*)(UINTN
)TempStackTop
194 // 16bit Realmode waking vector
196 DEBUG ((DEBUG_INFO
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
197 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
204 return EFI_UNSUPPORTED
;
208 Register image to memory profile.
210 @param FileName File name of the image.
211 @param ImageBase Image base address.
212 @param ImageSize Image size.
213 @param FileType File type of the image.
217 RegisterMemoryProfileImage (
218 IN EFI_GUID
*FileName
,
219 IN PHYSICAL_ADDRESS ImageBase
,
221 IN EFI_FV_FILETYPE FileType
225 EDKII_MEMORY_PROFILE_PROTOCOL
*ProfileProtocol
;
226 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*FilePath
;
227 UINT8 TempBuffer
[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
) + sizeof (EFI_DEVICE_PATH_PROTOCOL
)];
229 if ((PcdGet8 (PcdMemoryProfilePropertyMask
) & BIT0
) != 0) {
231 FilePath
= (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*)TempBuffer
;
232 Status
= gBS
->LocateProtocol (&gEdkiiMemoryProfileGuid
, NULL
, (VOID
**) &ProfileProtocol
);
233 if (!EFI_ERROR (Status
)) {
234 EfiInitializeFwVolDevicepathNode (FilePath
, FileName
);
235 SetDevicePathEndNode (FilePath
+ 1);
237 Status
= ProfileProtocol
->RegisterImage (
239 (EFI_DEVICE_PATH_PROTOCOL
*) FilePath
,
249 This is the Event notification function to reload BootScriptExecutor image
250 to RESERVED mem and save it to LockBox.
252 @param Event Pointer to this event
253 @param Context Event handler private data
257 ReadyToLockEventNotify (
266 EFI_HANDLE NewImageHandle
;
268 EFI_PHYSICAL_ADDRESS FfsBuffer
;
269 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext
;
270 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc
;
272 Status
= gBS
->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid
, NULL
, &Interface
);
273 if (EFI_ERROR (Status
)) {
278 // A workaround: Here we install a dummy handle
280 NewImageHandle
= NULL
;
281 Status
= gBS
->InstallProtocolInterface (
284 EFI_NATIVE_INTERFACE
,
287 ASSERT_EFI_ERROR (Status
);
290 // Reload BootScriptExecutor image itself to RESERVED mem
292 Status
= GetSectionFromAnyFv (
299 ASSERT_EFI_ERROR (Status
);
300 ImageContext
.Handle
= Buffer
;
301 ImageContext
.ImageRead
= PeCoffLoaderImageReadFromMemory
;
303 // Get information about the image being loaded
305 Status
= PeCoffLoaderGetImageInfo (&ImageContext
);
306 ASSERT_EFI_ERROR (Status
);
307 if (ImageContext
.SectionAlignment
> EFI_PAGE_SIZE
) {
308 Pages
= EFI_SIZE_TO_PAGES ((UINTN
) (ImageContext
.ImageSize
+ ImageContext
.SectionAlignment
));
310 Pages
= EFI_SIZE_TO_PAGES ((UINTN
) ImageContext
.ImageSize
);
312 FfsBuffer
= 0xFFFFFFFF;
313 Status
= gBS
->AllocatePages (
315 EfiReservedMemoryType
,
319 ASSERT_EFI_ERROR (Status
);
322 // Make sure that the buffer can be used to store code.
324 Status
= gDS
->GetMemorySpaceDescriptor (FfsBuffer
, &MemDesc
);
325 if (!EFI_ERROR (Status
) && (MemDesc
.Attributes
& EFI_MEMORY_XP
) != 0) {
326 gDS
->SetMemorySpaceAttributes (
328 EFI_PAGES_TO_SIZE (Pages
),
329 MemDesc
.Attributes
& (~EFI_MEMORY_XP
)
333 ImageContext
.ImageAddress
= (PHYSICAL_ADDRESS
)(UINTN
)FfsBuffer
;
335 // Align buffer on section boundary
337 ImageContext
.ImageAddress
+= ImageContext
.SectionAlignment
- 1;
338 ImageContext
.ImageAddress
&= ~((EFI_PHYSICAL_ADDRESS
)ImageContext
.SectionAlignment
- 1);
340 // Load the image to our new buffer
342 Status
= PeCoffLoaderLoadImage (&ImageContext
);
343 ASSERT_EFI_ERROR (Status
);
346 // Relocate the image in our new buffer
348 Status
= PeCoffLoaderRelocateImage (&ImageContext
);
349 ASSERT_EFI_ERROR (Status
);
352 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
354 gBS
->FreePool (Buffer
);
357 // Flush the instruction cache so the image data is written before we execute it
359 InvalidateInstructionCacheRange ((VOID
*)(UINTN
)ImageContext
.ImageAddress
, (UINTN
)ImageContext
.ImageSize
);
361 RegisterMemoryProfileImage (
363 ImageContext
.ImageAddress
,
364 ImageContext
.ImageSize
,
365 EFI_FV_FILETYPE_DRIVER
368 Status
= ((EFI_IMAGE_ENTRY_POINT
)(UINTN
)(ImageContext
.EntryPoint
)) (NewImageHandle
, gST
);
369 ASSERT_EFI_ERROR (Status
);
372 // Additional step for BootScript integrity
373 // Save BootScriptExecutor image
375 Status
= SaveLockBox (
376 &mBootScriptExecutorImageGuid
,
377 (VOID
*)(UINTN
)ImageContext
.ImageAddress
,
378 (UINTN
)ImageContext
.ImageSize
380 ASSERT_EFI_ERROR (Status
);
382 Status
= SetLockBoxAttributes (&mBootScriptExecutorImageGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
383 ASSERT_EFI_ERROR (Status
);
385 gBS
->CloseEvent (Event
);
389 Entrypoint of Boot script exector driver, this function will be executed in
390 normal boot phase and invoked by DXE dispatch.
392 @param[in] ImageHandle The firmware allocated handle for the EFI image.
393 @param[in] SystemTable A pointer to the EFI System Table.
395 @retval EFI_SUCCESS The entry point is executed successfully.
396 @retval other Some error occurs when executing this entry point.
400 BootScriptExecutorEntryPoint (
401 IN EFI_HANDLE ImageHandle
,
402 IN EFI_SYSTEM_TABLE
*SystemTable
407 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
408 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer
;
411 EFI_EVENT ReadyToLockEvent
;
416 if (!PcdGetBool (PcdAcpiS3Enable
)) {
417 return EFI_UNSUPPORTED
;
421 // Make sure AddressEncMask is contained to smallest supported address field.
423 mAddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
426 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
427 // point is loaded by DXE code which is the first time loaded. or else, it is already
428 // be reloaded be itself.This is a work-around
430 Status
= gBS
->LocateProtocol (&gEfiCallerIdGuid
, NULL
, &DevicePath
);
431 if (EFI_ERROR (Status
)) {
433 // Create ReadyToLock event to reload BootScriptExecutor image
434 // to RESERVED mem and save it to LockBox.
436 ReadyToLockEvent
= EfiCreateProtocolNotifyEvent (
437 &gEfiDxeSmmReadyToLockProtocolGuid
,
439 ReadyToLockEventNotify
,
443 ASSERT (ReadyToLockEvent
!= NULL
);
446 // the entry point is invoked after reloading. following code only run in RESERVED mem
448 if (PcdGetBool(PcdUse1GPageTable
)) {
449 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
450 if (RegEax
>= 0x80000001) {
451 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
452 if ((RegEdx
& BIT26
) != 0) {
453 mPage1GSupport
= TRUE
;
458 BufferSize
= sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE
);
460 BootScriptExecutorBuffer
= 0xFFFFFFFF;
461 Pages
= EFI_SIZE_TO_PAGES(BufferSize
);
462 Status
= gBS
->AllocatePages (
464 EfiReservedMemoryType
,
466 &BootScriptExecutorBuffer
468 ASSERT_EFI_ERROR (Status
);
470 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*)(UINTN
)BootScriptExecutorBuffer
;
471 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
= (UINTN
) S3BootScriptExecutorEntryFunction
;
473 Status
= SaveLockBox (
474 &gEfiBootScriptExecutorVariableGuid
,
475 &BootScriptExecutorBuffer
,
476 sizeof(BootScriptExecutorBuffer
)
478 ASSERT_EFI_ERROR (Status
);
481 // Additional step for BootScript integrity
482 // Save BootScriptExecutor context
484 Status
= SaveLockBox (
485 &gEfiBootScriptExecutorContextGuid
,
486 EfiBootScriptExecutorVariable
,
487 sizeof(*EfiBootScriptExecutorVariable
)
489 ASSERT_EFI_ERROR (Status
);
491 Status
= SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
492 ASSERT_EFI_ERROR (Status
);