From patchwork Thu Jul 2 12:31:24 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 50574 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f199.google.com (mail-lb0-f199.google.com [209.85.217.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id CDC0E218E1 for ; Thu, 2 Jul 2015 12:31:56 +0000 (UTC) Received: by lbcpe5 with SMTP id pe5sf12992237lbc.3 for ; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id:subject :precedence:reply-to:list-id:list-unsubscribe:list-archive:list-post :list-help:list-subscribe:mime-version:content-type :content-transfer-encoding:errors-to:x-original-sender :x-original-authentication-results:mailing-list; bh=3hoKNReoUP66Mm0gKmRtkYilSUAds9Y2QMTZclbACTU=; b=DBJVAgUDvnXfOmJjHnCqp6mOF4Ppg5a9hUyYKy02t8N9RBP/low24SHtG2dk7siegG C7OrJcvGwvsxfau6n+kh/At9QGzNsM16FRQeCXtVy3lxahEjQcQmslw5FYodZLmv2Zhn ZyzK57t6tvpS0QS3Lk0Lx/kdU8iQDI1Hyvd9k1E4L5C+esFw9+Pb3mKn4Pl4BUOIliXp BviEJLS2yi0HMaGk7Os1Nicn6BT4EPnQ5+d+AQwJOcMCasqfjIP7QyFN5jlev7nLRwRb Mac140GVTAXs3007VK+flrqRH66Uh6bOfAawDAuMcF4t8WuTOgTRwp04ZXS32MpoVUqb 1IeA== X-Gm-Message-State: ALoCoQmjU7U/CRVZAw8nqyztxsEJdu638a/K7YL0VhpcjwZJEO3mHVLsBJijpic8nviKhRhLMMBg X-Received: by 10.180.35.162 with SMTP id i2mr17642283wij.6.1435840315773; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.203.197 with SMTP id ks5ls340434lac.64.gmail; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) X-Received: by 10.112.10.138 with SMTP id i10mr23328186lbb.43.1435840315604; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) Received: from mail-lb0-f173.google.com (mail-lb0-f173.google.com. [209.85.217.173]) by mx.google.com with ESMTPS id t12si4337902lby.128.2015.07.02.05.31.55 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jul 2015 05:31:55 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.173 as permitted sender) client-ip=209.85.217.173; Received: by lbcpe5 with SMTP id pe5so30976802lbc.2 for ; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) X-Received: by 10.152.22.99 with SMTP id c3mr30924938laf.32.1435840315479; Thu, 02 Jul 2015 05:31:55 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.108.230 with SMTP id hn6csp447447lbb; Thu, 2 Jul 2015 05:31:54 -0700 (PDT) X-Received: by 10.43.12.136 with SMTP id pi8mr11694423icb.6.1435840313452; Thu, 02 Jul 2015 05:31:53 -0700 (PDT) Received: from lists.sourceforge.net (lists.sourceforge.net. [216.34.181.88]) by mx.google.com with ESMTPS id 69si5633844ior.53.2015.07.02.05.31.52 (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 02 Jul 2015 05:31:53 -0700 (PDT) Received-SPF: pass (google.com: domain of edk2-devel-bounces@lists.sourceforge.net designates 216.34.181.88 as permitted sender) client-ip=216.34.181.88; Received: from localhost ([127.0.0.1] helo=sfs-ml-3.v29.ch3.sourceforge.com) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1ZAdeV-0002n7-TT; Thu, 02 Jul 2015 12:31:39 +0000 Received: from sog-mx-3.v43.ch3.sourceforge.com ([172.29.43.193] helo=mx.sourceforge.net) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1ZAdeU-0002n2-Ii for edk2-devel@lists.sourceforge.net; Thu, 02 Jul 2015 12:31:38 +0000 Received-SPF: pass (sog-mx-3.v43.ch3.sourceforge.com: domain of linaro.org designates 209.85.212.180 as permitted sender) client-ip=209.85.212.180; envelope-from=ard.biesheuvel@linaro.org; helo=mail-wi0-f180.google.com; Received: from mail-wi0-f180.google.com ([209.85.212.180]) by sog-mx-3.v43.ch3.sourceforge.com with esmtps (TLSv1:RC4-SHA:128) (Exim 4.76) id 1ZAdeT-00004q-5I for edk2-devel@lists.sourceforge.net; Thu, 02 Jul 2015 12:31:38 +0000 Received: by wicgi11 with SMTP id gi11so72310918wic.0 for ; Thu, 02 Jul 2015 05:31:31 -0700 (PDT) X-Received: by 10.194.62.228 with SMTP id b4mr11364320wjs.2.1435840291073; Thu, 02 Jul 2015 05:31:31 -0700 (PDT) Received: from localhost.localdomain (ip16-2-212-87.adsl2.static.versatel.nl. [87.212.2.16]) by mx.google.com with ESMTPSA id l14sm7946895wjq.21.2015.07.02.05.31.29 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 02 Jul 2015 05:31:30 -0700 (PDT) From: Ard Biesheuvel To: edk2-devel@lists.sourceforge.net, matt.fleming@intel.com, jordan.l.justen@intel.com, liming.gao@intel.com, jiewen.yao@intel.com Date: Thu, 2 Jul 2015 14:31:24 +0200 Message-Id: <1435840284-20138-1-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 1.9.1 X-Spam-Score: -1.5 (-) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -1.5 SPF_CHECK_PASS SPF reports sender host as permitted sender for sender-domain -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1ZAdeT-00004q-5I Subject: [edk2] [RFC PATCH] MdeModulePkg: account for disjoint code and data regions in runtime relocations X-BeenThere: edk2-devel@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list Reply-To: edk2-devel@lists.sourceforge.net List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Errors-To: edk2-devel-bounces@lists.sourceforge.net X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ard.biesheuvel@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.173 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 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 --- 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 );