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.
663 GetPermissionAttributeForMemoryType (
664 IN EFI_MEMORY_TYPE MemoryType
669 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
671 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
674 TestBit
= LShiftU64 (1, MemoryType
);
677 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
678 return EFI_MEMORY_XP
;
685 Sort memory map entries based upon PhysicalStart, from low to high.
687 @param MemoryMap A pointer to the buffer in which firmware places
688 the current memory map.
689 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
690 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
695 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
696 IN UINTN MemoryMapSize
,
697 IN UINTN DescriptorSize
700 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
701 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
702 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
703 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
705 MemoryMapEntry
= MemoryMap
;
706 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
707 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
708 while (MemoryMapEntry
< MemoryMapEnd
) {
709 while (NextMemoryMapEntry
< MemoryMapEnd
) {
710 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
711 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
712 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
713 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
716 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
719 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
720 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
725 Merge adjacent memory map entries if they use the same memory protection policy
727 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
728 the current memory map.
729 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
730 MemoryMap buffer. On input, this is the size of
731 the current memory map. On output,
732 it is the size of new memory map after merge.
733 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
737 MergeMemoryMapForProtectionPolicy (
738 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
739 IN OUT UINTN
*MemoryMapSize
,
740 IN UINTN DescriptorSize
743 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
744 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
745 UINT64 MemoryBlockLength
;
746 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
747 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
750 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
752 MemoryMapEntry
= MemoryMap
;
753 NewMemoryMapEntry
= MemoryMap
;
754 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
755 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
756 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
757 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
760 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
761 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
763 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
764 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
765 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
766 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
767 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
768 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
771 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
774 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
779 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
780 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
783 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
790 Remove exec permissions from all regions whose type is identified by
791 PcdDxeNxMemoryProtectionPolicy
795 InitializeDxeNxMemoryProtectionPolicy (
801 UINTN DescriptorSize
;
802 UINT32 DescriptorVersion
;
803 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
804 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
805 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
809 EFI_GCD_MAP_ENTRY
*Entry
;
812 // Get the EFI memory map.
817 Status
= gBS
->GetMemoryMap (
824 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
826 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
827 ASSERT (MemoryMap
!= NULL
);
828 Status
= gBS
->GetMemoryMap (
835 if (EFI_ERROR (Status
)) {
836 FreePool (MemoryMap
);
838 } while (Status
== EFI_BUFFER_TOO_SMALL
);
839 ASSERT_EFI_ERROR (Status
);
841 DEBUG((DEBUG_ERROR
, "%a: applying strict permissions to active memory regions\n",
844 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
846 MemoryMapEntry
= MemoryMap
;
847 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
848 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
850 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
851 if (Attributes
!= 0) {
852 SetUefiImageMemoryAttributes (
853 MemoryMapEntry
->PhysicalStart
,
854 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
857 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
859 FreePool (MemoryMap
);
862 // Apply the policy for RAM regions that we know are present and
863 // accessible, but have not been added to the UEFI memory map (yet).
865 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
867 "%a: applying strict permissions to inactive memory regions\n",
870 CoreAcquireGcdMemoryLock ();
872 Link
= mGcdMemorySpaceMap
.ForwardLink
;
873 while (Link
!= &mGcdMemorySpaceMap
) {
875 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
877 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
878 Entry
->EndAddress
< MAX_ADDRESS
&&
879 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
880 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
882 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
883 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
886 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
887 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
890 ASSERT(gCpu
!= NULL
);
891 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
892 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
895 Link
= Link
->ForwardLink
;
897 CoreReleaseGcdMemoryLock ();
903 A notification for CPU_ARCH protocol.
905 @param[in] Event Event whose notification function is being invoked.
906 @param[in] Context Pointer to the notification function's context,
907 which is implementation-dependent.
912 MemoryProtectionCpuArchProtocolNotify (
918 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
919 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
921 EFI_HANDLE
*HandleBuffer
;
924 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
925 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
926 if (EFI_ERROR (Status
)) {
931 // Apply the memory protection policy on non-BScode/RTcode regions.
933 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
934 InitializeDxeNxMemoryProtectionPolicy ();
937 if (mImageProtectionPolicy
== 0) {
941 Status
= gBS
->LocateHandleBuffer (
943 &gEfiLoadedImageProtocolGuid
,
948 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
952 for (Index
= 0; Index
< NoHandles
; Index
++) {
953 Status
= gBS
->HandleProtocol (
955 &gEfiLoadedImageProtocolGuid
,
956 (VOID
**)&LoadedImage
958 if (EFI_ERROR(Status
)) {
961 Status
= gBS
->HandleProtocol (
963 &gEfiLoadedImageDevicePathProtocolGuid
,
964 (VOID
**)&LoadedImageDevicePath
966 if (EFI_ERROR(Status
)) {
967 LoadedImageDevicePath
= NULL
;
970 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
973 CoreCloseEvent (Event
);
978 ExitBootServices Callback function for memory protection.
981 MemoryProtectionExitBootServicesCallback (
985 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
989 // We need remove the RT protection, because RT relocation need write code segment
990 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
992 // Firmware does not own page tables after ExitBootServices(), so the OS would
993 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
994 // delay setting protections on RT code pages until after SetVirtualAddressMap().
995 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
997 if (mImageProtectionPolicy
!= 0) {
998 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
999 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1000 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1006 Initialize Memory Protection support.
1010 CoreInitializeMemoryProtection (
1018 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1021 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1022 // - code regions should have no EFI_MEMORY_XP attribute
1023 // - EfiConventionalMemory and EfiBootServicesData should use the
1026 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1027 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1028 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1029 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1030 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1032 if (mImageProtectionPolicy
!= 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1033 Status
= CoreCreateEvent (
1036 MemoryProtectionCpuArchProtocolNotify
,
1040 ASSERT_EFI_ERROR(Status
);
1043 // Register for protocol notifactions on this event
1045 Status
= CoreRegisterProtocolNotify (
1046 &gEfiCpuArchProtocolGuid
,
1050 ASSERT_EFI_ERROR(Status
);
1056 Returns whether we are currently executing in SMM mode
1067 if (gSmmBase2
!= NULL
) {
1068 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1074 Manage memory permission attributes on a memory range, according to the
1075 configured DXE memory protection policy.
1077 @param OldType The old memory type of the range
1078 @param NewType The new memory type of the range
1079 @param Memory The base address of the range
1080 @param Length The size of the range (in bytes)
1082 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1083 are updated in this case
1084 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1085 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1086 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1087 @return other Return value of gCpu->SetMemoryAttributes()
1092 ApplyMemoryProtectionPolicy (
1093 IN EFI_MEMORY_TYPE OldType
,
1094 IN EFI_MEMORY_TYPE NewType
,
1095 IN EFI_PHYSICAL_ADDRESS Memory
,
1099 UINT64 OldAttributes
;
1100 UINT64 NewAttributes
;
1103 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1104 // does not apply to allocations performed in SMM mode.
1111 // If the CPU arch protocol is not installed yet, we cannot manage memory
1112 // permission attributes, and it is the job of the driver that installs this
1113 // protocol to set the permissions on existing allocations.
1120 // Check if a DXE memory protection policy has been configured
1122 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1127 // Update the executable permissions according to the DXE memory
1128 // protection policy, but only if
1129 // - the policy is different between the old and the new type, or
1130 // - this is a newly added region (OldType == EfiMaxMemoryType)
1132 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1134 if (OldType
!= EfiMaxMemoryType
) {
1135 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1136 if (OldAttributes
== NewAttributes
) {
1137 // policy is the same between OldType and NewType
1140 } else if (NewAttributes
== 0) {
1141 // newly added region of a type that does not require protection
1145 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);