From 878ddf1fc3540a715f63594ed22b6929e881afb4 Mon Sep 17 00:00:00 2001 From: bbahnsen Date: Fri, 21 Apr 2006 22:54:32 +0000 Subject: Initial import. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524 --- EdkModulePkg/Core/Dxe/Image/Image.c | 1377 +++++++++++++++++++++++++++++++ EdkModulePkg/Core/Dxe/Image/ImageFile.c | 569 +++++++++++++ 2 files changed, 1946 insertions(+) create mode 100644 EdkModulePkg/Core/Dxe/Image/Image.c create mode 100644 EdkModulePkg/Core/Dxe/Image/ImageFile.c (limited to 'EdkModulePkg/Core/Dxe/Image') diff --git a/EdkModulePkg/Core/Dxe/Image/Image.c b/EdkModulePkg/Core/Dxe/Image/Image.c new file mode 100644 index 0000000000..c818cc2131 --- /dev/null +++ b/EdkModulePkg/Core/Dxe/Image/Image.c @@ -0,0 +1,1377 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Image.c + +Abstract: + + Core image handling services + +--*/ + +#include +// +// Module Globals +// + +// +// LIST of runtime images that need to be relocated. +// +LIST_ENTRY mRuntimeImageList = INITIALIZE_LIST_HEAD_VARIABLE (mRuntimeImageList); + +LOADED_IMAGE_PRIVATE_DATA *mCurrentImage = NULL; + +LOAD_PE32_IMAGE_PRIVATE_DATA mLoadPe32PrivateData = { + LOAD_PE32_IMAGE_PRIVATE_DATA_SIGNATURE, + NULL, + { + CoreLoadImageEx, + CoreUnloadImageEx + } +}; + + +// +// This code is needed to build the Image handle for the DXE Core +// +LOADED_IMAGE_PRIVATE_DATA mCorePrivateImage = { + LOADED_IMAGE_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // Image handle + EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, // Image type + TRUE, // If entrypoint has been called + NULL, // EntryPoint + { + EFI_LOADED_IMAGE_INFORMATION_REVISION, // Revision + NULL, // Parent handle + NULL, // System handle + + NULL, // Device handle + NULL, // File path + NULL, // Reserved + + 0, // LoadOptionsSize + NULL, // LoadOptions + + NULL, // ImageBase + 0, // ImageSize + EfiBootServicesCode, // ImageCodeType + EfiBootServicesData // ImageDataType + }, + (EFI_PHYSICAL_ADDRESS)0, // ImageBasePage + 0, // NumberOfPages + NULL, // FixupData + 0, // Tpl + EFI_SUCCESS, // Status + 0, // ExitDataSize + NULL, // ExitData + NULL, // JumpContext + 0, // Machine + NULL, // Ebc + FALSE, // RuntimeFixupValid + NULL, // RuntimeFixup + { NULL, NULL }, // Link +}; + + +EFI_STATUS +CoreInitializeImageServices ( + IN VOID *HobStart + ) +/*++ + +Routine Description: + + Add the Image Services to EFI Boot Services Table and install the protocol + interfaces for this image. + +Arguments: + + HobStart - The HOB to initialize + +Returns: + + Status code. + +--*/ +{ + EFI_STATUS Status; + LOADED_IMAGE_PRIVATE_DATA *Image; + EFI_PHYSICAL_ADDRESS DxeCoreImageBaseAddress; + UINT64 DxeCoreImageLength; + VOID *DxeCoreEntryPoint; + EFI_PEI_HOB_POINTERS DxeCoreHob; + // + // Searching for image hob + // + DxeCoreHob.Raw = HobStart; + while ((DxeCoreHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, DxeCoreHob.Raw)) != NULL) { + if (CompareGuid (&DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.Name, &gEfiHobMemoryAllocModuleGuid)) { + // + // Find Dxe Core HOB + // + break; + } + DxeCoreHob.Raw = GET_NEXT_HOB (DxeCoreHob); + } + ASSERT (DxeCoreHob.Raw != NULL); + + DxeCoreImageBaseAddress = DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress; + DxeCoreImageLength = DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength; + DxeCoreEntryPoint = (VOID *) (UINTN) DxeCoreHob.MemoryAllocationModule->EntryPoint; + gDxeCoreFileName = &DxeCoreHob.MemoryAllocationModule->ModuleName; + // + // Initialize the fields for an internal driver + // + Image = &mCorePrivateImage; + + Image->EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)DxeCoreEntryPoint; + Image->ImageBasePage = DxeCoreImageBaseAddress; + Image->NumberOfPages = (UINTN)(EFI_SIZE_TO_PAGES((UINTN)(DxeCoreImageLength))); + Image->Tpl = gEfiCurrentTpl; + Image->Info.SystemTable = gST; + Image->Info.ImageBase = (VOID *)(UINTN)DxeCoreImageBaseAddress; + Image->Info.ImageSize = DxeCoreImageLength; + + // + // Install the protocol interfaces for this image + // + Status = CoreInstallProtocolInterface ( + &Image->Handle, + &gEfiLoadedImageProtocolGuid, + EFI_NATIVE_INTERFACE, + &Image->Info + ); + ASSERT_EFI_ERROR (Status); + + mCurrentImage = Image; + + // + // Fill in DXE globals + // + gDxeCoreImageHandle = Image->Handle; + gDxeCoreLoadedImage = &Image->Info; + + // + // Export DXE Core PE Loader functionality + // + return CoreInstallProtocolInterface ( + &mLoadPe32PrivateData.Handle, + &gEfiLoadPeImageProtocolGuid, + EFI_NATIVE_INTERFACE, + &mLoadPe32PrivateData.Pe32Image + ); +} + + +EFI_STATUS +CoreShutdownImageServices ( + VOID + ) +/*++ + +Routine Description: + + Transfer control of runtime images to runtime service + +Arguments: + + None + +Returns: + + EFI_SUCCESS - Function successfully returned + +--*/ +{ + LIST_ENTRY *Link; + LOADED_IMAGE_PRIVATE_DATA *Image; + + // + // The Runtime AP is required for the core to function! + // + ASSERT (gRuntime != NULL); + + for (Link = mRuntimeImageList.ForwardLink; Link != &mRuntimeImageList; Link = Link->ForwardLink) { + Image = CR (Link, LOADED_IMAGE_PRIVATE_DATA, Link, LOADED_IMAGE_PRIVATE_DATA_SIGNATURE); + if (Image->RuntimeFixupValid) { + gRuntime->RegisterImage ( + gRuntime, + (UINT64)(UINTN)(Image->Info.ImageBase), + (EFI_SIZE_TO_PAGES ((UINTN)Image->Info.ImageSize)), + Image->RuntimeFixup + ); + } + } + + return EFI_SUCCESS; +} + + + +EFI_STATUS +CoreLoadPeImage ( + IN VOID *Pe32Handle, + IN LOADED_IMAGE_PRIVATE_DATA *Image, + IN EFI_PHYSICAL_ADDRESS DstBuffer OPTIONAL, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint OPTIONAL, + IN UINT32 Attribute + ) +/*++ + +Routine Description: + + Loads, relocates, and invokes a PE/COFF image + +Arguments: + + Pe32Handle - The handle of PE32 image + Image - PE image to be loaded + DstBuffer - The buffer to store the image + EntryPoint - A pointer to the entry point + Attribute - The bit mask of attributes to set for the load PE image + +Returns: + + EFI_SUCCESS - The file was loaded, relocated, and invoked + + EFI_OUT_OF_RESOURCES - There was not enough memory to load and relocate the PE/COFF file + + EFI_INVALID_PARAMETER - Invalid parameter + + EFI_BUFFER_TOO_SMALL - Buffer for image is too small + +--*/ +{ + EFI_STATUS Status; + UINTN Size; + + ZeroMem (&Image->ImageContext, sizeof (Image->ImageContext)); + + Image->ImageContext.Handle = Pe32Handle; + Image->ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)CoreReadImageFile; + + // + // Get information about the image being loaded + // + Status = gEfiPeiPeCoffLoader->GetImageInfo (gEfiPeiPeCoffLoader, &Image->ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate memory of the correct memory type aligned on the required image boundry + // + + if (DstBuffer == 0) { + // + // Allocate Destination Buffer as caller did not pass it in + // + + if (Image->ImageContext.SectionAlignment > EFI_PAGE_SIZE) { + Size = (UINTN)Image->ImageContext.ImageSize + Image->ImageContext.SectionAlignment; + } else { + Size = (UINTN)Image->ImageContext.ImageSize; + } + + Image->NumberOfPages = EFI_SIZE_TO_PAGES (Size); + + // + // If the image relocations have not been stripped, then load at any address. + // Otherwise load at the address at which it was linked. + // + Status = CoreAllocatePages ( + (Image->ImageContext.RelocationsStripped) ? AllocateAddress : AllocateAnyPages, + Image->ImageContext.ImageCodeMemoryType, + Image->NumberOfPages, + &Image->ImageContext.ImageAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Image->ImageBasePage = Image->ImageContext.ImageAddress; + + } else { + // + // Caller provided the destination buffer + // + + if (Image->ImageContext.RelocationsStripped && (Image->ImageContext.ImageAddress != DstBuffer)) { + // + // If the image relocations were stripped, and the caller provided a + // destination buffer address that does not match the address that the + // image is linked at, then the image cannot be loaded. + // + return EFI_INVALID_PARAMETER; + } + + if (Image->NumberOfPages != 0 && + Image->NumberOfPages < + (EFI_SIZE_TO_PAGES ((UINTN)Image->ImageContext.ImageSize + Image->ImageContext.SectionAlignment))) { + Image->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)Image->ImageContext.ImageSize + Image->ImageContext.SectionAlignment); + return EFI_BUFFER_TOO_SMALL; + } + + Image->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)Image->ImageContext.ImageSize + Image->ImageContext.SectionAlignment); + Image->ImageContext.ImageAddress = DstBuffer; + Image->ImageBasePage = Image->ImageContext.ImageAddress; + } + + Image->ImageContext.ImageAddress = + (Image->ImageContext.ImageAddress + Image->ImageContext.SectionAlignment - 1) & + ~((UINTN)Image->ImageContext.SectionAlignment - 1); + + // + // Load the image from the file into the allocated memory + // + Status = gEfiPeiPeCoffLoader->LoadImage (gEfiPeiPeCoffLoader, &Image->ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If this is a Runtime Driver, then allocate memory for the FixupData that + // is used to relocate the image when SetVirtualAddressMap() is called. The + // relocation is done by the Runtime AP. + // + if (Attribute & EFI_LOAD_PE_IMAGE_ATTRIBUTE_RUNTIME_REGISTRATION) { + if (Image->ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) { + Image->ImageContext.FixupData = CoreAllocateRuntimePool ((UINTN)(Image->ImageContext.FixupDataSize)); + if (Image->ImageContext.FixupData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Make a list off all the RT images so we can let the RT AP know about them + // + Image->RuntimeFixupValid = TRUE; + Image->RuntimeFixup = Image->ImageContext.FixupData; + InsertTailList (&mRuntimeImageList, &Image->Link); + } + } + + // + // Relocate the image in memory + // + Status = gEfiPeiPeCoffLoader->RelocateImage (gEfiPeiPeCoffLoader, &Image->ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Flush the Instruction Cache + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)Image->ImageContext.ImageAddress, (UINTN)Image->ImageContext.ImageSize); + + // + // Copy the machine type from the context to the image private data. This + // is needed during image unload to know if we should call an EBC protocol + // to unload the image. + // + Image->Machine = Image->ImageContext.Machine; + + // + // Get the image entry point. If it's an EBC image, then call into the + // interpreter to create a thunk for the entry point and use the returned + // value for the entry point. + // + Image->EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)Image->ImageContext.EntryPoint; + if (Image->ImageContext.Machine == EFI_IMAGE_MACHINE_EBC) { + // + // Locate the EBC interpreter protocol + // + Status = CoreLocateProtocol (&gEfiEbcProtocolGuid, NULL, (VOID **)&Image->Ebc); + if (EFI_ERROR(Status)) { + goto Done; + } + + // + // Register a callback for flushing the instruction cache so that created + // thunks can be flushed. + // + Status = Image->Ebc->RegisterICacheFlush (Image->Ebc, (EBC_ICACHE_FLUSH)InvalidateInstructionCacheRange); + if (EFI_ERROR(Status)) { + goto Done; + } + + // + // Create a thunk for the image's entry point. This will be the new + // entry point for the image. + // + Status = Image->Ebc->CreateThunk ( + Image->Ebc, + Image->Handle, + (VOID *)(UINTN)Image->ImageContext.EntryPoint, + (VOID **)&Image->EntryPoint + ); + if (EFI_ERROR(Status)) { + goto Done; + } + } + + // + // Fill in the image information for the Loaded Image Protocol + // + Image->Type = Image->ImageContext.ImageType; + Image->Info.ImageBase = (VOID *)(UINTN)Image->ImageContext.ImageAddress; + Image->Info.ImageSize = Image->ImageContext.ImageSize; + Image->Info.ImageCodeType = Image->ImageContext.ImageCodeMemoryType; + Image->Info.ImageDataType = Image->ImageContext.ImageDataMemoryType; + + // + // Fill in the entry point of the image if it is available + // + if (EntryPoint != NULL) { + *EntryPoint = Image->ImageContext.EntryPoint; + } + + // + // Print the load address and the PDB file name if it is available + // + + DEBUG_CODE ( + { + UINTN Index; + UINTN StartIndex; + CHAR8 EfiFileName[256]; + + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Loading driver at 0x%08x EntryPoint=0x%08x ", (UINTN)Image->ImageContext.ImageAddress, (UINTN)Image->ImageContext.EntryPoint)); + if (Image->ImageContext.PdbPointer != NULL) { + StartIndex = 0; + for (Index = 0; Image->ImageContext.PdbPointer[Index] != 0; Index++) { + if (Image->ImageContext.PdbPointer[Index] == '\\') { + StartIndex = Index + 1; + } + } + // + // Copy the PDB file name to our temporary string, and replace .pdb with .efi + // + for (Index = 0; Index < sizeof (EfiFileName); Index++) { + EfiFileName[Index] = Image->ImageContext.PdbPointer[Index + StartIndex]; + if (EfiFileName[Index] == 0) { + EfiFileName[Index] = '.'; + } + if (EfiFileName[Index] == '.') { + EfiFileName[Index + 1] = 'e'; + EfiFileName[Index + 2] = 'f'; + EfiFileName[Index + 3] = 'i'; + EfiFileName[Index + 4] = 0; + break; + } + } + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex])); + } + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "\n")); + } + ); + + return EFI_SUCCESS; + +Done: + // + // Free memory + // + CoreFreePages (Image->ImageContext.ImageAddress, Image->NumberOfPages); + return Status; +} + + +LOADED_IMAGE_PRIVATE_DATA * +CoreLoadedImageInfo ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + + Get the image's private data from its handle. + +Arguments: + + ImageHandle - The image handle + +Returns: + + Return the image private data associated with ImageHandle. + +--*/ +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + LOADED_IMAGE_PRIVATE_DATA *Image; + + Status = CoreHandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (!EFI_ERROR (Status)) { + Image = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS (LoadedImage); + } else { + DEBUG ((EFI_D_LOAD, "CoreLoadedImageInfo: Not an ImageHandle %x\n", ImageHandle)); + Image = NULL; + } + + return Image; +} + + +EFI_STATUS +CoreLoadImageCommon ( + IN BOOLEAN BootPolicy, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + IN EFI_PHYSICAL_ADDRESS DstBuffer OPTIONAL, + IN OUT UINTN *NumberOfPages OPTIONAL, + OUT EFI_HANDLE *ImageHandle, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint OPTIONAL, + IN UINT32 Attribute + ) +/*++ + +Routine Description: + + Loads an EFI image into memory and returns a handle to the image. + +Arguments: + + BootPolicy - If TRUE, indicates that the request originates from the boot manager, + and that the boot manager is attempting to load FilePath as a boot selection. + ParentImageHandle - The caller's image handle. + FilePath - The specific file path from which the image is loaded. + SourceBuffer - If not NULL, a pointer to the memory location containing a copy of + the image to be loaded. + SourceSize - The size in bytes of SourceBuffer. + DstBuffer - The buffer to store the image + NumberOfPages - If not NULL, a pointer to the image's page number, if this number + is not enough, return EFI_BUFFER_TOO_SMALL and this parameter contain + the required number. + ImageHandle - Pointer to the returned image handle that is created when the image + is successfully loaded. + EntryPoint - A pointer to the entry point + Attribute - The bit mask of attributes to set for the load PE image + +Returns: + + EFI_SUCCESS - The image was loaded into memory. + EFI_NOT_FOUND - The FilePath was not found. + EFI_INVALID_PARAMETER - One of the parameters has an invalid value. + EFI_BUFFER_TOO_SMALL - The buffer is too small + EFI_UNSUPPORTED - The image type is not supported, or the device path cannot be + parsed to locate the proper protocol for loading the file. + EFI_OUT_OF_RESOURCES - Image was not loaded due to insufficient resources. +--*/ +{ + LOADED_IMAGE_PRIVATE_DATA *Image; + LOADED_IMAGE_PRIVATE_DATA *ParentImage; + IMAGE_FILE_HANDLE FHand; + EFI_STATUS Status; + EFI_STATUS SecurityStatus; + EFI_HANDLE DeviceHandle; + UINT32 AuthenticationStatus; + EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath; + EFI_DEVICE_PATH_PROTOCOL *HandleFilePath; + UINTN FilePathSize; + + + ASSERT (gEfiCurrentTpl < EFI_TPL_NOTIFY); + ParentImage = NULL; + + // + // The caller must pass in a valid ParentImageHandle + // + if (ImageHandle == NULL || ParentImageHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + ParentImage = CoreLoadedImageInfo (ParentImageHandle); + if (ParentImage == NULL) { + DEBUG((EFI_D_LOAD|EFI_D_ERROR, "LoadImageEx: Parent handle not an image handle\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Get simple read access to the source file + // + OriginalFilePath = FilePath; + Status = CoreOpenImageFile ( + BootPolicy, + SourceBuffer, + SourceSize, + FilePath, + &DeviceHandle, + &FHand, + &AuthenticationStatus + ); + if (Status == EFI_ALREADY_STARTED) { + Image = NULL; + goto Done; + } else if (EFI_ERROR (Status)) { + return Status; + } + + // + // Verify the Authentication Status through the Security Architectural Protocol + // + if ((gSecurity != NULL) && (OriginalFilePath != NULL)) { + SecurityStatus = gSecurity->FileAuthenticationState ( + gSecurity, + AuthenticationStatus, + OriginalFilePath + ); + if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) { + Status = SecurityStatus; + Image = NULL; + goto Done; + } + } + + + // + // Allocate a new image structure + // + Image = CoreAllocateZeroBootServicesPool (sizeof(LOADED_IMAGE_PRIVATE_DATA)); + if (Image == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Pull out just the file portion of the DevicePath for the LoadedImage FilePath + // + Status = CoreHandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath); + if (!EFI_ERROR (Status)) { + FilePathSize = CoreDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); + FilePath = (EFI_DEVICE_PATH_PROTOCOL *) ( ((UINT8 *)FilePath) + FilePathSize ); + } + + // + // Initialize the fields for an internal driver + // + Image->Signature = LOADED_IMAGE_PRIVATE_DATA_SIGNATURE; + Image->Info.SystemTable = gST; + Image->Info.DeviceHandle = DeviceHandle; + Image->Info.Revision = EFI_LOADED_IMAGE_INFORMATION_REVISION; + Image->Info.FilePath = CoreDuplicateDevicePath (FilePath); + Image->Info.ParentHandle = ParentImageHandle; + + if (NumberOfPages != NULL) { + Image->NumberOfPages = *NumberOfPages ; + } else { + Image->NumberOfPages = 0 ; + } + + // + // Install the protocol interfaces for this image + // don't fire notifications yet + // + Status = CoreInstallProtocolInterfaceNotify ( + &Image->Handle, + &gEfiLoadedImageProtocolGuid, + EFI_NATIVE_INTERFACE, + &Image->Info, + FALSE + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Load the image. If EntryPoint is Null, it will not be set. + // + Status = CoreLoadPeImage (&FHand, Image, DstBuffer, EntryPoint, Attribute); + if (EFI_ERROR (Status)) { + if ((Status == EFI_BUFFER_TOO_SMALL) || (Status == EFI_OUT_OF_RESOURCES)) { + if (NumberOfPages != NULL) { + *NumberOfPages = Image->NumberOfPages; + } + } + goto Done; + } + + // + // Register the image in the Debug Image Info Table if the attribute is set + // + if (Attribute & EFI_LOAD_PE_IMAGE_ATTRIBUTE_DEBUG_IMAGE_INFO_TABLE_REGISTRATION) { + CoreNewDebugImageInfoEntry (EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL, &Image->Info, Image->Handle); + } + + // + //Reinstall loaded image protocol to fire any notifications + // + Status = CoreReinstallProtocolInterface ( + Image->Handle, + &gEfiLoadedImageProtocolGuid, + &Image->Info, + &Image->Info + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + + // + // Success. Return the image handle + // + *ImageHandle = Image->Handle; + +Done: + // + // All done accessing the source file + // If we allocated the Source buffer, free it + // + if (FHand.FreeBuffer) { + CoreFreePool (FHand.Source); + } + + // + // There was an error. If there's an Image structure, free it + // + if (EFI_ERROR (Status)) { + if (Image != NULL) { + CoreUnloadAndCloseImage (Image, (BOOLEAN)(DstBuffer == 0)); + *ImageHandle = NULL; + } + } + + return Status; +} + + + +EFI_STATUS +EFIAPI +CoreLoadImage ( + IN BOOLEAN BootPolicy, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + OUT EFI_HANDLE *ImageHandle + ) +/*++ + +Routine Description: + + Loads an EFI image into memory and returns a handle to the image. + +Arguments: + + BootPolicy - If TRUE, indicates that the request originates from the boot manager, + and that the boot manager is attempting to load FilePath as a boot selection. + ParentImageHandle - The caller's image handle. + FilePath - The specific file path from which the image is loaded. + SourceBuffer - If not NULL, a pointer to the memory location containing a copy of + the image to be loaded. + SourceSize - The size in bytes of SourceBuffer. + ImageHandle - Pointer to the returned image handle that is created when the image + is successfully loaded. + +Returns: + + EFI_SUCCESS - The image was loaded into memory. + EFI_NOT_FOUND - The FilePath was not found. + EFI_INVALID_PARAMETER - One of the parameters has an invalid value. + EFI_UNSUPPORTED - The image type is not supported, or the device path cannot be + parsed to locate the proper protocol for loading the file. + EFI_OUT_OF_RESOURCES - Image was not loaded due to insufficient resources. +--*/ +{ + EFI_STATUS Status; + + PERF_START (NULL, "LoadImage", NULL, 0); + + Status = CoreLoadImageCommon ( + BootPolicy, + ParentImageHandle, + FilePath, + SourceBuffer, + SourceSize, + (EFI_PHYSICAL_ADDRESS)NULL, + NULL, + ImageHandle, + NULL, + EFI_LOAD_PE_IMAGE_ATTRIBUTE_RUNTIME_REGISTRATION | EFI_LOAD_PE_IMAGE_ATTRIBUTE_DEBUG_IMAGE_INFO_TABLE_REGISTRATION + ); + + PERF_END (NULL, "LoadImage", NULL, 0); + + return Status; +} + + +EFI_STATUS +EFIAPI +CoreLoadImageEx ( + IN EFI_PE32_IMAGE_PROTOCOL *This, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + IN EFI_PHYSICAL_ADDRESS DstBuffer OPTIONAL, + OUT UINTN *NumberOfPages OPTIONAL, + OUT EFI_HANDLE *ImageHandle, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint OPTIONAL, + IN UINT32 Attribute + ) +/*++ + +Routine Description: + + Loads an EFI image into memory and returns a handle to the image with extended parameters. + +Arguments: + + This - Calling context + ParentImageHandle - The caller's image handle. + FilePath - The specific file path from which the image is loaded. + SourceBuffer - If not NULL, a pointer to the memory location containing a copy of + the image to be loaded. + SourceSize - The size in bytes of SourceBuffer. + DstBuffer - The buffer to store the image. + NumberOfPages - For input, specifies the space size of the image by caller if not NULL. + For output, specifies the actual space size needed. + ImageHandle - Image handle for output. + EntryPoint - Image entry point for output. + Attribute - The bit mask of attributes to set for the load PE image. + +Returns: + + EFI_SUCCESS - The image was loaded into memory. + EFI_NOT_FOUND - The FilePath was not found. + EFI_INVALID_PARAMETER - One of the parameters has an invalid value. + EFI_UNSUPPORTED - The image type is not supported, or the device path cannot be + parsed to locate the proper protocol for loading the file. + EFI_OUT_OF_RESOURCES - Image was not loaded due to insufficient resources. +--*/ +{ + return CoreLoadImageCommon ( + TRUE, + ParentImageHandle, + FilePath, + SourceBuffer, + SourceSize, + DstBuffer, + NumberOfPages, + ImageHandle, + EntryPoint, + Attribute + ); +} + + + + +EFI_STATUS +EFIAPI +CoreStartImage ( + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ) +/*++ + +Routine Description: + + Transfer control to a loaded image's entry point. + +Arguments: + + ImageHandle - Handle of image to be started. + + ExitDataSize - Pointer of the size to ExitData + + ExitData - Pointer to a pointer to a data buffer that includes a Null-terminated + Unicode string, optionally followed by additional binary data. The string + is a description that the caller may use to further indicate the reason for + the image¡¯s exit. + +Returns: + + EFI_INVALID_PARAMETER - Invalid parameter + + EFI_OUT_OF_RESOURCES - No enough buffer to allocate + + EFI_SUCCESS - Successfully transfer control to the image's entry point. + +--*/ +{ + EFI_STATUS Status; + LOADED_IMAGE_PRIVATE_DATA *Image; + LOADED_IMAGE_PRIVATE_DATA *LastImage; + UINT64 HandleDatabaseKey; + UINTN SetJumpFlag; + + Image = CoreLoadedImageInfo (ImageHandle); + if (Image == NULL_HANDLE || Image->Started) { + return EFI_INVALID_PARAMETER; + } + + // + // Don't profile Objects or invalid start requests + // + PERF_START (ImageHandle, START_IMAGE_TOK, NULL, 0); + + if (sizeof (UINTN) == 4 && Image->Machine == EFI_IMAGE_MACHINE_X64) { + return EFI_UNSUPPORTED; + } else if (sizeof (UINTN) == 8 && Image->Machine == EFI_IMAGE_MACHINE_IA32) { + return EFI_UNSUPPORTED; + } else { + // + // For orther possible cases + // + } + + // + // Push the current start image context, and + // link the current image to the head. This is the + // only image that can call Exit() + // + HandleDatabaseKey = CoreGetHandleDatabaseKey(); + LastImage = mCurrentImage; + mCurrentImage = Image; + Image->Tpl = gEfiCurrentTpl; + + // + // Set long jump for Exit() support + // + Image->JumpContext = CoreAllocateBootServicesPool (sizeof (*Image->JumpContext)); + if (Image->JumpContext == NULL) { + PERF_END (ImageHandle, START_IMAGE_TOK, NULL, 0); + return EFI_OUT_OF_RESOURCES; + } + + SetJumpFlag = SetJump (Image->JumpContext); + // + // The initial call to SetJump() must always return 0. + // Subsequent calls to LongJump() cause a non-zero value to be returned by SetJump(). + // + if (!SetJumpFlag) { + // + // Call the image's entry point + // + Image->Started = TRUE; + Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable); + + // + // Add some debug information if the image returned with error. + // This make the user aware and check if the driver image have already released + // all the resource in this situation. + // + DEBUG_CODE ( + if (EFI_ERROR (Image->Status)) { + DEBUG ((EFI_D_ERROR, "Error: Image at %08X start failed: %x\n", Image->Info.ImageBase, Image->Status)); + } + ); + + // + // If the image returns, exit it through Exit() + // + CoreExit (ImageHandle, Image->Status, 0, NULL); + } + + // + // Image has completed. Verify the tpl is the same + // + ASSERT (Image->Tpl == gEfiCurrentTpl); + CoreRestoreTpl (Image->Tpl); + + CoreFreePool (Image->JumpContext); + + // + // Pop the current start image context + // + mCurrentImage = LastImage; + + // + // Go connect any handles that were created or modified while the image executed. + // + CoreConnectHandlesByKey (HandleDatabaseKey); + + // + // Handle the image's returned ExitData + // + DEBUG_CODE ( + if (Image->ExitDataSize != 0 || Image->ExitData != NULL) { + + DEBUG ( + (EFI_D_LOAD, + "StartImage: ExitDataSize %d, ExitData %x", + Image->ExitDataSize, + Image->ExitData) + ); + if (Image->ExitData != NULL) { + DEBUG ((EFI_D_LOAD, " (%hs)", Image->ExitData)); + } + DEBUG ((EFI_D_LOAD, "\n")); + } + ); + + // + // Return the exit data to the caller + // + if (ExitData != NULL && ExitDataSize != NULL) { + *ExitDataSize = Image->ExitDataSize; + *ExitData = Image->ExitData; + } else { + // + // Caller doesn't want the exit data, free it + // + CoreFreePool (Image->ExitData); + Image->ExitData = NULL; + } + + // + // Save the Status because Image will get destroyed if it is unloaded. + // + Status = Image->Status; + + // + // If the image returned an error, or if the image is an application + // unload it + // + if (EFI_ERROR (Image->Status) || Image->Type == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { + CoreUnloadAndCloseImage (Image, TRUE); + } + + // + // Done + // + PERF_END (ImageHandle, START_IMAGE_TOK, NULL, 0); + return Status; +} + + +VOID +CoreUnloadAndCloseImage ( + IN LOADED_IMAGE_PRIVATE_DATA *Image, + IN BOOLEAN FreePage + ) +/*++ + +Routine Description: + + Unloads EFI image from memory. + +Arguments: + + Image - EFI image + FreePage - Free allocated pages + +Returns: + + None + +--*/ +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN HandleIndex; + EFI_GUID **ProtocolGuidArray; + UINTN ArrayCount; + UINTN ProtocolIndex; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfo; + UINTN OpenInfoCount; + UINTN OpenInfoIndex; + + if (Image->Ebc != NULL) { + // + // If EBC protocol exists we must perform cleanups for this image. + // + Image->Ebc->UnloadImage (Image->Ebc, Image->Handle); + } + + // + // Unload image, free Image->ImageContext->ModHandle + // + gEfiPeiPeCoffLoader->UnloadImage (gEfiPeiPeCoffLoader, &Image->ImageContext); + + // + // Free our references to the image handle + // + if (Image->Handle != NULL_HANDLE) { + + Status = CoreLocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &HandleBuffer + ); + if (!EFI_ERROR (Status)) { + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + Status = CoreProtocolsPerHandle ( + HandleBuffer[HandleIndex], + &ProtocolGuidArray, + &ArrayCount + ); + if (!EFI_ERROR (Status)) { + for (ProtocolIndex = 0; ProtocolIndex < ArrayCount; ProtocolIndex++) { + Status = CoreOpenProtocolInformation ( + HandleBuffer[HandleIndex], + ProtocolGuidArray[ProtocolIndex], + &OpenInfo, + &OpenInfoCount + ); + if (!EFI_ERROR (Status)) { + for (OpenInfoIndex = 0; OpenInfoIndex < OpenInfoCount; OpenInfoIndex++) { + if (OpenInfo[OpenInfoIndex].AgentHandle == Image->Handle) { + Status = CoreCloseProtocol ( + HandleBuffer[HandleIndex], + ProtocolGuidArray[ProtocolIndex], + Image->Handle, + OpenInfo[OpenInfoIndex].ControllerHandle + ); + } + } + if (OpenInfo != NULL) { + CoreFreePool(OpenInfo); + } + } + } + if (ProtocolGuidArray != NULL) { + CoreFreePool(ProtocolGuidArray); + } + } + } + if (HandleBuffer != NULL) { + CoreFreePool (HandleBuffer); + } + } + + CoreRemoveDebugImageInfoEntry (Image->Handle); + + Status = CoreUninstallProtocolInterface ( + Image->Handle, + &gEfiLoadedImageProtocolGuid, + &Image->Info + ); + } + + if (Image->RuntimeFixupValid) { + // + // Remove the Image from the Runtime Image list as we are about to Free it! + // + RemoveEntryList (&Image->Link); + } + + // + // Free the Image from memory + // + if ((Image->ImageBasePage != 0) && FreePage) { + CoreFreePages (Image->ImageBasePage, Image->NumberOfPages); + } + + // + // Done with the Image structure + // + if (Image->Info.FilePath != NULL) { + CoreFreePool (Image->Info.FilePath); + } + + if (Image->FixupData != NULL) { + CoreFreePool (Image->FixupData); + } + + CoreFreePool (Image); +} + + + +EFI_STATUS +EFIAPI +CoreExit ( + IN EFI_HANDLE ImageHandle, + IN EFI_STATUS Status, + IN UINTN ExitDataSize, + IN CHAR16 *ExitData OPTIONAL + ) +/*++ + +Routine Description: + + Terminates the currently loaded EFI image and returns control to boot services. + +Arguments: + + ImageHandle - Handle that identifies the image. This parameter is passed to the image + on entry. + Status - The image¡¯s exit code. + ExitDataSize - The size, in bytes, of ExitData. Ignored if ExitStatus is + EFI_SUCCESS. + ExitData - Pointer to a data buffer that includes a Null-terminated Unicode string, + optionally followed by additional binary data. The string is a + description that the caller may use to further indicate the reason for + the image¡¯s exit. + +Returns: + + EFI_INVALID_PARAMETER - Image handle is NULL or it is not current image. + + EFI_SUCCESS - Successfully terminates the currently loaded EFI image. + + EFI_ACCESS_DENIED - Should never reach there. + + EFI_OUT_OF_RESOURCES - Could not allocate pool + +--*/ +{ + LOADED_IMAGE_PRIVATE_DATA *Image; + + Image = CoreLoadedImageInfo (ImageHandle); + if (Image == NULL_HANDLE) { + return EFI_INVALID_PARAMETER; + } + + if (!Image->Started) { + // + // The image has not been started so just free its resources + // + CoreUnloadAndCloseImage (Image, TRUE); + return EFI_SUCCESS; + } + + // + // Image has been started, verify this image can exit + // + if (Image != mCurrentImage) { + DEBUG ((EFI_D_LOAD|EFI_D_ERROR, "Exit: Image is not exitable image\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Set status + // + Image->Status = Status; + + // + // If there's ExitData info, move it + // + if (ExitData != NULL) { + Image->ExitDataSize = ExitDataSize; + Image->ExitData = CoreAllocateBootServicesPool (Image->ExitDataSize); + if (Image->ExitData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Image->ExitData, ExitData, Image->ExitDataSize); + } + + // + // return to StartImage + // + LongJump (Image->JumpContext, (UINTN)-1); + + // + // If we return from LongJump, then it is an error + // + ASSERT (FALSE); + return EFI_ACCESS_DENIED; +} + + + +EFI_STATUS +EFIAPI +CoreUnloadImage ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + + Unloads an image. + +Arguments: + + ImageHandle - Handle that identifies the image to be unloaded. + +Returns: + + EFI_SUCCESS - The image has been unloaded. + EFI_UNSUPPORTED - The image has been sarted, and does not support unload. + EFI_INVALID_PARAMPETER - ImageHandle is not a valid image handle. + +--*/ +{ + EFI_STATUS Status; + LOADED_IMAGE_PRIVATE_DATA *Image; + + Image = CoreLoadedImageInfo (ImageHandle); + if (Image == NULL ) { + // + // The image handle is not valid + // + return EFI_INVALID_PARAMETER; + } + + if (Image->Started) { + // + // The image has been started, request it to unload. + // + Status = EFI_UNSUPPORTED; + if (Image->Info.Unload != NULL) { + Status = Image->Info.Unload (ImageHandle); + } + + } else { + // + // This Image hasn't been started, thus it can be unloaded + // + Status = EFI_SUCCESS; + } + + + if (!EFI_ERROR (Status)) { + // + // if the Image was not started or Unloaded O.K. then clean up + // + CoreUnloadAndCloseImage (Image, TRUE); + } + + return Status; +} + + +EFI_STATUS +EFIAPI +CoreUnloadImageEx ( + IN EFI_PE32_IMAGE_PROTOCOL *This, + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + + Unload the specified image. + +Arguments: + + This - Indicates the calling context. + + ImageHandle - The specified image handle. + +Returns: + + EFI_INVALID_PARAMETER - Image handle is NULL. + + EFI_UNSUPPORTED - Attempt to unload an unsupported image. + + EFI_SUCCESS - Image successfully unloaded. + +--*/ +{ + return CoreUnloadImage (ImageHandle); +} diff --git a/EdkModulePkg/Core/Dxe/Image/ImageFile.c b/EdkModulePkg/Core/Dxe/Image/ImageFile.c new file mode 100644 index 0000000000..999cd694ad --- /dev/null +++ b/EdkModulePkg/Core/Dxe/Image/ImageFile.c @@ -0,0 +1,569 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ImageFile.c + + +Abstract: + + + + +Revision History + +--*/ + +#include + + +VOID +CoreDevicePathToFileName ( + IN FILEPATH_DEVICE_PATH *FilePath, + OUT CHAR16 **String + ); + + + +EFI_STATUS +CoreOpenImageFile ( + IN BOOLEAN BootPolicy, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + IN OUT EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_HANDLE *DeviceHandle, + IN IMAGE_FILE_HANDLE *ImageFileHandle, + OUT UINT32 *AuthenticationStatus + ) +/*++ + +Routine Description: + + Opens a file for (simple) reading. The simple read abstraction + will access the file either from a memory copy, from a file + system interface, or from the load file interface. + +Arguments: + + BootPolicy - Policy for Open Image File. + SourceBuffer - Pointer to the memory location containing copy + of the image to be loaded. + SourceSize - The size in bytes of SourceBuffer. + FilePath - The specific file path from which the image is loaded + DeviceHandle - Pointer to the return device handle. + ImageFileHandle - Pointer to the image file handle. + AuthenticationStatus - Pointer to a caller-allocated UINT32 in which the authentication status is returned. + +Returns: + + EFI_SUCCESS - Image file successfully opened. + + EFI_LOAD_ERROR - If the caller passed a copy of the file, and SourceSize is 0. + + EFI_INVALID_PARAMETER - File path is not valid. + + EFI_NOT_FOUND - File not found. + +--*/ +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *TempFilePath; + FILEPATH_DEVICE_PATH *FilePathNode; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FwVolFilePathNode; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; + EFI_FILE_HANDLE FileHandle; + EFI_FILE_HANDLE LastHandle; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + EFI_FIRMWARE_VOLUME_PROTOCOL *FwVol; + EFI_SECTION_TYPE SectionType; + UINT8 *Pe32Buffer; + UINTN Pe32BufferSize; + EFI_FV_FILETYPE Type; + EFI_FV_FILE_ATTRIBUTES Attrib; + EFI_FILE_INFO *FileInfo; + UINTN FileInfoSize; + EFI_GUID *NameGuid; + + *AuthenticationStatus = 0; + ZeroMem (ImageFileHandle, sizeof (IMAGE_FILE_HANDLE)); + ImageFileHandle->Signature = IMAGE_FILE_HANDLE_SIGNATURE; + + // + // If the caller passed a copy of the file, then just use it + // + if (SourceBuffer != NULL) { + ImageFileHandle->Source = SourceBuffer; + ImageFileHandle->SourceSize = SourceSize; + *DeviceHandle = NULL; + if (SourceSize > 0) { + Status = EFI_SUCCESS; + } else { + Status = EFI_LOAD_ERROR; + } + goto Done; + } + + // + // Make sure FilePath is valid + // + if (FilePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if it's in a Firmware Volume + // + FwVolFilePathNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)FilePath; + Status = CoreDevicePathToInterface ( + &gEfiFirmwareVolumeProtocolGuid, + (EFI_DEVICE_PATH_PROTOCOL **)&FwVolFilePathNode, + (VOID*)&FwVol, + DeviceHandle + ); + if (!EFI_ERROR (Status)) { + // + // For FwVol File system there is only a single file name that is a GUID. + // + NameGuid = EfiGetNameGuidFromFwVolDevicePathNode (FwVolFilePathNode); + if (NameGuid != NULL) { + + SectionType = EFI_SECTION_PE32; + Pe32Buffer = NULL; + Status = FwVol->ReadSection ( + FwVol, + NameGuid, + SectionType, + 0, + (VOID **)&Pe32Buffer, + &Pe32BufferSize, + AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + // + // Try a raw file, since a PE32 SECTION does not exist + // + if (Pe32Buffer != NULL) { + CoreFreePool (Pe32Buffer); + *AuthenticationStatus = 0; + } + Pe32Buffer = NULL; + Status = FwVol->ReadFile ( + FwVol, + NameGuid, + (VOID **)&Pe32Buffer, + &Pe32BufferSize, + &Type, + &Attrib, + AuthenticationStatus + ); + } + + if (!EFI_ERROR (Status)) { + // + // One of the reads passed so we are done + // + ImageFileHandle->Source = Pe32Buffer; + ImageFileHandle->SourceSize = Pe32BufferSize; + ImageFileHandle->FreeBuffer = TRUE; + goto Done; + } + } + } + + // + // Attempt to access the file via a file system interface + // + FilePathNode = (FILEPATH_DEVICE_PATH *) FilePath; + Status = CoreDevicePathToInterface ( + &gEfiSimpleFileSystemProtocolGuid, + (EFI_DEVICE_PATH_PROTOCOL **)&FilePathNode, + (VOID*)&Volume, + DeviceHandle + ); + if (!EFI_ERROR (Status)) { + // + // Open the Volume to get the File System handle + // + Status = Volume->OpenVolume (Volume, &FileHandle); + if (!EFI_ERROR (Status)) { + + // + // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the + // directory information and filename can be seperate. The goal is to inch + // our way down each device path node and close the previous node + // + while (!IsDevicePathEnd (&FilePathNode->Header)) { + if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH || + DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) { + Status = EFI_UNSUPPORTED; + } + + if (EFI_ERROR (Status)) { + // + // Exit loop on Error + // + break; + } + + LastHandle = FileHandle; + FileHandle = NULL; + Status = LastHandle->Open ( + LastHandle, + &FileHandle, + FilePathNode->PathName, + EFI_FILE_MODE_READ, + 0 + ); + + // + // Close the previous node + // + LastHandle->Close (LastHandle); + + FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header); + } + + if (!EFI_ERROR (Status)) { + // + // We have found the file. Now we need to read it. Before we can read the file we need to + // figure out how big the file is. + // + FileInfo = NULL; + FileInfoSize = sizeof (EFI_FILE_INFO); + while (CoreGrowBuffer (&Status, (VOID **)&FileInfo, FileInfoSize)) { + // + // Automatically allocate buffer of the correct size and make the call + // + Status = FileHandle->GetInfo ( + FileHandle, + &gEfiFileInfoGuid, + &FileInfoSize, + FileInfo + ); + } + if (!EFI_ERROR (Status)) { + // + // Allocate space for the file + // + ImageFileHandle->Source = CoreAllocateBootServicesPool ((UINTN)FileInfo->FileSize); + if (ImageFileHandle->Source != NULL) { + // + // Read the file into the buffer we allocated + // + ImageFileHandle->SourceSize = (UINTN)FileInfo->FileSize; + ImageFileHandle->FreeBuffer = TRUE; + Status = FileHandle->Read (FileHandle, &ImageFileHandle->SourceSize, ImageFileHandle->Source); + + // + // Close the file since we are done + // + FileHandle->Close (FileHandle); + } else { + Status = EFI_OUT_OF_RESOURCES; + } + + goto Done; + } + } + } + } + + + // + // Try LoadFile style + // + + TempFilePath = FilePath; + Status = CoreDevicePathToInterface ( + &gEfiLoadFileProtocolGuid, + &TempFilePath, + (VOID*)&LoadFile, + DeviceHandle + ); + if (!EFI_ERROR (Status)) { + // + // Call LoadFile with the correct buffer size + // + while (CoreGrowBuffer (&Status, (VOID **)&ImageFileHandle->Source, ImageFileHandle->SourceSize)) { + Status = LoadFile->LoadFile ( + LoadFile, + TempFilePath, + BootPolicy, + &ImageFileHandle->SourceSize, + ImageFileHandle->Source + ); + // + // If success or other error happens, stop loop + // + if (Status != EFI_BUFFER_TOO_SMALL) { + break; + } + } + + if (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED) { + ImageFileHandle->FreeBuffer = TRUE; + goto Done; + } + } + + // + // Nothing else to try + // + DEBUG ((EFI_D_LOAD|EFI_D_WARN, "CoreOpenImageFile: Device did not support a known load protocol\n")); + Status = EFI_NOT_FOUND; + +Done: + + // + // If the file was not accessed, clean up + // + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + if (ImageFileHandle->FreeBuffer) { + // + // Free the source buffer if we allocated it + // + CoreFreePool (ImageFileHandle->Source); + } + } + + return Status; +} + + + +EFI_STATUS +EFIAPI +CoreReadImageFile ( + IN VOID *UserHandle, + IN UINTN Offset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +/*++ + +Routine Description: + + Read image file (specified by UserHandle) into user specified buffer with specified offset + and length. + +Arguments: + + UserHandle - Image file handle + + Offset - Offset to the source file + + ReadSize - For input, pointer of size to read; + For output, pointer of size actually read. + + Buffer - Buffer to write into + +Returns: + + EFI_SUCCESS - Successfully read the specified part of file into buffer. + +--*/ +{ + UINTN EndPosition; + IMAGE_FILE_HANDLE *FHand; + + FHand = (IMAGE_FILE_HANDLE *)UserHandle; + ASSERT (FHand->Signature == IMAGE_FILE_HANDLE_SIGNATURE); + + // + // Move data from our local copy of the file + // + EndPosition = Offset + *ReadSize; + if (EndPosition > FHand->SourceSize) { + *ReadSize = (UINT32)(FHand->SourceSize - Offset); + } + if (Offset >= FHand->SourceSize) { + *ReadSize = 0; + } + + CopyMem (Buffer, (CHAR8 *)FHand->Source + Offset, *ReadSize); + return EFI_SUCCESS; +} + +EFI_STATUS +CoreDevicePathToInterface ( + IN EFI_GUID *Protocol, + IN EFI_DEVICE_PATH_PROTOCOL **FilePath, + OUT VOID **Interface, + OUT EFI_HANDLE *Handle + ) +/*++ + +Routine Description: + + Search a handle to a device on a specified device path that supports a specified protocol, + interface of that protocol on that handle is another output. + +Arguments: + + Protocol - The protocol to search for + + FilePath - The specified device path + + Interface - Interface of the protocol on the handle + + Handle - The handle to the device on the specified device path that supports the protocol. + +Returns: + + Status code. + +--*/ +{ + EFI_STATUS Status; + + Status = CoreLocateDevicePath (Protocol, FilePath, Handle); + if (!EFI_ERROR (Status)) { + Status = CoreHandleProtocol (*Handle, Protocol, Interface); + } + return Status; +} + + +VOID +CoreDevicePathToFileName ( + IN FILEPATH_DEVICE_PATH *FilePath, + OUT CHAR16 **String + ) +/*++ + +Routine Description: + + Transfer a device's full path a string. + +Arguments: + + FilePath - Device path + + String - The string represent the device's full path + +Returns: + + None + +--*/ +{ + UINTN StringSize; + FILEPATH_DEVICE_PATH *FilePathNode; + CHAR16 *Str; + + *String = NULL; + StringSize = 0; + FilePathNode = FilePath; + while (!IsDevicePathEnd (&FilePathNode->Header)) { + + // + // For filesystem access each node should be a filepath component + // + if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH || + DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) { + + return; + } + + StringSize += StrLen (FilePathNode->PathName); + + FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header); + } + + *String = CoreAllocateBootServicesPool (StringSize); + if (*String == NULL) { + return; + } + + FilePathNode = FilePath; + Str = *String; + while (!IsDevicePathEnd (&FilePathNode->Header)) { + StrCat (Str, FilePathNode->PathName); + FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header); + } +} + + +BOOLEAN +CoreGrowBuffer ( + IN OUT EFI_STATUS *Status, + IN OUT VOID **Buffer, + IN UINTN BufferSize + ) +/*++ + +Routine Description: + + Helper function called as part of the code needed + to allocate the proper sized buffer for various + EFI interfaces. + +Arguments: + + Status - Current status + + Buffer - Current allocated buffer, or NULL + + BufferSize - Current buffer size needed + +Returns: + + TRUE - if the buffer was reallocated and the caller + should try the API again. + + FALSE - buffer could not be allocated and the caller + should not try the API again. + +--*/ +{ + BOOLEAN TryAgain; + + TryAgain = FALSE; + // + // If this is an initial request, buffer will be null with a new buffer size + // + if (*Buffer == NULL) { + *Status = EFI_BUFFER_TOO_SMALL; + } + + if (BufferSize == 0) { + return TRUE; + } + + // + // If the status code is "buffer too small", resize the buffer + // + + if (*Status == EFI_BUFFER_TOO_SMALL) { + if (*Buffer != NULL) { + CoreFreePool (*Buffer); + } + + *Buffer = CoreAllocateBootServicesPool (BufferSize); + if (*Buffer != NULL) { + TryAgain = TRUE; + } else { + *Status = EFI_OUT_OF_RESOURCES; + } + } + + // + // If there's an error, free the buffer + // + if ((!TryAgain) && (EFI_ERROR (*Status)) && (*Buffer)) { + CoreFreePool (*Buffer); + *Buffer = NULL; + } + + return TryAgain; +} + -- cgit v1.2.3