/** @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", | |
__func__, | |
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", | |
__func__ | |
)); | |
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", | |
__func__ | |
)); | |
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); | |
} |