mirror of https://github.com/acidanthera/audk.git
386 lines
13 KiB
C
386 lines
13 KiB
C
/** @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();
|
|
}
|
|
|
|
|
|
|
|
|