Files
Bejean Mosher 0bcefec921 feat: Support calling into x64 FSP
FSP 2.4 introduces the possibility of FSP built for x64 architecture.
This adds support for x64 FSP calling conventions based on the header x64
support attribute. Support for x64 FSP-T requires entering long mode
prior to FSP-T execution.

Signed-off-by: Bejean Mosher <bejean.mosher@intel.com>
2025-02-06 10:08:15 -07:00

263 lines
10 KiB
C

/** @file
Copyright (c) 2017 - 2023, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <FspApiLibInternal.h>
#include <Library/FspApiLib.h>
#include <Library/BoardInitLib.h>
#include <Library/BootloaderCoreLib.h>
#include <Library/MemoryAllocationLib.h>
/**
This FSP API is called after TempRamInit and initializes the memory.
This FSP API accepts a pointer to a data structure that will be platform dependent
and defined for each FSP binary. This will be documented in Integration guide with
each FSP release.
After FspMemInit completes its execution, it passes the pointer to the HobList and
returns to the boot loader from where it was called. BootLoader is responsible to
migrate it's stack and data to Memory.
FspMemoryInit, TempRamExit and FspSiliconInit APIs provide an alternate method to
complete the silicon initialization and provides bootloader an opportunity to get
control after system memory is available and before the temporary RAM is torn down.
@param[in] FspmBase The base address of FSPM.
@param[out] HobList Pointer to receive the address of the HOB list.
@retval EFI_SUCCESS FSP execution environment was initialized successfully.
@retval EFI_INVALID_PARAMETER Input parameters are invalid.
@retval EFI_UNSUPPORTED The FSP calling conditions were not met.
@retval EFI_DEVICE_ERROR FSP initialization failed.
@retval EFI_OUT_OF_RESOURCES Stack range requested by FSP is not met.
@retval FSP_STATUS_RESET_REQUIREDx A reset is reuired. These status codes will not be returned during S3.
**/
EFI_STATUS
EFIAPI
CallFspMemoryInit (
IN UINT32 FspmBase,
OUT VOID **HobList
)
{
UINT8 *FspmUpdPtr;
UINT8 *DefaultMemoryInitUpd;
FSP_INFO_HEADER *FspHeader;
FSP_MEMORY_INIT FspMemoryInit;
FSPM_UPD_COMMON *FspmUpdCommon;
FSPM_UPD_COMMON_FSP24 *FspmUpdCommon24;
EFI_STATUS Status;
UINTN NewStack;
FspHeader = (FSP_INFO_HEADER *)(UINTN)(FspmBase + FSP_INFO_HEADER_OFF);
ASSERT (FspHeader->Signature == FSP_INFO_HEADER_SIGNATURE);
ASSERT (FspHeader->ImageBase == FspmBase);
FspmUpdPtr = AllocatePool(FspHeader->CfgRegionSize);
if (FspmUpdPtr == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// Copy default UPD data
DefaultMemoryInitUpd = (UINT8 *)(UINTN)(FspHeader->ImageBase + FspHeader->CfgRegionOffset);
CopyMem (FspmUpdPtr, DefaultMemoryInitUpd, FspHeader->CfgRegionSize);
FspmUpdCommon = (FSPM_UPD_COMMON *)FspmUpdPtr;
/* Update architectural UPD fields */
if (FspmUpdCommon->FspUpdHeader.Revision < FSP_HEADER_REVISION_3) {
FspmUpdCommon->FspmArchUpd.BootLoaderTolumSize = 0;
FspmUpdCommon->FspmArchUpd.BootMode = (UINT32)GetBootMode();
FspmUpdCommon->FspmArchUpd.NvsBufferPtr = (UINT32)(UINTN)FindNvsData();
NewStack = PcdGet32 (PcdFSPMStackTop);
if (NewStack == 0xFFFFFFFF) {
NewStack = (UINT32)FspmUpdCommon->FspmArchUpd.StackBase + (UINT32)FspmUpdCommon->FspmArchUpd.StackSize;
}
} else {
FspmUpdCommon24 = (FSPM_UPD_COMMON_FSP24 *)FspmUpdPtr;
FspmUpdCommon24->FspmArchUpd.BootLoaderTolumSize = 0;
FspmUpdCommon24->FspmArchUpd.BootMode = (UINT32)GetBootMode();
// This must be set to NULL if ImageAttribute indicates FSP Variable support
if ((FspHeader->ImageAttribute & IMAGE_ATTRIBUTE_VAR_SERVICES_SUPPORT) == IMAGE_ATTRIBUTE_VAR_SERVICES_SUPPORT) {
FspmUpdCommon24->FspmArchUpd.NvsBufferPtr = (UINTN)NULL;
} else {
FspmUpdCommon24->FspmArchUpd.NvsBufferPtr = (UINTN)FindNvsData();
}
NewStack = PcdGet32 (PcdFSPMStackTop);
if (NewStack == 0xFFFFFFFF) {
NewStack = (UINT32)FspmUpdCommon24->FspmArchUpd.StackBase + (UINT32)FspmUpdCommon24->FspmArchUpd.StackSize;
}
}
UpdateFspConfig (FspmUpdPtr);
ASSERT (FspHeader->FspMemoryInitEntryOffset != 0);
FspMemoryInit = (FSP_MEMORY_INIT)(UINTN)(FspHeader->ImageBase + \
FspHeader->FspMemoryInitEntryOffset);
DEBUG ((DEBUG_INFO, "Call FspMemoryInit ... "));
// Four Build combination possibilities:
// x64 SBL/x64 FSP
// ia32 SBL/x64 FSP - NOT SUPPORTED
// x64 SBL/ia32 FSP
// ia32 SBL/ia32 FSP
#if FixedPcdGetBool(PcdFSPM64Bit)
if (IS_X64) {
if (NewStack != 0) {
Status = FspmSwitchStack64 ((VOID *)(UINTN)FspMemoryInit, (VOID *)FspmUpdPtr, (VOID *)HobList, (VOID *)NewStack);
} else {
Status = FspMemoryInit (FspmUpdPtr, HobList);
}
} else {
CpuHalt("64-bit FSP not supported in 32-bit Slimbootloader build.\n");
}
#else //FixedPcdGetBool(PcdFSPM64Bit)
if (IS_X64) {
if (NewStack != 0) {
Status = FspmSwitchStack ((VOID *)(UINTN)FspMemoryInit, (VOID *)FspmUpdPtr, (VOID *)HobList, (VOID *)NewStack);
} else {
Status = Execute32BitCode ((UINTN)FspMemoryInit, (UINTN)FspmUpdPtr, (UINTN)HobList, FALSE);
}
Status = (UINTN)LShiftU64 (Status & ((UINTN)MAX_INT32 + 1), 32) | (Status & MAX_INT32);
} else {
if (NewStack != 0) {
Status = FspmSwitchStack ((VOID *)(UINTN)FspMemoryInit, (VOID *)FspmUpdPtr, (VOID *)HobList, (VOID *)NewStack);
} else {
Status = FspMemoryInit (FspmUpdPtr, HobList);
}
}
#endif //FixedPcdGetBool(PcdFSPM64Bit)
DEBUG ((DEBUG_INFO, "%r\n", Status));
return Status;
}
/**
* This calls the FspMultiPhaseMemInit entry point to find out if more phases of
* memory init remain, and executes them if so.
*
* @return EFI_STATUS
*/
EFI_STATUS
EFIAPI
FspMultiPhaseMemInitHandler(VOID)
{
EFI_STATUS Status;
FSP_MULTI_PHASE_PARAMS MultiPhaseInitParams;
FSP_MULTI_PHASE_GET_NUMBER_OF_PHASES_PARAMS GetNumPhasesParams;
MultiPhaseInitParams.MultiPhaseAction = EnumMultiPhaseGetNumberOfPhases;
MultiPhaseInitParams.PhaseIndex = 0;
MultiPhaseInitParams.MultiPhaseParamPtr = (VOID*)&GetNumPhasesParams;
GetNumPhasesParams.PhasesExecuted = 0;
// If FSP binary doesn't support 2.4 spec, multi phase mem init will return EFI_UNSUPPORTED
Status = CallFspMultiPhaseMemoryInit(&MultiPhaseInitParams);
if (EFI_ERROR(Status)) {
return Status;
}
MultiPhaseInitParams.MultiPhaseAction = EnumMultiPhaseExecutePhase;
MultiPhaseInitParams.MultiPhaseParamPtr = NULL;
// Loop through all phases. Break on error status or FSP_STATUS_* not
// handled by variable services handler
for (MultiPhaseInitParams.PhaseIndex = 1;
MultiPhaseInitParams.PhaseIndex <= GetNumPhasesParams.NumberOfPhases &&
!EFI_ERROR(Status) &&
!(Status & ENCODE_RESET_REQUEST(0));
MultiPhaseInitParams.PhaseIndex++)
{
Status = CallFspMultiPhaseMemoryInit(&MultiPhaseInitParams);
ASSERT_EFI_ERROR(Status);
Status = FspVariableHandler(Status,CallFspMultiPhaseMemoryInit);
ASSERT_EFI_ERROR(Status);
}
return Status;
}
/**
This FSP API provides multi-phase memory and silicon initialization, which brings
greater modularity to the existing FspMemoryInit() and FspSiliconInit() API. Increased
modularity is achieved by adding an extra API to FSP-M and FSP-S. This allows the
bootloader to add board specific initialization steps throughout the MemoryInit and
SiliconInit flows as needed. The FspMemoryInit() API is always called before
FspMultiPhaseMemInit(); it is the first phase of memory initialization. Similarly, the
FspSiliconInit() API is always called before FspMultiPhaseSiInit(); it is the first phase of
silicon initialization. After the first phase, subsequent phases are invoked by calling the
FspMultiPhaseMem/SiInit() API.
The FspMultiPhaseMemInit() API may only be called after the FspMemoryInit() API and
before the FspSiliconInit() API; or in the case that FSP-T is being used, before the
TempRamExit() API. The FspMultiPhaseSiInit() API may only be called after the
FspSiliconInit() API and before NotifyPhase() API; or in the case that FSP-I is being used,
before the FspSmmInit() API. The multi-phase APIs may not be called at any other time.
@param[in] MultiPhaseInitParamPtr Pointer to provide multi-phase init parameters.
@retval EFI_SUCCESS FSP execution environment was initialized successfully.
@retval EFI_INVALID_PARAMETER Input parameters are invalid.
@retval EFI_UNSUPPORTED The FSP calling conditions were not met.
@retval EFI_DEVICE_ERROR FSP initialization failed.
@retval EFI_OUT_OF_RESOURCES Stack range requested by FSP is not met.
@retval FSP_STATUS_RESET_REQUIREDx A reset is reuired. These status codes will not be returned during S3.
@retval FSP_STATUS_VARIABLE_REQUEST An FSP variable access is required.
**/
EFI_STATUS
EFIAPI
CallFspMultiPhaseMemoryInit (
IN FSP_MULTI_PHASE_PARAMS *MultiPhaseInitParamPtr
)
{
FSP_INFO_HEADER *FspHeader;
FSP_MULTI_PHASE_MEM_INIT FspMultiPhaseMemoryInit;
EFI_STATUS Status;
UINT32 FspmBase;
FspmBase = PCD_GET32_WITH_ADJUST (PcdFSPMBase);
FspHeader = (FSP_INFO_HEADER *)(UINTN)(FspmBase + FSP_INFO_HEADER_OFF);
ASSERT (FspHeader->Signature == FSP_INFO_HEADER_SIGNATURE);
ASSERT (FspHeader->ImageBase == FspmBase);
if (FspHeader->HeaderRevision < FSP24_HEADER_REVISION
|| FspHeader->FspMultiPhaseMemInitEntryOffset == 0)
{
return EFI_UNSUPPORTED;
}
FspMultiPhaseMemoryInit = (FSP_MULTI_PHASE_MEM_INIT)(UINTN)(FspHeader->ImageBase +
FspHeader->FspMultiPhaseMemInitEntryOffset);
DEBUG ((DEBUG_INFO, "Call FspMultiPhaseMemoryInit ... "));
// Four Build combination possibilities:
// x64 SBL/x64 FSP
// ia32 SBL/x64 FSP - NOT SUPPORTED
// x64 SBL/ia32 FSP
// ia32 SBL/ia32 FSP
#if FixedPcdGetBool(PcdFSPM64Bit)
if (IS_X64) {
Status = FspMultiPhaseMemoryInit (MultiPhaseInitParamPtr);
} else {
// This should not be reachable because CallFspMemoryInit() will halt.
CpuHalt("64-bit FSP not supported in 32-bit Slimbootloader build.\n");
}
#else //FixedPcdGetBool(PcdFSPM64Bit)
if (IS_X64) {
Status = Execute32BitCode ((UINTN)FspMultiPhaseMemoryInit, (UINTN)MultiPhaseInitParamPtr, (UINTN)NULL, FALSE);
Status = (UINTN)LShiftU64 (Status & ((UINTN)MAX_INT32 + 1), 32) | (Status & MAX_INT32);
} else {
Status = FspMultiPhaseMemoryInit (MultiPhaseInitParamPtr);
}
#endif //FixedPcdGetBool(PcdFSPM64Bit)
DEBUG ((DEBUG_INFO, "%r\n", Status));
return Status;
}