| /** @file | |
| * PE/COFF emulator protocol implementation to start Linux kernel | |
| * images from non-native firmware | |
| * | |
| * Copyright (c) 2020, ARM Ltd. All rights reserved.<BR> | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause-Patent | |
| * | |
| */ | |
| #include <PiDxe.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/PeCoffLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Protocol/PeCoffImageEmulator.h> | |
| #pragma pack (1) | |
| typedef struct { | |
| UINT8 Type; | |
| UINT8 Size; | |
| UINT16 MachineType; | |
| UINT32 EntryPoint; | |
| } PE_COMPAT_TYPE1; | |
| #pragma pack () | |
| STATIC | |
| BOOLEAN | |
| EFIAPI | |
| IsImageSupported ( | |
| IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, | |
| IN UINT16 ImageType, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL | |
| ) | |
| { | |
| return ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION; | |
| } | |
| STATIC | |
| EFI_IMAGE_ENTRY_POINT | |
| EFIAPI | |
| GetCompatEntryPoint ( | |
| IN EFI_PHYSICAL_ADDRESS ImageBase | |
| ) | |
| { | |
| EFI_IMAGE_DOS_HEADER *DosHdr; | |
| UINTN PeCoffHeaderOffset; | |
| EFI_IMAGE_NT_HEADERS32 *Pe32; | |
| EFI_IMAGE_SECTION_HEADER *Section; | |
| UINTN NumberOfSections; | |
| PE_COMPAT_TYPE1 *PeCompat; | |
| UINTN PeCompatEnd; | |
| DosHdr = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageBase; | |
| if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { | |
| return NULL; | |
| } | |
| PeCoffHeaderOffset = DosHdr->e_lfanew; | |
| Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)ImageBase + PeCoffHeaderOffset); | |
| Section = (EFI_IMAGE_SECTION_HEADER *)((UINTN)&Pe32->OptionalHeader + | |
| Pe32->FileHeader.SizeOfOptionalHeader); | |
| NumberOfSections = (UINTN)Pe32->FileHeader.NumberOfSections; | |
| while (NumberOfSections--) { | |
| if (!CompareMem (Section->Name, ".compat", sizeof (Section->Name))) { | |
| // | |
| // Dereference the section contents to find the mixed mode entry point | |
| // | |
| PeCompat = (PE_COMPAT_TYPE1 *)((UINTN)ImageBase + Section->VirtualAddress); | |
| PeCompatEnd = (UINTN)(VOID *)PeCompat + Section->Misc.VirtualSize; | |
| while (PeCompat->Type != 0 && (UINTN)(VOID *)PeCompat < PeCompatEnd) { | |
| if ((PeCompat->Type == 1) && | |
| (PeCompat->Size >= sizeof (PE_COMPAT_TYPE1)) && | |
| EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeCompat->MachineType)) | |
| { | |
| return (EFI_IMAGE_ENTRY_POINT)((UINTN)ImageBase + PeCompat->EntryPoint); | |
| } | |
| PeCompat = (PE_COMPAT_TYPE1 *)((UINTN)PeCompat + PeCompat->Size); | |
| ASSERT ((UINTN)(VOID *)PeCompat < PeCompatEnd); | |
| } | |
| } | |
| Section++; | |
| } | |
| return NULL; | |
| } | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| RegisterImage ( | |
| IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, | |
| IN EFI_PHYSICAL_ADDRESS ImageBase, | |
| IN UINT64 ImageSize, | |
| IN OUT EFI_IMAGE_ENTRY_POINT *EntryPoint | |
| ) | |
| { | |
| EFI_IMAGE_ENTRY_POINT CompatEntryPoint; | |
| CompatEntryPoint = GetCompatEntryPoint (ImageBase); | |
| if (CompatEntryPoint == NULL) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| *EntryPoint = CompatEntryPoint; | |
| return EFI_SUCCESS; | |
| } | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| UnregisterImage ( | |
| IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, | |
| IN EFI_PHYSICAL_ADDRESS ImageBase | |
| ) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| STATIC EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL mCompatLoaderPeCoffEmuProtocol = { | |
| IsImageSupported, | |
| RegisterImage, | |
| UnregisterImage, | |
| EDKII_PECOFF_IMAGE_EMULATOR_VERSION, | |
| EFI_IMAGE_MACHINE_X64 | |
| }; | |
| EFI_STATUS | |
| EFIAPI | |
| CompatImageLoaderDxeEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| return gBS->InstallProtocolInterface ( | |
| &ImageHandle, | |
| &gEdkiiPeCoffImageEmulatorProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| &mCompatLoaderPeCoffEmuProtocol | |
| ); | |
| } |