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, Intel Corporation. All rights reserved.<BR>
23 This program and the accompanying materials
24 are licensed and made available under the terms and conditions of the BSD License
25 which accompanies this distribution. The full text of the license may be found at
26 http://opensource.org/licenses/bsd-license.php
28 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
29 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
34 #include <Library/BaseLib.h>
35 #include <Library/BaseMemoryLib.h>
36 #include <Library/MemoryAllocationLib.h>
37 #include <Library/UefiBootServicesTableLib.h>
38 #include <Library/DxeServicesTableLib.h>
39 #include <Library/DebugLib.h>
40 #include <Library/UefiLib.h>
42 #include <Guid/EventGroup.h>
43 #include <Guid/MemoryAttributesTable.h>
44 #include <Guid/PropertiesTable.h>
46 #include <Protocol/FirmwareVolume2.h>
47 #include <Protocol/BlockIo.h>
48 #include <Protocol/SimpleFileSystem.h>
52 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)
53 #define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)
56 // Image type definitions
58 #define IMAGE_UNKNOWN 0x00000001
59 #define IMAGE_FROM_FV 0x00000002
62 // Protection policy bit definition
64 #define DO_NOT_PROTECT 0x00000000
65 #define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001
67 #define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
68 #define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
70 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
71 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
73 UINT32 mImageProtectionPolicy
;
75 extern LIST_ENTRY mGcdMemorySpaceMap
;
78 Sort code section in image record, based upon CodeSegmentBase from low to high.
80 @param ImageRecord image record to be sorted
83 SortImageRecordCodeSection (
84 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
88 Check if code section in image record is valid.
90 @param ImageRecord image record to be checked
92 @retval TRUE image record is valid
93 @retval FALSE image record is invalid
96 IsImageRecordCodeSectionValid (
97 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
103 @param[in] File This is a pointer to the device path of the file that is
106 @return UINT32 Image Type
110 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
114 EFI_HANDLE DeviceHandle
;
115 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
118 return IMAGE_UNKNOWN
;
122 // First check to see if File is from a Firmware Volume
125 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) File
;
126 Status
= gBS
->LocateDevicePath (
127 &gEfiFirmwareVolume2ProtocolGuid
,
131 if (!EFI_ERROR (Status
)) {
132 Status
= gBS
->OpenProtocol (
134 &gEfiFirmwareVolume2ProtocolGuid
,
138 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
140 if (!EFI_ERROR (Status
)) {
141 return IMAGE_FROM_FV
;
144 return IMAGE_UNKNOWN
;
148 Get UEFI image protection policy based upon image type.
150 @param[in] ImageType The UEFI image type
152 @return UEFI image protection policy
155 GetProtectionPolicyFromImageType (
159 if ((ImageType
& mImageProtectionPolicy
) == 0) {
160 return DO_NOT_PROTECT
;
162 return PROTECT_IF_ALIGNED_ELSE_ALLOW
;
167 Get UEFI image protection policy based upon loaded image device path.
169 @param[in] LoadedImage The loaded image protocol
170 @param[in] LoadedImageDevicePath The loaded image device path protocol
172 @return UEFI image protection policy
175 GetUefiImageProtectionPolicy (
176 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
177 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
182 UINT32 ProtectionPolicy
;
188 if (gSmmBase2
!= NULL
) {
189 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
198 if (LoadedImage
== gDxeCoreLoadedImage
) {
199 ImageType
= IMAGE_FROM_FV
;
201 ImageType
= GetImageType (LoadedImageDevicePath
);
203 ProtectionPolicy
= GetProtectionPolicyFromImageType (ImageType
);
204 return ProtectionPolicy
;
209 Set UEFI image memory attributes.
211 @param[in] BaseAddress Specified start address
212 @param[in] Length Specified length
213 @param[in] Attributes Specified attributes
216 SetUefiImageMemoryAttributes (
217 IN UINT64 BaseAddress
,
223 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
224 UINT64 FinalAttributes
;
226 Status
= CoreGetMemorySpaceDescriptor(BaseAddress
, &Descriptor
);
227 ASSERT_EFI_ERROR(Status
);
229 FinalAttributes
= (Descriptor
.Attributes
& CACHE_ATTRIBUTE_MASK
) | (Attributes
& MEMORY_ATTRIBUTE_MASK
);
231 DEBUG ((DEBUG_INFO
, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress
, Length
, FinalAttributes
));
233 ASSERT(gCpu
!= NULL
);
234 gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, Length
, FinalAttributes
);
238 Set UEFI image protection attributes.
240 @param[in] ImageRecord A UEFI image record
241 @param[in] Protect TRUE: Protect the UEFI image.
242 FALSE: Unprotect the UEFI image.
245 SetUefiImageProtectionAttributes (
246 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
,
250 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
251 LIST_ENTRY
*ImageRecordCodeSectionLink
;
252 LIST_ENTRY
*ImageRecordCodeSectionEndLink
;
253 LIST_ENTRY
*ImageRecordCodeSectionList
;
258 ImageRecordCodeSectionList
= &ImageRecord
->CodeSegmentList
;
260 CurrentBase
= ImageRecord
->ImageBase
;
261 ImageEnd
= ImageRecord
->ImageBase
+ ImageRecord
->ImageSize
;
263 ImageRecordCodeSectionLink
= ImageRecordCodeSectionList
->ForwardLink
;
264 ImageRecordCodeSectionEndLink
= ImageRecordCodeSectionList
;
265 while (ImageRecordCodeSectionLink
!= ImageRecordCodeSectionEndLink
) {
266 ImageRecordCodeSection
= CR (
267 ImageRecordCodeSectionLink
,
268 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
270 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
272 ImageRecordCodeSectionLink
= ImageRecordCodeSectionLink
->ForwardLink
;
274 ASSERT (CurrentBase
<= ImageRecordCodeSection
->CodeSegmentBase
);
275 if (CurrentBase
< ImageRecordCodeSection
->CodeSegmentBase
) {
280 Attribute
= EFI_MEMORY_XP
;
284 SetUefiImageMemoryAttributes (
286 ImageRecordCodeSection
->CodeSegmentBase
- CurrentBase
,
294 Attribute
= EFI_MEMORY_RO
;
298 SetUefiImageMemoryAttributes (
299 ImageRecordCodeSection
->CodeSegmentBase
,
300 ImageRecordCodeSection
->CodeSegmentSize
,
303 CurrentBase
= ImageRecordCodeSection
->CodeSegmentBase
+ ImageRecordCodeSection
->CodeSegmentSize
;
308 ASSERT (CurrentBase
<= ImageEnd
);
309 if (CurrentBase
< ImageEnd
) {
314 Attribute
= EFI_MEMORY_XP
;
318 SetUefiImageMemoryAttributes (
320 ImageEnd
- CurrentBase
,
328 Return if the PE image section is aligned.
330 @param[in] SectionAlignment PE/COFF section alignment
331 @param[in] MemoryType PE/COFF image memory type
333 @retval TRUE The PE image section is aligned.
334 @retval FALSE The PE image section is not aligned.
337 IsMemoryProtectionSectionAligned (
338 IN UINT32 SectionAlignment
,
339 IN EFI_MEMORY_TYPE MemoryType
342 UINT32 PageAlignment
;
344 switch (MemoryType
) {
345 case EfiRuntimeServicesCode
:
346 case EfiACPIMemoryNVS
:
347 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
349 case EfiRuntimeServicesData
:
350 case EfiACPIReclaimMemory
:
352 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
354 case EfiBootServicesCode
:
356 case EfiReservedMemoryType
:
357 PageAlignment
= EFI_PAGE_SIZE
;
361 PageAlignment
= EFI_PAGE_SIZE
;
365 if ((SectionAlignment
& (PageAlignment
- 1)) != 0) {
375 @param[in] ImageRecord A UEFI image record
379 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
382 LIST_ENTRY
*CodeSegmentListHead
;
383 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
385 CodeSegmentListHead
= &ImageRecord
->CodeSegmentList
;
386 while (!IsListEmpty (CodeSegmentListHead
)) {
387 ImageRecordCodeSection
= CR (
388 CodeSegmentListHead
->ForwardLink
,
389 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
391 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
393 RemoveEntryList (&ImageRecordCodeSection
->Link
);
394 FreePool (ImageRecordCodeSection
);
397 if (ImageRecord
->Link
.ForwardLink
!= NULL
) {
398 RemoveEntryList (&ImageRecord
->Link
);
400 FreePool (ImageRecord
);
404 Protect or unprotect UEFI image common function.
406 @param[in] LoadedImage The loaded image protocol
407 @param[in] LoadedImageDevicePath The loaded image device path protocol
408 @param[in] Protect TRUE: Protect the UEFI image.
409 FALSE: Unprotect the UEFI image.
412 ProtectUefiImageCommon (
413 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
414 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
,
419 EFI_IMAGE_DOS_HEADER
*DosHdr
;
420 UINT32 PeCoffHeaderOffset
;
421 UINT32 SectionAlignment
;
422 EFI_IMAGE_SECTION_HEADER
*Section
;
423 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
;
426 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
428 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
431 UINT32 ProtectionPolicy
;
433 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
434 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
440 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
441 switch (ProtectionPolicy
) {
444 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
451 ImageRecord
= AllocateZeroPool (sizeof(*ImageRecord
));
452 if (ImageRecord
== NULL
) {
455 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
458 // Step 1: record whole region
460 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
461 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
463 ImageAddress
= LoadedImage
->ImageBase
;
465 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
466 if (PdbPointer
!= NULL
) {
467 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
471 // Check PE/COFF image
473 DosHdr
= (EFI_IMAGE_DOS_HEADER
*) (UINTN
) ImageAddress
;
474 PeCoffHeaderOffset
= 0;
475 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
476 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
479 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*) (UINTN
) ImageAddress
+ PeCoffHeaderOffset
);
480 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
481 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
482 // It might be image in SMM.
487 // Get SectionAlignment
489 if (Hdr
.Pe32
->FileHeader
.Machine
== IMAGE_FILE_MACHINE_IA64
&& Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
491 // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
492 // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
493 // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
494 // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
496 Magic
= EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
;
499 // Get the magic value from the PE/COFF Optional Header
501 Magic
= Hdr
.Pe32
->OptionalHeader
.Magic
;
503 if (Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
504 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
506 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
509 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
511 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
513 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
514 if (PdbPointer
!= NULL
) {
515 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
520 Section
= (EFI_IMAGE_SECTION_HEADER
*) (
521 (UINT8
*) (UINTN
) ImageAddress
+
524 sizeof(EFI_IMAGE_FILE_HEADER
) +
525 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
527 ImageRecord
->CodeSegmentCount
= 0;
528 InitializeListHead (&ImageRecord
->CodeSegmentList
);
529 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
530 Name
= Section
[Index
].Name
;
533 " Section - '%c%c%c%c%c%c%c%c'\n",
545 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
546 // can always be mapped read-only, classify a section as a code section only
547 // if it has the executable attribute set and the writable attribute cleared.
549 // This adheres more closely to the PE/COFF spec, and avoids issues with
550 // Linux OS loaders that may consist of a single read/write/execute section.
552 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
553 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
554 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
555 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
556 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
557 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
558 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
559 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
560 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
561 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
564 // Step 2: record code section
566 ImageRecordCodeSection
= AllocatePool (sizeof(*ImageRecordCodeSection
));
567 if (ImageRecordCodeSection
== NULL
) {
570 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
572 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
573 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE(Section
[Index
].SizeOfRawData
, SectionAlignment
);
575 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
577 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
578 ImageRecord
->CodeSegmentCount
++;
582 if (ImageRecord
->CodeSegmentCount
== 0) {
583 DEBUG ((DEBUG_ERROR
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
584 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
585 if (PdbPointer
!= NULL
) {
586 DEBUG ((DEBUG_ERROR
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
594 SortImageRecordCodeSection (ImageRecord
);
596 // Check overlap all section in ImageBase/Size
598 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
599 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
604 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
605 // Given that the loader always allocates full pages, we know the space after the image is not used.
607 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
610 // CPU ARCH present. Update memory attribute directly.
612 SetUefiImageProtectionAttributes (ImageRecord
, Protect
);
617 FreeImageRecord (ImageRecord
);
626 @param[in] LoadedImage The loaded image protocol
627 @param[in] LoadedImageDevicePath The loaded image device path protocol
631 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
632 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
635 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
636 ProtectUefiImageCommon (LoadedImage
, LoadedImageDevicePath
, TRUE
);
641 Unprotect UEFI image.
643 @param[in] LoadedImage The loaded image protocol
644 @param[in] LoadedImageDevicePath The loaded image device path protocol
648 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
649 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
652 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
653 ProtectUefiImageCommon (LoadedImage
, LoadedImageDevicePath
, FALSE
);
658 Return the EFI memory permission attribute associated with memory
659 type 'MemoryType' under the configured DXE memory protection policy.
661 @param MemoryType Memory type.
665 GetPermissionAttributeForMemoryType (
666 IN EFI_MEMORY_TYPE MemoryType
671 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
673 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
676 TestBit
= LShiftU64 (1, MemoryType
);
679 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
680 return EFI_MEMORY_XP
;
687 Sort memory map entries based upon PhysicalStart, from low to high.
689 @param MemoryMap A pointer to the buffer in which firmware places
690 the current memory map.
691 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
692 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
697 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
698 IN UINTN MemoryMapSize
,
699 IN UINTN DescriptorSize
702 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
703 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
704 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
705 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
707 MemoryMapEntry
= MemoryMap
;
708 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
709 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
710 while (MemoryMapEntry
< MemoryMapEnd
) {
711 while (NextMemoryMapEntry
< MemoryMapEnd
) {
712 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
713 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
714 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
715 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
718 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
721 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
722 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
727 Merge adjacent memory map entries if they use the same memory protection policy
729 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
730 the current memory map.
731 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
732 MemoryMap buffer. On input, this is the size of
733 the current memory map. On output,
734 it is the size of new memory map after merge.
735 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
739 MergeMemoryMapForProtectionPolicy (
740 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
741 IN OUT UINTN
*MemoryMapSize
,
742 IN UINTN DescriptorSize
745 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
746 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
747 UINT64 MemoryBlockLength
;
748 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
749 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
752 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
754 MemoryMapEntry
= MemoryMap
;
755 NewMemoryMapEntry
= MemoryMap
;
756 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
757 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
758 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
759 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
762 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
763 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
765 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
766 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
767 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
768 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
769 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
770 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
773 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
776 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
781 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
782 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
785 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
792 Remove exec permissions from all regions whose type is identified by
793 PcdDxeNxMemoryProtectionPolicy.
797 InitializeDxeNxMemoryProtectionPolicy (
803 UINTN DescriptorSize
;
804 UINT32 DescriptorVersion
;
805 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
806 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
807 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
811 EFI_GCD_MAP_ENTRY
*Entry
;
814 // Get the EFI memory map.
819 Status
= gBS
->GetMemoryMap (
826 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
828 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
829 ASSERT (MemoryMap
!= NULL
);
830 Status
= gBS
->GetMemoryMap (
837 if (EFI_ERROR (Status
)) {
838 FreePool (MemoryMap
);
840 } while (Status
== EFI_BUFFER_TOO_SMALL
);
841 ASSERT_EFI_ERROR (Status
);
843 DEBUG((DEBUG_ERROR
, "%a: applying strict permissions to active memory regions\n",
846 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
848 MemoryMapEntry
= MemoryMap
;
849 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
850 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
852 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
853 if (Attributes
!= 0) {
854 SetUefiImageMemoryAttributes (
855 MemoryMapEntry
->PhysicalStart
,
856 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
859 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
861 FreePool (MemoryMap
);
864 // Apply the policy for RAM regions that we know are present and
865 // accessible, but have not been added to the UEFI memory map (yet).
867 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
869 "%a: applying strict permissions to inactive memory regions\n",
872 CoreAcquireGcdMemoryLock ();
874 Link
= mGcdMemorySpaceMap
.ForwardLink
;
875 while (Link
!= &mGcdMemorySpaceMap
) {
877 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
879 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
880 Entry
->EndAddress
< MAX_ADDRESS
&&
881 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
882 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
884 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
885 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
888 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
889 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
892 ASSERT(gCpu
!= NULL
);
893 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
894 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
897 Link
= Link
->ForwardLink
;
899 CoreReleaseGcdMemoryLock ();
905 A notification for CPU_ARCH protocol.
907 @param[in] Event Event whose notification function is being invoked.
908 @param[in] Context Pointer to the notification function's context,
909 which is implementation-dependent.
914 MemoryProtectionCpuArchProtocolNotify (
920 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
921 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
923 EFI_HANDLE
*HandleBuffer
;
926 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
927 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
928 if (EFI_ERROR (Status
)) {
933 // Apply the memory protection policy on non-BScode/RTcode regions.
935 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
936 InitializeDxeNxMemoryProtectionPolicy ();
939 if (mImageProtectionPolicy
== 0) {
943 Status
= gBS
->LocateHandleBuffer (
945 &gEfiLoadedImageProtocolGuid
,
950 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
954 for (Index
= 0; Index
< NoHandles
; Index
++) {
955 Status
= gBS
->HandleProtocol (
957 &gEfiLoadedImageProtocolGuid
,
958 (VOID
**)&LoadedImage
960 if (EFI_ERROR(Status
)) {
963 Status
= gBS
->HandleProtocol (
965 &gEfiLoadedImageDevicePathProtocolGuid
,
966 (VOID
**)&LoadedImageDevicePath
968 if (EFI_ERROR(Status
)) {
969 LoadedImageDevicePath
= NULL
;
972 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
975 CoreCloseEvent (Event
);
980 ExitBootServices Callback function for memory protection.
983 MemoryProtectionExitBootServicesCallback (
987 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
991 // We need remove the RT protection, because RT relocation need write code segment
992 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
994 // Firmware does not own page tables after ExitBootServices(), so the OS would
995 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
996 // delay setting protections on RT code pages until after SetVirtualAddressMap().
997 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
999 if (mImageProtectionPolicy
!= 0) {
1000 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1001 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1002 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1008 Initialize Memory Protection support.
1012 CoreInitializeMemoryProtection (
1020 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1023 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1024 // - code regions should have no EFI_MEMORY_XP attribute
1025 // - EfiConventionalMemory and EfiBootServicesData should use the
1028 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1029 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1030 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1031 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1032 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1034 if (mImageProtectionPolicy
!= 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1035 Status
= CoreCreateEvent (
1038 MemoryProtectionCpuArchProtocolNotify
,
1042 ASSERT_EFI_ERROR(Status
);
1045 // Register for protocol notifactions on this event
1047 Status
= CoreRegisterProtocolNotify (
1048 &gEfiCpuArchProtocolGuid
,
1052 ASSERT_EFI_ERROR(Status
);
1058 Returns whether we are currently executing in SMM mode.
1069 if (gSmmBase2
!= NULL
) {
1070 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1076 Manage memory permission attributes on a memory range, according to the
1077 configured DXE memory protection policy.
1079 @param OldType The old memory type of the range
1080 @param NewType The new memory type of the range
1081 @param Memory The base address of the range
1082 @param Length The size of the range (in bytes)
1084 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1085 are updated in this case
1086 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1087 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1088 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1089 @return other Return value of gCpu->SetMemoryAttributes()
1094 ApplyMemoryProtectionPolicy (
1095 IN EFI_MEMORY_TYPE OldType
,
1096 IN EFI_MEMORY_TYPE NewType
,
1097 IN EFI_PHYSICAL_ADDRESS Memory
,
1101 UINT64 OldAttributes
;
1102 UINT64 NewAttributes
;
1105 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1106 // does not apply to allocations performed in SMM mode.
1113 // If the CPU arch protocol is not installed yet, we cannot manage memory
1114 // permission attributes, and it is the job of the driver that installs this
1115 // protocol to set the permissions on existing allocations.
1122 // Check if a DXE memory protection policy has been configured
1124 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1129 // Update the executable permissions according to the DXE memory
1130 // protection policy, but only if
1131 // - the policy is different between the old and the new type, or
1132 // - this is a newly added region (OldType == EfiMaxMemoryType)
1134 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1136 if (OldType
!= EfiMaxMemoryType
) {
1137 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1138 if (OldAttributes
== NewAttributes
) {
1139 // policy is the same between OldType and NewType
1142 } else if (NewAttributes
== 0) {
1143 // newly added region of a type that does not require protection
1147 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);