+/** @file\r
+ This is the code for Boot Script Executer module.\r
+\r
+ This driver is dispatched by Dxe core and the driver will reload itself to ACPI NVS memory\r
+ in the entry point. The functionality is to interpret and restore the S3 boot script\r
+\r
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "ScriptExecute.h"\r
+\r
+EFI_PHYSICAL_ADDRESS mPerfDataMemAddress;\r
+UINT64 mS3BootScriptEntryTick;\r
+UINT64 mScriptStartTick;\r
+UINT64 mScriptEndTick;\r
+\r
+EFI_GUID mBootScriptExecutorImageGuid = {\r
+ 0x9a8d3433, 0x9fe8, 0x42b6, 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b\r
+};\r
+\r
+/**\r
+ The event callback is used to get the base address of boot performance data structure on\r
+ LegacyBoot event and ExitBootServices event.\r
+\r
+ @param Event The event handle.\r
+ @param Context The event context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+OnBootEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN VarSize;\r
+\r
+ VarSize = sizeof (EFI_PHYSICAL_ADDRESS);\r
+ Status = gRT->GetVariable (\r
+ L"PerfDataMemAddr",\r
+ &gPerformanceProtocolGuid,\r
+ NULL,\r
+ &VarSize,\r
+ &mPerfDataMemAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ mPerfDataMemAddress = 0;\r
+ }\r
+}\r
+\r
+/**\r
+ Record S3 Script execution time and adjust total S3 resume time for script running.\r
+**/\r
+VOID\r
+WriteToOsS3PerformanceData (\r
+ VOID\r
+ )\r
+{\r
+ UINT64 Ticker;\r
+ UINT64 StartValue;\r
+ UINT64 EndValue;\r
+ UINT64 Freq;\r
+ UINT64 ScriptExecuteTicks;\r
+ PERF_HEADER *PerfHeader;\r
+ PERF_DATA *PerfData;\r
+\r
+ Ticker = GetPerformanceCounter ();\r
+\r
+ PerfHeader = (PERF_HEADER *)(UINTN)mPerfDataMemAddress;\r
+ if (PerfHeader == NULL) {\r
+ return;\r
+ }\r
+\r
+ Freq = GetPerformanceCounterProperties (&StartValue, &EndValue);\r
+ Freq = DivU64x32 (Freq, 1000);\r
+\r
+ if (EndValue >= StartValue) {\r
+ ScriptExecuteTicks = mScriptEndTick - mScriptStartTick;\r
+ PerfHeader->S3Resume += Ticker - mS3BootScriptEntryTick;\r
+ } else {\r
+ ScriptExecuteTicks = mScriptStartTick - mScriptEndTick;\r
+ PerfHeader->S3Resume += mS3BootScriptEntryTick - Ticker;\r
+ }\r
+ if (PerfHeader->S3EntryNum < PERF_PEI_ENTRY_MAX_NUM) {\r
+ PerfData = &PerfHeader->S3Entry[PerfHeader->S3EntryNum];\r
+ PerfData->Duration = (UINT32) DivU64x32 (ScriptExecuteTicks, (UINT32) Freq);;\r
+ AsciiStrnCpy (PerfData->Token, "ScriptExec", PERF_TOKEN_LENGTH);\r
+ PerfHeader->S3EntryNum++;\r
+ }\r
+}\r
+\r
+/**\r
+ Entry function of Boot script exector. This function will be executed in\r
+ S3 boot path.\r
+ This function should not return, because it is invoked by switch stack.\r
+\r
+ @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT\r
+ @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE\r
+\r
+ @retval EFI_INVALID_PARAMETER - OS waking vector not found\r
+ @retval EFI_UNSUPPORTED - something wrong when we resume to OS\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+S3BootScriptExecutorEntryFunction (\r
+ IN ACPI_S3_CONTEXT *AcpiS3Context,\r
+ IN PEI_S3_RESUME_STATE *PeiS3ResumeState\r
+ )\r
+{\r
+ EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
+ EFI_STATUS Status;\r
+ UINTN TempStackTop;\r
+ UINTN TempStack[0x10];\r
+ UINTN AsmTransferControl16Address;\r
+\r
+ PERF_CODE (\r
+ mS3BootScriptEntryTick = GetPerformanceCounter ();\r
+ );\r
+\r
+ //\r
+ // Disable interrupt of Debug timer, since new IDT table cannot handle it.\r
+ //\r
+ SaveAndSetDebugTimerInterrupt (FALSE);\r
+\r
+ //\r
+ // Restore IDT for debug\r
+ //\r
+ SetIdtEntry (AcpiS3Context);\r
+\r
+ //\r
+ // Initialize Debug Agent to support source level debug in S3 path.\r
+ //\r
+ InitializeDebugAgent (DEBUG_AGENT_INIT_S3, NULL, NULL);\r
+\r
+ //\r
+ // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL\r
+ // for that parameter.\r
+ //\r
+ PERF_CODE (\r
+ mScriptStartTick = GetPerformanceCounter ();\r
+ );\r
+ Status = S3BootScriptExecute ();\r
+ PERF_CODE (\r
+ mScriptEndTick = GetPerformanceCounter ();\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ AsmWbinvd ();\r
+\r
+ //\r
+ // Get ACPI Table Address\r
+ //\r
+ Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));\r
+\r
+ if ((Facs == NULL) ||\r
+ (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ||\r
+ ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) {\r
+ CpuDeadLoop();\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.\r
+ //\r
+ if (PeiS3ResumeState != 0) {\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ //\r
+ // X64 S3 Resume\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));\r
+ PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl32;\r
+\r
+ //\r
+ // more step needed - because relative address is handled differently between X64 and IA32.\r
+ //\r
+ AsmTransferControl16Address = (UINTN)AsmTransferControl16;\r
+ AsmFixAddress16 = (UINT32)AsmTransferControl16Address;\r
+ AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12));\r
+\r
+ AsmDisablePaging64 (\r
+ PeiS3ResumeState->ReturnCs,\r
+ (UINT32)PeiS3ResumeState->ReturnEntryPoint,\r
+ (UINT32)(UINTN)AcpiS3Context,\r
+ (UINT32)(UINTN)PeiS3ResumeState,\r
+ (UINT32)PeiS3ResumeState->ReturnStackPointer\r
+ );\r
+ } else {\r
+ //\r
+ // IA32 S3 Resume\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));\r
+ PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl;\r
+\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,\r
+ (VOID *)(UINTN)AcpiS3Context,\r
+ (VOID *)(UINTN)PeiS3ResumeState,\r
+ (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer\r
+ );\r
+ }\r
+\r
+ //\r
+ // Never run to here\r
+ //\r
+ CpuDeadLoop();\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ PERF_CODE (\r
+ WriteToOsS3PerformanceData ();\r
+ );\r
+\r
+ if (Facs->XFirmwareWakingVector != 0) {\r
+ //\r
+ // Switch to native waking vector\r
+ //\r
+ TempStackTop = (UINTN)&TempStack + sizeof(TempStack);\r
+ if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&\r
+ ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&\r
+ ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {\r
+ //\r
+ // X64 long mode waking vector\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,\r
+ NULL,\r
+ NULL,\r
+ (VOID *)(UINTN)TempStackTop\r
+ );\r
+ } else {\r
+ // Unsupported for 32bit DXE, 64bit OS vector\r
+ DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));\r
+ ASSERT (FALSE);\r
+ }\r
+ } else {\r
+ //\r
+ // IA32 protected mode waking vector (Page disabled)\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ AsmDisablePaging64 (\r
+ 0x10,\r
+ (UINT32)Facs->XFirmwareWakingVector,\r
+ 0,\r
+ 0,\r
+ (UINT32)TempStackTop\r
+ );\r
+ } else {\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,\r
+ NULL,\r
+ NULL,\r
+ (VOID *)(UINTN)TempStackTop\r
+ );\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // 16bit Realmode waking vector\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector));\r
+ AsmTransferControl (Facs->FirmwareWakingVector, 0x0);\r
+ }\r
+\r
+ //\r
+ // Never run to here\r
+ //\r
+ CpuDeadLoop();\r
+ return EFI_UNSUPPORTED;\r
+}\r
+/**\r
+ Entrypoint of Boot script exector driver, this function will be executed in\r
+ normal boot phase and invoked by DXE dispatch.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point is executed successfully.\r
+ @retval other Some error occurs when executing this entry point.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootScriptExecutorEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ UINT8 *Buffer;\r
+ UINTN BufferSize;\r
+ UINTN Pages;\r
+ EFI_PHYSICAL_ADDRESS FfsBuffer;\r
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
+ BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;\r
+ EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer;\r
+ EFI_STATUS Status;\r
+ VOID *DevicePath;\r
+ EFI_HANDLE NewImageHandle;\r
+\r
+ //\r
+ // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry\r
+ // point is loaded by DXE code which is the first time loaded. or else, it is already\r
+ // be reloaded be itself.This is a work-around\r
+ //\r
+ Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ //\r
+ // This is the first-time loaded by DXE core. reload itself to NVS mem\r
+ //\r
+ //\r
+ // A workarouond: Here we install a dummy handle\r
+ //\r
+ NewImageHandle = NULL;\r
+ Status = gBS->InstallProtocolInterface (\r
+ &NewImageHandle,\r
+ &gEfiCallerIdGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ NULL\r
+ );\r
+\r
+ Status = GetSectionFromAnyFv (\r
+ &gEfiCallerIdGuid,\r
+ EFI_SECTION_PE32,\r
+ 0,\r
+ (VOID **) &Buffer,\r
+ &BufferSize\r
+ );\r
+ ImageContext.Handle = Buffer;\r
+ ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;\r
+ //\r
+ // Get information about the image being loaded\r
+ //\r
+ Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment);\r
+ FfsBuffer = 0xFFFFFFFF;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiACPIMemoryNVS,\r
+ Pages,\r
+ &FfsBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;\r
+ //\r
+ // Align buffer on section boundry\r
+ //\r
+ ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;\r
+ ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);\r
+ //\r
+ // Load the image to our new buffer\r
+ //\r
+ Status = PeCoffLoaderLoadImage (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (FfsBuffer, Pages);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Relocate the image in our new buffer\r
+ //\r
+ Status = PeCoffLoaderRelocateImage (&ImageContext);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ PeCoffLoaderUnloadImage (&ImageContext);\r
+ gBS->FreePages (FfsBuffer, Pages);\r
+ return Status;\r
+ }\r
+ //\r
+ // Flush the instruction cache so the image data is written before we execute it\r
+ //\r
+ InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);\r
+ Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (FfsBuffer, Pages);\r
+ return Status;\r
+ }\r
+ //\r
+ // Additional step for BootScript integrity\r
+ // Save BootScriptExecutor image\r
+ //\r
+ Status = SaveLockBox (\r
+ &mBootScriptExecutorImageGuid,\r
+ (VOID *)(UINTN)ImageContext.ImageAddress,\r
+ (UINTN)ImageContext.ImageSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ } else {\r
+ //\r
+ // the entry point is invoked after reloading. following code only run in ACPI NVS\r
+ //\r
+ BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);\r
+\r
+ BootScriptExecutorBuffer = 0xFFFFFFFF;\r
+ Pages = EFI_SIZE_TO_PAGES(BufferSize);\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiACPIMemoryNVS,\r
+ Pages,\r
+ &BootScriptExecutorBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;\r
+ EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;\r
+\r
+ Status = SaveLockBox (\r
+ &gEfiBootScriptExecutorVariableGuid,\r
+ &BootScriptExecutorBuffer,\r
+ sizeof(BootScriptExecutorBuffer)\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Additional step for BootScript integrity\r
+ // Save BootScriptExecutor context\r
+ //\r
+ Status = SaveLockBox (\r
+ &gEfiBootScriptExecutorContextGuid,\r
+ EfiBootScriptExecutorVariable,\r
+ sizeof(*EfiBootScriptExecutorVariable)\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ PERF_CODE (\r
+ EFI_EVENT Event;\r
+\r
+ gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ OnBootEvent,\r
+ NULL,\r
+ &gEfiEventExitBootServicesGuid,\r
+ &Event\r
+ );\r
+\r
+ EfiCreateEventLegacyBootEx(\r
+ TPL_NOTIFY,\r
+ OnBootEvent,\r
+ NULL,\r
+ &Event\r
+ );\r
+ );\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r