/** @file | |
This is the code for Boot Script Executer module. | |
This driver is dispatched by Dxe core and the driver will reload itself to ACPI NVS memory | |
in the entry point. The functionality is to interpret and restore the S3 boot script | |
Copyright (c) 2013-2016 Intel Corporation. | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "ScriptExecute.h" | |
#pragma pack(1) | |
typedef union { | |
struct { | |
UINT32 LimitLow : 16; | |
UINT32 BaseLow : 16; | |
UINT32 BaseMid : 8; | |
UINT32 Type : 4; | |
UINT32 System : 1; | |
UINT32 Dpl : 2; | |
UINT32 Present : 1; | |
UINT32 LimitHigh : 4; | |
UINT32 Software : 1; | |
UINT32 Reserved : 1; | |
UINT32 DefaultSize : 1; | |
UINT32 Granularity : 1; | |
UINT32 BaseHigh : 8; | |
} Bits; | |
UINT64 Uint64; | |
} IA32_GDT; | |
#pragma pack() | |
EFI_GUID mBootScriptExecutorImageGuid = { | |
0x9a8d3433, 0x9fe8, 0x42b6, {0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b} | |
}; | |
// | |
// Global Descriptor Table (GDT) | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = { | |
/* selector { Global Segment Descriptor } */ | |
/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
/* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
/* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, | |
/* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, | |
/* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
/* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, | |
/* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, | |
/* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}}, | |
/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
}; | |
// | |
// IA32 Gdt register | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = { | |
sizeof (mGdtEntries) - 1, | |
(UINTN) mGdtEntries | |
}; | |
/** | |
Entry function of Boot script exector. This function will be executed in | |
S3 boot path. | |
This function should not return, because it is invoked by switch stack. | |
@param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT | |
@param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE | |
@retval EFI_INVALID_PARAMETER - OS waking vector not found | |
@retval EFI_UNSUPPORTED - something wrong when we resume to OS | |
**/ | |
EFI_STATUS | |
EFIAPI | |
S3BootScriptExecutorEntryFunction ( | |
IN ACPI_S3_CONTEXT *AcpiS3Context, | |
IN PEI_S3_RESUME_STATE *PeiS3ResumeState | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Disable interrupt of Debug timer, since new IDT table cannot handle it. | |
// | |
SaveAndSetDebugTimerInterrupt (FALSE); | |
// | |
// Restore IDT for debug | |
// | |
SetIdtEntry (AcpiS3Context); | |
// | |
// Initialize Debug Agent to support source level debug in S3 path. | |
// | |
InitializeDebugAgent (DEBUG_AGENT_INIT_S3, NULL, NULL); | |
// | |
// Because not install BootScriptExecute PPI(used just in this module), So just pass NULL | |
// for that parameter. | |
// | |
Status = S3BootScriptExecute (); | |
AsmWbinvd (); | |
// | |
// We need turn back to S3Resume - install boot script done ppi and report status code on S3resume. | |
// | |
if (PeiS3ResumeState != 0) { | |
// | |
// Need report status back to S3ResumePeim. | |
// If boot script execution is failed, S3ResumePeim wil report the error status code. | |
// | |
PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status; | |
// | |
// IA32 S3 Resume | |
// | |
DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n")); | |
PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)PlatformTransferControl16; | |
SwitchStack ( | |
(SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint, | |
(VOID *)(UINTN)AcpiS3Context, | |
(VOID *)(UINTN)PeiS3ResumeState, | |
(VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer | |
); | |
// | |
// Never run to here | |
// | |
CpuDeadLoop(); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Never run to here | |
// | |
CpuDeadLoop(); | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
Entrypoint of Boot script exector driver, this function will be executed in | |
normal boot phase and invoked by DXE dispatch. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BootScriptExecutorEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
UINT8 *Buffer; | |
UINTN BufferSize; | |
UINTN Pages; | |
EFI_PHYSICAL_ADDRESS FfsBuffer; | |
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; | |
BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; | |
EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; | |
EFI_STATUS Status; | |
VOID *DevicePath; | |
EFI_HANDLE NewImageHandle; | |
// | |
// Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry | |
// point is loaded by DXE code which is the first time loaded. or else, it is already | |
// be reloaded be itself.This is a work-around | |
// | |
Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); | |
if (EFI_ERROR (Status)) { | |
// | |
// This is the first-time loaded by DXE core. reload itself to NVS mem | |
// | |
// | |
// A workarouond: Here we install a dummy handle | |
// | |
NewImageHandle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&NewImageHandle, | |
&gEfiCallerIdGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
Status = GetSectionFromAnyFv ( | |
&gEfiCallerIdGuid, | |
EFI_SECTION_PE32, | |
0, | |
(VOID **) &Buffer, | |
&BufferSize | |
); | |
ImageContext.Handle = Buffer; | |
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; | |
// | |
// Get information about the image being loaded | |
// | |
Status = PeCoffLoaderGetImageInfo (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment); | |
FfsBuffer = 0xFFFFFFFF; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiACPIMemoryNVS, | |
Pages, | |
&FfsBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; | |
// | |
// Align buffer on section boundary | |
// | |
ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; | |
ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); | |
// | |
// Load the image to our new buffer | |
// | |
Status = PeCoffLoaderLoadImage (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
gBS->FreePages (FfsBuffer, Pages); | |
return Status; | |
} | |
// | |
// Relocate the image in our new buffer | |
// | |
Status = PeCoffLoaderRelocateImage (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
PeCoffLoaderUnloadImage (&ImageContext); | |
gBS->FreePages (FfsBuffer, Pages); | |
return Status; | |
} | |
// | |
// Flush the instruction cache so the image data is written before we execute it | |
// | |
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); | |
Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); | |
if (EFI_ERROR (Status)) { | |
gBS->FreePages (FfsBuffer, Pages); | |
return Status; | |
} | |
// | |
// Additional step for BootScript integrity | |
// Save BootScriptExecutor image | |
// | |
Status = SaveLockBox ( | |
&mBootScriptExecutorImageGuid, | |
(VOID *)(UINTN)ImageContext.ImageAddress, | |
(UINTN)ImageContext.ImageSize | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); | |
ASSERT_EFI_ERROR (Status); | |
} else { | |
// | |
// the entry point is invoked after reloading. following code only run in ACPI NVS | |
// | |
BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); | |
BootScriptExecutorBuffer = 0xFFFFFFFF; | |
Pages = EFI_SIZE_TO_PAGES(BufferSize); | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiACPIMemoryNVS, | |
Pages, | |
&BootScriptExecutorBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer; | |
EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ; | |
Status = SaveLockBox ( | |
&gEfiBootScriptExecutorVariableGuid, | |
&BootScriptExecutorBuffer, | |
sizeof(BootScriptExecutorBuffer) | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Additional step for BootScript integrity | |
// Save BootScriptExecutor context | |
// | |
Status = SaveLockBox ( | |
&gEfiBootScriptExecutorContextGuid, | |
EfiBootScriptExecutorVariable, | |
sizeof(*EfiBootScriptExecutorVariable) | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); | |
ASSERT_EFI_ERROR (Status); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Platform specific mechanism to transfer control to 16bit OS waking vector | |
@param[in] AcpiWakingVector The 16bit OS waking vector | |
@param[in] AcpiLowMemoryBase A buffer under 1M which could be used during the transfer | |
**/ | |
VOID | |
PlatformTransferControl16 ( | |
IN UINT32 AcpiWakingVector, | |
IN UINT32 AcpiLowMemoryBase | |
) | |
{ | |
UINT32 NewValue; | |
UINT64 BaseAddress; | |
UINT64 SmramLength; | |
UINTN Index; | |
DEBUG (( EFI_D_INFO, "PlatformTransferControl - Entry\r\n")); | |
// | |
// Need to make sure the GDT is loaded with values that support long mode and real mode. | |
// | |
AsmWriteGdtr (&mGdt); | |
// | |
// Disable eSram block (this will also clear/zero eSRAM) | |
// We only use eSRAM in the PEI phase. Disable now that we are resuming the OS | |
// | |
NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK); | |
NewValue |= BLOCK_DISABLE_PG; | |
QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK, NewValue); | |
// | |
// Update HMBOUND to top of DDR3 memory and LOCK | |
// We disabled eSRAM so now we move HMBOUND down to top of DDR3 | |
// | |
QNCGetTSEGMemoryRange (&BaseAddress, &SmramLength); | |
NewValue = (UINT32)(BaseAddress + SmramLength); | |
DEBUG ((EFI_D_INFO,"Locking HMBOUND at: = 0x%8x\n",NewValue)); | |
QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HMBOUND_REG, (NewValue | HMBOUND_LOCK)); | |
// | |
// Lock all IMR regions now that HMBOUND is locked | |
// | |
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) { | |
NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index); | |
NewValue |= IMR_LOCK; | |
QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index, NewValue); | |
} | |
// | |
// Call ASM routine to switch to real mode and jump to 16bit OS waking vector | |
// | |
AsmTransferControl(AcpiWakingVector, 0); | |
// | |
// Never run to here | |
// | |
CpuDeadLoop(); | |
} | |