mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-01 12:53:27 +01:00
353 lines
9.4 KiB
C
353 lines
9.4 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;
|
|
UINT32 FvImageSize;
|
|
|
|
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, &FvImageSize);
|
|
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;
|
|
UINT32 DxeCoreSize;
|
|
EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint;
|
|
EFI_PEI_FILE_HANDLE FileHandle;
|
|
VOID *UefiImage;
|
|
UINT32 UefiImageSize;
|
|
|
|
//
|
|
// 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, &UefiImage, &UefiImageSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = LoadUefiImage (UefiImage, UefiImageSize, &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;
|
|
}
|