mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-30 12:43:41 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
335 lines
13 KiB
C
335 lines
13 KiB
C
//
|
|
/// @file OsxFatBinaryDrv/OsxFatBinary.c
|
|
///
|
|
/// Fat Binary driver
|
|
///
|
|
/// Fat Binary driver to add Fat Binary support to the LoadImage-function
|
|
///
|
|
|
|
//
|
|
// CHANGELOG:
|
|
//
|
|
// UNKNOWN DATE
|
|
// Kabyl and rafirafi
|
|
// Initial implementation
|
|
//
|
|
|
|
#include "OsxFatBinaryDrv.h"
|
|
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Protocol/LoadedImage.h>
|
|
#include <Protocol/LoadFile.h>
|
|
#include <Protocol/SimpleFileSystem.h>
|
|
#include <Guid/FileInfo.h>
|
|
|
|
// UefiMain
|
|
/// The user Entry Point for Application. The user code starts with this function
|
|
/// as the real entry point for the application.
|
|
///
|
|
/// @param[in] ImageHandle The firmware allocated handle for the EFI image.
|
|
/// @param[in] SystemTable A pointer to the EFI System Table.
|
|
///
|
|
/// @retval EFI_SUCCESS The entry point is executed successfully.
|
|
/// @retval other Some error occurs when executing this entry point.
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UefiMain(IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable)
|
|
{
|
|
OverrideFunctions ();
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// OverrideFunctions
|
|
/// Overrides the original LoadImage function with the Fat Binary-compatible
|
|
/// and saves a pointer to the old.
|
|
///
|
|
/// @retval EFI_SUCCESS The override is executed successfully.
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OverrideFunctions()
|
|
{
|
|
OrigLoadImage = gBS->LoadImage;
|
|
gBS->LoadImage = OvrLoadImage;
|
|
|
|
gBS->Hdr.CRC32 = 0;
|
|
gBS->CalculateCrc32(gBS, sizeof(EFI_BOOT_SERVICES), &gBS->Hdr.CRC32);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// OvrLoadImage
|
|
/// Loads an EFI image into memory. Supports the Fat Binary format.
|
|
///
|
|
/// @param[in] 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. Ignored if SourceBuffer is
|
|
/// not NULL.
|
|
/// @param[in] ParentImageHandle The caller's image handle.
|
|
/// @param[in] DevicePath The DeviceHandle specific file path from which the image is
|
|
/// loaded.
|
|
/// @param[in] SourceBuffer If not NULL, a pointer to the memory location containing a copy
|
|
/// of the image to be loaded.
|
|
/// @param[in] SourceSize The size in bytes of SourceBuffer. Ignored if SourceBuffer is NULL.
|
|
/// @param[out] ImageHandle The pointer to the returned image handle that is created when the
|
|
/// image is successfully loaded.
|
|
///
|
|
/// @retval EFI_SUCCESS Image was loaded into memory correctly.
|
|
/// @retval EFI_NOT_FOUND Both SourceBuffer and DevicePath are NULL.
|
|
/// @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
|
/// @retval EFI_UNSUPPORTED The image type is not supported.
|
|
/// @retval EFI_OUT_OF_RESOURCES Image was not loaded due to insufficient resources.
|
|
/// @retval EFI_LOAD_ERROR Image was not loaded because the image format was corrupt or not
|
|
/// understood.
|
|
/// @retval EFI_DEVICE_ERROR Image was not loaded because the device returned a read error.
|
|
/// @retval EFI_ACCESS_DENIED Image was not loaded because the platform policy prohibits the
|
|
/// image from being loaded. NULL is returned in *ImageHandle.
|
|
/// @retval EFI_SECURITY_VIOLATION Image was loaded and an ImageHandle was created with a
|
|
/// valid EFI_LOADED_IMAGE_PROTOCOL. However, the current
|
|
/// platform policy specifies that the image should not be started.
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OvrLoadImage(IN BOOLEAN BootPolicy,
|
|
IN EFI_HANDLE ParentImageHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
|
|
IN VOID *SourceBuffer OPTIONAL,
|
|
IN UINTN SourceSize,
|
|
OUT EFI_HANDLE *ImageHandle)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS Status2;
|
|
|
|
FAT_HEADER *FatHeader;
|
|
FAT_ARCH *FatArch;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
|
|
EFI_LOAD_FILE_PROTOCOL *LoadFile;
|
|
|
|
VOID *SrcBuffer;
|
|
BOOLEAN FreeSourceBuffer;
|
|
BOOLEAN FreeSrcBuffer;
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
|
|
EFI_HANDLE DeviceHandle;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
EFI_FILE_HANDLE LastHandle;
|
|
FILEPATH_DEVICE_PATH *FilePathNode;
|
|
EFI_FILE_INFO *FileInfo;
|
|
UINTN FileInfoSize;
|
|
FILEPATH_DEVICE_PATH *OrigFilePathNode;
|
|
|
|
UINT32 Index;
|
|
|
|
EFI_LOADED_IMAGE_PROTOCOL *Image;
|
|
|
|
FreeSourceBuffer = FALSE;
|
|
RemainingDevicePath = NULL;
|
|
DeviceHandle = 0;
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
while (SourceBuffer == NULL) {
|
|
if (FilePath == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
// Attempt to access the file via a file system interface
|
|
FilePathNode = (FILEPATH_DEVICE_PATH *)FilePath;
|
|
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, (EFI_DEVICE_PATH_PROTOCOL **)&FilePathNode, &DeviceHandle);
|
|
if (!EFI_ERROR(Status)) {
|
|
RemainingDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FilePathNode;
|
|
Status = gBS->HandleProtocol(DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Volume);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
// Open the volume to get the File System handle
|
|
Status = Volume->OpenVolume(Volume, &FileHandle);
|
|
if (!EFI_ERROR(Status)) {
|
|
//
|
|
// Duplicate the device path to avoid the access to unaligned device path node.
|
|
// Because the device path consists of one or more FILE_PATH_MEDIA_DEVICE_PATH
|
|
// nodes, It assures the fields in device path nodes are 2 byte aligned.
|
|
//
|
|
FilePathNode = (FILEPATH_DEVICE_PATH *)DuplicateDevicePath((EFI_DEVICE_PATH_PROTOCOL *)FilePathNode);
|
|
if (FilePathNode == NULL) {
|
|
FileHandle->Close(FileHandle);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
} else {
|
|
OrigFilePathNode = FilePathNode;
|
|
//
|
|
// Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
|
|
// directory information and filename can be separate. 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)) {
|
|
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);
|
|
}
|
|
|
|
// Free the allocated memory pool
|
|
gBS->FreePool(OrigFilePathNode);
|
|
}
|
|
|
|
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 = 0;
|
|
Status = FileHandle->GetInfo(FileHandle, &gEfiFileInfoGuid, &FileInfoSize, FileInfo);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
// inc size by 2 because some drivers (HFSPlus.efi) do not count 0 at the end of file name
|
|
FileInfoSize += 2;
|
|
gBS->AllocatePool(EfiBootServicesData, FileInfoSize, (VOID **)&FileInfo);
|
|
if (FileInfo != NULL) {
|
|
Status = FileHandle->GetInfo(FileHandle, &gEfiFileInfoGuid, &FileInfoSize, FileInfo);
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
// ASSERT(FileInfo != NULL);
|
|
if (FileInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
gBS->AllocatePool(EfiBootServicesData, (UINTN)FileInfo->FileSize, &SourceBuffer);
|
|
|
|
if (SourceBuffer != NULL) {
|
|
SourceSize = (UINTN)FileInfo->FileSize;
|
|
FreeSourceBuffer = TRUE;
|
|
Status = FileHandle->Read(FileHandle, &SourceSize, SourceBuffer);
|
|
|
|
FileHandle->Close(FileHandle);
|
|
gBS->FreePool(FileInfo);
|
|
FileInfo = NULL; //FreePoll will not zero pointer
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try LoadFile style
|
|
//
|
|
RemainingDevicePath = FilePath;
|
|
Status = gBS->LocateDevicePath(&gEfiLoadFileProtocolGuid, &RemainingDevicePath, &DeviceHandle);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = gBS->HandleProtocol(DeviceHandle, &gEfiLoadFileProtocolGuid, (VOID**)&LoadFile);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
// ASSERT(SourceSize == 0);
|
|
// ASSERT(SourceBuffer == NULL);
|
|
|
|
// Call LoadFile with the correct buffer size
|
|
Status = LoadFile->LoadFile(LoadFile, RemainingDevicePath, BootPolicy, &SourceSize, SourceBuffer);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
gBS->AllocatePool(EfiBootServicesData, SourceSize, &SourceBuffer);
|
|
Status = (SourceBuffer == NULL) ?
|
|
EFI_OUT_OF_RESOURCES :
|
|
LoadFile->LoadFile(LoadFile, RemainingDevicePath, BootPolicy, &SourceSize, SourceBuffer);
|
|
}
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
FreeSourceBuffer = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
} // while
|
|
|
|
SrcBuffer = NULL;
|
|
FreeSrcBuffer = FALSE;
|
|
|
|
if (SourceBuffer != NULL) {
|
|
FatHeader = (FAT_HEADER *)SourceBuffer;
|
|
if (FatHeader->Magic == FAT_BINARY_MAGIC) {
|
|
FatArch = (FAT_ARCH *)(FatHeader + 1);
|
|
for (Index = 0; Index < FatHeader->NumFatArch; Index++, FatArch++) {
|
|
#if defined(EFI32) || defined(MDE_CPU_IA32)
|
|
if (FatArch->CpuType == CPU_TYPE_X86 && FatArch->CpuSubtype == CPU_SUBTYPE_I386_ALL)
|
|
#elif defined(EFIX64) || defined(MDE_CPU_X64)
|
|
if (FatArch->CpuType == CPU_TYPE_X86_64 && FatArch->CpuSubtype == CPU_SUBTYPE_I386_ALL)
|
|
#else
|
|
#error "Undefined Platform"
|
|
#endif
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
SourceSize = FatArch->Size;
|
|
gBS->AllocatePool(EfiBootServicesData, SourceSize, &SrcBuffer);
|
|
// ASSERT(SrcBuffer != NULL);
|
|
if (SrcBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
gBS->CopyMem(SrcBuffer, (UINT8 *)SourceBuffer + FatArch->Offset, SourceSize);
|
|
|
|
FreeSrcBuffer = TRUE;
|
|
} else {
|
|
SrcBuffer = SourceBuffer;
|
|
}
|
|
}
|
|
|
|
Status = OrigLoadImage(BootPolicy, ParentImageHandle, FilePath, SrcBuffer, SourceSize, ImageHandle);
|
|
|
|
if (FreeSrcBuffer && SrcBuffer) {
|
|
gBS->FreePool(SrcBuffer);
|
|
}
|
|
|
|
if (FreeSourceBuffer && SourceBuffer) {
|
|
gBS->FreePool(SourceBuffer);
|
|
}
|
|
|
|
//
|
|
// dmazar: some boards do not put device handle to EfiLoadedImageProtocol->DeviceHandle
|
|
// when image is loaded from SrcBuffer, and then boot.efi fails.
|
|
// we'll fix EfiLoadedImageProtocol here.
|
|
//
|
|
if (!EFI_ERROR(Status) && DeviceHandle != 0) {
|
|
Status2 = gBS->OpenProtocol(
|
|
*ImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&Image,
|
|
gImageHandle,
|
|
NULL,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (!EFI_ERROR(Status2) && Image->DeviceHandle != DeviceHandle) {
|
|
Image->DeviceHandle = DeviceHandle;
|
|
Image->FilePath = DuplicateDevicePath(RemainingDevicePath);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|