Hello all,

This is a proof of concept patch that fixes the problems that occur when
enabling the EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA
MemoryProtectionAttribute, which may split PE/COFF memory images into disjoint
regions in virtual memory.

It is not a complete solution, but it works both on AARCH64 (ArmVirtQemu) and
X64 (Ovmf). With the 4 KB alignment patch and Laszlo's end-of-DXE series 
applied,
I can boot into the Linux kernel (Ubuntu 3.13.0-55-generic) without problems,
while the memory map reveals that the splitting has in fact occurred.

Note that this may require 64 KB section alignment for non-ELF based toolchains,
if they may potentially emit EFI_IMAGE_REL_BASED_LOW or EFI_IMAGE_REL_BASED_HIGH
relocations. For toolchains that rely on GenFw for the ELF to PE/COFF
conversion, this is not required.

------------------8<----------------------

When reapplying PE/COFF relocations for the switch to virtual mode, we
should not assume that the virtual memory image is identical to the
PE/COFF file image, since PE/COFF may no longer be adjacent in virtual
memory.

So instead of using a fixed adjustment derived from the virtual address of
ImageBase, apply ConvertPointer () to each relocation target to obtain the
new value.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheu...@linaro.org>
---
 MdeModulePkg/Core/RuntimeDxe/Runtime.c | 229 +++++++++++++++++++-
 1 file changed, 227 insertions(+), 2 deletions(-)

diff --git a/MdeModulePkg/Core/RuntimeDxe/Runtime.c 
b/MdeModulePkg/Core/RuntimeDxe/Runtime.c
index c61301cf80d8..c8dbfdd5a3ef 100644
--- a/MdeModulePkg/Core/RuntimeDxe/Runtime.c
+++ b/MdeModulePkg/Core/RuntimeDxe/Runtime.c
@@ -213,6 +213,232 @@ RuntimeDriverConvertInternalPointer (
 }
 
 /**
+  Reapply fixups on a fixed up PE32/PE32+ image to allow virutal calling at EFI
+  runtime.
+
+  This function reapplies relocation fixups to the PE/COFF image specified by 
ImageBase
+  and ImageSize so the image will execute correctly when the PE/COFF image is 
mapped
+  to the address specified by VirtualImageBase.  RelocationData must be 
identical
+  to the FixupData buffer from the PE_COFF_LOADER_IMAGE_CONTEXT structure
+  after this PE/COFF image was relocated with 
BasePecoffLib::PeCoffLoaderRelocateImage().
+
+  Note that if the platform does not maintain coherency between the 
instruction cache(s) and the data
+  cache(s) in hardware, then the caller is responsible for performing cache 
maintenance operations
+  prior to transferring control to a PE/COFF image that is loaded using this 
funation.
+
+  @param  ImageBase          The base address of a PE/COFF image that has been 
loaded
+                             and relocated into system memory.
+  @param  ImageSize          The size, in bytes, of the PE/COFF image.
+  @param  RelocationData     A pointer to the relocation data that was 
collected when the PE/COFF
+                             image was relocated using 
PeCoffLoaderRelocateImage().
+
+  @return EFI_SUCCESS        The fixups have been reapplied successfully.
+
+  @return EFI_UNSUPPORTED    ImageBase does not point to a valid PE image
+  @return EFI_UNSUPPORTED    No reloc directory found or contents invalid
+  @return EFI_UNSUPPORTED    Image uses high/low relocations and section 
alignment < 64 KB
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+RuntimeDriverRelocateImageForRuntime (
+  IN  PHYSICAL_ADDRESS        ImageBase,
+  IN  UINTN                   ImageSize,
+  IN  VOID                    *RelocationData
+  )
+{
+  CHAR8                               *OldBase;
+  EFI_IMAGE_DOS_HEADER                *DosHdr;
+  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
+  UINT32                              NumberOfRvaAndSizes;
+  EFI_IMAGE_DATA_DIRECTORY            *DataDirectory;
+  EFI_IMAGE_DATA_DIRECTORY            *RelocDir;
+  EFI_IMAGE_BASE_RELOCATION           *RelocBase;
+  EFI_IMAGE_BASE_RELOCATION           *RelocBaseEnd;
+  UINT16                              *Reloc;
+  UINT16                              *RelocEnd;
+  CHAR8                               *Fixup;
+  CHAR8                               *FixupBase;
+  UINT16                              *Fixup16;
+  UINT32                              *Fixup32;
+  UINT64                              *Fixup64;
+  CHAR8                               *FixupData;
+  UINT16                              Magic;
+  UINTN                               ConvertAddress;
+
+  OldBase = (CHAR8 *)((UINTN)ImageBase);
+
+  //
+  // Find the image's relocate dir info
+  //
+  DosHdr = (EFI_IMAGE_DOS_HEADER *)OldBase;
+  if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
+    //
+    // Valid DOS header so get address of PE header
+    //
+    Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(((CHAR8 *)DosHdr) + 
DosHdr->e_lfanew);
+  } else {
+    //
+    // No Dos header so assume image starts with PE header.
+    //
+    Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)OldBase;
+  }
+
+  if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+    //
+    // Not a valid PE image so Exit
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  Magic = Hdr.Pe32->OptionalHeader.Magic;
+
+  if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+    //
+    // Use PE32 offset
+    //
+    NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
+    DataDirectory = (EFI_IMAGE_DATA_DIRECTORY 
*)&(Hdr.Pe32->OptionalHeader.DataDirectory[0]);
+  } else {
+    //
+    // Use PE32+ offset
+    //
+    NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
+    DataDirectory = (EFI_IMAGE_DATA_DIRECTORY 
*)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[0]);
+  }
+
+  //
+  // Find the relocation block
+  //
+  // Per the PE/COFF spec, you can't assume that a given data directory
+  // is present in the image. You have to check the NumberOfRvaAndSizes in
+  // the optional header to verify a desired directory entry is there.
+  //
+  if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
+    RelocDir      = DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC;
+    RelocBase     = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(ImageBase + 
RelocDir->VirtualAddress);
+    RelocBaseEnd  = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(ImageBase + 
RelocDir->VirtualAddress + RelocDir->Size);
+  } else {
+    //
+    // Cannot find relocations, cannot continue to relocate the image, ASSERT 
for this invalid image.
+    //
+    ASSERT (FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // ASSERT for the invalid image when RelocBase and RelocBaseEnd are both 
NULL.
+  //
+  ASSERT (RelocBase != NULL && RelocBaseEnd != NULL);
+
+  //
+  // Run the whole relocation block. And re-fixup data that has not been
+  // modified. The FixupData is used to see if the image has been modified
+  // since it was relocated. This is so data sections that have been updated
+  // by code will not be fixed up, since that would set them back to
+  // defaults.
+  //
+  FixupData = RelocationData;
+  while (RelocBase < RelocBaseEnd) {
+    //
+    // Add check for RelocBase->SizeOfBlock field.
+    //
+    if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > 
RelocDir->Size)) {
+      //
+      // Data invalid, cannot continue to relocate the image, just return.
+      //
+      return EFI_UNSUPPORTED;
+    }
+
+    Reloc     = (UINT16 *) ((UINT8 *) RelocBase + sizeof 
(EFI_IMAGE_BASE_RELOCATION));
+    RelocEnd  = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock);
+    FixupBase = (CHAR8 *) ((UINTN)ImageBase) + RelocBase->VirtualAddress;
+
+    //
+    // Run this relocation record
+    //
+    while (Reloc < RelocEnd) {
+
+      Fixup = FixupBase + (*Reloc & 0xFFF);
+      switch ((*Reloc) >> 12) {
+
+      case EFI_IMAGE_REL_BASED_ABSOLUTE:
+        break;
+
+      case EFI_IMAGE_REL_BASED_HIGH:
+        //
+        // In order to be able to apply relocations to code and data regions
+        // that may be disjoint in the virtual mapping, we disallow relocations
+        // of type EFI_IMAGE_REL_BASED_HIGH or EFI_IMAGE_REL_BASED_LOW, unless
+        // the section alignment is at least 64 KB.
+        //
+        if (Hdr.Pe32->OptionalHeader.SectionAlignment < SIZE_64KB) {
+          return EFI_UNSUPPORTED;
+        }
+        Fixup16 = (UINT16 *) Fixup;
+        if (*(UINT16 *) FixupData == *Fixup16) {
+          ConvertAddress = (UINTN) *Fixup16 << 16;
+          RuntimeDriverConvertInternalPointer ((VOID **) &ConvertAddress);
+          *Fixup16 = (UINT16) (ConvertAddress >> 16);
+        }
+
+        FixupData = FixupData + sizeof (UINT16);
+        break;
+
+      case EFI_IMAGE_REL_BASED_LOW:
+        if (Hdr.Pe32->OptionalHeader.SectionAlignment < SIZE_64KB) {
+          return EFI_UNSUPPORTED;
+        }
+        //
+        // Since the adjustment is guaranteed to be a multiple of 64 KB,
+        // there is nothing to do here.
+        //
+        break;
+
+      case EFI_IMAGE_REL_BASED_HIGHLOW:
+        Fixup32       = (UINT32 *) Fixup;
+        FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
+        if (*(UINT32 *) FixupData == *Fixup32) {
+          ConvertAddress = (UINTN) *Fixup32;
+          RuntimeDriverConvertInternalPointer ((VOID **) &ConvertAddress);
+          *Fixup32 = (UINT32) ConvertAddress;
+        }
+
+        FixupData = FixupData + sizeof (UINT32);
+        break;
+
+      case EFI_IMAGE_REL_BASED_DIR64:
+        Fixup64       = (UINT64 *)Fixup;
+        FixupData = ALIGN_POINTER (FixupData, sizeof (UINT64));
+        if (*(UINT64 *) FixupData == *Fixup64) {
+          ConvertAddress = (UINTN) *Fixup64;
+          RuntimeDriverConvertInternalPointer ((VOID **) &ConvertAddress);
+          *Fixup64 = (UINT64) ConvertAddress;
+        }
+
+        FixupData = FixupData + sizeof (UINT64);
+        break;
+
+      default:
+        ASSERT (FALSE);
+      }
+      //
+      // Next relocation record
+      //
+      Reloc += 1;
+    }
+    //
+    // next reloc block
+    //
+    RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
 
   Changes the runtime addressing mode of EFI firmware from physical to virtual.
 
@@ -312,9 +538,8 @@ RuntimeDriverSetVirtualAddressMap (
       Status  = RuntimeDriverConvertPointer (0, (VOID **) &VirtImageBase);
       ASSERT_EFI_ERROR (Status);
 
-      PeCoffLoaderRelocateImageForRuntime (
+      RuntimeDriverRelocateImageForRuntime (
         (EFI_PHYSICAL_ADDRESS) (UINTN) RuntimeImage->ImageBase,
-        VirtImageBase,
         (UINTN) RuntimeImage->ImageSize,
         RuntimeImage->RelocationData
         );
-- 
1.9.1


------------------------------------------------------------------------------
Don't Limit Your Business. Reach for the Cloud.
GigeNET's Cloud Solutions provide you with the tools and support that
you need to offload your IT needs and focus on growing your business.
Configured For All Businesses. Start Your Cloud Today.
https://www.gigenetcloud.com/
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to