/** @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.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "PeilessStartupInternal.h" | |
#include <Library/DebugLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/PcdLib.h> | |
#include <Guid/MemoryTypeInformation.h> | |
#include <Guid/MemoryAllocationHob.h> | |
#include <Guid/PcdDataBaseSignatureGuid.h> | |
#include <Register/Intel/Cpuid.h> | |
#include <Library/PrePiLib.h> | |
#include "X64/PageTables.h" | |
#include <Library/ReportStatusCodeLib.h> | |
#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; | |
} |