/** @file
Responsibility of this file is to load the DXE Core from a Firmware Volume.
Copyright (c) 2016 HP Development Company, L.P.
Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PeilessStartupInternal.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "X64/PageTables.h"
#include
#define STACK_SIZE 0x20000
extern EFI_GUID gEfiNonCcFvGuid;
/**
Transfers control to DxeCore.
This function performs a CPU architecture specific operations to execute
the entry point of DxeCore
@param DxeCoreEntryPoint The entry point of DxeCore.
**/
VOID
HandOffToDxeCore (
IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint
)
{
VOID *BaseOfStack;
VOID *TopOfStack;
UINTN PageTables;
//
// Clear page 0 and mark it as allocated if NULL pointer detection is enabled.
//
if (IsNullDetectionEnabled ()) {
ClearFirst4KPage (GetHobList ());
BuildMemoryAllocationHob (0, EFI_PAGES_TO_SIZE (1), EfiBootServicesData);
}
//
// Allocate 128KB for the Stack
//
BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (STACK_SIZE));
ASSERT (BaseOfStack != NULL);
//
// Compute the top of the stack we were allocated. Pre-allocate a UINTN
// for safety.
//
TopOfStack = (VOID *)((UINTN)BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT);
TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
DEBUG ((DEBUG_INFO, "BaseOfStack=0x%x, TopOfStack=0x%x\n", BaseOfStack, TopOfStack));
//
// Create page table and save PageMapLevel4 to CR3
//
PageTables = CreateIdentityMappingPageTables (
(EFI_PHYSICAL_ADDRESS)(UINTN)BaseOfStack,
STACK_SIZE
);
if (PageTables == 0) {
DEBUG ((DEBUG_ERROR, "Failed to create idnetity mapping page tables.\n"));
CpuDeadLoop ();
}
AsmWriteCr3 (PageTables);
//
// Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore.
//
UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN)BaseOfStack, STACK_SIZE);
DEBUG ((DEBUG_INFO, "SwitchStack then Jump to DxeCore\n"));
//
// Transfer the control to the entry point of DxeCore.
//
SwitchStack (
(SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint,
GetHobList (),
NULL,
TopOfStack
);
}
/**
Searches DxeCore in all firmware Volumes and loads the first
instance that contains DxeCore.
@return FileHandle of DxeCore to load DxeCore.
**/
EFI_STATUS
FindDxeCore (
IN INTN FvInstance,
IN OUT EFI_PEI_FILE_HANDLE *FileHandle
)
{
EFI_STATUS Status;
EFI_PEI_FV_HANDLE VolumeHandle;
if (FileHandle == NULL) {
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
*FileHandle = NULL;
//
// Caller passed in a specific FV to try, so only try that one
//
Status = FfsFindNextVolume (FvInstance, &VolumeHandle);
if (!EFI_ERROR (Status)) {
Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle);
if (*FileHandle) {
// Assume the FV that contains multiple compressed FVs.
// So decompress the compressed FVs
Status = FfsProcessFvFile (*FileHandle);
ASSERT_EFI_ERROR (Status);
Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle);
}
}
return Status;
}
/**
* This is a FFS_CHECK_SECTION_HOOK which is defined by caller to check
* if the section is an EFI_SECTION_FIRMWARE_VOLUME_IMAGE and if it is
* a NonCc FV.
*
* @param Section The section in which we're checking for the NonCc FV.
* @return EFI_STATUS The section is the NonCc FV.
*/
EFI_STATUS
EFIAPI
CheckSectionHookForDxeNonCc (
IN EFI_COMMON_SECTION_HEADER *Section
)
{
VOID *Buffer;
EFI_STATUS Status;
EFI_FV_INFO FvImageInfo;
if (Section->Type != EFI_SECTION_FIRMWARE_VOLUME_IMAGE) {
return EFI_INVALID_PARAMETER;
}
if (IS_SECTION2 (Section)) {
Buffer = (VOID *)((UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER2));
} else {
Buffer = (VOID *)((UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER));
}
ZeroMem (&FvImageInfo, sizeof (FvImageInfo));
Status = FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE)(UINTN)Buffer, &FvImageInfo);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Cannot get volume info! %r\n", Status));
return Status;
}
return CompareGuid (&FvImageInfo.FvName, &gEfiNonCcFvGuid) ? EFI_SUCCESS : EFI_NOT_FOUND;
}
/**
* Find the NonCc FV.
*
* @param FvInstance The FvInstance number.
* @return EFI_STATUS Successfuly find the NonCc FV.
*/
EFI_STATUS
EFIAPI
FindDxeNonCc (
IN INTN FvInstance
)
{
EFI_STATUS Status;
EFI_PEI_FV_HANDLE VolumeHandle;
EFI_PEI_FILE_HANDLE FileHandle;
EFI_PEI_FV_HANDLE FvImageHandle;
EFI_FV_INFO FvImageInfo;
UINT32 FvAlignment;
VOID *FvBuffer;
FileHandle = NULL;
//
// Caller passed in a specific FV to try, so only try that one
//
Status = FfsFindNextVolume (FvInstance, &VolumeHandle);
ASSERT (Status == EFI_SUCCESS);
Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, &FileHandle);
ASSERT (FileHandle != NULL);
//
// Find FvImage in FvFile
//
Status = FfsFindSectionDataWithHook (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, CheckSectionHookForDxeNonCc, FileHandle, (VOID **)&FvImageHandle);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Collect FvImage Info.
//
ZeroMem (&FvImageInfo, sizeof (FvImageInfo));
Status = FfsGetVolumeInfo (FvImageHandle, &FvImageInfo);
ASSERT_EFI_ERROR (Status);
//
// FvAlignment must be more than 8 bytes required by FvHeader structure.
//
FvAlignment = 1 << ((FvImageInfo.FvAttributes & EFI_FVB2_ALIGNMENT) >> 16);
if (FvAlignment < 8) {
FvAlignment = 8;
}
//
// Check FvImage
//
if ((UINTN)FvImageInfo.FvStart % FvAlignment != 0) {
FvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINT32)FvImageInfo.FvSize), FvAlignment);
if (FvBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (FvBuffer, FvImageInfo.FvStart, (UINTN)FvImageInfo.FvSize);
//
// Update FvImageInfo after reload FvImage to new aligned memory
//
FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE)FvBuffer, &FvImageInfo);
}
//
// Inform HOB consumer phase, i.e. DXE core, the existence of this FV
//
BuildFvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)FvImageInfo.FvStart, FvImageInfo.FvSize);
//
// Makes the encapsulated volume show up in DXE phase to skip processing of
// encapsulated file again.
//
BuildFv2Hob (
(EFI_PHYSICAL_ADDRESS)(UINTN)FvImageInfo.FvStart,
FvImageInfo.FvSize,
&FvImageInfo.FvName,
&(((EFI_FFS_FILE_HEADER *)FileHandle)->Name)
);
return Status;
}
/**
This function finds DXE Core in the firmware volume and transfer the control to
DXE core.
@return EFI_SUCCESS DXE core was successfully loaded.
@return EFI_OUT_OF_RESOURCES There are not enough resources to load DXE core.
**/
EFI_STATUS
EFIAPI
DxeLoadCore (
IN INTN FvInstance
)
{
EFI_STATUS Status;
EFI_FV_FILE_INFO DxeCoreFileInfo;
EFI_PHYSICAL_ADDRESS DxeCoreAddress;
UINT64 DxeCoreSize;
EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint;
EFI_PEI_FILE_HANDLE FileHandle;
VOID *PeCoffImage;
//
// Look in all the FVs present and find the DXE Core FileHandle
//
Status = FindDxeCore (FvInstance, &FileHandle);
if (EFI_ERROR (Status)) {
ASSERT (FALSE);
return Status;
}
if (!TdIsEnabled ()) {
FindDxeNonCc (FvInstance);
}
//
// Load the DXE Core from a Firmware Volume.
//
Status = FfsFindSectionDataWithHook (EFI_SECTION_PE32, NULL, FileHandle, &PeCoffImage);
if (EFI_ERROR (Status)) {
return Status;
}
Status = LoadPeCoffImage (PeCoffImage, &DxeCoreAddress, &DxeCoreSize, &DxeCoreEntryPoint);
ASSERT_EFI_ERROR (Status);
//
// Extract the DxeCore GUID file name.
//
Status = FfsGetFileInfo (FileHandle, &DxeCoreFileInfo);
ASSERT_EFI_ERROR (Status);
//
// Add HOB for the DXE Core
//
BuildModuleHob (
&DxeCoreFileInfo.FileName,
DxeCoreAddress,
ALIGN_VALUE (DxeCoreSize, EFI_PAGE_SIZE),
DxeCoreEntryPoint
);
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
"Loading DXE CORE at 0x%11p EntryPoint=0x%11p\n",
(VOID *)(UINTN)DxeCoreAddress,
FUNCTION_ENTRY_POINT (DxeCoreEntryPoint)
));
// Transfer control to the DXE Core
// The hand off state is simply a pointer to the HOB list
//
HandOffToDxeCore (DxeCoreEntryPoint);
//
// If we get here, then the DXE Core returned. This is an error
// DxeCore should not return.
//
ASSERT (FALSE);
CpuDeadLoop ();
return EFI_OUT_OF_RESOURCES;
}