/** @file FIT Load Image Support Copyright (c) 2023, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FitLib.h" #define STACK_SIZE 0x20000 CONST EFI_PEI_PPI_DESCRIPTOR gReadyToPayloadSignalPpi = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gUplReadyToPayloadPpiGuid, NULL }; EFI_PEI_PPI_DESCRIPTOR mEndOfPeiSignalPpi = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiEndOfPeiSignalPpiGuid, NULL }; #if (FixedPcdGetBool (PcdHandOffFdtEnable) == 0) /** Notify ReadyToPayLoad signal. @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param[in] NotifyDescriptor Address of the notification descriptor data structure. @param[in] Ppi Address of the PPI that was installed. @retval EFI_SUCCESS Hobs data is discovered. @return Others No Hobs data is discovered. **/ EFI_STATUS EFIAPI EndOfPeiPpiNotifyCallback ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi ) { EFI_STATUS Status; // // Ready to Payload phase signal // Status = PeiServicesInstallPpi (&gReadyToPayloadSignalPpi); return Status; } EFI_PEI_NOTIFY_DESCRIPTOR mEndOfPeiNotifyList[] = { { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiEndOfPeiSignalPpiGuid, EndOfPeiPpiNotifyCallback } }; #endif /** The wrapper function of PeiLoadImageLoadImage(). @param This - Pointer to EFI_PEI_LOAD_FILE_PPI. @param FileHandle - Pointer to the FFS file header of the image. @param ImageAddressArg - Pointer to PE/TE image. @param ImageSizeArg - Size of PE/TE image. @param EntryPoint - Pointer to entry point of specified image file for output. @param AuthenticationState - Pointer to attestation authentication state of image. @return Status of PeiLoadImageLoadImage(). **/ EFI_STATUS EFIAPI PeiLoadFileLoadPayload ( IN CONST EFI_PEI_LOAD_FILE_PPI *This, IN EFI_PEI_FILE_HANDLE FileHandle, OUT EFI_PHYSICAL_ADDRESS *ImageAddressArg OPTIONAL, OUT UINT64 *ImageSizeArg OPTIONAL, OUT EFI_PHYSICAL_ADDRESS *EntryPoint, OUT UINT32 *AuthenticationState ) { EFI_STATUS Status; FIT_IMAGE_CONTEXT Context; UINTN Instance; VOID *Binary; FIT_RELOCATE_ITEM *RelocateTable; UNIVERSAL_PAYLOAD_BASE *PayloadBase; UINTN Length; UINTN Delta; UINTN Index; #if (FixedPcdGetBool (PcdHandOffFdtEnable)) VOID *BaseOfStack; VOID *TopOfStack; UNIVERSAL_PAYLOAD_DEVICE_TREE *Fdt; VOID *Hob; Fdt = NULL; #endif Instance = 0; do { Status = PeiServicesFfsFindSectionData3 (EFI_SECTION_RAW, Instance++, FileHandle, &Binary, AuthenticationState); if (EFI_ERROR (Status)) { return Status; } ZeroMem (&Context, sizeof (Context)); Status = ParseFitImage (Binary, &Context); } while (EFI_ERROR (Status)); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return Status; } DEBUG ( ( DEBUG_INFO, "Before Rebase Payload File Base: 0x%08x, File Size: 0x%08X, EntryPoint: 0x%08x\n", Context.PayloadBaseAddress, Context.PayloadSize, Context.PayloadEntryPoint ) ); Context.PayloadBaseAddress = (EFI_PHYSICAL_ADDRESS)AllocatePages (EFI_SIZE_TO_PAGES (Context.PayloadSize)); RelocateTable = (FIT_RELOCATE_ITEM *)(UINTN)(Context.PayloadBaseAddress + Context.RelocateTableOffset); CopyMem ((VOID *)Context.PayloadBaseAddress, Binary, Context.PayloadSize); if (Context.PayloadBaseAddress > Context.PayloadLoadAddress) { Delta = Context.PayloadBaseAddress - Context.PayloadLoadAddress; Context.PayloadEntryPoint += Delta; for (Index = 0; Index < Context.RelocateTableCount; Index++) { if ((RelocateTable[Index].RelocateType == 10) || (RelocateTable[Index].RelocateType == 3)) { *((UINT64 *)(Context.PayloadBaseAddress + RelocateTable[Index].Offset)) = *((UINT64 *)(Context.PayloadBaseAddress + RelocateTable[Index].Offset)) + Delta; } } } else { Delta = Context.PayloadLoadAddress - Context.PayloadBaseAddress; Context.PayloadEntryPoint -= Delta; for (Index = 0; Index < Context.RelocateTableCount; Index++) { if ((RelocateTable[Index].RelocateType == 10) || (RelocateTable[Index].RelocateType == 3)) { *((UINT64 *)(Context.PayloadBaseAddress + RelocateTable[Index].Offset)) = *((UINT64 *)(Context.PayloadBaseAddress + RelocateTable[Index].Offset)) - Delta; } } } DEBUG ( ( DEBUG_INFO, "After Rebase Payload File Base: 0x%08x, File Size: 0x%08X, EntryPoint: 0x%08x\n", Context.PayloadBaseAddress, Context.PayloadSize, Context.PayloadEntryPoint ) ); Length = sizeof (UNIVERSAL_PAYLOAD_BASE); PayloadBase = BuildGuidHob ( &gUniversalPayloadBaseGuid, Length ); PayloadBase->Entry = (EFI_PHYSICAL_ADDRESS)Context.ImageBase; *ImageAddressArg = Context.PayloadBaseAddress; *ImageSizeArg = Context.PayloadSize; *EntryPoint = Context.PayloadEntryPoint; #if (FixedPcdGetBool (PcdHandOffFdtEnable)) Status = PeiServicesInstallPpi (&mEndOfPeiSignalPpi); ASSERT_EFI_ERROR (Status); Status = PeiServicesInstallPpi (&gReadyToPayloadSignalPpi); ASSERT_EFI_ERROR (Status); Hob = GetFirstGuidHob (&gUniversalPayloadDeviceTreeGuid); if (Hob != NULL) { Fdt = (UNIVERSAL_PAYLOAD_DEVICE_TREE *)GET_GUID_HOB_DATA (Hob); } // // 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); // // Transfer the control to the entry point of UniveralPayloadEntry. // SwitchStack ( (SWITCH_STACK_ENTRY_POINT)(UINTN)Context.PayloadEntryPoint, (VOID *)(Fdt->DeviceTreeAddress), NULL, TopOfStack ); #else Status = PeiServicesNotifyPpi (&mEndOfPeiNotifyList[0]); ASSERT_EFI_ERROR (Status); #endif return EFI_SUCCESS; } EFI_PEI_LOAD_FILE_PPI mPeiLoadFilePpi = { PeiLoadFileLoadPayload }; EFI_PEI_PPI_DESCRIPTOR gPpiLoadFilePpiList = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiPeiLoadFilePpiGuid, &mPeiLoadFilePpi }; #if (FixedPcdGetBool (PcdHandOffFdtEnable)) /** Discover Hobs data and report data into a FDT. @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param[in] NotifyDescriptor Address of the notification descriptor data structure. @param[in] Ppi Address of the PPI that was installed. @retval EFI_SUCCESS Hobs data is discovered. @return Others No Hobs data is discovered. **/ EFI_STATUS EFIAPI FdtPpiNotifyCallback ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi ); EFI_PEI_NOTIFY_DESCRIPTOR mReadyToPayloadNotifyList[] = { { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gUplReadyToPayloadPpiGuid, FdtPpiNotifyCallback } }; #endif /** Print FDT data. @param[in] FdtBase Address of the Fdt data. **/ VOID PrintFdt ( IN VOID *FdtBase ) { UINT8 *Fdt; UINT32 i; Fdt = NULL; i = 0; DEBUG ((DEBUG_ERROR, "FDT DTB data:")); for (Fdt = FdtBase, i = 0; i < Fdt32ToCpu (((FDT_HEADER *)FdtBase)->TotalSize); i++, Fdt++) { if (i % 16 == 0) { DEBUG ((DEBUG_ERROR, "\n")); } DEBUG ((DEBUG_ERROR, "%02x ", *Fdt)); } DEBUG ((DEBUG_ERROR, "\n")); } /** It will build FDT for UPL consumed. @param[in] FdtBase Address of the Fdt data. @retval EFI_SUCCESS If it completed successfully. @retval Others If it failed to build required FDT. **/ EFI_STATUS BuildFdtForUPL ( IN VOID *FdtBase ); #if (FixedPcdGetBool (PcdHandOffFdtEnable)) /** Discover Hobs data and report data into a FDT. @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param[in] NotifyDescriptor Address of the notification descriptor data structure. @param[in] Ppi Address of the PPI that was installed. @retval EFI_SUCCESS Hobs data is discovered. @return Others No Hobs data is discovered. **/ EFI_STATUS EFIAPI FdtPpiNotifyCallback ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi ) { EFI_STATUS Status; UNIVERSAL_PAYLOAD_DEVICE_TREE *Fdt; UINT32 FdtSize; UINTN FdtPages; VOID *FdtBase; UINT32 Data32; Fdt = NULL; FdtSize = PcdGet8 (PcdFDTPageSize) * EFI_PAGE_SIZE; FdtPages = EFI_SIZE_TO_PAGES (FdtSize); FdtBase = AllocatePages (FdtPages); if (FdtBase == NULL) { DEBUG ((DEBUG_ERROR, "%a: AllocatePages failed\n", __func__)); return EFI_NOT_FOUND; } Status = FdtCreateEmptyTree (FdtBase, (UINT32)FdtSize); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: cannot create FDT\n", __func__)); } // Set cell property of root node Data32 = CpuToFdt32 (2); Status = FdtSetProperty (FdtBase, 0, "#address-cells", &Data32, sizeof (UINT32)); Status = FdtSetProperty (FdtBase, 0, "#size-cells", &Data32, sizeof (UINT32)); Status = BuildFdtForUPL (FdtBase); ASSERT_EFI_ERROR (Status); PrintFdt (FdtBase); Fdt = BuildGuidHob (&gUniversalPayloadDeviceTreeGuid, sizeof (UNIVERSAL_PAYLOAD_DEVICE_TREE)); if (Fdt == NULL) { DEBUG ((DEBUG_ERROR, "%a: Build FDT Hob failed\n", __func__)); return EFI_NOT_FOUND; } DEBUG (( DEBUG_ERROR, "%a: fdt at 0x%x (size %d)\n", __func__, FdtBase, Fdt32ToCpu (((FDT_HEADER *)FdtBase)->TotalSize) )); Fdt->Header.Revision = UNIVERSAL_PAYLOAD_DEVICE_TREE_REVISION; Fdt->Header.Length = sizeof (UNIVERSAL_PAYLOAD_DEVICE_TREE); Fdt->DeviceTreeAddress = (UINT64)FdtBase; return Status; } #endif /** Install Pei Load File PPI. @param FileHandle Handle of the file being invoked. @param PeiServices Describes the list of possible PEI Services. @retval EFI_SUCESS The entry point executes successfully. @retval Others Some error occurs during the execution of this function. **/ EFI_STATUS EFIAPI InitializeFitPayloadLoaderPeim ( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { EFI_STATUS Status; Status = PeiServicesInstallPpi (&gPpiLoadFilePpiList); #if (FixedPcdGetBool (PcdHandOffFdtEnable)) // // Build FDT in end of PEI notify callback. // Status = PeiServicesNotifyPpi (&mReadyToPayloadNotifyList[0]); ASSERT_EFI_ERROR (Status); #endif return Status; }