2 UEFI Memory Protection support.
4 If the UEFI image is page aligned, the image code section is set to read only
5 and the image data section is set to non-executable.
7 1) This policy is applied for all UEFI image including boot service driver,
8 runtime driver or application.
9 2) This policy is applied only if the UEFI image meets the page alignment
11 3) This policy is applied only if the Source UEFI image matches the
12 PcdImageProtectionPolicy definition.
13 4) This policy is not applied to the non-PE image region.
15 The DxeCore calls CpuArchProtocol->SetMemoryAttributes() to protect
16 the image. If the CpuArch protocol is not installed yet, the DxeCore
17 enqueues the protection request. Once the CpuArch is installed, the
18 DxeCore dequeues the protection request and applies policy.
20 Once the image is unloaded, the protection is removed automatically.
22 Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
23 SPDX-License-Identifier: BSD-2-Clause-Patent
28 #include <Library/BaseLib.h>
29 #include <Library/BaseMemoryLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/DxeServicesTableLib.h>
33 #include <Library/DebugLib.h>
34 #include <Library/UefiLib.h>
36 #include <Guid/EventGroup.h>
37 #include <Guid/MemoryAttributesTable.h>
38 #include <Guid/PropertiesTable.h>
40 #include <Protocol/FirmwareVolume2.h>
41 #include <Protocol/SimpleFileSystem.h>
44 #include "Mem/HeapGuard.h"
46 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)
47 #define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)
50 // Image type definitions
52 #define IMAGE_UNKNOWN 0x00000001
53 #define IMAGE_FROM_FV 0x00000002
56 // Protection policy bit definition
58 #define DO_NOT_PROTECT 0x00000000
59 #define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001
61 #define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
62 #define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
64 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
65 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
67 UINT32 mImageProtectionPolicy
;
69 extern LIST_ENTRY mGcdMemorySpaceMap
;
71 STATIC LIST_ENTRY mProtectedImageRecordList
;
74 Sort code section in image record, based upon CodeSegmentBase from low to high.
76 @param ImageRecord image record to be sorted
79 SortImageRecordCodeSection (
80 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
84 Check if code section in image record is valid.
86 @param ImageRecord image record to be checked
88 @retval TRUE image record is valid
89 @retval FALSE image record is invalid
92 IsImageRecordCodeSectionValid (
93 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
99 @param[in] File This is a pointer to the device path of the file that is
102 @return UINT32 Image Type
106 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
110 EFI_HANDLE DeviceHandle
;
111 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
114 return IMAGE_UNKNOWN
;
118 // First check to see if File is from a Firmware Volume
121 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) File
;
122 Status
= gBS
->LocateDevicePath (
123 &gEfiFirmwareVolume2ProtocolGuid
,
127 if (!EFI_ERROR (Status
)) {
128 Status
= gBS
->OpenProtocol (
130 &gEfiFirmwareVolume2ProtocolGuid
,
134 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
136 if (!EFI_ERROR (Status
)) {
137 return IMAGE_FROM_FV
;
140 return IMAGE_UNKNOWN
;
144 Get UEFI image protection policy based upon image type.
146 @param[in] ImageType The UEFI image type
148 @return UEFI image protection policy
151 GetProtectionPolicyFromImageType (
155 if ((ImageType
& mImageProtectionPolicy
) == 0) {
156 return DO_NOT_PROTECT
;
158 return PROTECT_IF_ALIGNED_ELSE_ALLOW
;
163 Get UEFI image protection policy based upon loaded image device path.
165 @param[in] LoadedImage The loaded image protocol
166 @param[in] LoadedImageDevicePath The loaded image device path protocol
168 @return UEFI image protection policy
171 GetUefiImageProtectionPolicy (
172 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
173 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
178 UINT32 ProtectionPolicy
;
184 if (gSmmBase2
!= NULL
) {
185 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
194 if (LoadedImage
== gDxeCoreLoadedImage
) {
195 ImageType
= IMAGE_FROM_FV
;
197 ImageType
= GetImageType (LoadedImageDevicePath
);
199 ProtectionPolicy
= GetProtectionPolicyFromImageType (ImageType
);
200 return ProtectionPolicy
;
205 Set UEFI image memory attributes.
207 @param[in] BaseAddress Specified start address
208 @param[in] Length Specified length
209 @param[in] Attributes Specified attributes
212 SetUefiImageMemoryAttributes (
213 IN UINT64 BaseAddress
,
219 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
220 UINT64 FinalAttributes
;
222 Status
= CoreGetMemorySpaceDescriptor(BaseAddress
, &Descriptor
);
223 ASSERT_EFI_ERROR(Status
);
225 FinalAttributes
= (Descriptor
.Attributes
& CACHE_ATTRIBUTE_MASK
) | (Attributes
& MEMORY_ATTRIBUTE_MASK
);
227 DEBUG ((DEBUG_INFO
, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress
, Length
, FinalAttributes
));
229 ASSERT(gCpu
!= NULL
);
230 gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, Length
, FinalAttributes
);
234 Set UEFI image protection attributes.
236 @param[in] ImageRecord A UEFI image record
239 SetUefiImageProtectionAttributes (
240 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
243 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
244 LIST_ENTRY
*ImageRecordCodeSectionLink
;
245 LIST_ENTRY
*ImageRecordCodeSectionEndLink
;
246 LIST_ENTRY
*ImageRecordCodeSectionList
;
250 ImageRecordCodeSectionList
= &ImageRecord
->CodeSegmentList
;
252 CurrentBase
= ImageRecord
->ImageBase
;
253 ImageEnd
= ImageRecord
->ImageBase
+ ImageRecord
->ImageSize
;
255 ImageRecordCodeSectionLink
= ImageRecordCodeSectionList
->ForwardLink
;
256 ImageRecordCodeSectionEndLink
= ImageRecordCodeSectionList
;
257 while (ImageRecordCodeSectionLink
!= ImageRecordCodeSectionEndLink
) {
258 ImageRecordCodeSection
= CR (
259 ImageRecordCodeSectionLink
,
260 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
262 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
264 ImageRecordCodeSectionLink
= ImageRecordCodeSectionLink
->ForwardLink
;
266 ASSERT (CurrentBase
<= ImageRecordCodeSection
->CodeSegmentBase
);
267 if (CurrentBase
< ImageRecordCodeSection
->CodeSegmentBase
) {
271 SetUefiImageMemoryAttributes (
273 ImageRecordCodeSection
->CodeSegmentBase
- CurrentBase
,
280 SetUefiImageMemoryAttributes (
281 ImageRecordCodeSection
->CodeSegmentBase
,
282 ImageRecordCodeSection
->CodeSegmentSize
,
285 CurrentBase
= ImageRecordCodeSection
->CodeSegmentBase
+ ImageRecordCodeSection
->CodeSegmentSize
;
290 ASSERT (CurrentBase
<= ImageEnd
);
291 if (CurrentBase
< ImageEnd
) {
295 SetUefiImageMemoryAttributes (
297 ImageEnd
- CurrentBase
,
305 Return if the PE image section is aligned.
307 @param[in] SectionAlignment PE/COFF section alignment
308 @param[in] MemoryType PE/COFF image memory type
310 @retval TRUE The PE image section is aligned.
311 @retval FALSE The PE image section is not aligned.
314 IsMemoryProtectionSectionAligned (
315 IN UINT32 SectionAlignment
,
316 IN EFI_MEMORY_TYPE MemoryType
319 UINT32 PageAlignment
;
321 switch (MemoryType
) {
322 case EfiRuntimeServicesCode
:
323 case EfiACPIMemoryNVS
:
324 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
326 case EfiRuntimeServicesData
:
327 case EfiACPIReclaimMemory
:
329 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
331 case EfiBootServicesCode
:
333 case EfiReservedMemoryType
:
334 PageAlignment
= EFI_PAGE_SIZE
;
338 PageAlignment
= EFI_PAGE_SIZE
;
342 if ((SectionAlignment
& (PageAlignment
- 1)) != 0) {
352 @param[in] ImageRecord A UEFI image record
356 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
359 LIST_ENTRY
*CodeSegmentListHead
;
360 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
362 CodeSegmentListHead
= &ImageRecord
->CodeSegmentList
;
363 while (!IsListEmpty (CodeSegmentListHead
)) {
364 ImageRecordCodeSection
= CR (
365 CodeSegmentListHead
->ForwardLink
,
366 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
368 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
370 RemoveEntryList (&ImageRecordCodeSection
->Link
);
371 FreePool (ImageRecordCodeSection
);
374 if (ImageRecord
->Link
.ForwardLink
!= NULL
) {
375 RemoveEntryList (&ImageRecord
->Link
);
377 FreePool (ImageRecord
);
381 Protect UEFI PE/COFF image.
383 @param[in] LoadedImage The loaded image protocol
384 @param[in] LoadedImageDevicePath The loaded image device path protocol
388 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
389 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
393 EFI_IMAGE_DOS_HEADER
*DosHdr
;
394 UINT32 PeCoffHeaderOffset
;
395 UINT32 SectionAlignment
;
396 EFI_IMAGE_SECTION_HEADER
*Section
;
397 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
;
400 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
402 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
404 UINT32 ProtectionPolicy
;
406 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
407 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
413 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
414 switch (ProtectionPolicy
) {
417 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
424 ImageRecord
= AllocateZeroPool (sizeof(*ImageRecord
));
425 if (ImageRecord
== NULL
) {
428 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
431 // Step 1: record whole region
433 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
434 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
436 ImageAddress
= LoadedImage
->ImageBase
;
438 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
439 if (PdbPointer
!= NULL
) {
440 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
444 // Check PE/COFF image
446 DosHdr
= (EFI_IMAGE_DOS_HEADER
*) (UINTN
) ImageAddress
;
447 PeCoffHeaderOffset
= 0;
448 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
449 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
452 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*) (UINTN
) ImageAddress
+ PeCoffHeaderOffset
);
453 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
454 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
455 // It might be image in SMM.
460 // Get SectionAlignment
462 if (Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
463 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
465 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
468 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
470 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
472 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
473 if (PdbPointer
!= NULL
) {
474 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
479 Section
= (EFI_IMAGE_SECTION_HEADER
*) (
480 (UINT8
*) (UINTN
) ImageAddress
+
483 sizeof(EFI_IMAGE_FILE_HEADER
) +
484 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
486 ImageRecord
->CodeSegmentCount
= 0;
487 InitializeListHead (&ImageRecord
->CodeSegmentList
);
488 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
489 Name
= Section
[Index
].Name
;
492 " Section - '%c%c%c%c%c%c%c%c'\n",
504 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
505 // can always be mapped read-only, classify a section as a code section only
506 // if it has the executable attribute set and the writable attribute cleared.
508 // This adheres more closely to the PE/COFF spec, and avoids issues with
509 // Linux OS loaders that may consist of a single read/write/execute section.
511 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
512 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
513 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
514 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
515 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
516 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
517 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
518 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
519 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
520 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
523 // Step 2: record code section
525 ImageRecordCodeSection
= AllocatePool (sizeof(*ImageRecordCodeSection
));
526 if (ImageRecordCodeSection
== NULL
) {
529 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
531 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
532 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE(Section
[Index
].SizeOfRawData
, SectionAlignment
);
534 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
536 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
537 ImageRecord
->CodeSegmentCount
++;
541 if (ImageRecord
->CodeSegmentCount
== 0) {
543 // If a UEFI executable consists of a single read+write+exec PE/COFF
544 // section, that isn't actually an error. The image can be launched
545 // alright, only image protection cannot be applied to it fully.
547 // One example that elicits this is (some) Linux kernels (with the EFI stub
550 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
551 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
552 if (PdbPointer
!= NULL
) {
553 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
561 SortImageRecordCodeSection (ImageRecord
);
563 // Check overlap all section in ImageBase/Size
565 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
566 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
571 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
572 // Given that the loader always allocates full pages, we know the space after the image is not used.
574 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
577 // CPU ARCH present. Update memory attribute directly.
579 SetUefiImageProtectionAttributes (ImageRecord
);
582 // Record the image record in the list so we can undo the protections later
584 InsertTailList (&mProtectedImageRecordList
, &ImageRecord
->Link
);
591 Unprotect UEFI image.
593 @param[in] LoadedImage The loaded image protocol
594 @param[in] LoadedImageDevicePath The loaded image device path protocol
598 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
599 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
602 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
603 LIST_ENTRY
*ImageRecordLink
;
605 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
606 for (ImageRecordLink
= mProtectedImageRecordList
.ForwardLink
;
607 ImageRecordLink
!= &mProtectedImageRecordList
;
608 ImageRecordLink
= ImageRecordLink
->ForwardLink
) {
611 IMAGE_PROPERTIES_RECORD
,
613 IMAGE_PROPERTIES_RECORD_SIGNATURE
616 if (ImageRecord
->ImageBase
== (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
) {
617 SetUefiImageMemoryAttributes (ImageRecord
->ImageBase
,
618 ImageRecord
->ImageSize
,
620 FreeImageRecord (ImageRecord
);
628 Return the EFI memory permission attribute associated with memory
629 type 'MemoryType' under the configured DXE memory protection policy.
631 @param MemoryType Memory type.
635 GetPermissionAttributeForMemoryType (
636 IN EFI_MEMORY_TYPE MemoryType
641 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
643 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
646 TestBit
= LShiftU64 (1, MemoryType
);
649 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
650 return EFI_MEMORY_XP
;
657 Sort memory map entries based upon PhysicalStart, from low to high.
659 @param MemoryMap A pointer to the buffer in which firmware places
660 the current memory map.
661 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
662 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
667 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
668 IN UINTN MemoryMapSize
,
669 IN UINTN DescriptorSize
672 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
673 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
674 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
675 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
677 MemoryMapEntry
= MemoryMap
;
678 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
679 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
680 while (MemoryMapEntry
< MemoryMapEnd
) {
681 while (NextMemoryMapEntry
< MemoryMapEnd
) {
682 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
683 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
684 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
685 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
688 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
691 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
692 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
697 Merge adjacent memory map entries if they use the same memory protection policy
699 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
700 the current memory map.
701 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
702 MemoryMap buffer. On input, this is the size of
703 the current memory map. On output,
704 it is the size of new memory map after merge.
705 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
709 MergeMemoryMapForProtectionPolicy (
710 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
711 IN OUT UINTN
*MemoryMapSize
,
712 IN UINTN DescriptorSize
715 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
716 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
717 UINT64 MemoryBlockLength
;
718 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
719 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
722 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
724 MemoryMapEntry
= MemoryMap
;
725 NewMemoryMapEntry
= MemoryMap
;
726 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
727 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
728 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
729 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
732 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
733 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
735 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
736 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
737 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
738 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
739 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
740 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
743 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
746 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
751 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
752 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
755 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
762 Remove exec permissions from all regions whose type is identified by
763 PcdDxeNxMemoryProtectionPolicy.
767 InitializeDxeNxMemoryProtectionPolicy (
773 UINTN DescriptorSize
;
774 UINT32 DescriptorVersion
;
775 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
776 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
777 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
781 EFI_GCD_MAP_ENTRY
*Entry
;
782 EFI_PEI_HOB_POINTERS Hob
;
783 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
784 EFI_PHYSICAL_ADDRESS StackBase
;
787 // Get the EFI memory map.
792 Status
= gBS
->GetMemoryMap (
799 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
801 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
802 ASSERT (MemoryMap
!= NULL
);
803 Status
= gBS
->GetMemoryMap (
810 if (EFI_ERROR (Status
)) {
811 FreePool (MemoryMap
);
813 } while (Status
== EFI_BUFFER_TOO_SMALL
);
814 ASSERT_EFI_ERROR (Status
);
817 if (PcdGetBool (PcdCpuStackGuard
)) {
819 // Get the base of stack from Hob.
821 Hob
.Raw
= GetHobList ();
822 while ((Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION
, Hob
.Raw
)) != NULL
) {
823 MemoryHob
= Hob
.MemoryAllocation
;
824 if (CompareGuid(&gEfiHobMemoryAllocStackGuid
, &MemoryHob
->AllocDescriptor
.Name
)) {
827 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",
829 MemoryHob
->AllocDescriptor
.MemoryBaseAddress
,
830 MemoryHob
->AllocDescriptor
.MemoryLength
833 StackBase
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
;
835 // Ensure the base of the stack is page-size aligned.
837 ASSERT ((StackBase
& EFI_PAGE_MASK
) == 0);
840 Hob
.Raw
= GET_NEXT_HOB (Hob
);
844 // Ensure the base of stack can be found from Hob when stack guard is
847 ASSERT (StackBase
!= 0);
852 "%a: applying strict permissions to active memory regions\n",
856 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
858 MemoryMapEntry
= MemoryMap
;
859 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
860 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
862 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
863 if (Attributes
!= 0) {
864 SetUefiImageMemoryAttributes (
865 MemoryMapEntry
->PhysicalStart
,
866 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
870 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
873 if (MemoryMapEntry
->PhysicalStart
== 0 &&
874 PcdGet8 (PcdNullPointerDetectionPropertyMask
) != 0) {
876 ASSERT (MemoryMapEntry
->NumberOfPages
> 0);
877 SetUefiImageMemoryAttributes (
879 EFI_PAGES_TO_SIZE (1),
880 EFI_MEMORY_RP
| Attributes
);
884 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
887 if (StackBase
!= 0 &&
888 (StackBase
>= MemoryMapEntry
->PhysicalStart
&&
889 StackBase
< MemoryMapEntry
->PhysicalStart
+
890 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
)) &&
891 PcdGetBool (PcdCpuStackGuard
)) {
893 SetUefiImageMemoryAttributes (
895 EFI_PAGES_TO_SIZE (1),
896 EFI_MEMORY_RP
| Attributes
);
900 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
902 FreePool (MemoryMap
);
905 // Apply the policy for RAM regions that we know are present and
906 // accessible, but have not been added to the UEFI memory map (yet).
908 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
911 "%a: applying strict permissions to inactive memory regions\n",
915 CoreAcquireGcdMemoryLock ();
917 Link
= mGcdMemorySpaceMap
.ForwardLink
;
918 while (Link
!= &mGcdMemorySpaceMap
) {
920 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
922 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
923 Entry
->EndAddress
< MAX_ADDRESS
&&
924 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
925 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
927 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
928 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
931 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
932 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
935 ASSERT(gCpu
!= NULL
);
936 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
937 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
940 Link
= Link
->ForwardLink
;
942 CoreReleaseGcdMemoryLock ();
948 A notification for CPU_ARCH protocol.
950 @param[in] Event Event whose notification function is being invoked.
951 @param[in] Context Pointer to the notification function's context,
952 which is implementation-dependent.
957 MemoryProtectionCpuArchProtocolNotify (
963 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
964 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
966 EFI_HANDLE
*HandleBuffer
;
969 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
970 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
971 if (EFI_ERROR (Status
)) {
976 // Apply the memory protection policy on non-BScode/RTcode regions.
978 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
979 InitializeDxeNxMemoryProtectionPolicy ();
983 // Call notify function meant for Heap Guard.
985 HeapGuardCpuArchProtocolNotify ();
987 if (mImageProtectionPolicy
== 0) {
991 Status
= gBS
->LocateHandleBuffer (
993 &gEfiLoadedImageProtocolGuid
,
998 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
1002 for (Index
= 0; Index
< NoHandles
; Index
++) {
1003 Status
= gBS
->HandleProtocol (
1004 HandleBuffer
[Index
],
1005 &gEfiLoadedImageProtocolGuid
,
1006 (VOID
**)&LoadedImage
1008 if (EFI_ERROR(Status
)) {
1011 Status
= gBS
->HandleProtocol (
1012 HandleBuffer
[Index
],
1013 &gEfiLoadedImageDevicePathProtocolGuid
,
1014 (VOID
**)&LoadedImageDevicePath
1016 if (EFI_ERROR(Status
)) {
1017 LoadedImageDevicePath
= NULL
;
1020 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
1022 FreePool (HandleBuffer
);
1025 CoreCloseEvent (Event
);
1029 ExitBootServices Callback function for memory protection.
1032 MemoryProtectionExitBootServicesCallback (
1036 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
1040 // We need remove the RT protection, because RT relocation need write code segment
1041 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1043 // Firmware does not own page tables after ExitBootServices(), so the OS would
1044 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1045 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1046 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1048 if (mImageProtectionPolicy
!= 0) {
1049 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1050 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1051 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1057 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1058 order to skip unfixable NULL pointer access issues detected in OptionROM or
1061 @param[in] Event The Event this notify function registered to.
1062 @param[in] Context Pointer to the context data registered to the Event.
1066 DisableNullDetectionAtTheEndOfDxe (
1072 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
1074 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1076 // Disable NULL pointer detection by enabling first 4K page
1078 Status
= CoreGetMemorySpaceDescriptor (0, &Desc
);
1079 ASSERT_EFI_ERROR (Status
);
1081 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
1082 Status
= CoreSetMemorySpaceCapabilities (
1085 Desc
.Capabilities
| EFI_MEMORY_RP
1087 ASSERT_EFI_ERROR (Status
);
1090 Status
= CoreSetMemorySpaceAttributes (
1093 Desc
.Attributes
& ~EFI_MEMORY_RP
1095 ASSERT_EFI_ERROR (Status
);
1097 CoreCloseEvent (Event
);
1098 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1104 Initialize Memory Protection support.
1108 CoreInitializeMemoryProtection (
1114 EFI_EVENT EndOfDxeEvent
;
1117 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1119 InitializeListHead (&mProtectedImageRecordList
);
1122 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1123 // - code regions should have no EFI_MEMORY_XP attribute
1124 // - EfiConventionalMemory and EfiBootServicesData should use the
1127 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1128 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1129 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1130 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1131 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1133 Status
= CoreCreateEvent (
1136 MemoryProtectionCpuArchProtocolNotify
,
1140 ASSERT_EFI_ERROR(Status
);
1143 // Register for protocol notifactions on this event
1145 Status
= CoreRegisterProtocolNotify (
1146 &gEfiCpuArchProtocolGuid
,
1150 ASSERT_EFI_ERROR(Status
);
1153 // Register a callback to disable NULL pointer detection at EndOfDxe
1155 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
))
1157 Status
= CoreCreateEventEx (
1160 DisableNullDetectionAtTheEndOfDxe
,
1162 &gEfiEndOfDxeEventGroupGuid
,
1165 ASSERT_EFI_ERROR (Status
);
1172 Returns whether we are currently executing in SMM mode.
1183 if (gSmmBase2
!= NULL
) {
1184 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1190 Manage memory permission attributes on a memory range, according to the
1191 configured DXE memory protection policy.
1193 @param OldType The old memory type of the range
1194 @param NewType The new memory type of the range
1195 @param Memory The base address of the range
1196 @param Length The size of the range (in bytes)
1198 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1199 are updated in this case
1200 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1201 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1202 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1203 @return other Return value of gCpu->SetMemoryAttributes()
1208 ApplyMemoryProtectionPolicy (
1209 IN EFI_MEMORY_TYPE OldType
,
1210 IN EFI_MEMORY_TYPE NewType
,
1211 IN EFI_PHYSICAL_ADDRESS Memory
,
1215 UINT64 OldAttributes
;
1216 UINT64 NewAttributes
;
1219 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1220 // does not apply to allocations performed in SMM mode.
1227 // If the CPU arch protocol is not installed yet, we cannot manage memory
1228 // permission attributes, and it is the job of the driver that installs this
1229 // protocol to set the permissions on existing allocations.
1236 // Check if a DXE memory protection policy has been configured
1238 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1243 // Don't overwrite Guard pages, which should be the first and/or last page,
1246 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
)) {
1247 if (IsGuardPage (Memory
)) {
1248 Memory
+= EFI_PAGE_SIZE
;
1249 Length
-= EFI_PAGE_SIZE
;
1255 if (IsGuardPage (Memory
+ Length
- EFI_PAGE_SIZE
)) {
1256 Length
-= EFI_PAGE_SIZE
;
1264 // Update the executable permissions according to the DXE memory
1265 // protection policy, but only if
1266 // - the policy is different between the old and the new type, or
1267 // - this is a newly added region (OldType == EfiMaxMemoryType)
1269 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1271 if (OldType
!= EfiMaxMemoryType
) {
1272 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1273 if (OldAttributes
== NewAttributes
) {
1274 // policy is the same between OldType and NewType
1277 } else if (NewAttributes
== 0) {
1278 // newly added region of a type that does not require protection
1282 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);