This is the UEFI counterpart to my Linux series which generalizes
mixed mode support into a feature that requires very little internal
knowledge about the architecture specifics of booting Linux on the
part of the bootloader or firmware.
Instead, we add a .compat PE/COFF header containing an array of
PE_COMPAT nodes containing <machine type, entrypoint> tuples that
describe alternate entrypoints into the image for different native
machine types, e.g., IA-32 in a 64-bit image so it can be booted
from IA-32 firmware.
This patch implements the PE/COFF emulator protocol to take this new
section into account, so that such images can simply be loaded via
LoadImage/StartImage, e.g., straight from the shell.
This feature is based on the EDK2 specific PE/COFF emulator protocol
that was introduced in commit 57df17fe26 ("MdeModulePkg/DxeCore:
invoke the emulator protocol for foreign images", 2019-04-14).
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2564
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: Laszlo Ersek <lersek@redhat.com>
		
	
		
			
				
	
	
		
			144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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);
 | |
| }
 |