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) {
584 // If a UEFI executable consists of a single read+write+exec PE/COFF
585 // section, that isn't actually an error. The image can be launched
586 // alright, only image protection cannot be applied to it fully.
588 // One example that elicits this is (some) Linux kernels (with the EFI stub
591 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
592 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
593 if (PdbPointer
!= NULL
) {
594 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
602 SortImageRecordCodeSection (ImageRecord
);
604 // Check overlap all section in ImageBase/Size
606 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
607 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
612 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
613 // Given that the loader always allocates full pages, we know the space after the image is not used.
615 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
618 // CPU ARCH present. Update memory attribute directly.
620 SetUefiImageProtectionAttributes (ImageRecord
, Protect
);
625 FreeImageRecord (ImageRecord
);
634 @param[in] LoadedImage The loaded image protocol
635 @param[in] LoadedImageDevicePath The loaded image device path protocol
639 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
640 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
643 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
644 ProtectUefiImageCommon (LoadedImage
, LoadedImageDevicePath
, TRUE
);
649 Unprotect UEFI image.
651 @param[in] LoadedImage The loaded image protocol
652 @param[in] LoadedImageDevicePath The loaded image device path protocol
656 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
657 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
660 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
661 ProtectUefiImageCommon (LoadedImage
, LoadedImageDevicePath
, FALSE
);
666 Return the EFI memory permission attribute associated with memory
667 type 'MemoryType' under the configured DXE memory protection policy.
669 @param MemoryType Memory type.
673 GetPermissionAttributeForMemoryType (
674 IN EFI_MEMORY_TYPE MemoryType
679 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
681 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
684 TestBit
= LShiftU64 (1, MemoryType
);
687 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
688 return EFI_MEMORY_XP
;
695 Sort memory map entries based upon PhysicalStart, from low to high.
697 @param MemoryMap A pointer to the buffer in which firmware places
698 the current memory map.
699 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
700 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
705 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
706 IN UINTN MemoryMapSize
,
707 IN UINTN DescriptorSize
710 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
711 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
712 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
713 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
715 MemoryMapEntry
= MemoryMap
;
716 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
717 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
718 while (MemoryMapEntry
< MemoryMapEnd
) {
719 while (NextMemoryMapEntry
< MemoryMapEnd
) {
720 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
721 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
722 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
723 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
726 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
729 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
730 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
735 Merge adjacent memory map entries if they use the same memory protection policy
737 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
738 the current memory map.
739 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
740 MemoryMap buffer. On input, this is the size of
741 the current memory map. On output,
742 it is the size of new memory map after merge.
743 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
747 MergeMemoryMapForProtectionPolicy (
748 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
749 IN OUT UINTN
*MemoryMapSize
,
750 IN UINTN DescriptorSize
753 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
754 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
755 UINT64 MemoryBlockLength
;
756 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
757 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
760 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
762 MemoryMapEntry
= MemoryMap
;
763 NewMemoryMapEntry
= MemoryMap
;
764 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
765 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
766 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
767 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
770 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
771 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
773 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
774 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
775 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
776 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
777 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
778 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
781 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
784 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
789 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
790 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
793 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
800 Remove exec permissions from all regions whose type is identified by
801 PcdDxeNxMemoryProtectionPolicy.
805 InitializeDxeNxMemoryProtectionPolicy (
811 UINTN DescriptorSize
;
812 UINT32 DescriptorVersion
;
813 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
814 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
815 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
819 EFI_GCD_MAP_ENTRY
*Entry
;
822 // Get the EFI memory map.
827 Status
= gBS
->GetMemoryMap (
834 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
836 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
837 ASSERT (MemoryMap
!= NULL
);
838 Status
= gBS
->GetMemoryMap (
845 if (EFI_ERROR (Status
)) {
846 FreePool (MemoryMap
);
848 } while (Status
== EFI_BUFFER_TOO_SMALL
);
849 ASSERT_EFI_ERROR (Status
);
851 DEBUG((DEBUG_ERROR
, "%a: applying strict permissions to active memory regions\n",
854 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
856 MemoryMapEntry
= MemoryMap
;
857 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
858 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
860 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
861 if (Attributes
!= 0) {
862 SetUefiImageMemoryAttributes (
863 MemoryMapEntry
->PhysicalStart
,
864 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
867 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
869 FreePool (MemoryMap
);
872 // Apply the policy for RAM regions that we know are present and
873 // accessible, but have not been added to the UEFI memory map (yet).
875 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
877 "%a: applying strict permissions to inactive memory regions\n",
880 CoreAcquireGcdMemoryLock ();
882 Link
= mGcdMemorySpaceMap
.ForwardLink
;
883 while (Link
!= &mGcdMemorySpaceMap
) {
885 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
887 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
888 Entry
->EndAddress
< MAX_ADDRESS
&&
889 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
890 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
892 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
893 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
896 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
897 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
900 ASSERT(gCpu
!= NULL
);
901 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
902 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
905 Link
= Link
->ForwardLink
;
907 CoreReleaseGcdMemoryLock ();
913 A notification for CPU_ARCH protocol.
915 @param[in] Event Event whose notification function is being invoked.
916 @param[in] Context Pointer to the notification function's context,
917 which is implementation-dependent.
922 MemoryProtectionCpuArchProtocolNotify (
928 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
929 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
931 EFI_HANDLE
*HandleBuffer
;
934 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
935 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
936 if (EFI_ERROR (Status
)) {
941 // Apply the memory protection policy on non-BScode/RTcode regions.
943 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
944 InitializeDxeNxMemoryProtectionPolicy ();
947 if (mImageProtectionPolicy
== 0) {
951 Status
= gBS
->LocateHandleBuffer (
953 &gEfiLoadedImageProtocolGuid
,
958 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
962 for (Index
= 0; Index
< NoHandles
; Index
++) {
963 Status
= gBS
->HandleProtocol (
965 &gEfiLoadedImageProtocolGuid
,
966 (VOID
**)&LoadedImage
968 if (EFI_ERROR(Status
)) {
971 Status
= gBS
->HandleProtocol (
973 &gEfiLoadedImageDevicePathProtocolGuid
,
974 (VOID
**)&LoadedImageDevicePath
976 if (EFI_ERROR(Status
)) {
977 LoadedImageDevicePath
= NULL
;
980 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
983 CoreCloseEvent (Event
);
988 ExitBootServices Callback function for memory protection.
991 MemoryProtectionExitBootServicesCallback (
995 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
999 // We need remove the RT protection, because RT relocation need write code segment
1000 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1002 // Firmware does not own page tables after ExitBootServices(), so the OS would
1003 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1004 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1005 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1007 if (mImageProtectionPolicy
!= 0) {
1008 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1009 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1010 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1016 Initialize Memory Protection support.
1020 CoreInitializeMemoryProtection (
1028 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1031 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1032 // - code regions should have no EFI_MEMORY_XP attribute
1033 // - EfiConventionalMemory and EfiBootServicesData should use the
1036 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1037 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1038 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1039 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1040 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1042 if (mImageProtectionPolicy
!= 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1043 Status
= CoreCreateEvent (
1046 MemoryProtectionCpuArchProtocolNotify
,
1050 ASSERT_EFI_ERROR(Status
);
1053 // Register for protocol notifactions on this event
1055 Status
= CoreRegisterProtocolNotify (
1056 &gEfiCpuArchProtocolGuid
,
1060 ASSERT_EFI_ERROR(Status
);
1066 Returns whether we are currently executing in SMM mode.
1077 if (gSmmBase2
!= NULL
) {
1078 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1084 Manage memory permission attributes on a memory range, according to the
1085 configured DXE memory protection policy.
1087 @param OldType The old memory type of the range
1088 @param NewType The new memory type of the range
1089 @param Memory The base address of the range
1090 @param Length The size of the range (in bytes)
1092 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1093 are updated in this case
1094 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1095 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1096 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1097 @return other Return value of gCpu->SetMemoryAttributes()
1102 ApplyMemoryProtectionPolicy (
1103 IN EFI_MEMORY_TYPE OldType
,
1104 IN EFI_MEMORY_TYPE NewType
,
1105 IN EFI_PHYSICAL_ADDRESS Memory
,
1109 UINT64 OldAttributes
;
1110 UINT64 NewAttributes
;
1113 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1114 // does not apply to allocations performed in SMM mode.
1121 // If the CPU arch protocol is not installed yet, we cannot manage memory
1122 // permission attributes, and it is the job of the driver that installs this
1123 // protocol to set the permissions on existing allocations.
1130 // Check if a DXE memory protection policy has been configured
1132 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1137 // Update the executable permissions according to the DXE memory
1138 // protection policy, but only if
1139 // - the policy is different between the old and the new type, or
1140 // - this is a newly added region (OldType == EfiMaxMemoryType)
1142 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1144 if (OldType
!= EfiMaxMemoryType
) {
1145 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1146 if (OldAttributes
== NewAttributes
) {
1147 // policy is the same between OldType and NewType
1150 } else if (NewAttributes
== 0) {
1151 // newly added region of a type that does not require protection
1155 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);