mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-01 12:53:27 +01:00
563 lines
14 KiB
C
563 lines
14 KiB
C
/** @file
|
|
UEFI Image Loader library implementation for UE Images.
|
|
|
|
Copyright (c) 2023, Marvin Häuser. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
|
|
#include <Base.h>
|
|
#include <Uefi/UefiBaseType.h>
|
|
#include <Uefi/UefiSpec.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/BaseOverflowLib.h>
|
|
#include <Library/CacheMaintenanceLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/UeImageLib.h>
|
|
#include <Library/UefiImageLib.h>
|
|
#include <Library/UefiImageExtraActionLib.h>
|
|
|
|
#include "UeSupport.h"
|
|
|
|
STATIC CONST UINT16 mPeMachines[] = {
|
|
IMAGE_FILE_MACHINE_I386,
|
|
IMAGE_FILE_MACHINE_X64,
|
|
IMAGE_FILE_MACHINE_ARMTHUMB_MIXED,
|
|
IMAGE_FILE_MACHINE_ARM64,
|
|
IMAGE_FILE_MACHINE_RISCV32,
|
|
IMAGE_FILE_MACHINE_RISCV64,
|
|
IMAGE_FILE_MACHINE_RISCV128
|
|
};
|
|
|
|
RETURN_STATUS
|
|
UefiImageInitializeContextPreHashUe (
|
|
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN CONST VOID *FileBuffer,
|
|
IN UINT32 FileSize,
|
|
IN UINT8 ImageOrigin
|
|
)
|
|
{
|
|
return UeInitializeContextPreHash (&Context->Ctx.Ue, FileBuffer, FileSize);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageInitializeContextPostHashUe (
|
|
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeInitializeContextPostHash (&Context->Ctx.Ue);
|
|
}
|
|
|
|
BOOLEAN
|
|
UefiImageHashImageDefaultUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN OUT VOID *HashContext,
|
|
IN UEFI_IMAGE_LOADER_HASH_UPDATE HashUpdate
|
|
)
|
|
{
|
|
return UeHashImageDefault (&Context->Ctx.Ue, HashContext, HashUpdate);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoadImageUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT VOID *Destination,
|
|
IN UINT32 DestinationSize
|
|
)
|
|
{
|
|
return UeLoadImage (&Context->Ctx.Ue, Destination, DestinationSize);
|
|
}
|
|
|
|
//
|
|
// In-place semantics are currently unsupported.
|
|
//
|
|
|
|
// LCOV_EXCL_START
|
|
BOOLEAN
|
|
UefiImageImageIsInplaceUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoadImageInplaceUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
ASSERT (Context != NULL);
|
|
|
|
if (Context->Ctx.Ue.XIP) {
|
|
Context->Ctx.Ue.ImageBuffer = (UINT8 *) Context->Ctx.Ue.FileBuffer;
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRelocateImageInplaceUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
// LCOV_EXCL_STOP
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoaderGetRuntimeContextSizeUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT32 *Size
|
|
)
|
|
{
|
|
return UeLoaderGetRuntimeContextSize (&Context->Ctx.Ue, Size);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRelocateImageUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT64 BaseAddress,
|
|
OUT VOID *RuntimeContext OPTIONAL,
|
|
IN UINT32 RuntimeContextSize
|
|
)
|
|
{
|
|
return UeRelocateImage (
|
|
&Context->Ctx.Ue,
|
|
BaseAddress,
|
|
(UE_LOADER_RUNTIME_CONTEXT *)RuntimeContext,
|
|
RuntimeContextSize
|
|
);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRuntimeRelocateImageUe (
|
|
IN OUT VOID *Image,
|
|
IN UINT32 ImageSize,
|
|
IN UINT64 BaseAddress,
|
|
IN CONST VOID *RuntimeContext
|
|
)
|
|
{
|
|
return UeRelocateImageForRuntime (
|
|
Image,
|
|
ImageSize,
|
|
(UE_LOADER_RUNTIME_CONTEXT *)RuntimeContext,
|
|
BaseAddress
|
|
);
|
|
}
|
|
|
|
VOID
|
|
UefiImageDiscardSegmentsUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
//
|
|
// Anything discardable is not loaded in the first place.
|
|
//
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetSymbolsPathUe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT CONST CHAR8 **SymbolsPath,
|
|
OUT UINT32 *SymbolsPathSize
|
|
)
|
|
{
|
|
return UeGetSymbolsPath (&Context->Ctx.Ue, SymbolsPath, SymbolsPathSize);
|
|
}
|
|
|
|
//
|
|
// UE does not support embedded certificates (yet).
|
|
//
|
|
|
|
// LCOV_EXCL_START
|
|
RETURN_STATUS
|
|
UefiImageGetFirstCertificateUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT CONST WIN_CERTIFICATE **Certificate
|
|
)
|
|
{
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetNextCertificateUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN OUT CONST WIN_CERTIFICATE **Certificate
|
|
)
|
|
{
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetHiiDataRvaUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT32 *HiiRva,
|
|
OUT UINT32 *HiiSize
|
|
)
|
|
{
|
|
//
|
|
// UE does not support legacy HII.
|
|
//
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
// LCOV_EXCL_STOP
|
|
|
|
UINT32
|
|
UefiImageGetEntryPointAddressUe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetEntryPointAddress (&Context->Ctx.Ue);
|
|
}
|
|
|
|
UINT16
|
|
UefiImageGetMachineUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
UINT16 UeMachine;
|
|
|
|
UeMachine = UeGetMachine (&Context->Ctx.Ue);
|
|
if (UeMachine >= ARRAY_SIZE (mPeMachines)) {
|
|
DEBUG_RAISE ();
|
|
return 0xFFFF;
|
|
}
|
|
|
|
return mPeMachines[UeMachine];
|
|
}
|
|
|
|
UINT16
|
|
UefiImageGetSubsystemUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetSubsystem (&Context->Ctx.Ue) + 10;
|
|
}
|
|
|
|
UINT32
|
|
UefiImageGetSegmentAlignmentUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetSegmentAlignment (&Context->Ctx.Ue);
|
|
}
|
|
|
|
UINT32
|
|
UefiImageGetImageSizeUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetImageSize (&Context->Ctx.Ue);
|
|
}
|
|
|
|
UINT64
|
|
UefiImageGetBaseAddressUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetBaseAddress (&Context->Ctx.Ue);
|
|
}
|
|
|
|
BOOLEAN
|
|
UefiImageGetRelocsStrippedUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeGetRelocsStripped (&Context->Ctx.Ue);
|
|
}
|
|
|
|
UINTN
|
|
UefiImageLoaderGetImageAddressUe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeLoaderGetImageAddress (&Context->Ctx.Ue);
|
|
}
|
|
|
|
UINTN
|
|
UefiImageLoaderGetDebugAddressUe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return UeLoaderGetImageDebugAddress (&Context->Ctx.Ue);
|
|
}
|
|
|
|
STATIC
|
|
UINT32
|
|
InternalPermissionsToAttributes (
|
|
IN UINT8 Permissions
|
|
)
|
|
{
|
|
switch (Permissions) {
|
|
case UeSegmentPermX:
|
|
{
|
|
return EFI_MEMORY_RP | EFI_MEMORY_RO;
|
|
}
|
|
|
|
case UeSegmentPermRX:
|
|
{
|
|
return EFI_MEMORY_RO;
|
|
}
|
|
|
|
case UeSegmentPermRW:
|
|
{
|
|
return EFI_MEMORY_XP;
|
|
}
|
|
|
|
case UeSegmentPermR:
|
|
default:
|
|
{
|
|
ASSERT (Permissions == UeSegmentPermR);
|
|
return EFI_MEMORY_XP | EFI_MEMORY_RO;
|
|
}
|
|
}
|
|
}
|
|
|
|
UEFI_IMAGE_RECORD *
|
|
UefiImageLoaderGetImageRecordUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
UEFI_IMAGE_RECORD *ImageRecord;
|
|
UINTN ImageAddress;
|
|
UINT32 ImageSize;
|
|
UINT32 ImageSizeRecord;
|
|
UINT32 NumRecordSegments;
|
|
UEFI_IMAGE_RECORD_SEGMENT *RecordSegment;
|
|
UINT16 NumSegments;
|
|
UINT8 SegmentIterSize;
|
|
CONST UINT32 *SegmentImageInfos;
|
|
CONST UINT8 *SegmentImageInfoPtr;
|
|
UINT32 SegmentImageInfo;
|
|
UINT32 SegmentSize;
|
|
UINT8 SegmentPermissions;
|
|
UINT32 RangeSize;
|
|
UINT8 Permissions;
|
|
|
|
ASSERT (Context != NULL);
|
|
|
|
NumSegments = UeGetSegmentImageInfos (
|
|
&Context->Ctx.Ue,
|
|
&SegmentImageInfos,
|
|
&SegmentIterSize
|
|
);
|
|
|
|
ImageRecord = AllocatePool (
|
|
sizeof (*ImageRecord)
|
|
+ NumSegments * sizeof (*ImageRecord->Segments)
|
|
);
|
|
if (ImageRecord == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ImageRecord->Signature = UEFI_IMAGE_RECORD_SIGNATURE;
|
|
InitializeListHead (&ImageRecord->Link);
|
|
|
|
SegmentImageInfo = *SegmentImageInfos;
|
|
|
|
RangeSize = UE_SEGMENT_SIZE (SegmentImageInfo);
|
|
Permissions = UE_SEGMENT_PERMISSIONS (SegmentImageInfo);
|
|
|
|
ImageSizeRecord = RangeSize;
|
|
NumRecordSegments = 0;
|
|
|
|
STATIC_ASSERT (
|
|
OFFSET_OF (UE_SEGMENT, ImageInfo) == 0 &&
|
|
OFFSET_OF (UE_SEGMENT, ImageInfo) == OFFSET_OF (UE_SEGMENT_XIP, ImageInfo),
|
|
"Below's logic assumes the given layout."
|
|
);
|
|
|
|
for (
|
|
SegmentImageInfoPtr = (CONST UINT8 *) SegmentImageInfos + SegmentIterSize;
|
|
SegmentImageInfoPtr < (CONST UINT8 *) SegmentImageInfos + (UINT32) SegmentIterSize * NumSegments;
|
|
ImageSizeRecord += SegmentSize,
|
|
SegmentImageInfoPtr += SegmentIterSize
|
|
) {
|
|
SegmentImageInfo = *(CONST UINT32 *) SegmentImageInfoPtr;
|
|
|
|
SegmentSize = UE_SEGMENT_SIZE (SegmentImageInfo);
|
|
SegmentPermissions = UE_SEGMENT_PERMISSIONS (SegmentImageInfo);
|
|
//
|
|
// Skip Image segments with the same memory permissions as the current range
|
|
// as they can be merged.
|
|
//
|
|
if (SegmentPermissions == Permissions) {
|
|
RangeSize += SegmentSize;
|
|
continue;
|
|
}
|
|
//
|
|
// Create an Image record LoadTable for the current memory permission range.
|
|
//
|
|
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
|
|
RecordSegment->Size = RangeSize;
|
|
RecordSegment->Attributes = InternalPermissionsToAttributes (Permissions);
|
|
++NumRecordSegments;
|
|
//
|
|
// Start a Image record LoadTable with the current Image LoadTable.
|
|
//
|
|
RangeSize = SegmentSize;
|
|
Permissions = SegmentPermissions;
|
|
}
|
|
//
|
|
// Create an Image record LoadTable for the current memory permission range.
|
|
//
|
|
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
|
|
RecordSegment->Size = RangeSize;
|
|
RecordSegment->Attributes = InternalPermissionsToAttributes (Permissions);
|
|
++NumRecordSegments;
|
|
|
|
ImageAddress = UeLoaderGetImageAddress (&Context->Ctx.Ue);
|
|
ImageSize = UeGetImageSize (&Context->Ctx.Ue);
|
|
ASSERT (ImageSize == ImageSizeRecord);
|
|
|
|
ImageRecord->NumSegments = NumRecordSegments;
|
|
ImageRecord->StartAddress = ImageAddress;
|
|
ImageRecord->EndAddress = ImageAddress + ImageSize;
|
|
//
|
|
// Zero the remaining array entries to avoid uninitialised data.
|
|
//
|
|
ZeroMem (
|
|
ImageRecord->Segments + NumRecordSegments,
|
|
(NumSegments - NumRecordSegments) * sizeof (*ImageRecord->Segments)
|
|
);
|
|
|
|
return ImageRecord;
|
|
}
|
|
|
|
// LCOV_EXCL_START
|
|
RETURN_STATUS
|
|
UefiImageDebugLocateImageUe (
|
|
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN Address,
|
|
IN UINT8 ImageOrigin
|
|
)
|
|
{
|
|
ASSERT (Context != NULL);
|
|
(VOID) Address;
|
|
//
|
|
// UE does not support this feature.
|
|
//
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
// LCOV_EXCL_STOP
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetFixedAddressUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT64 *Address
|
|
)
|
|
{
|
|
BOOLEAN FixedAddress;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Address != NULL);
|
|
|
|
FixedAddress = UeGetFixedAddress (&Context->Ctx.Ue);
|
|
if (!FixedAddress) {
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
|
|
*Address = Context->Ctx.Ue.BaseAddress;
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
UefiImageDebugPrintSegmentsUe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
CONST CHAR8 *Name;
|
|
CONST UE_SEGMENT *Segments;
|
|
UINT16 NumSegments;
|
|
UINT16 SegmentIndex;
|
|
UINT32 SegmentFileOffset;
|
|
UINT32 SegmentImageAddress;
|
|
CONST UE_SEGMENT_NAME *NameTable;
|
|
UINT32 ImageSize;
|
|
|
|
NumSegments = UeGetSegments (&Context->Ctx.Ue, &Segments);
|
|
|
|
Status = UeGetSegmentNames (&Context->Ctx.Ue, &NameTable);
|
|
if (RETURN_ERROR (Status)) {
|
|
NameTable = NULL;
|
|
}
|
|
|
|
SegmentFileOffset = Context->Ctx.Ue.SegmentsFileOffset;
|
|
//
|
|
// The first Image segment must begin the Image memory space.
|
|
//
|
|
SegmentImageAddress = 0;
|
|
|
|
for (SegmentIndex = 0; SegmentIndex < NumSegments; ++SegmentIndex) {
|
|
if (NameTable != NULL) {
|
|
Name = (CONST CHAR8 *)NameTable[SegmentIndex];
|
|
} else {
|
|
STATIC_ASSERT (
|
|
sizeof (*NameTable) == sizeof ("Unknown"),
|
|
"The following may cause prohibited memory accesses."
|
|
);
|
|
|
|
Name = "Unknown";
|
|
}
|
|
|
|
ImageSize = UE_SEGMENT_SIZE (Segments[SegmentIndex].ImageInfo);
|
|
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
" Segment - '%c%c%c%c%c%c%c%c'\n"
|
|
" ImageAddress - 0x%08x\n"
|
|
" ImageSize - 0x%08x\n"
|
|
" FileOffset - 0x%08x\n"
|
|
" FileSize - 0x%08x\n"
|
|
" Permissions - 0x%08x\n",
|
|
Name[0], Name[1], Name[2], Name[3], Name[4], Name[5], Name[6], Name[7],
|
|
SegmentImageAddress,
|
|
ImageSize,
|
|
SegmentFileOffset,
|
|
Segments[SegmentIndex].FileSize,
|
|
UE_SEGMENT_PERMISSIONS (Segments[SegmentIndex].ImageInfo)
|
|
));
|
|
|
|
SegmentImageAddress += ImageSize;
|
|
SegmentFileOffset += Segments[SegmentIndex].FileSize;
|
|
}
|
|
}
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST UEFI_IMAGE_FORMAT_SUPPORT mUeSupport = {
|
|
UefiImageInitializeContextPreHashUe,
|
|
UefiImageHashImageDefaultUe,
|
|
UefiImageInitializeContextPostHashUe,
|
|
UefiImageLoadImageUe,
|
|
UefiImageImageIsInplaceUe,
|
|
UefiImageLoadImageInplaceUe,
|
|
UefiImageRelocateImageInplaceUe,
|
|
UefiImageLoaderGetRuntimeContextSizeUe,
|
|
UefiImageRelocateImageUe,
|
|
UefiImageRuntimeRelocateImageUe,
|
|
UefiImageDiscardSegmentsUe,
|
|
UefiImageGetSymbolsPathUe,
|
|
UefiImageGetFirstCertificateUe,
|
|
UefiImageGetNextCertificateUe,
|
|
UefiImageGetHiiDataRvaUe,
|
|
UefiImageGetEntryPointAddressUe,
|
|
UefiImageGetMachineUe,
|
|
UefiImageGetSubsystemUe,
|
|
UefiImageGetSegmentAlignmentUe,
|
|
UefiImageGetImageSizeUe,
|
|
UefiImageGetBaseAddressUe,
|
|
UefiImageGetRelocsStrippedUe,
|
|
UefiImageLoaderGetImageAddressUe,
|
|
UefiImageLoaderGetDebugAddressUe,
|
|
UefiImageLoaderGetImageRecordUe,
|
|
UefiImageDebugLocateImageUe,
|
|
UefiImageGetFixedAddressUe,
|
|
UefiImageDebugPrintSegmentsUe
|
|
};
|