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>
39 #include <Protocol/FirmwareVolume2.h>
40 #include <Protocol/SimpleFileSystem.h>
43 #include "Mem/HeapGuard.h"
46 // Image type definitions
48 #define IMAGE_UNKNOWN 0x00000001
49 #define IMAGE_FROM_FV 0x00000002
52 // Protection policy bit definition
54 #define DO_NOT_PROTECT 0x00000000
55 #define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001
57 #define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
58 #define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
60 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
61 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
63 UINT32 mImageProtectionPolicy
;
65 extern LIST_ENTRY mGcdMemorySpaceMap
;
67 STATIC LIST_ENTRY mProtectedImageRecordList
;
70 Sort code section in image record, based upon CodeSegmentBase from low to high.
72 @param ImageRecord image record to be sorted
75 SortImageRecordCodeSection (
76 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
80 Check if code section in image record is valid.
82 @param ImageRecord image record to be checked
84 @retval TRUE image record is valid
85 @retval FALSE image record is invalid
88 IsImageRecordCodeSectionValid (
89 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
95 @param[in] File This is a pointer to the device path of the file that is
98 @return UINT32 Image Type
102 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
106 EFI_HANDLE DeviceHandle
;
107 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
110 return IMAGE_UNKNOWN
;
114 // First check to see if File is from a Firmware Volume
117 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)File
;
118 Status
= gBS
->LocateDevicePath (
119 &gEfiFirmwareVolume2ProtocolGuid
,
123 if (!EFI_ERROR (Status
)) {
124 Status
= gBS
->OpenProtocol (
126 &gEfiFirmwareVolume2ProtocolGuid
,
130 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
132 if (!EFI_ERROR (Status
)) {
133 return IMAGE_FROM_FV
;
137 return IMAGE_UNKNOWN
;
141 Get UEFI image protection policy based upon image type.
143 @param[in] ImageType The UEFI image type
145 @return UEFI image protection policy
148 GetProtectionPolicyFromImageType (
152 if ((ImageType
& mImageProtectionPolicy
) == 0) {
153 return DO_NOT_PROTECT
;
155 return PROTECT_IF_ALIGNED_ELSE_ALLOW
;
160 Get UEFI image protection policy based upon loaded image device path.
162 @param[in] LoadedImage The loaded image protocol
163 @param[in] LoadedImageDevicePath The loaded image device path protocol
165 @return UEFI image protection policy
168 GetUefiImageProtectionPolicy (
169 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
170 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
175 UINT32 ProtectionPolicy
;
181 if (gSmmBase2
!= NULL
) {
182 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
192 if (LoadedImage
== gDxeCoreLoadedImage
) {
193 ImageType
= IMAGE_FROM_FV
;
195 ImageType
= GetImageType (LoadedImageDevicePath
);
198 ProtectionPolicy
= GetProtectionPolicyFromImageType (ImageType
);
199 return ProtectionPolicy
;
203 Set UEFI image memory attributes.
205 @param[in] BaseAddress Specified start address
206 @param[in] Length Specified length
207 @param[in] Attributes Specified attributes
210 SetUefiImageMemoryAttributes (
211 IN UINT64 BaseAddress
,
217 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
218 UINT64 FinalAttributes
;
220 Status
= CoreGetMemorySpaceDescriptor (BaseAddress
, &Descriptor
);
221 ASSERT_EFI_ERROR (Status
);
223 FinalAttributes
= (Descriptor
.Attributes
& EFI_CACHE_ATTRIBUTE_MASK
) | (Attributes
& EFI_MEMORY_ATTRIBUTE_MASK
);
225 DEBUG ((DEBUG_INFO
, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress
, Length
, FinalAttributes
));
227 ASSERT (gCpu
!= NULL
);
228 gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, Length
, FinalAttributes
);
232 Set UEFI image protection attributes.
234 @param[in] ImageRecord A UEFI image record
237 SetUefiImageProtectionAttributes (
238 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
241 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
242 LIST_ENTRY
*ImageRecordCodeSectionLink
;
243 LIST_ENTRY
*ImageRecordCodeSectionEndLink
;
244 LIST_ENTRY
*ImageRecordCodeSectionList
;
248 ImageRecordCodeSectionList
= &ImageRecord
->CodeSegmentList
;
250 CurrentBase
= ImageRecord
->ImageBase
;
251 ImageEnd
= ImageRecord
->ImageBase
+ ImageRecord
->ImageSize
;
253 ImageRecordCodeSectionLink
= ImageRecordCodeSectionList
->ForwardLink
;
254 ImageRecordCodeSectionEndLink
= ImageRecordCodeSectionList
;
255 while (ImageRecordCodeSectionLink
!= ImageRecordCodeSectionEndLink
) {
256 ImageRecordCodeSection
= CR (
257 ImageRecordCodeSectionLink
,
258 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
260 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
262 ImageRecordCodeSectionLink
= ImageRecordCodeSectionLink
->ForwardLink
;
264 ASSERT (CurrentBase
<= ImageRecordCodeSection
->CodeSegmentBase
);
265 if (CurrentBase
< ImageRecordCodeSection
->CodeSegmentBase
) {
269 SetUefiImageMemoryAttributes (
271 ImageRecordCodeSection
->CodeSegmentBase
- CurrentBase
,
279 SetUefiImageMemoryAttributes (
280 ImageRecordCodeSection
->CodeSegmentBase
,
281 ImageRecordCodeSection
->CodeSegmentSize
,
284 CurrentBase
= ImageRecordCodeSection
->CodeSegmentBase
+ ImageRecordCodeSection
->CodeSegmentSize
;
290 ASSERT (CurrentBase
<= ImageEnd
);
291 if (CurrentBase
< ImageEnd
) {
295 SetUefiImageMemoryAttributes (
297 ImageEnd
- CurrentBase
,
306 Return if the PE image section is aligned.
308 @param[in] SectionAlignment PE/COFF section alignment
309 @param[in] MemoryType PE/COFF image memory type
311 @retval TRUE The PE image section is aligned.
312 @retval FALSE The PE image section is not aligned.
315 IsMemoryProtectionSectionAligned (
316 IN UINT32 SectionAlignment
,
317 IN EFI_MEMORY_TYPE MemoryType
320 UINT32 PageAlignment
;
322 switch (MemoryType
) {
323 case EfiRuntimeServicesCode
:
324 case EfiACPIMemoryNVS
:
325 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
327 case EfiRuntimeServicesData
:
328 case EfiACPIReclaimMemory
:
330 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
332 case EfiBootServicesCode
:
334 case EfiReservedMemoryType
:
335 PageAlignment
= EFI_PAGE_SIZE
;
339 PageAlignment
= EFI_PAGE_SIZE
;
343 if ((SectionAlignment
& (PageAlignment
- 1)) != 0) {
353 @param[in] ImageRecord A UEFI image record
357 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
360 LIST_ENTRY
*CodeSegmentListHead
;
361 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
363 CodeSegmentListHead
= &ImageRecord
->CodeSegmentList
;
364 while (!IsListEmpty (CodeSegmentListHead
)) {
365 ImageRecordCodeSection
= CR (
366 CodeSegmentListHead
->ForwardLink
,
367 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
369 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
371 RemoveEntryList (&ImageRecordCodeSection
->Link
);
372 FreePool (ImageRecordCodeSection
);
375 if (ImageRecord
->Link
.ForwardLink
!= NULL
) {
376 RemoveEntryList (&ImageRecord
->Link
);
379 FreePool (ImageRecord
);
383 Protect UEFI PE/COFF image.
385 @param[in] LoadedImage The loaded image protocol
386 @param[in] LoadedImageDevicePath The loaded image device path protocol
390 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
391 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
395 EFI_IMAGE_DOS_HEADER
*DosHdr
;
396 UINT32 PeCoffHeaderOffset
;
397 UINT32 SectionAlignment
;
398 EFI_IMAGE_SECTION_HEADER
*Section
;
399 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
;
402 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
404 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
406 UINT32 ProtectionPolicy
;
408 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
409 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
415 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
416 switch (ProtectionPolicy
) {
419 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
426 ImageRecord
= AllocateZeroPool (sizeof (*ImageRecord
));
427 if (ImageRecord
== NULL
) {
431 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
434 // Step 1: record whole region
436 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
437 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
439 ImageAddress
= LoadedImage
->ImageBase
;
441 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*)(UINTN
)ImageAddress
);
442 if (PdbPointer
!= NULL
) {
443 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
447 // Check PE/COFF image
449 DosHdr
= (EFI_IMAGE_DOS_HEADER
*)(UINTN
)ImageAddress
;
450 PeCoffHeaderOffset
= 0;
451 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
452 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
455 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*)(UINTN
)ImageAddress
+ PeCoffHeaderOffset
);
456 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
457 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
458 // It might be image in SMM.
463 // Get SectionAlignment
465 if (Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
466 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
468 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
471 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
475 "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
478 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*)(UINTN
)ImageAddress
);
479 if (PdbPointer
!= NULL
) {
480 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
486 Section
= (EFI_IMAGE_SECTION_HEADER
*)(
487 (UINT8
*)(UINTN
)ImageAddress
+
490 sizeof (EFI_IMAGE_FILE_HEADER
) +
491 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
493 ImageRecord
->CodeSegmentCount
= 0;
494 InitializeListHead (&ImageRecord
->CodeSegmentList
);
495 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
496 Name
= Section
[Index
].Name
;
499 " Section - '%c%c%c%c%c%c%c%c'\n",
511 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
512 // can always be mapped read-only, classify a section as a code section only
513 // if it has the executable attribute set and the writable attribute cleared.
515 // This adheres more closely to the PE/COFF spec, and avoids issues with
516 // Linux OS loaders that may consist of a single read/write/execute section.
518 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
519 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
520 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
521 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
522 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
523 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
524 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
525 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
526 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
527 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
530 // Step 2: record code section
532 ImageRecordCodeSection
= AllocatePool (sizeof (*ImageRecordCodeSection
));
533 if (ImageRecordCodeSection
== NULL
) {
537 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
539 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
540 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE (Section
[Index
].SizeOfRawData
, SectionAlignment
);
542 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
544 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
545 ImageRecord
->CodeSegmentCount
++;
549 if (ImageRecord
->CodeSegmentCount
== 0) {
551 // If a UEFI executable consists of a single read+write+exec PE/COFF
552 // section, that isn't actually an error. The image can be launched
553 // alright, only image protection cannot be applied to it fully.
555 // One example that elicits this is (some) Linux kernels (with the EFI stub
558 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
559 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*)(UINTN
)ImageAddress
);
560 if (PdbPointer
!= NULL
) {
561 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
570 SortImageRecordCodeSection (ImageRecord
);
572 // Check overlap all section in ImageBase/Size
574 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
575 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
580 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
581 // Given that the loader always allocates full pages, we know the space after the image is not used.
583 ImageRecord
->ImageSize
= ALIGN_VALUE (LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
586 // CPU ARCH present. Update memory attribute directly.
588 SetUefiImageProtectionAttributes (ImageRecord
);
591 // Record the image record in the list so we can undo the protections later
593 InsertTailList (&mProtectedImageRecordList
, &ImageRecord
->Link
);
600 Unprotect UEFI image.
602 @param[in] LoadedImage The loaded image protocol
603 @param[in] LoadedImageDevicePath The loaded image device path protocol
607 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
608 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
611 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
612 LIST_ENTRY
*ImageRecordLink
;
614 if (PcdGet32 (PcdImageProtectionPolicy
) != 0) {
615 for (ImageRecordLink
= mProtectedImageRecordList
.ForwardLink
;
616 ImageRecordLink
!= &mProtectedImageRecordList
;
617 ImageRecordLink
= ImageRecordLink
->ForwardLink
)
621 IMAGE_PROPERTIES_RECORD
,
623 IMAGE_PROPERTIES_RECORD_SIGNATURE
626 if (ImageRecord
->ImageBase
== (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
) {
627 SetUefiImageMemoryAttributes (
628 ImageRecord
->ImageBase
,
629 ImageRecord
->ImageSize
,
632 FreeImageRecord (ImageRecord
);
640 Return the EFI memory permission attribute associated with memory
641 type 'MemoryType' under the configured DXE memory protection policy.
643 @param MemoryType Memory type.
647 GetPermissionAttributeForMemoryType (
648 IN EFI_MEMORY_TYPE MemoryType
653 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
655 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
658 TestBit
= LShiftU64 (1, MemoryType
);
661 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
662 return EFI_MEMORY_XP
;
669 Sort memory map entries based upon PhysicalStart, from low to high.
671 @param MemoryMap A pointer to the buffer in which firmware places
672 the current memory map.
673 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
674 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
679 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
680 IN UINTN MemoryMapSize
,
681 IN UINTN DescriptorSize
684 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
685 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
686 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
687 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
689 MemoryMapEntry
= MemoryMap
;
690 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
691 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*)((UINT8
*)MemoryMap
+ MemoryMapSize
);
692 while (MemoryMapEntry
< MemoryMapEnd
) {
693 while (NextMemoryMapEntry
< MemoryMapEnd
) {
694 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
695 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
696 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
697 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof (EFI_MEMORY_DESCRIPTOR
));
700 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
703 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
704 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
709 Merge adjacent memory map entries if they use the same memory protection policy
711 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
712 the current memory map.
713 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
714 MemoryMap buffer. On input, this is the size of
715 the current memory map. On output,
716 it is the size of new memory map after merge.
717 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
721 MergeMemoryMapForProtectionPolicy (
722 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
723 IN OUT UINTN
*MemoryMapSize
,
724 IN UINTN DescriptorSize
727 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
728 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
729 UINT64 MemoryBlockLength
;
730 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
731 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
734 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
736 MemoryMapEntry
= MemoryMap
;
737 NewMemoryMapEntry
= MemoryMap
;
738 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*)((UINT8
*)MemoryMap
+ *MemoryMapSize
);
739 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
740 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
741 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
744 MemoryBlockLength
= (UINT64
)(EFI_PAGES_TO_SIZE ((UINTN
)MemoryMapEntry
->NumberOfPages
));
745 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
747 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
748 (Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
)) &&
749 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
))
751 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
752 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
753 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
756 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
759 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
764 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
765 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
768 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
774 Remove exec permissions from all regions whose type is identified by
775 PcdDxeNxMemoryProtectionPolicy.
779 InitializeDxeNxMemoryProtectionPolicy (
785 UINTN DescriptorSize
;
786 UINT32 DescriptorVersion
;
787 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
788 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
789 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
793 EFI_GCD_MAP_ENTRY
*Entry
;
794 EFI_PEI_HOB_POINTERS Hob
;
795 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
796 EFI_PHYSICAL_ADDRESS StackBase
;
799 // Get the EFI memory map.
804 Status
= gBS
->GetMemoryMap (
811 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
813 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*)AllocatePool (MemoryMapSize
);
814 ASSERT (MemoryMap
!= NULL
);
815 Status
= gBS
->GetMemoryMap (
822 if (EFI_ERROR (Status
)) {
823 FreePool (MemoryMap
);
825 } while (Status
== EFI_BUFFER_TOO_SMALL
);
827 ASSERT_EFI_ERROR (Status
);
830 if (PcdGetBool (PcdCpuStackGuard
)) {
832 // Get the base of stack from Hob.
834 Hob
.Raw
= GetHobList ();
835 while ((Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION
, Hob
.Raw
)) != NULL
) {
836 MemoryHob
= Hob
.MemoryAllocation
;
837 if (CompareGuid (&gEfiHobMemoryAllocStackGuid
, &MemoryHob
->AllocDescriptor
.Name
)) {
840 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",
842 MemoryHob
->AllocDescriptor
.MemoryBaseAddress
,
843 MemoryHob
->AllocDescriptor
.MemoryLength
846 StackBase
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
;
848 // Ensure the base of the stack is page-size aligned.
850 ASSERT ((StackBase
& EFI_PAGE_MASK
) == 0);
854 Hob
.Raw
= GET_NEXT_HOB (Hob
);
858 // Ensure the base of stack can be found from Hob when stack guard is
861 ASSERT (StackBase
!= 0);
866 "%a: applying strict permissions to active memory regions\n",
870 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
872 MemoryMapEntry
= MemoryMap
;
873 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*)((UINT8
*)MemoryMap
+ MemoryMapSize
);
874 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
875 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
876 if (Attributes
!= 0) {
877 SetUefiImageMemoryAttributes (
878 MemoryMapEntry
->PhysicalStart
,
879 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
884 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
887 if ((MemoryMapEntry
->PhysicalStart
== 0) &&
888 (PcdGet8 (PcdNullPointerDetectionPropertyMask
) != 0))
890 ASSERT (MemoryMapEntry
->NumberOfPages
> 0);
891 SetUefiImageMemoryAttributes (
893 EFI_PAGES_TO_SIZE (1),
894 EFI_MEMORY_RP
| Attributes
899 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
902 if ((StackBase
!= 0) &&
903 ((StackBase
>= MemoryMapEntry
->PhysicalStart
) &&
904 (StackBase
< MemoryMapEntry
->PhysicalStart
+
905 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
))) &&
906 PcdGetBool (PcdCpuStackGuard
))
908 SetUefiImageMemoryAttributes (
910 EFI_PAGES_TO_SIZE (1),
911 EFI_MEMORY_RP
| Attributes
916 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
919 FreePool (MemoryMap
);
922 // Apply the policy for RAM regions that we know are present and
923 // accessible, but have not been added to the UEFI memory map (yet).
925 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
928 "%a: applying strict permissions to inactive memory regions\n",
932 CoreAcquireGcdMemoryLock ();
934 Link
= mGcdMemorySpaceMap
.ForwardLink
;
935 while (Link
!= &mGcdMemorySpaceMap
) {
936 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
938 if ((Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
) &&
939 (Entry
->EndAddress
< MAX_ADDRESS
) &&
940 ((Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
941 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)))
943 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
944 (Entry
->Attributes
& EFI_CACHE_ATTRIBUTE_MASK
);
948 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
950 Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
954 ASSERT (gCpu
!= NULL
);
955 gCpu
->SetMemoryAttributes (
958 Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
963 Link
= Link
->ForwardLink
;
966 CoreReleaseGcdMemoryLock ();
971 A notification for CPU_ARCH protocol.
973 @param[in] Event Event whose notification function is being invoked.
974 @param[in] Context Pointer to the notification function's context,
975 which is implementation-dependent.
980 MemoryProtectionCpuArchProtocolNotify (
986 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
987 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
989 EFI_HANDLE
*HandleBuffer
;
992 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
993 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
994 if (EFI_ERROR (Status
)) {
999 // Apply the memory protection policy on non-BScode/RTcode regions.
1001 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1002 InitializeDxeNxMemoryProtectionPolicy ();
1006 // Call notify function meant for Heap Guard.
1008 HeapGuardCpuArchProtocolNotify ();
1010 if (mImageProtectionPolicy
== 0) {
1014 Status
= gBS
->LocateHandleBuffer (
1016 &gEfiLoadedImageProtocolGuid
,
1021 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
1025 for (Index
= 0; Index
< NoHandles
; Index
++) {
1026 Status
= gBS
->HandleProtocol (
1027 HandleBuffer
[Index
],
1028 &gEfiLoadedImageProtocolGuid
,
1029 (VOID
**)&LoadedImage
1031 if (EFI_ERROR (Status
)) {
1035 Status
= gBS
->HandleProtocol (
1036 HandleBuffer
[Index
],
1037 &gEfiLoadedImageDevicePathProtocolGuid
,
1038 (VOID
**)&LoadedImageDevicePath
1040 if (EFI_ERROR (Status
)) {
1041 LoadedImageDevicePath
= NULL
;
1044 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
1047 FreePool (HandleBuffer
);
1050 CoreCloseEvent (Event
);
1054 ExitBootServices Callback function for memory protection.
1057 MemoryProtectionExitBootServicesCallback (
1061 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
1065 // We need remove the RT protection, because RT relocation need write code segment
1066 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1068 // Firmware does not own page tables after ExitBootServices(), so the OS would
1069 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1070 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1071 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1073 if (mImageProtectionPolicy
!= 0) {
1074 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1075 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1076 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE (RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1082 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1083 order to skip unfixable NULL pointer access issues detected in OptionROM or
1086 @param[in] Event The Event this notify function registered to.
1087 @param[in] Context Pointer to the context data registered to the Event.
1091 DisableNullDetectionAtTheEndOfDxe (
1097 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
1099 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1101 // Disable NULL pointer detection by enabling first 4K page
1103 Status
= CoreGetMemorySpaceDescriptor (0, &Desc
);
1104 ASSERT_EFI_ERROR (Status
);
1106 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
1107 Status
= CoreSetMemorySpaceCapabilities (
1110 Desc
.Capabilities
| EFI_MEMORY_RP
1112 ASSERT_EFI_ERROR (Status
);
1115 Status
= CoreSetMemorySpaceAttributes (
1118 Desc
.Attributes
& ~EFI_MEMORY_RP
1120 ASSERT_EFI_ERROR (Status
);
1123 // Page 0 might have be allocated to avoid misuses. Free it here anyway.
1125 CoreFreePages (0, 1);
1127 CoreCloseEvent (Event
);
1128 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1134 Initialize Memory Protection support.
1138 CoreInitializeMemoryProtection (
1144 EFI_EVENT EndOfDxeEvent
;
1147 mImageProtectionPolicy
= PcdGet32 (PcdImageProtectionPolicy
);
1149 InitializeListHead (&mProtectedImageRecordList
);
1152 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1153 // - code regions should have no EFI_MEMORY_XP attribute
1154 // - EfiConventionalMemory and EfiBootServicesData should use the
1157 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1158 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1159 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1161 GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1162 GetPermissionAttributeForMemoryType (EfiConventionalMemory
)
1165 Status
= CoreCreateEvent (
1168 MemoryProtectionCpuArchProtocolNotify
,
1172 ASSERT_EFI_ERROR (Status
);
1175 // Register for protocol notifactions on this event
1177 Status
= CoreRegisterProtocolNotify (
1178 &gEfiCpuArchProtocolGuid
,
1182 ASSERT_EFI_ERROR (Status
);
1185 // Register a callback to disable NULL pointer detection at EndOfDxe
1187 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
))
1190 Status
= CoreCreateEventEx (
1193 DisableNullDetectionAtTheEndOfDxe
,
1195 &gEfiEndOfDxeEventGroupGuid
,
1198 ASSERT_EFI_ERROR (Status
);
1205 Returns whether we are currently executing in SMM mode.
1216 if (gSmmBase2
!= NULL
) {
1217 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1224 Manage memory permission attributes on a memory range, according to the
1225 configured DXE memory protection policy.
1227 @param OldType The old memory type of the range
1228 @param NewType The new memory type of the range
1229 @param Memory The base address of the range
1230 @param Length The size of the range (in bytes)
1232 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1233 are updated in this case
1234 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1235 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1236 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1237 @return other Return value of gCpu->SetMemoryAttributes()
1242 ApplyMemoryProtectionPolicy (
1243 IN EFI_MEMORY_TYPE OldType
,
1244 IN EFI_MEMORY_TYPE NewType
,
1245 IN EFI_PHYSICAL_ADDRESS Memory
,
1249 UINT64 OldAttributes
;
1250 UINT64 NewAttributes
;
1253 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1254 // does not apply to allocations performed in SMM mode.
1261 // If the CPU arch protocol is not installed yet, we cannot manage memory
1262 // permission attributes, and it is the job of the driver that installs this
1263 // protocol to set the permissions on existing allocations.
1270 // Check if a DXE memory protection policy has been configured
1272 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1277 // Don't overwrite Guard pages, which should be the first and/or last page,
1280 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
)) {
1281 if (IsGuardPage (Memory
)) {
1282 Memory
+= EFI_PAGE_SIZE
;
1283 Length
-= EFI_PAGE_SIZE
;
1289 if (IsGuardPage (Memory
+ Length
- EFI_PAGE_SIZE
)) {
1290 Length
-= EFI_PAGE_SIZE
;
1298 // Update the executable permissions according to the DXE memory
1299 // protection policy, but only if
1300 // - the policy is different between the old and the new type, or
1301 // - this is a newly added region (OldType == EfiMaxMemoryType)
1303 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1305 if (OldType
!= EfiMaxMemoryType
) {
1306 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1307 if (OldAttributes
== NewAttributes
) {
1308 // policy is the same between OldType and NewType
1311 } else if (NewAttributes
== 0) {
1312 // newly added region of a type that does not require protection
1316 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);