Add usage of EFI_MEMORY_SP and EFI_MEMORY_CPU_CRYPTO attributes introduced in UEFI 2.8 (UEFI 2.8, mantis 1919 and 1872) Use attributes bitmasks, defined in MdePkg. Signed-off-by: Oleksiy Yakovlev <oleksiyy@ami.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200702205039.52400-3-oleksiyy@ami.com> Reviewed-by: Dandan Bi <dandan.bi@intel.com> Tested-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			1285 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1285 lines
		
	
	
		
			40 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 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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 <Protocol/FirmwareVolume2.h>
 | |
| #include <Protocol/SimpleFileSystem.h>
 | |
| 
 | |
| #include "DxeMain.h"
 | |
| #include "Mem/HeapGuard.h"
 | |
| 
 | |
| //
 | |
| // 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 & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_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;
 | |
|   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->OptionalHeader.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;
 | |
|   EFI_PEI_HOB_POINTERS              Hob;
 | |
|   EFI_HOB_MEMORY_ALLOCATION         *MemoryHob;
 | |
|   EFI_PHYSICAL_ADDRESS              StackBase;
 | |
| 
 | |
|   //
 | |
|   // 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);
 | |
| 
 | |
|   StackBase = 0;
 | |
|   if (PcdGetBool (PcdCpuStackGuard)) {
 | |
|     //
 | |
|     // Get the base of stack from Hob.
 | |
|     //
 | |
|     Hob.Raw = GetHobList ();
 | |
|     while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
 | |
|       MemoryHob = Hob.MemoryAllocation;
 | |
|       if (CompareGuid(&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {
 | |
|         DEBUG ((
 | |
|           DEBUG_INFO,
 | |
|           "%a: StackBase = 0x%016lx  StackSize = 0x%016lx\n",
 | |
|           __FUNCTION__,
 | |
|           MemoryHob->AllocDescriptor.MemoryBaseAddress,
 | |
|           MemoryHob->AllocDescriptor.MemoryLength
 | |
|           ));
 | |
| 
 | |
|         StackBase = MemoryHob->AllocDescriptor.MemoryBaseAddress;
 | |
|         //
 | |
|         // Ensure the base of the stack is page-size aligned.
 | |
|         //
 | |
|         ASSERT ((StackBase & EFI_PAGE_MASK) == 0);
 | |
|         break;
 | |
|       }
 | |
|       Hob.Raw = GET_NEXT_HOB (Hob);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Ensure the base of stack can be found from Hob when stack guard is
 | |
|     // enabled.
 | |
|     //
 | |
|     ASSERT (StackBase != 0);
 | |
|   }
 | |
| 
 | |
|   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);
 | |
| 
 | |
|       //
 | |
|       // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
 | |
|       // enabled.
 | |
|       //
 | |
|       if (MemoryMapEntry->PhysicalStart == 0 &&
 | |
|           PcdGet8 (PcdNullPointerDetectionPropertyMask) != 0) {
 | |
| 
 | |
|         ASSERT (MemoryMapEntry->NumberOfPages > 0);
 | |
|         SetUefiImageMemoryAttributes (
 | |
|           0,
 | |
|           EFI_PAGES_TO_SIZE (1),
 | |
|           EFI_MEMORY_RP | Attributes);
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
 | |
|       // guard is enabled.
 | |
|       //
 | |
|       if (StackBase != 0 &&
 | |
|           (StackBase >= MemoryMapEntry->PhysicalStart &&
 | |
|            StackBase <  MemoryMapEntry->PhysicalStart +
 | |
|                         LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT)) &&
 | |
|           PcdGetBool (PcdCpuStackGuard)) {
 | |
| 
 | |
|         SetUefiImageMemoryAttributes (
 | |
|           StackBase,
 | |
|           EFI_PAGES_TO_SIZE (1),
 | |
|           EFI_MEMORY_RP | 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 & EFI_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)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Apply the memory protection policy on non-BScode/RTcode regions.
 | |
|   //
 | |
|   if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
 | |
|     InitializeDxeNxMemoryProtectionPolicy ();
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Call notify function meant for Heap Guard.
 | |
|   //
 | |
|   HeapGuardCpuArchProtocolNotify ();
 | |
| 
 | |
|   if (mImageProtectionPolicy == 0) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->LocateHandleBuffer (
 | |
|                   ByProtocol,
 | |
|                   &gEfiLoadedImageProtocolGuid,
 | |
|                   NULL,
 | |
|                   &NoHandles,
 | |
|                   &HandleBuffer
 | |
|                   );
 | |
|   if (EFI_ERROR (Status) && (NoHandles == 0)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   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);
 | |
|   }
 | |
|   FreePool (HandleBuffer);
 | |
| 
 | |
| Done:
 | |
|   CoreCloseEvent (Event);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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);
 | |
| 
 | |
|   //
 | |
|   // Page 0 might have be allocated to avoid misuses. Free it here anyway.
 | |
|   //
 | |
|   CoreFreePages (0, 1);
 | |
| 
 | |
|   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
 | |
|   //
 | |
|   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));
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Don't overwrite Guard pages, which should be the first and/or last page,
 | |
|   // if any.
 | |
|   //
 | |
|   if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL)) {
 | |
|     if (IsGuardPage (Memory))  {
 | |
|       Memory += EFI_PAGE_SIZE;
 | |
|       Length -= EFI_PAGE_SIZE;
 | |
|       if (Length == 0) {
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (IsGuardPage (Memory + Length - EFI_PAGE_SIZE))  {
 | |
|       Length -= EFI_PAGE_SIZE;
 | |
|       if (Length == 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);
 | |
| }
 |