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
;
136 return IMAGE_UNKNOWN
;
140 Get UEFI image protection policy based upon image type.
142 @param[in] ImageType The UEFI image type
144 @return UEFI image protection policy
147 GetProtectionPolicyFromImageType (
151 if ((ImageType
& mImageProtectionPolicy
) == 0) {
152 return DO_NOT_PROTECT
;
154 return PROTECT_IF_ALIGNED_ELSE_ALLOW
;
159 Get UEFI image protection policy based upon loaded image device path.
161 @param[in] LoadedImage The loaded image protocol
162 @param[in] LoadedImageDevicePath The loaded image device path protocol
164 @return UEFI image protection policy
167 GetUefiImageProtectionPolicy (
168 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
169 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
174 UINT32 ProtectionPolicy
;
180 if (gSmmBase2
!= NULL
) {
181 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
190 if (LoadedImage
== gDxeCoreLoadedImage
) {
191 ImageType
= IMAGE_FROM_FV
;
193 ImageType
= GetImageType (LoadedImageDevicePath
);
195 ProtectionPolicy
= GetProtectionPolicyFromImageType (ImageType
);
196 return ProtectionPolicy
;
201 Set UEFI image memory attributes.
203 @param[in] BaseAddress Specified start address
204 @param[in] Length Specified length
205 @param[in] Attributes Specified attributes
208 SetUefiImageMemoryAttributes (
209 IN UINT64 BaseAddress
,
215 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
216 UINT64 FinalAttributes
;
218 Status
= CoreGetMemorySpaceDescriptor(BaseAddress
, &Descriptor
);
219 ASSERT_EFI_ERROR(Status
);
221 FinalAttributes
= (Descriptor
.Attributes
& EFI_CACHE_ATTRIBUTE_MASK
) | (Attributes
& EFI_MEMORY_ATTRIBUTE_MASK
);
223 DEBUG ((DEBUG_INFO
, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress
, Length
, FinalAttributes
));
225 ASSERT(gCpu
!= NULL
);
226 gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, Length
, FinalAttributes
);
230 Set UEFI image protection attributes.
232 @param[in] ImageRecord A UEFI image record
235 SetUefiImageProtectionAttributes (
236 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
239 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
240 LIST_ENTRY
*ImageRecordCodeSectionLink
;
241 LIST_ENTRY
*ImageRecordCodeSectionEndLink
;
242 LIST_ENTRY
*ImageRecordCodeSectionList
;
246 ImageRecordCodeSectionList
= &ImageRecord
->CodeSegmentList
;
248 CurrentBase
= ImageRecord
->ImageBase
;
249 ImageEnd
= ImageRecord
->ImageBase
+ ImageRecord
->ImageSize
;
251 ImageRecordCodeSectionLink
= ImageRecordCodeSectionList
->ForwardLink
;
252 ImageRecordCodeSectionEndLink
= ImageRecordCodeSectionList
;
253 while (ImageRecordCodeSectionLink
!= ImageRecordCodeSectionEndLink
) {
254 ImageRecordCodeSection
= CR (
255 ImageRecordCodeSectionLink
,
256 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
258 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
260 ImageRecordCodeSectionLink
= ImageRecordCodeSectionLink
->ForwardLink
;
262 ASSERT (CurrentBase
<= ImageRecordCodeSection
->CodeSegmentBase
);
263 if (CurrentBase
< ImageRecordCodeSection
->CodeSegmentBase
) {
267 SetUefiImageMemoryAttributes (
269 ImageRecordCodeSection
->CodeSegmentBase
- CurrentBase
,
276 SetUefiImageMemoryAttributes (
277 ImageRecordCodeSection
->CodeSegmentBase
,
278 ImageRecordCodeSection
->CodeSegmentSize
,
281 CurrentBase
= ImageRecordCodeSection
->CodeSegmentBase
+ ImageRecordCodeSection
->CodeSegmentSize
;
286 ASSERT (CurrentBase
<= ImageEnd
);
287 if (CurrentBase
< ImageEnd
) {
291 SetUefiImageMemoryAttributes (
293 ImageEnd
- CurrentBase
,
301 Return if the PE image section is aligned.
303 @param[in] SectionAlignment PE/COFF section alignment
304 @param[in] MemoryType PE/COFF image memory type
306 @retval TRUE The PE image section is aligned.
307 @retval FALSE The PE image section is not aligned.
310 IsMemoryProtectionSectionAligned (
311 IN UINT32 SectionAlignment
,
312 IN EFI_MEMORY_TYPE MemoryType
315 UINT32 PageAlignment
;
317 switch (MemoryType
) {
318 case EfiRuntimeServicesCode
:
319 case EfiACPIMemoryNVS
:
320 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
322 case EfiRuntimeServicesData
:
323 case EfiACPIReclaimMemory
:
325 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
327 case EfiBootServicesCode
:
329 case EfiReservedMemoryType
:
330 PageAlignment
= EFI_PAGE_SIZE
;
334 PageAlignment
= EFI_PAGE_SIZE
;
338 if ((SectionAlignment
& (PageAlignment
- 1)) != 0) {
348 @param[in] ImageRecord A UEFI image record
352 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
355 LIST_ENTRY
*CodeSegmentListHead
;
356 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
358 CodeSegmentListHead
= &ImageRecord
->CodeSegmentList
;
359 while (!IsListEmpty (CodeSegmentListHead
)) {
360 ImageRecordCodeSection
= CR (
361 CodeSegmentListHead
->ForwardLink
,
362 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
364 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
366 RemoveEntryList (&ImageRecordCodeSection
->Link
);
367 FreePool (ImageRecordCodeSection
);
370 if (ImageRecord
->Link
.ForwardLink
!= NULL
) {
371 RemoveEntryList (&ImageRecord
->Link
);
373 FreePool (ImageRecord
);
377 Protect UEFI PE/COFF image.
379 @param[in] LoadedImage The loaded image protocol
380 @param[in] LoadedImageDevicePath The loaded image device path protocol
384 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
385 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
389 EFI_IMAGE_DOS_HEADER
*DosHdr
;
390 UINT32 PeCoffHeaderOffset
;
391 UINT32 SectionAlignment
;
392 EFI_IMAGE_SECTION_HEADER
*Section
;
393 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
;
396 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
398 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
400 UINT32 ProtectionPolicy
;
402 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
403 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
409 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
410 switch (ProtectionPolicy
) {
413 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
420 ImageRecord
= AllocateZeroPool (sizeof(*ImageRecord
));
421 if (ImageRecord
== NULL
) {
424 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
427 // Step 1: record whole region
429 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
430 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
432 ImageAddress
= LoadedImage
->ImageBase
;
434 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
435 if (PdbPointer
!= NULL
) {
436 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
440 // Check PE/COFF image
442 DosHdr
= (EFI_IMAGE_DOS_HEADER
*) (UINTN
) ImageAddress
;
443 PeCoffHeaderOffset
= 0;
444 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
445 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
448 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*) (UINTN
) ImageAddress
+ PeCoffHeaderOffset
);
449 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
450 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
451 // It might be image in SMM.
456 // Get SectionAlignment
458 if (Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
459 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
461 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
464 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
466 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
468 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
469 if (PdbPointer
!= NULL
) {
470 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
475 Section
= (EFI_IMAGE_SECTION_HEADER
*) (
476 (UINT8
*) (UINTN
) ImageAddress
+
479 sizeof(EFI_IMAGE_FILE_HEADER
) +
480 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
482 ImageRecord
->CodeSegmentCount
= 0;
483 InitializeListHead (&ImageRecord
->CodeSegmentList
);
484 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
485 Name
= Section
[Index
].Name
;
488 " Section - '%c%c%c%c%c%c%c%c'\n",
500 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
501 // can always be mapped read-only, classify a section as a code section only
502 // if it has the executable attribute set and the writable attribute cleared.
504 // This adheres more closely to the PE/COFF spec, and avoids issues with
505 // Linux OS loaders that may consist of a single read/write/execute section.
507 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
508 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
509 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
510 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
511 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
512 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
513 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
514 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
515 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
516 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
519 // Step 2: record code section
521 ImageRecordCodeSection
= AllocatePool (sizeof(*ImageRecordCodeSection
));
522 if (ImageRecordCodeSection
== NULL
) {
525 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
527 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
528 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE(Section
[Index
].SizeOfRawData
, SectionAlignment
);
530 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
532 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
533 ImageRecord
->CodeSegmentCount
++;
537 if (ImageRecord
->CodeSegmentCount
== 0) {
539 // If a UEFI executable consists of a single read+write+exec PE/COFF
540 // section, that isn't actually an error. The image can be launched
541 // alright, only image protection cannot be applied to it fully.
543 // One example that elicits this is (some) Linux kernels (with the EFI stub
546 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
547 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
548 if (PdbPointer
!= NULL
) {
549 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
557 SortImageRecordCodeSection (ImageRecord
);
559 // Check overlap all section in ImageBase/Size
561 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
562 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
567 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
568 // Given that the loader always allocates full pages, we know the space after the image is not used.
570 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
573 // CPU ARCH present. Update memory attribute directly.
575 SetUefiImageProtectionAttributes (ImageRecord
);
578 // Record the image record in the list so we can undo the protections later
580 InsertTailList (&mProtectedImageRecordList
, &ImageRecord
->Link
);
587 Unprotect UEFI image.
589 @param[in] LoadedImage The loaded image protocol
590 @param[in] LoadedImageDevicePath The loaded image device path protocol
594 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
595 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
598 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
599 LIST_ENTRY
*ImageRecordLink
;
601 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
602 for (ImageRecordLink
= mProtectedImageRecordList
.ForwardLink
;
603 ImageRecordLink
!= &mProtectedImageRecordList
;
604 ImageRecordLink
= ImageRecordLink
->ForwardLink
) {
607 IMAGE_PROPERTIES_RECORD
,
609 IMAGE_PROPERTIES_RECORD_SIGNATURE
612 if (ImageRecord
->ImageBase
== (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
) {
613 SetUefiImageMemoryAttributes (ImageRecord
->ImageBase
,
614 ImageRecord
->ImageSize
,
616 FreeImageRecord (ImageRecord
);
624 Return the EFI memory permission attribute associated with memory
625 type 'MemoryType' under the configured DXE memory protection policy.
627 @param MemoryType Memory type.
631 GetPermissionAttributeForMemoryType (
632 IN EFI_MEMORY_TYPE MemoryType
637 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
639 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
642 TestBit
= LShiftU64 (1, MemoryType
);
645 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
646 return EFI_MEMORY_XP
;
653 Sort memory map entries based upon PhysicalStart, from low to high.
655 @param MemoryMap A pointer to the buffer in which firmware places
656 the current memory map.
657 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
658 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
663 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
664 IN UINTN MemoryMapSize
,
665 IN UINTN DescriptorSize
668 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
669 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
670 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
671 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
673 MemoryMapEntry
= MemoryMap
;
674 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
675 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
676 while (MemoryMapEntry
< MemoryMapEnd
) {
677 while (NextMemoryMapEntry
< MemoryMapEnd
) {
678 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
679 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
680 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
681 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
684 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
687 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
688 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
693 Merge adjacent memory map entries if they use the same memory protection policy
695 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
696 the current memory map.
697 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
698 MemoryMap buffer. On input, this is the size of
699 the current memory map. On output,
700 it is the size of new memory map after merge.
701 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
705 MergeMemoryMapForProtectionPolicy (
706 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
707 IN OUT UINTN
*MemoryMapSize
,
708 IN UINTN DescriptorSize
711 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
712 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
713 UINT64 MemoryBlockLength
;
714 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
715 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
718 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
720 MemoryMapEntry
= MemoryMap
;
721 NewMemoryMapEntry
= MemoryMap
;
722 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
723 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
724 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
725 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
728 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
729 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
731 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
732 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
733 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
734 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
735 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
736 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
739 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
742 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
747 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
748 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
751 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
758 Remove exec permissions from all regions whose type is identified by
759 PcdDxeNxMemoryProtectionPolicy.
763 InitializeDxeNxMemoryProtectionPolicy (
769 UINTN DescriptorSize
;
770 UINT32 DescriptorVersion
;
771 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
772 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
773 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
777 EFI_GCD_MAP_ENTRY
*Entry
;
778 EFI_PEI_HOB_POINTERS Hob
;
779 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
780 EFI_PHYSICAL_ADDRESS StackBase
;
783 // Get the EFI memory map.
788 Status
= gBS
->GetMemoryMap (
795 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
797 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
798 ASSERT (MemoryMap
!= NULL
);
799 Status
= gBS
->GetMemoryMap (
806 if (EFI_ERROR (Status
)) {
807 FreePool (MemoryMap
);
809 } while (Status
== EFI_BUFFER_TOO_SMALL
);
810 ASSERT_EFI_ERROR (Status
);
813 if (PcdGetBool (PcdCpuStackGuard
)) {
815 // Get the base of stack from Hob.
817 Hob
.Raw
= GetHobList ();
818 while ((Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION
, Hob
.Raw
)) != NULL
) {
819 MemoryHob
= Hob
.MemoryAllocation
;
820 if (CompareGuid(&gEfiHobMemoryAllocStackGuid
, &MemoryHob
->AllocDescriptor
.Name
)) {
823 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",
825 MemoryHob
->AllocDescriptor
.MemoryBaseAddress
,
826 MemoryHob
->AllocDescriptor
.MemoryLength
829 StackBase
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
;
831 // Ensure the base of the stack is page-size aligned.
833 ASSERT ((StackBase
& EFI_PAGE_MASK
) == 0);
836 Hob
.Raw
= GET_NEXT_HOB (Hob
);
840 // Ensure the base of stack can be found from Hob when stack guard is
843 ASSERT (StackBase
!= 0);
848 "%a: applying strict permissions to active memory regions\n",
852 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
854 MemoryMapEntry
= MemoryMap
;
855 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
856 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
858 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
859 if (Attributes
!= 0) {
860 SetUefiImageMemoryAttributes (
861 MemoryMapEntry
->PhysicalStart
,
862 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
866 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
869 if (MemoryMapEntry
->PhysicalStart
== 0 &&
870 PcdGet8 (PcdNullPointerDetectionPropertyMask
) != 0) {
872 ASSERT (MemoryMapEntry
->NumberOfPages
> 0);
873 SetUefiImageMemoryAttributes (
875 EFI_PAGES_TO_SIZE (1),
876 EFI_MEMORY_RP
| Attributes
);
880 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
883 if (StackBase
!= 0 &&
884 (StackBase
>= MemoryMapEntry
->PhysicalStart
&&
885 StackBase
< MemoryMapEntry
->PhysicalStart
+
886 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
)) &&
887 PcdGetBool (PcdCpuStackGuard
)) {
889 SetUefiImageMemoryAttributes (
891 EFI_PAGES_TO_SIZE (1),
892 EFI_MEMORY_RP
| Attributes
);
896 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
898 FreePool (MemoryMap
);
901 // Apply the policy for RAM regions that we know are present and
902 // accessible, but have not been added to the UEFI memory map (yet).
904 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
907 "%a: applying strict permissions to inactive memory regions\n",
911 CoreAcquireGcdMemoryLock ();
913 Link
= mGcdMemorySpaceMap
.ForwardLink
;
914 while (Link
!= &mGcdMemorySpaceMap
) {
916 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
918 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
919 Entry
->EndAddress
< MAX_ADDRESS
&&
920 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
921 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
923 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
924 (Entry
->Attributes
& EFI_CACHE_ATTRIBUTE_MASK
);
927 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
928 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
931 ASSERT(gCpu
!= NULL
);
932 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
933 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
936 Link
= Link
->ForwardLink
;
938 CoreReleaseGcdMemoryLock ();
944 A notification for CPU_ARCH protocol.
946 @param[in] Event Event whose notification function is being invoked.
947 @param[in] Context Pointer to the notification function's context,
948 which is implementation-dependent.
953 MemoryProtectionCpuArchProtocolNotify (
959 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
960 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
962 EFI_HANDLE
*HandleBuffer
;
965 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
966 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
967 if (EFI_ERROR (Status
)) {
972 // Apply the memory protection policy on non-BScode/RTcode regions.
974 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
975 InitializeDxeNxMemoryProtectionPolicy ();
979 // Call notify function meant for Heap Guard.
981 HeapGuardCpuArchProtocolNotify ();
983 if (mImageProtectionPolicy
== 0) {
987 Status
= gBS
->LocateHandleBuffer (
989 &gEfiLoadedImageProtocolGuid
,
994 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
998 for (Index
= 0; Index
< NoHandles
; Index
++) {
999 Status
= gBS
->HandleProtocol (
1000 HandleBuffer
[Index
],
1001 &gEfiLoadedImageProtocolGuid
,
1002 (VOID
**)&LoadedImage
1004 if (EFI_ERROR(Status
)) {
1007 Status
= gBS
->HandleProtocol (
1008 HandleBuffer
[Index
],
1009 &gEfiLoadedImageDevicePathProtocolGuid
,
1010 (VOID
**)&LoadedImageDevicePath
1012 if (EFI_ERROR(Status
)) {
1013 LoadedImageDevicePath
= NULL
;
1016 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
1018 FreePool (HandleBuffer
);
1021 CoreCloseEvent (Event
);
1025 ExitBootServices Callback function for memory protection.
1028 MemoryProtectionExitBootServicesCallback (
1032 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
1036 // We need remove the RT protection, because RT relocation need write code segment
1037 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1039 // Firmware does not own page tables after ExitBootServices(), so the OS would
1040 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1041 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1042 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1044 if (mImageProtectionPolicy
!= 0) {
1045 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1046 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1047 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1053 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1054 order to skip unfixable NULL pointer access issues detected in OptionROM or
1057 @param[in] Event The Event this notify function registered to.
1058 @param[in] Context Pointer to the context data registered to the Event.
1062 DisableNullDetectionAtTheEndOfDxe (
1068 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
1070 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1072 // Disable NULL pointer detection by enabling first 4K page
1074 Status
= CoreGetMemorySpaceDescriptor (0, &Desc
);
1075 ASSERT_EFI_ERROR (Status
);
1077 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
1078 Status
= CoreSetMemorySpaceCapabilities (
1081 Desc
.Capabilities
| EFI_MEMORY_RP
1083 ASSERT_EFI_ERROR (Status
);
1086 Status
= CoreSetMemorySpaceAttributes (
1089 Desc
.Attributes
& ~EFI_MEMORY_RP
1091 ASSERT_EFI_ERROR (Status
);
1094 // Page 0 might have be allocated to avoid misuses. Free it here anyway.
1096 CoreFreePages (0, 1);
1098 CoreCloseEvent (Event
);
1099 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1105 Initialize Memory Protection support.
1109 CoreInitializeMemoryProtection (
1115 EFI_EVENT EndOfDxeEvent
;
1118 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1120 InitializeListHead (&mProtectedImageRecordList
);
1123 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1124 // - code regions should have no EFI_MEMORY_XP attribute
1125 // - EfiConventionalMemory and EfiBootServicesData should use the
1128 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1129 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1130 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1131 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1132 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1134 Status
= CoreCreateEvent (
1137 MemoryProtectionCpuArchProtocolNotify
,
1141 ASSERT_EFI_ERROR(Status
);
1144 // Register for protocol notifactions on this event
1146 Status
= CoreRegisterProtocolNotify (
1147 &gEfiCpuArchProtocolGuid
,
1151 ASSERT_EFI_ERROR(Status
);
1154 // Register a callback to disable NULL pointer detection at EndOfDxe
1156 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
))
1158 Status
= CoreCreateEventEx (
1161 DisableNullDetectionAtTheEndOfDxe
,
1163 &gEfiEndOfDxeEventGroupGuid
,
1166 ASSERT_EFI_ERROR (Status
);
1173 Returns whether we are currently executing in SMM mode.
1184 if (gSmmBase2
!= NULL
) {
1185 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1191 Manage memory permission attributes on a memory range, according to the
1192 configured DXE memory protection policy.
1194 @param OldType The old memory type of the range
1195 @param NewType The new memory type of the range
1196 @param Memory The base address of the range
1197 @param Length The size of the range (in bytes)
1199 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1200 are updated in this case
1201 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1202 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1203 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1204 @return other Return value of gCpu->SetMemoryAttributes()
1209 ApplyMemoryProtectionPolicy (
1210 IN EFI_MEMORY_TYPE OldType
,
1211 IN EFI_MEMORY_TYPE NewType
,
1212 IN EFI_PHYSICAL_ADDRESS Memory
,
1216 UINT64 OldAttributes
;
1217 UINT64 NewAttributes
;
1220 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1221 // does not apply to allocations performed in SMM mode.
1228 // If the CPU arch protocol is not installed yet, we cannot manage memory
1229 // permission attributes, and it is the job of the driver that installs this
1230 // protocol to set the permissions on existing allocations.
1237 // Check if a DXE memory protection policy has been configured
1239 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1244 // Don't overwrite Guard pages, which should be the first and/or last page,
1247 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
)) {
1248 if (IsGuardPage (Memory
)) {
1249 Memory
+= EFI_PAGE_SIZE
;
1250 Length
-= EFI_PAGE_SIZE
;
1256 if (IsGuardPage (Memory
+ Length
- EFI_PAGE_SIZE
)) {
1257 Length
-= EFI_PAGE_SIZE
;
1265 // Update the executable permissions according to the DXE memory
1266 // protection policy, but only if
1267 // - the policy is different between the old and the new type, or
1268 // - this is a newly added region (OldType == EfiMaxMemoryType)
1270 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1272 if (OldType
!= EfiMaxMemoryType
) {
1273 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1274 if (OldAttributes
== NewAttributes
) {
1275 // policy is the same between OldType and NewType
1278 } else if (NewAttributes
== 0) {
1279 // newly added region of a type that does not require protection
1283 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);