| /**@file | |
| Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR> | |
| Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR> | |
| Portions copyright (c) 2011 - 2018, ARM Ltd. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PiDxe.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/PeCoffLib.h> | |
| #include <Library/PeCoffExtraActionLib.h> | |
| #include <Library/StandaloneMmMmuLib.h> | |
| typedef RETURN_STATUS (*REGION_PERMISSION_UPDATE_FUNC) ( | |
| IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
| IN UINT64 Length | |
| ); | |
| STATIC | |
| RETURN_STATUS | |
| UpdatePeCoffPermissions ( | |
| IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, | |
| IN REGION_PERMISSION_UPDATE_FUNC NoExecUpdater, | |
| IN REGION_PERMISSION_UPDATE_FUNC ReadOnlyUpdater | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; | |
| EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; | |
| UINTN Size; | |
| UINTN ReadSize; | |
| UINT32 SectionHeaderOffset; | |
| UINTN NumberOfSections; | |
| UINTN Index; | |
| EFI_IMAGE_SECTION_HEADER SectionHeader; | |
| PE_COFF_LOADER_IMAGE_CONTEXT TmpContext; | |
| EFI_PHYSICAL_ADDRESS Base; | |
| // | |
| // We need to copy ImageContext since PeCoffLoaderGetImageInfo () | |
| // will mangle the ImageAddress field | |
| // | |
| CopyMem (&TmpContext, ImageContext, sizeof (TmpContext)); | |
| if (TmpContext.PeCoffHeaderOffset == 0) { | |
| Status = PeCoffLoaderGetImageInfo (&TmpContext); | |
| if (RETURN_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: PeCoffLoaderGetImageInfo () failed (Status = %r)\n", | |
| __func__, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| } | |
| if (TmpContext.IsTeImage && | |
| (TmpContext.ImageAddress == ImageContext->ImageAddress)) | |
| { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: ignoring XIP TE image at 0x%lx\n", | |
| __func__, | |
| ImageContext->ImageAddress | |
| )); | |
| return RETURN_SUCCESS; | |
| } | |
| if (TmpContext.SectionAlignment < EFI_PAGE_SIZE) { | |
| // | |
| // The sections need to be at least 4 KB aligned, since that is the | |
| // granularity at which we can tighten permissions. So just clear the | |
| // noexec permissions on the entire region. | |
| // | |
| if (!TmpContext.IsTeImage) { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: non-TE Image at 0x%lx has SectionAlignment < 4 KB (%lu)\n", | |
| __func__, | |
| ImageContext->ImageAddress, | |
| TmpContext.SectionAlignment | |
| )); | |
| } | |
| Base = ImageContext->ImageAddress & ~(EFI_PAGE_SIZE - 1); | |
| Size = ImageContext->ImageAddress - Base + ImageContext->ImageSize; | |
| return NoExecUpdater (Base, ALIGN_VALUE (Size, EFI_PAGE_SIZE)); | |
| } | |
| // | |
| // Read the PE/COFF Header. For PE32 (32-bit) this will read in too much | |
| // data, but that should not hurt anything. Hdr.Pe32->OptionalHeader.Magic | |
| // determines if this is a PE32 or PE32+ image. The magic is in the same | |
| // location in both images. | |
| // | |
| Hdr.Union = &HdrData; | |
| Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION); | |
| ReadSize = Size; | |
| Status = TmpContext.ImageRead ( | |
| TmpContext.Handle, | |
| TmpContext.PeCoffHeaderOffset, | |
| &Size, | |
| Hdr.Pe32 | |
| ); | |
| if (RETURN_ERROR (Status) || (Size != ReadSize)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: TmpContext.ImageRead () failed (Status = %r)\n", | |
| __func__, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE); | |
| SectionHeaderOffset = TmpContext.PeCoffHeaderOffset + sizeof (UINT32) + | |
| sizeof (EFI_IMAGE_FILE_HEADER); | |
| NumberOfSections = (UINTN)(Hdr.Pe32->FileHeader.NumberOfSections); | |
| switch (Hdr.Pe32->OptionalHeader.Magic) { | |
| case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: | |
| SectionHeaderOffset += Hdr.Pe32->FileHeader.SizeOfOptionalHeader; | |
| break; | |
| case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: | |
| SectionHeaderOffset += Hdr.Pe32Plus->FileHeader.SizeOfOptionalHeader; | |
| break; | |
| default: | |
| ASSERT (FALSE); | |
| } | |
| // | |
| // Iterate over the sections | |
| // | |
| for (Index = 0; Index < NumberOfSections; Index++) { | |
| // | |
| // Read section header from file | |
| // | |
| Size = sizeof (EFI_IMAGE_SECTION_HEADER); | |
| ReadSize = Size; | |
| Status = TmpContext.ImageRead ( | |
| TmpContext.Handle, | |
| SectionHeaderOffset, | |
| &Size, | |
| &SectionHeader | |
| ); | |
| if (RETURN_ERROR (Status) || (Size != ReadSize)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: TmpContext.ImageRead () failed (Status = %r)\n", | |
| __func__, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| Base = TmpContext.ImageAddress + SectionHeader.VirtualAddress; | |
| if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) { | |
| if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_WRITE) == 0) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: Mapping section %d of image at 0x%lx with RO-XN permissions and size 0x%x\n", | |
| __func__, | |
| Index, | |
| Base, | |
| SectionHeader.Misc.VirtualSize | |
| )); | |
| ReadOnlyUpdater (Base, SectionHeader.Misc.VirtualSize); | |
| } else { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: Mapping section %d of image at 0x%lx with RW-XN permissions and size 0x%x\n", | |
| __func__, | |
| Index, | |
| Base, | |
| SectionHeader.Misc.VirtualSize | |
| )); | |
| } | |
| } else { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: Mapping section %d of image at 0x%lx with RO-X permissions and size 0x%x\n", | |
| __func__, | |
| Index, | |
| Base, | |
| SectionHeader.Misc.VirtualSize | |
| )); | |
| ReadOnlyUpdater (Base, SectionHeader.Misc.VirtualSize); | |
| NoExecUpdater (Base, SectionHeader.Misc.VirtualSize); | |
| } | |
| SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); | |
| } | |
| return RETURN_SUCCESS; | |
| } | |
| /** | |
| Performs additional actions after a PE/COFF image has been loaded and relocated. | |
| If ImageContext is NULL, then ASSERT(). | |
| @param ImageContext Pointer to the image context structure that describes the | |
| PE/COFF image that has already been loaded and relocated. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PeCoffLoaderRelocateImageExtraAction ( | |
| IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext | |
| ) | |
| { | |
| UpdatePeCoffPermissions ( | |
| ImageContext, | |
| ArmClearMemoryRegionNoExec, | |
| ArmSetMemoryRegionReadOnly | |
| ); | |
| } | |
| /** | |
| Performs additional actions just before a PE/COFF image is unloaded. Any resources | |
| that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed. | |
| If ImageContext is NULL, then ASSERT(). | |
| @param ImageContext Pointer to the image context structure that describes the | |
| PE/COFF image that is being unloaded. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PeCoffLoaderUnloadImageExtraAction ( | |
| IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext | |
| ) | |
| { | |
| UpdatePeCoffPermissions ( | |
| ImageContext, | |
| ArmSetMemoryRegionNoExec, | |
| ArmClearMemoryRegionReadOnly | |
| ); | |
| } |