| /** @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; | |
| } |