Commit c673216f53
introduces FFS_CHECK_SECTION_HOOK and add it as the
second input parameter in FfsFindSectionData. This change breaks the build
of ArmVirtPkg. To fix this issue, the new version of FfsFindSectionData
is renamed as FfsFindSectionDataWithHook in this patch. In the following
patch the original FfsFindSectionData will be added back.
FfsFindSectionData is renamed as FfsFindSectionDataWithHook. Accordingly
PeilessStartupLib in OvmfPkg should be updated as well. To prevent the
build from being broken, the changes in OvmfPkg are in this patch.
Signed-off-by: Min Xu <min.m.xu@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
351 lines
9.3 KiB
C
351 lines
9.3 KiB
C
/** @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;
|
|
}
|