blob: 7a5bb412d0d2d789348dac9a05875ca5e9575fc2 [file] [log] [blame]
/**@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
);
}