In commit 7eb927db3e ("MdeModulePkg/DxeCore: implement memory protection
policy", 2017-02-24), we added two informative messages with the
InitializeDxeNxMemoryProtectionPolicy() function:
> InitializeDxeNxMemoryProtectionPolicy: applying strict permissions to
> active memory regions
and
> InitializeDxeNxMemoryProtectionPolicy: applying strict permissions to
> inactive memory regions
The messages don't report errors or warnings, thus downgrade their log
masks from DEBUG_ERROR to DEBUG_INFO.
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1520485
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Star Zeng <star.zeng@intel.com>
		
	
		
			
				
	
	
		
			1215 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1215 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  UEFI Memory Protection support.
 | 
						|
 | 
						|
  If the UEFI image is page aligned, the image code section is set to read only
 | 
						|
  and the image data section is set to non-executable.
 | 
						|
 | 
						|
  1) This policy is applied for all UEFI image including boot service driver,
 | 
						|
     runtime driver or application.
 | 
						|
  2) This policy is applied only if the UEFI image meets the page alignment
 | 
						|
     requirement.
 | 
						|
  3) This policy is applied only if the Source UEFI image matches the
 | 
						|
     PcdImageProtectionPolicy definition.
 | 
						|
  4) This policy is not applied to the non-PE image region.
 | 
						|
 | 
						|
  The DxeCore calls CpuArchProtocol->SetMemoryAttributes() to protect
 | 
						|
  the image. If the CpuArch protocol is not installed yet, the DxeCore
 | 
						|
  enqueues the protection request. Once the CpuArch is installed, the
 | 
						|
  DxeCore dequeues the protection request and applies policy.
 | 
						|
 | 
						|
  Once the image is unloaded, the protection is removed automatically.
 | 
						|
 | 
						|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
 | 
						|
This program and the accompanying materials
 | 
						|
are licensed and made available under the terms and conditions of the BSD License
 | 
						|
which accompanies this distribution.  The full text of the license may be found at
 | 
						|
http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include <PiDxe.h>
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/UefiBootServicesTableLib.h>
 | 
						|
#include <Library/DxeServicesTableLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/UefiLib.h>
 | 
						|
 | 
						|
#include <Guid/EventGroup.h>
 | 
						|
#include <Guid/MemoryAttributesTable.h>
 | 
						|
#include <Guid/PropertiesTable.h>
 | 
						|
 | 
						|
#include <Protocol/FirmwareVolume2.h>
 | 
						|
#include <Protocol/BlockIo.h>
 | 
						|
#include <Protocol/SimpleFileSystem.h>
 | 
						|
 | 
						|
#include "DxeMain.h"
 | 
						|
 | 
						|
#define CACHE_ATTRIBUTE_MASK   (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)
 | 
						|
#define MEMORY_ATTRIBUTE_MASK  (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)
 | 
						|
 | 
						|
//
 | 
						|
// Image type definitions
 | 
						|
//
 | 
						|
#define IMAGE_UNKNOWN                         0x00000001
 | 
						|
#define IMAGE_FROM_FV                         0x00000002
 | 
						|
 | 
						|
//
 | 
						|
// Protection policy bit definition
 | 
						|
//
 | 
						|
#define DO_NOT_PROTECT                         0x00000000
 | 
						|
#define PROTECT_IF_ALIGNED_ELSE_ALLOW          0x00000001
 | 
						|
 | 
						|
#define MEMORY_TYPE_OS_RESERVED_MIN            0x80000000
 | 
						|
#define MEMORY_TYPE_OEM_RESERVED_MIN           0x70000000
 | 
						|
 | 
						|
#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
 | 
						|
  ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
 | 
						|
 | 
						|
UINT32   mImageProtectionPolicy;
 | 
						|
 | 
						|
extern LIST_ENTRY         mGcdMemorySpaceMap;
 | 
						|
 | 
						|
STATIC LIST_ENTRY         mProtectedImageRecordList;
 | 
						|
 | 
						|
/**
 | 
						|
  Sort code section in image record, based upon CodeSegmentBase from low to high.
 | 
						|
 | 
						|
  @param  ImageRecord    image record to be sorted
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SortImageRecordCodeSection (
 | 
						|
  IN IMAGE_PROPERTIES_RECORD              *ImageRecord
 | 
						|
  );
 | 
						|
 | 
						|
/**
 | 
						|
  Check if code section in image record is valid.
 | 
						|
 | 
						|
  @param  ImageRecord    image record to be checked
 | 
						|
 | 
						|
  @retval TRUE  image record is valid
 | 
						|
  @retval FALSE image record is invalid
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsImageRecordCodeSectionValid (
 | 
						|
  IN IMAGE_PROPERTIES_RECORD              *ImageRecord
 | 
						|
  );
 | 
						|
 | 
						|
/**
 | 
						|
  Get the image type.
 | 
						|
 | 
						|
  @param[in]    File       This is a pointer to the device path of the file that is
 | 
						|
                           being dispatched.
 | 
						|
 | 
						|
  @return UINT32           Image Type
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
GetImageType (
 | 
						|
  IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                        Status;
 | 
						|
  EFI_HANDLE                        DeviceHandle;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL          *TempDevicePath;
 | 
						|
 | 
						|
  if (File == NULL) {
 | 
						|
    return IMAGE_UNKNOWN;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // First check to see if File is from a Firmware Volume
 | 
						|
  //
 | 
						|
  DeviceHandle      = NULL;
 | 
						|
  TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
 | 
						|
  Status = gBS->LocateDevicePath (
 | 
						|
                  &gEfiFirmwareVolume2ProtocolGuid,
 | 
						|
                  &TempDevicePath,
 | 
						|
                  &DeviceHandle
 | 
						|
                  );
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = gBS->OpenProtocol (
 | 
						|
                    DeviceHandle,
 | 
						|
                    &gEfiFirmwareVolume2ProtocolGuid,
 | 
						|
                    NULL,
 | 
						|
                    NULL,
 | 
						|
                    NULL,
 | 
						|
                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL
 | 
						|
                    );
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      return IMAGE_FROM_FV;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return IMAGE_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get UEFI image protection policy based upon image type.
 | 
						|
 | 
						|
  @param[in]  ImageType    The UEFI image type
 | 
						|
 | 
						|
  @return UEFI image protection policy
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
GetProtectionPolicyFromImageType (
 | 
						|
  IN UINT32  ImageType
 | 
						|
  )
 | 
						|
{
 | 
						|
  if ((ImageType & mImageProtectionPolicy) == 0) {
 | 
						|
    return DO_NOT_PROTECT;
 | 
						|
  } else {
 | 
						|
    return PROTECT_IF_ALIGNED_ELSE_ALLOW;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get UEFI image protection policy based upon loaded image device path.
 | 
						|
 | 
						|
  @param[in]  LoadedImage              The loaded image protocol
 | 
						|
  @param[in]  LoadedImageDevicePath    The loaded image device path protocol
 | 
						|
 | 
						|
  @return UEFI image protection policy
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
GetUefiImageProtectionPolicy (
 | 
						|
  IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN     InSmm;
 | 
						|
  UINT32      ImageType;
 | 
						|
  UINT32      ProtectionPolicy;
 | 
						|
 | 
						|
  //
 | 
						|
  // Check SMM
 | 
						|
  //
 | 
						|
  InSmm = FALSE;
 | 
						|
  if (gSmmBase2 != NULL) {
 | 
						|
    gSmmBase2->InSmm (gSmmBase2, &InSmm);
 | 
						|
  }
 | 
						|
  if (InSmm) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check DevicePath
 | 
						|
  //
 | 
						|
  if (LoadedImage == gDxeCoreLoadedImage) {
 | 
						|
    ImageType = IMAGE_FROM_FV;
 | 
						|
  } else {
 | 
						|
    ImageType = GetImageType (LoadedImageDevicePath);
 | 
						|
  }
 | 
						|
  ProtectionPolicy = GetProtectionPolicyFromImageType (ImageType);
 | 
						|
  return ProtectionPolicy;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Set UEFI image memory attributes.
 | 
						|
 | 
						|
  @param[in]  BaseAddress            Specified start address
 | 
						|
  @param[in]  Length                 Specified length
 | 
						|
  @param[in]  Attributes             Specified attributes
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SetUefiImageMemoryAttributes (
 | 
						|
  IN UINT64   BaseAddress,
 | 
						|
  IN UINT64   Length,
 | 
						|
  IN UINT64   Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                       Status;
 | 
						|
  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  Descriptor;
 | 
						|
  UINT64                           FinalAttributes;
 | 
						|
 | 
						|
  Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor);
 | 
						|
  ASSERT_EFI_ERROR(Status);
 | 
						|
 | 
						|
  FinalAttributes = (Descriptor.Attributes & CACHE_ATTRIBUTE_MASK) | (Attributes & MEMORY_ATTRIBUTE_MASK);
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes));
 | 
						|
 | 
						|
  ASSERT(gCpu != NULL);
 | 
						|
  gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Set UEFI image protection attributes.
 | 
						|
 | 
						|
  @param[in]  ImageRecord    A UEFI image record
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SetUefiImageProtectionAttributes (
 | 
						|
  IN IMAGE_PROPERTIES_RECORD     *ImageRecord
 | 
						|
  )
 | 
						|
{
 | 
						|
  IMAGE_PROPERTIES_RECORD_CODE_SECTION      *ImageRecordCodeSection;
 | 
						|
  LIST_ENTRY                                *ImageRecordCodeSectionLink;
 | 
						|
  LIST_ENTRY                                *ImageRecordCodeSectionEndLink;
 | 
						|
  LIST_ENTRY                                *ImageRecordCodeSectionList;
 | 
						|
  UINT64                                    CurrentBase;
 | 
						|
  UINT64                                    ImageEnd;
 | 
						|
 | 
						|
  ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;
 | 
						|
 | 
						|
  CurrentBase = ImageRecord->ImageBase;
 | 
						|
  ImageEnd    = ImageRecord->ImageBase + ImageRecord->ImageSize;
 | 
						|
 | 
						|
  ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink;
 | 
						|
  ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList;
 | 
						|
  while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) {
 | 
						|
    ImageRecordCodeSection = CR (
 | 
						|
                               ImageRecordCodeSectionLink,
 | 
						|
                               IMAGE_PROPERTIES_RECORD_CODE_SECTION,
 | 
						|
                               Link,
 | 
						|
                               IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
 | 
						|
                               );
 | 
						|
    ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink;
 | 
						|
 | 
						|
    ASSERT (CurrentBase <= ImageRecordCodeSection->CodeSegmentBase);
 | 
						|
    if (CurrentBase < ImageRecordCodeSection->CodeSegmentBase) {
 | 
						|
      //
 | 
						|
      // DATA
 | 
						|
      //
 | 
						|
      SetUefiImageMemoryAttributes (
 | 
						|
        CurrentBase,
 | 
						|
        ImageRecordCodeSection->CodeSegmentBase - CurrentBase,
 | 
						|
        EFI_MEMORY_XP
 | 
						|
        );
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // CODE
 | 
						|
    //
 | 
						|
    SetUefiImageMemoryAttributes (
 | 
						|
      ImageRecordCodeSection->CodeSegmentBase,
 | 
						|
      ImageRecordCodeSection->CodeSegmentSize,
 | 
						|
      EFI_MEMORY_RO
 | 
						|
      );
 | 
						|
    CurrentBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Last DATA
 | 
						|
  //
 | 
						|
  ASSERT (CurrentBase <= ImageEnd);
 | 
						|
  if (CurrentBase < ImageEnd) {
 | 
						|
    //
 | 
						|
    // DATA
 | 
						|
    //
 | 
						|
    SetUefiImageMemoryAttributes (
 | 
						|
      CurrentBase,
 | 
						|
      ImageEnd - CurrentBase,
 | 
						|
      EFI_MEMORY_XP
 | 
						|
      );
 | 
						|
  }
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Return if the PE image section is aligned.
 | 
						|
 | 
						|
  @param[in]  SectionAlignment    PE/COFF section alignment
 | 
						|
  @param[in]  MemoryType          PE/COFF image memory type
 | 
						|
 | 
						|
  @retval TRUE  The PE image section is aligned.
 | 
						|
  @retval FALSE The PE image section is not aligned.
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsMemoryProtectionSectionAligned (
 | 
						|
  IN UINT32           SectionAlignment,
 | 
						|
  IN EFI_MEMORY_TYPE  MemoryType
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32  PageAlignment;
 | 
						|
 | 
						|
  switch (MemoryType) {
 | 
						|
  case EfiRuntimeServicesCode:
 | 
						|
  case EfiACPIMemoryNVS:
 | 
						|
    PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
 | 
						|
    break;
 | 
						|
  case EfiRuntimeServicesData:
 | 
						|
  case EfiACPIReclaimMemory:
 | 
						|
    ASSERT (FALSE);
 | 
						|
    PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
 | 
						|
    break;
 | 
						|
  case EfiBootServicesCode:
 | 
						|
  case EfiLoaderCode:
 | 
						|
  case EfiReservedMemoryType:
 | 
						|
    PageAlignment = EFI_PAGE_SIZE;
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    ASSERT (FALSE);
 | 
						|
    PageAlignment = EFI_PAGE_SIZE;
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((SectionAlignment & (PageAlignment - 1)) != 0) {
 | 
						|
    return FALSE;
 | 
						|
  } else {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free Image record.
 | 
						|
 | 
						|
  @param[in]  ImageRecord    A UEFI image record
 | 
						|
**/
 | 
						|
VOID
 | 
						|
FreeImageRecord (
 | 
						|
  IN IMAGE_PROPERTIES_RECORD              *ImageRecord
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                           *CodeSegmentListHead;
 | 
						|
  IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
 | 
						|
 | 
						|
  CodeSegmentListHead = &ImageRecord->CodeSegmentList;
 | 
						|
  while (!IsListEmpty (CodeSegmentListHead)) {
 | 
						|
    ImageRecordCodeSection = CR (
 | 
						|
                               CodeSegmentListHead->ForwardLink,
 | 
						|
                               IMAGE_PROPERTIES_RECORD_CODE_SECTION,
 | 
						|
                               Link,
 | 
						|
                               IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
 | 
						|
                               );
 | 
						|
    RemoveEntryList (&ImageRecordCodeSection->Link);
 | 
						|
    FreePool (ImageRecordCodeSection);
 | 
						|
  }
 | 
						|
 | 
						|
  if (ImageRecord->Link.ForwardLink != NULL) {
 | 
						|
    RemoveEntryList (&ImageRecord->Link);
 | 
						|
  }
 | 
						|
  FreePool (ImageRecord);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Protect UEFI PE/COFF image.
 | 
						|
 | 
						|
  @param[in]  LoadedImage              The loaded image protocol
 | 
						|
  @param[in]  LoadedImageDevicePath    The loaded image device path protocol
 | 
						|
**/
 | 
						|
VOID
 | 
						|
ProtectUefiImage (
 | 
						|
  IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  VOID                                 *ImageAddress;
 | 
						|
  EFI_IMAGE_DOS_HEADER                 *DosHdr;
 | 
						|
  UINT32                               PeCoffHeaderOffset;
 | 
						|
  UINT32                               SectionAlignment;
 | 
						|
  EFI_IMAGE_SECTION_HEADER             *Section;
 | 
						|
  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
 | 
						|
  UINT8                                *Name;
 | 
						|
  UINTN                                Index;
 | 
						|
  IMAGE_PROPERTIES_RECORD              *ImageRecord;
 | 
						|
  CHAR8                                *PdbPointer;
 | 
						|
  IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
 | 
						|
  UINT16                               Magic;
 | 
						|
  BOOLEAN                              IsAligned;
 | 
						|
  UINT32                               ProtectionPolicy;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
 | 
						|
  DEBUG ((DEBUG_INFO, "  - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
 | 
						|
 | 
						|
  if (gCpu == NULL) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
 | 
						|
  switch (ProtectionPolicy) {
 | 
						|
  case DO_NOT_PROTECT:
 | 
						|
    return ;
 | 
						|
  case PROTECT_IF_ALIGNED_ELSE_ALLOW:
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    ASSERT(FALSE);
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  ImageRecord = AllocateZeroPool (sizeof(*ImageRecord));
 | 
						|
  if (ImageRecord == NULL) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
  ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE;
 | 
						|
 | 
						|
  //
 | 
						|
  // Step 1: record whole region
 | 
						|
  //
 | 
						|
  ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;
 | 
						|
  ImageRecord->ImageSize = LoadedImage->ImageSize;
 | 
						|
 | 
						|
  ImageAddress = LoadedImage->ImageBase;
 | 
						|
 | 
						|
  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
 | 
						|
  if (PdbPointer != NULL) {
 | 
						|
    DEBUG ((DEBUG_VERBOSE, "  Image - %a\n", PdbPointer));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check PE/COFF image
 | 
						|
  //
 | 
						|
  DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress;
 | 
						|
  PeCoffHeaderOffset = 0;
 | 
						|
  if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
 | 
						|
    PeCoffHeaderOffset = DosHdr->e_lfanew;
 | 
						|
  }
 | 
						|
 | 
						|
  Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset);
 | 
						|
  if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
 | 
						|
    DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
 | 
						|
    // It might be image in SMM.
 | 
						|
    goto Finish;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get SectionAlignment
 | 
						|
  //
 | 
						|
  if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
 | 
						|
    //
 | 
						|
    // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
 | 
						|
    //       in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
 | 
						|
    //       Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
 | 
						|
    //       then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
 | 
						|
    //
 | 
						|
    Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Get the magic value from the PE/COFF Optional Header
 | 
						|
    //
 | 
						|
    Magic = Hdr.Pe32->OptionalHeader.Magic;
 | 
						|
  }
 | 
						|
  if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
 | 
						|
    SectionAlignment  = Hdr.Pe32->OptionalHeader.SectionAlignment;
 | 
						|
  } else {
 | 
						|
    SectionAlignment  = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
 | 
						|
  }
 | 
						|
 | 
						|
  IsAligned = IsMemoryProtectionSectionAligned (SectionAlignment, LoadedImage->ImageCodeType);
 | 
						|
  if (!IsAligned) {
 | 
						|
    DEBUG ((DEBUG_VERBOSE, "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
 | 
						|
      SectionAlignment));
 | 
						|
    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
 | 
						|
    if (PdbPointer != NULL) {
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));
 | 
						|
    }
 | 
						|
    goto Finish;
 | 
						|
  }
 | 
						|
 | 
						|
  Section = (EFI_IMAGE_SECTION_HEADER *) (
 | 
						|
               (UINT8 *) (UINTN) ImageAddress +
 | 
						|
               PeCoffHeaderOffset +
 | 
						|
               sizeof(UINT32) +
 | 
						|
               sizeof(EFI_IMAGE_FILE_HEADER) +
 | 
						|
               Hdr.Pe32->FileHeader.SizeOfOptionalHeader
 | 
						|
               );
 | 
						|
  ImageRecord->CodeSegmentCount = 0;
 | 
						|
  InitializeListHead (&ImageRecord->CodeSegmentList);
 | 
						|
  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
 | 
						|
    Name = Section[Index].Name;
 | 
						|
    DEBUG ((
 | 
						|
      DEBUG_VERBOSE,
 | 
						|
      "  Section - '%c%c%c%c%c%c%c%c'\n",
 | 
						|
      Name[0],
 | 
						|
      Name[1],
 | 
						|
      Name[2],
 | 
						|
      Name[3],
 | 
						|
      Name[4],
 | 
						|
      Name[5],
 | 
						|
      Name[6],
 | 
						|
      Name[7]
 | 
						|
      ));
 | 
						|
 | 
						|
    //
 | 
						|
    // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
 | 
						|
    // can always be mapped read-only, classify a section as a code section only
 | 
						|
    // if it has the executable attribute set and the writable attribute cleared.
 | 
						|
    //
 | 
						|
    // This adheres more closely to the PE/COFF spec, and avoids issues with
 | 
						|
    // Linux OS loaders that may consist of a single read/write/execute section.
 | 
						|
    //
 | 
						|
    if ((Section[Index].Characteristics & (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) {
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  VirtualSize          - 0x%08x\n", Section[Index].Misc.VirtualSize));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  VirtualAddress       - 0x%08x\n", Section[Index].VirtualAddress));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  SizeOfRawData        - 0x%08x\n", Section[Index].SizeOfRawData));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  PointerToRawData     - 0x%08x\n", Section[Index].PointerToRawData));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  NumberOfRelocations  - 0x%08x\n", Section[Index].NumberOfRelocations));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  NumberOfLinenumbers  - 0x%08x\n", Section[Index].NumberOfLinenumbers));
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "  Characteristics      - 0x%08x\n", Section[Index].Characteristics));
 | 
						|
 | 
						|
      //
 | 
						|
      // Step 2: record code section
 | 
						|
      //
 | 
						|
      ImageRecordCodeSection = AllocatePool (sizeof(*ImageRecordCodeSection));
 | 
						|
      if (ImageRecordCodeSection == NULL) {
 | 
						|
        return ;
 | 
						|
      }
 | 
						|
      ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE;
 | 
						|
 | 
						|
      ImageRecordCodeSection->CodeSegmentBase = (UINTN)ImageAddress + Section[Index].VirtualAddress;
 | 
						|
      ImageRecordCodeSection->CodeSegmentSize = ALIGN_VALUE(Section[Index].SizeOfRawData, SectionAlignment);
 | 
						|
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection->CodeSegmentBase, ImageRecordCodeSection->CodeSegmentSize));
 | 
						|
 | 
						|
      InsertTailList (&ImageRecord->CodeSegmentList, &ImageRecordCodeSection->Link);
 | 
						|
      ImageRecord->CodeSegmentCount++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (ImageRecord->CodeSegmentCount == 0) {
 | 
						|
    //
 | 
						|
    // If a UEFI executable consists of a single read+write+exec PE/COFF
 | 
						|
    // section, that isn't actually an error. The image can be launched
 | 
						|
    // alright, only image protection cannot be applied to it fully.
 | 
						|
    //
 | 
						|
    // One example that elicits this is (some) Linux kernels (with the EFI stub
 | 
						|
    // of course).
 | 
						|
    //
 | 
						|
    DEBUG ((DEBUG_WARN, "!!!!!!!!  ProtectUefiImageCommon - CodeSegmentCount is 0  !!!!!!!!\n"));
 | 
						|
    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
 | 
						|
    if (PdbPointer != NULL) {
 | 
						|
      DEBUG ((DEBUG_WARN, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));
 | 
						|
    }
 | 
						|
    goto Finish;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Final
 | 
						|
  //
 | 
						|
  SortImageRecordCodeSection (ImageRecord);
 | 
						|
  //
 | 
						|
  // Check overlap all section in ImageBase/Size
 | 
						|
  //
 | 
						|
  if (!IsImageRecordCodeSectionValid (ImageRecord)) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "IsImageRecordCodeSectionValid - FAIL\n"));
 | 
						|
    goto Finish;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
 | 
						|
  // Given that the loader always allocates full pages, we know the space after the image is not used.
 | 
						|
  //
 | 
						|
  ImageRecord->ImageSize = ALIGN_VALUE(LoadedImage->ImageSize, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
  //
 | 
						|
  // CPU ARCH present. Update memory attribute directly.
 | 
						|
  //
 | 
						|
  SetUefiImageProtectionAttributes (ImageRecord);
 | 
						|
 | 
						|
  //
 | 
						|
  // Record the image record in the list so we can undo the protections later
 | 
						|
  //
 | 
						|
  InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);
 | 
						|
 | 
						|
Finish:
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Unprotect UEFI image.
 | 
						|
 | 
						|
  @param[in]  LoadedImage              The loaded image protocol
 | 
						|
  @param[in]  LoadedImageDevicePath    The loaded image device path protocol
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UnprotectUefiImage (
 | 
						|
  IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  IMAGE_PROPERTIES_RECORD    *ImageRecord;
 | 
						|
  LIST_ENTRY                 *ImageRecordLink;
 | 
						|
 | 
						|
  if (PcdGet32(PcdImageProtectionPolicy) != 0) {
 | 
						|
    for (ImageRecordLink = mProtectedImageRecordList.ForwardLink;
 | 
						|
         ImageRecordLink != &mProtectedImageRecordList;
 | 
						|
         ImageRecordLink = ImageRecordLink->ForwardLink) {
 | 
						|
      ImageRecord = CR (
 | 
						|
                      ImageRecordLink,
 | 
						|
                      IMAGE_PROPERTIES_RECORD,
 | 
						|
                      Link,
 | 
						|
                      IMAGE_PROPERTIES_RECORD_SIGNATURE
 | 
						|
                      );
 | 
						|
 | 
						|
      if (ImageRecord->ImageBase == (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase) {
 | 
						|
        SetUefiImageMemoryAttributes (ImageRecord->ImageBase,
 | 
						|
                                      ImageRecord->ImageSize,
 | 
						|
                                      0);
 | 
						|
        FreeImageRecord (ImageRecord);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Return the EFI memory permission attribute associated with memory
 | 
						|
  type 'MemoryType' under the configured DXE memory protection policy.
 | 
						|
 | 
						|
  @param MemoryType       Memory type.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
UINT64
 | 
						|
GetPermissionAttributeForMemoryType (
 | 
						|
  IN EFI_MEMORY_TYPE    MemoryType
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64 TestBit;
 | 
						|
 | 
						|
  if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
 | 
						|
    TestBit = BIT63;
 | 
						|
  } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
 | 
						|
    TestBit = BIT62;
 | 
						|
  } else {
 | 
						|
    TestBit = LShiftU64 (1, MemoryType);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {
 | 
						|
    return EFI_MEMORY_XP;
 | 
						|
  } else {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Sort memory map entries based upon PhysicalStart, from low to high.
 | 
						|
 | 
						|
  @param  MemoryMap              A pointer to the buffer in which firmware places
 | 
						|
                                 the current memory map.
 | 
						|
  @param  MemoryMapSize          Size, in bytes, of the MemoryMap buffer.
 | 
						|
  @param  DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
VOID
 | 
						|
SortMemoryMap (
 | 
						|
  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
 | 
						|
  IN UINTN                      MemoryMapSize,
 | 
						|
  IN UINTN                      DescriptorSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       TempMemoryMap;
 | 
						|
 | 
						|
  MemoryMapEntry = MemoryMap;
 | 
						|
  NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
 | 
						|
  while (MemoryMapEntry < MemoryMapEnd) {
 | 
						|
    while (NextMemoryMapEntry < MemoryMapEnd) {
 | 
						|
      if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
 | 
						|
        CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
 | 
						|
        CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
 | 
						|
        CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
 | 
						|
      }
 | 
						|
 | 
						|
      NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
 | 
						|
    }
 | 
						|
 | 
						|
    MemoryMapEntry      = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
    NextMemoryMapEntry  = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Merge adjacent memory map entries if they use the same memory protection policy
 | 
						|
 | 
						|
  @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places
 | 
						|
                                          the current memory map.
 | 
						|
  @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the
 | 
						|
                                          MemoryMap buffer. On input, this is the size of
 | 
						|
                                          the current memory map.  On output,
 | 
						|
                                          it is the size of new memory map after merge.
 | 
						|
  @param[in]       DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
VOID
 | 
						|
MergeMemoryMapForProtectionPolicy (
 | 
						|
  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
 | 
						|
  IN OUT UINTN                  *MemoryMapSize,
 | 
						|
  IN UINTN                      DescriptorSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;
 | 
						|
  UINT64                      MemoryBlockLength;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *NewMemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;
 | 
						|
  UINT64                      Attributes;
 | 
						|
 | 
						|
  SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);
 | 
						|
 | 
						|
  MemoryMapEntry = MemoryMap;
 | 
						|
  NewMemoryMapEntry = MemoryMap;
 | 
						|
  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
 | 
						|
  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
 | 
						|
    CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
 | 
						|
    NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
 | 
						|
    do {
 | 
						|
      MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
 | 
						|
      Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
 | 
						|
 | 
						|
      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
 | 
						|
          Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&
 | 
						|
          ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
 | 
						|
        MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
 | 
						|
        if (NewMemoryMapEntry != MemoryMapEntry) {
 | 
						|
          NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
 | 
						|
        }
 | 
						|
 | 
						|
        NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
 | 
						|
        continue;
 | 
						|
      } else {
 | 
						|
        MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    } while (TRUE);
 | 
						|
 | 
						|
    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
    NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
 | 
						|
  }
 | 
						|
 | 
						|
  *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
 | 
						|
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Remove exec permissions from all regions whose type is identified by
 | 
						|
  PcdDxeNxMemoryProtectionPolicy.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
VOID
 | 
						|
InitializeDxeNxMemoryProtectionPolicy (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                             MemoryMapSize;
 | 
						|
  UINTN                             MapKey;
 | 
						|
  UINTN                             DescriptorSize;
 | 
						|
  UINT32                            DescriptorVersion;
 | 
						|
  EFI_MEMORY_DESCRIPTOR             *MemoryMap;
 | 
						|
  EFI_MEMORY_DESCRIPTOR             *MemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR             *MemoryMapEnd;
 | 
						|
  EFI_STATUS                        Status;
 | 
						|
  UINT64                            Attributes;
 | 
						|
  LIST_ENTRY                        *Link;
 | 
						|
  EFI_GCD_MAP_ENTRY                 *Entry;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the EFI memory map.
 | 
						|
  //
 | 
						|
  MemoryMapSize = 0;
 | 
						|
  MemoryMap     = NULL;
 | 
						|
 | 
						|
  Status = gBS->GetMemoryMap (
 | 
						|
                  &MemoryMapSize,
 | 
						|
                  MemoryMap,
 | 
						|
                  &MapKey,
 | 
						|
                  &DescriptorSize,
 | 
						|
                  &DescriptorVersion
 | 
						|
                  );
 | 
						|
  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
 | 
						|
  do {
 | 
						|
    MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
 | 
						|
    ASSERT (MemoryMap != NULL);
 | 
						|
    Status = gBS->GetMemoryMap (
 | 
						|
                    &MemoryMapSize,
 | 
						|
                    MemoryMap,
 | 
						|
                    &MapKey,
 | 
						|
                    &DescriptorSize,
 | 
						|
                    &DescriptorVersion
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      FreePool (MemoryMap);
 | 
						|
    }
 | 
						|
  } while (Status == EFI_BUFFER_TOO_SMALL);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_INFO,
 | 
						|
    "%a: applying strict permissions to active memory regions\n",
 | 
						|
    __FUNCTION__
 | 
						|
    ));
 | 
						|
 | 
						|
  MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);
 | 
						|
 | 
						|
  MemoryMapEntry = MemoryMap;
 | 
						|
  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
 | 
						|
  while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
 | 
						|
 | 
						|
    Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
 | 
						|
    if (Attributes != 0) {
 | 
						|
      SetUefiImageMemoryAttributes (
 | 
						|
        MemoryMapEntry->PhysicalStart,
 | 
						|
        LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),
 | 
						|
        Attributes);
 | 
						|
    }
 | 
						|
    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
  }
 | 
						|
  FreePool (MemoryMap);
 | 
						|
 | 
						|
  //
 | 
						|
  // Apply the policy for RAM regions that we know are present and
 | 
						|
  // accessible, but have not been added to the UEFI memory map (yet).
 | 
						|
  //
 | 
						|
  if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {
 | 
						|
    DEBUG ((
 | 
						|
      DEBUG_INFO,
 | 
						|
      "%a: applying strict permissions to inactive memory regions\n",
 | 
						|
      __FUNCTION__
 | 
						|
      ));
 | 
						|
 | 
						|
    CoreAcquireGcdMemoryLock ();
 | 
						|
 | 
						|
    Link = mGcdMemorySpaceMap.ForwardLink;
 | 
						|
    while (Link != &mGcdMemorySpaceMap) {
 | 
						|
 | 
						|
      Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
 | 
						|
 | 
						|
      if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&
 | 
						|
          Entry->EndAddress < MAX_ADDRESS &&
 | 
						|
          (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
 | 
						|
            (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {
 | 
						|
 | 
						|
        Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |
 | 
						|
                     (Entry->Attributes & CACHE_ATTRIBUTE_MASK);
 | 
						|
 | 
						|
        DEBUG ((DEBUG_INFO,
 | 
						|
          "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
 | 
						|
          Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,
 | 
						|
          Attributes));
 | 
						|
 | 
						|
        ASSERT(gCpu != NULL);
 | 
						|
        gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,
 | 
						|
          Entry->EndAddress - Entry->BaseAddress + 1, Attributes);
 | 
						|
      }
 | 
						|
 | 
						|
      Link = Link->ForwardLink;
 | 
						|
    }
 | 
						|
    CoreReleaseGcdMemoryLock ();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  A notification for CPU_ARCH protocol.
 | 
						|
 | 
						|
  @param[in]  Event                 Event whose notification function is being invoked.
 | 
						|
  @param[in]  Context               Pointer to the notification function's context,
 | 
						|
                                    which is implementation-dependent.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
MemoryProtectionCpuArchProtocolNotify (
 | 
						|
  IN EFI_EVENT                Event,
 | 
						|
  IN VOID                     *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                  Status;
 | 
						|
  EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath;
 | 
						|
  UINTN                       NoHandles;
 | 
						|
  EFI_HANDLE                  *HandleBuffer;
 | 
						|
  UINTN                       Index;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "MemoryProtectionCpuArchProtocolNotify:\n"));
 | 
						|
  Status = CoreLocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Apply the memory protection policy on non-BScode/RTcode regions.
 | 
						|
  //
 | 
						|
  if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
 | 
						|
    InitializeDxeNxMemoryProtectionPolicy ();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mImageProtectionPolicy == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = gBS->LocateHandleBuffer (
 | 
						|
                  ByProtocol,
 | 
						|
                  &gEfiLoadedImageProtocolGuid,
 | 
						|
                  NULL,
 | 
						|
                  &NoHandles,
 | 
						|
                  &HandleBuffer
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status) && (NoHandles == 0)) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < NoHandles; Index++) {
 | 
						|
    Status = gBS->HandleProtocol (
 | 
						|
                    HandleBuffer[Index],
 | 
						|
                    &gEfiLoadedImageProtocolGuid,
 | 
						|
                    (VOID **)&LoadedImage
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    Status = gBS->HandleProtocol (
 | 
						|
                    HandleBuffer[Index],
 | 
						|
                    &gEfiLoadedImageDevicePathProtocolGuid,
 | 
						|
                    (VOID **)&LoadedImageDevicePath
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      LoadedImageDevicePath = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
 | 
						|
  }
 | 
						|
 | 
						|
  CoreCloseEvent (Event);
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  ExitBootServices Callback function for memory protection.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
MemoryProtectionExitBootServicesCallback (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_RUNTIME_IMAGE_ENTRY       *RuntimeImage;
 | 
						|
  LIST_ENTRY                    *Link;
 | 
						|
 | 
						|
  //
 | 
						|
  // We need remove the RT protection, because RT relocation need write code segment
 | 
						|
  // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
 | 
						|
  //
 | 
						|
  // Firmware does not own page tables after ExitBootServices(), so the OS would
 | 
						|
  // have to relax protection of RT code pages across SetVirtualAddressMap(), or
 | 
						|
  // delay setting protections on RT code pages until after SetVirtualAddressMap().
 | 
						|
  // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
 | 
						|
  //
 | 
						|
  if (mImageProtectionPolicy != 0) {
 | 
						|
    for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {
 | 
						|
      RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
 | 
						|
      SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE(RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
 | 
						|
  order to skip unfixable NULL pointer access issues detected in OptionROM or
 | 
						|
  boot loaders.
 | 
						|
 | 
						|
  @param[in]  Event     The Event this notify function registered to.
 | 
						|
  @param[in]  Context   Pointer to the context data registered to the Event.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
DisableNullDetectionAtTheEndOfDxe (
 | 
						|
  EFI_EVENT                               Event,
 | 
						|
  VOID                                    *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                        Status;
 | 
						|
  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Desc;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
 | 
						|
  //
 | 
						|
  // Disable NULL pointer detection by enabling first 4K page
 | 
						|
  //
 | 
						|
  Status = CoreGetMemorySpaceDescriptor (0, &Desc);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  if ((Desc.Capabilities & EFI_MEMORY_RP) == 0) {
 | 
						|
    Status = CoreSetMemorySpaceCapabilities (
 | 
						|
              0,
 | 
						|
              EFI_PAGE_SIZE,
 | 
						|
              Desc.Capabilities | EFI_MEMORY_RP
 | 
						|
              );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  Status = CoreSetMemorySpaceAttributes (
 | 
						|
            0,
 | 
						|
            EFI_PAGE_SIZE,
 | 
						|
            Desc.Attributes & ~EFI_MEMORY_RP
 | 
						|
            );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  CoreCloseEvent (Event);
 | 
						|
  DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
 | 
						|
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize Memory Protection support.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
CoreInitializeMemoryProtection (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  EFI_EVENT   Event;
 | 
						|
  EFI_EVENT   EndOfDxeEvent;
 | 
						|
  VOID        *Registration;
 | 
						|
 | 
						|
  mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);
 | 
						|
 | 
						|
  InitializeListHead (&mProtectedImageRecordList);
 | 
						|
 | 
						|
  //
 | 
						|
  // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
 | 
						|
  // - code regions should have no EFI_MEMORY_XP attribute
 | 
						|
  // - EfiConventionalMemory and EfiBootServicesData should use the
 | 
						|
  //   same attribute
 | 
						|
  // - heap guard should not be enabled for the same type of memory
 | 
						|
  //
 | 
						|
  ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);
 | 
						|
  ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);
 | 
						|
  ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);
 | 
						|
  ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
 | 
						|
          GetPermissionAttributeForMemoryType (EfiConventionalMemory));
 | 
						|
  ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPoolType)) == 0);
 | 
						|
  ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPageType)) == 0);
 | 
						|
 | 
						|
  if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
 | 
						|
    Status = CoreCreateEvent (
 | 
						|
               EVT_NOTIFY_SIGNAL,
 | 
						|
               TPL_CALLBACK,
 | 
						|
               MemoryProtectionCpuArchProtocolNotify,
 | 
						|
               NULL,
 | 
						|
               &Event
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR(Status);
 | 
						|
 | 
						|
    //
 | 
						|
    // Register for protocol notifactions on this event
 | 
						|
    //
 | 
						|
    Status = CoreRegisterProtocolNotify (
 | 
						|
               &gEfiCpuArchProtocolGuid,
 | 
						|
               Event,
 | 
						|
               &Registration
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR(Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Register a callback to disable NULL pointer detection at EndOfDxe
 | 
						|
  //
 | 
						|
  if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7))
 | 
						|
       == (BIT0|BIT7)) {
 | 
						|
    Status = CoreCreateEventEx (
 | 
						|
                    EVT_NOTIFY_SIGNAL,
 | 
						|
                    TPL_NOTIFY,
 | 
						|
                    DisableNullDetectionAtTheEndOfDxe,
 | 
						|
                    NULL,
 | 
						|
                    &gEfiEndOfDxeEventGroupGuid,
 | 
						|
                    &EndOfDxeEvent
 | 
						|
                    );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Returns whether we are currently executing in SMM mode.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
IsInSmm (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN     InSmm;
 | 
						|
 | 
						|
  InSmm = FALSE;
 | 
						|
  if (gSmmBase2 != NULL) {
 | 
						|
    gSmmBase2->InSmm (gSmmBase2, &InSmm);
 | 
						|
  }
 | 
						|
  return InSmm;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Manage memory permission attributes on a memory range, according to the
 | 
						|
  configured DXE memory protection policy.
 | 
						|
 | 
						|
  @param  OldType           The old memory type of the range
 | 
						|
  @param  NewType           The new memory type of the range
 | 
						|
  @param  Memory            The base address of the range
 | 
						|
  @param  Length            The size of the range (in bytes)
 | 
						|
 | 
						|
  @return EFI_SUCCESS       If we are executing in SMM mode. No permission attributes
 | 
						|
                            are updated in this case
 | 
						|
  @return EFI_SUCCESS       If the the CPU arch protocol is not installed yet
 | 
						|
  @return EFI_SUCCESS       If no DXE memory protection policy has been configured
 | 
						|
  @return EFI_SUCCESS       If OldType and NewType use the same permission attributes
 | 
						|
  @return other             Return value of gCpu->SetMemoryAttributes()
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
ApplyMemoryProtectionPolicy (
 | 
						|
  IN  EFI_MEMORY_TYPE       OldType,
 | 
						|
  IN  EFI_MEMORY_TYPE       NewType,
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS  Memory,
 | 
						|
  IN  UINT64                Length
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64  OldAttributes;
 | 
						|
  UINT64  NewAttributes;
 | 
						|
 | 
						|
  //
 | 
						|
  // The policy configured in PcdDxeNxMemoryProtectionPolicy
 | 
						|
  // does not apply to allocations performed in SMM mode.
 | 
						|
  //
 | 
						|
  if (IsInSmm ()) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If the CPU arch protocol is not installed yet, we cannot manage memory
 | 
						|
  // permission attributes, and it is the job of the driver that installs this
 | 
						|
  // protocol to set the permissions on existing allocations.
 | 
						|
  //
 | 
						|
  if (gCpu == NULL) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check if a DXE memory protection policy has been configured
 | 
						|
  //
 | 
						|
  if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the executable permissions according to the DXE memory
 | 
						|
  // protection policy, but only if
 | 
						|
  // - the policy is different between the old and the new type, or
 | 
						|
  // - this is a newly added region (OldType == EfiMaxMemoryType)
 | 
						|
  //
 | 
						|
  NewAttributes = GetPermissionAttributeForMemoryType (NewType);
 | 
						|
 | 
						|
  if (OldType != EfiMaxMemoryType) {
 | 
						|
    OldAttributes = GetPermissionAttributeForMemoryType (OldType);
 | 
						|
    if (OldAttributes == NewAttributes) {
 | 
						|
      // policy is the same between OldType and NewType
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
  } else if (NewAttributes == 0) {
 | 
						|
    // newly added region of a type that does not require protection
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
 | 
						|
}
 |