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
;
77 STATIC LIST_ENTRY mProtectedImageRecordList
;
80 Sort code section in image record, based upon CodeSegmentBase from low to high.
82 @param ImageRecord image record to be sorted
85 SortImageRecordCodeSection (
86 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
90 Check if code section in image record is valid.
92 @param ImageRecord image record to be checked
94 @retval TRUE image record is valid
95 @retval FALSE image record is invalid
98 IsImageRecordCodeSectionValid (
99 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
105 @param[in] File This is a pointer to the device path of the file that is
108 @return UINT32 Image Type
112 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
116 EFI_HANDLE DeviceHandle
;
117 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
120 return IMAGE_UNKNOWN
;
124 // First check to see if File is from a Firmware Volume
127 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) File
;
128 Status
= gBS
->LocateDevicePath (
129 &gEfiFirmwareVolume2ProtocolGuid
,
133 if (!EFI_ERROR (Status
)) {
134 Status
= gBS
->OpenProtocol (
136 &gEfiFirmwareVolume2ProtocolGuid
,
140 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
142 if (!EFI_ERROR (Status
)) {
143 return IMAGE_FROM_FV
;
146 return IMAGE_UNKNOWN
;
150 Get UEFI image protection policy based upon image type.
152 @param[in] ImageType The UEFI image type
154 @return UEFI image protection policy
157 GetProtectionPolicyFromImageType (
161 if ((ImageType
& mImageProtectionPolicy
) == 0) {
162 return DO_NOT_PROTECT
;
164 return PROTECT_IF_ALIGNED_ELSE_ALLOW
;
169 Get UEFI image protection policy based upon loaded image device path.
171 @param[in] LoadedImage The loaded image protocol
172 @param[in] LoadedImageDevicePath The loaded image device path protocol
174 @return UEFI image protection policy
177 GetUefiImageProtectionPolicy (
178 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
179 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
184 UINT32 ProtectionPolicy
;
190 if (gSmmBase2
!= NULL
) {
191 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
200 if (LoadedImage
== gDxeCoreLoadedImage
) {
201 ImageType
= IMAGE_FROM_FV
;
203 ImageType
= GetImageType (LoadedImageDevicePath
);
205 ProtectionPolicy
= GetProtectionPolicyFromImageType (ImageType
);
206 return ProtectionPolicy
;
211 Set UEFI image memory attributes.
213 @param[in] BaseAddress Specified start address
214 @param[in] Length Specified length
215 @param[in] Attributes Specified attributes
218 SetUefiImageMemoryAttributes (
219 IN UINT64 BaseAddress
,
225 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
226 UINT64 FinalAttributes
;
228 Status
= CoreGetMemorySpaceDescriptor(BaseAddress
, &Descriptor
);
229 ASSERT_EFI_ERROR(Status
);
231 FinalAttributes
= (Descriptor
.Attributes
& CACHE_ATTRIBUTE_MASK
) | (Attributes
& MEMORY_ATTRIBUTE_MASK
);
233 DEBUG ((DEBUG_INFO
, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress
, Length
, FinalAttributes
));
235 ASSERT(gCpu
!= NULL
);
236 gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, Length
, FinalAttributes
);
240 Set UEFI image protection attributes.
242 @param[in] ImageRecord A UEFI image record
245 SetUefiImageProtectionAttributes (
246 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
249 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
250 LIST_ENTRY
*ImageRecordCodeSectionLink
;
251 LIST_ENTRY
*ImageRecordCodeSectionEndLink
;
252 LIST_ENTRY
*ImageRecordCodeSectionList
;
256 ImageRecordCodeSectionList
= &ImageRecord
->CodeSegmentList
;
258 CurrentBase
= ImageRecord
->ImageBase
;
259 ImageEnd
= ImageRecord
->ImageBase
+ ImageRecord
->ImageSize
;
261 ImageRecordCodeSectionLink
= ImageRecordCodeSectionList
->ForwardLink
;
262 ImageRecordCodeSectionEndLink
= ImageRecordCodeSectionList
;
263 while (ImageRecordCodeSectionLink
!= ImageRecordCodeSectionEndLink
) {
264 ImageRecordCodeSection
= CR (
265 ImageRecordCodeSectionLink
,
266 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
268 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
270 ImageRecordCodeSectionLink
= ImageRecordCodeSectionLink
->ForwardLink
;
272 ASSERT (CurrentBase
<= ImageRecordCodeSection
->CodeSegmentBase
);
273 if (CurrentBase
< ImageRecordCodeSection
->CodeSegmentBase
) {
277 SetUefiImageMemoryAttributes (
279 ImageRecordCodeSection
->CodeSegmentBase
- CurrentBase
,
286 SetUefiImageMemoryAttributes (
287 ImageRecordCodeSection
->CodeSegmentBase
,
288 ImageRecordCodeSection
->CodeSegmentSize
,
291 CurrentBase
= ImageRecordCodeSection
->CodeSegmentBase
+ ImageRecordCodeSection
->CodeSegmentSize
;
296 ASSERT (CurrentBase
<= ImageEnd
);
297 if (CurrentBase
< ImageEnd
) {
301 SetUefiImageMemoryAttributes (
303 ImageEnd
- CurrentBase
,
311 Return if the PE image section is aligned.
313 @param[in] SectionAlignment PE/COFF section alignment
314 @param[in] MemoryType PE/COFF image memory type
316 @retval TRUE The PE image section is aligned.
317 @retval FALSE The PE image section is not aligned.
320 IsMemoryProtectionSectionAligned (
321 IN UINT32 SectionAlignment
,
322 IN EFI_MEMORY_TYPE MemoryType
325 UINT32 PageAlignment
;
327 switch (MemoryType
) {
328 case EfiRuntimeServicesCode
:
329 case EfiACPIMemoryNVS
:
330 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
332 case EfiRuntimeServicesData
:
333 case EfiACPIReclaimMemory
:
335 PageAlignment
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
337 case EfiBootServicesCode
:
339 case EfiReservedMemoryType
:
340 PageAlignment
= EFI_PAGE_SIZE
;
344 PageAlignment
= EFI_PAGE_SIZE
;
348 if ((SectionAlignment
& (PageAlignment
- 1)) != 0) {
358 @param[in] ImageRecord A UEFI image record
362 IN IMAGE_PROPERTIES_RECORD
*ImageRecord
365 LIST_ENTRY
*CodeSegmentListHead
;
366 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
368 CodeSegmentListHead
= &ImageRecord
->CodeSegmentList
;
369 while (!IsListEmpty (CodeSegmentListHead
)) {
370 ImageRecordCodeSection
= CR (
371 CodeSegmentListHead
->ForwardLink
,
372 IMAGE_PROPERTIES_RECORD_CODE_SECTION
,
374 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
376 RemoveEntryList (&ImageRecordCodeSection
->Link
);
377 FreePool (ImageRecordCodeSection
);
380 if (ImageRecord
->Link
.ForwardLink
!= NULL
) {
381 RemoveEntryList (&ImageRecord
->Link
);
383 FreePool (ImageRecord
);
387 Protect UEFI PE/COFF image.
389 @param[in] LoadedImage The loaded image protocol
390 @param[in] LoadedImageDevicePath The loaded image device path protocol
394 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
395 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
399 EFI_IMAGE_DOS_HEADER
*DosHdr
;
400 UINT32 PeCoffHeaderOffset
;
401 UINT32 SectionAlignment
;
402 EFI_IMAGE_SECTION_HEADER
*Section
;
403 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
;
406 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
408 IMAGE_PROPERTIES_RECORD_CODE_SECTION
*ImageRecordCodeSection
;
411 UINT32 ProtectionPolicy
;
413 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
414 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
420 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
421 switch (ProtectionPolicy
) {
424 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
431 ImageRecord
= AllocateZeroPool (sizeof(*ImageRecord
));
432 if (ImageRecord
== NULL
) {
435 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
438 // Step 1: record whole region
440 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
441 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
443 ImageAddress
= LoadedImage
->ImageBase
;
445 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
446 if (PdbPointer
!= NULL
) {
447 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
451 // Check PE/COFF image
453 DosHdr
= (EFI_IMAGE_DOS_HEADER
*) (UINTN
) ImageAddress
;
454 PeCoffHeaderOffset
= 0;
455 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
456 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
459 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*) (UINTN
) ImageAddress
+ PeCoffHeaderOffset
);
460 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
461 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
462 // It might be image in SMM.
467 // Get SectionAlignment
469 if (Hdr
.Pe32
->FileHeader
.Machine
== IMAGE_FILE_MACHINE_IA64
&& Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
471 // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
472 // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
473 // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
474 // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
476 Magic
= EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
;
479 // Get the magic value from the PE/COFF Optional Header
481 Magic
= Hdr
.Pe32
->OptionalHeader
.Magic
;
483 if (Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
484 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
486 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
489 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
491 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
493 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
494 if (PdbPointer
!= NULL
) {
495 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
500 Section
= (EFI_IMAGE_SECTION_HEADER
*) (
501 (UINT8
*) (UINTN
) ImageAddress
+
504 sizeof(EFI_IMAGE_FILE_HEADER
) +
505 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
507 ImageRecord
->CodeSegmentCount
= 0;
508 InitializeListHead (&ImageRecord
->CodeSegmentList
);
509 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
510 Name
= Section
[Index
].Name
;
513 " Section - '%c%c%c%c%c%c%c%c'\n",
525 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
526 // can always be mapped read-only, classify a section as a code section only
527 // if it has the executable attribute set and the writable attribute cleared.
529 // This adheres more closely to the PE/COFF spec, and avoids issues with
530 // Linux OS loaders that may consist of a single read/write/execute section.
532 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
533 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
534 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
535 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
536 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
537 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
538 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
539 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
540 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
541 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
544 // Step 2: record code section
546 ImageRecordCodeSection
= AllocatePool (sizeof(*ImageRecordCodeSection
));
547 if (ImageRecordCodeSection
== NULL
) {
550 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
552 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
553 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE(Section
[Index
].SizeOfRawData
, SectionAlignment
);
555 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
557 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
558 ImageRecord
->CodeSegmentCount
++;
562 if (ImageRecord
->CodeSegmentCount
== 0) {
564 // If a UEFI executable consists of a single read+write+exec PE/COFF
565 // section, that isn't actually an error. The image can be launched
566 // alright, only image protection cannot be applied to it fully.
568 // One example that elicits this is (some) Linux kernels (with the EFI stub
571 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
572 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
573 if (PdbPointer
!= NULL
) {
574 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
582 SortImageRecordCodeSection (ImageRecord
);
584 // Check overlap all section in ImageBase/Size
586 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
587 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
592 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
593 // Given that the loader always allocates full pages, we know the space after the image is not used.
595 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
598 // CPU ARCH present. Update memory attribute directly.
600 SetUefiImageProtectionAttributes (ImageRecord
);
603 // Record the image record in the list so we can undo the protections later
605 InsertTailList (&mProtectedImageRecordList
, &ImageRecord
->Link
);
612 Unprotect UEFI image.
614 @param[in] LoadedImage The loaded image protocol
615 @param[in] LoadedImageDevicePath The loaded image device path protocol
619 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
620 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
623 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
624 LIST_ENTRY
*ImageRecordLink
;
626 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
627 for (ImageRecordLink
= mProtectedImageRecordList
.ForwardLink
;
628 ImageRecordLink
!= &mProtectedImageRecordList
;
629 ImageRecordLink
= ImageRecordLink
->ForwardLink
) {
632 IMAGE_PROPERTIES_RECORD
,
634 IMAGE_PROPERTIES_RECORD_SIGNATURE
637 if (ImageRecord
->ImageBase
== (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
) {
638 SetUefiImageMemoryAttributes (ImageRecord
->ImageBase
,
639 ImageRecord
->ImageSize
,
641 FreeImageRecord (ImageRecord
);
649 Return the EFI memory permission attribute associated with memory
650 type 'MemoryType' under the configured DXE memory protection policy.
652 @param MemoryType Memory type.
656 GetPermissionAttributeForMemoryType (
657 IN EFI_MEMORY_TYPE MemoryType
662 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
664 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
667 TestBit
= LShiftU64 (1, MemoryType
);
670 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
671 return EFI_MEMORY_XP
;
678 Sort memory map entries based upon PhysicalStart, from low to high.
680 @param MemoryMap A pointer to the buffer in which firmware places
681 the current memory map.
682 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
683 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
688 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
689 IN UINTN MemoryMapSize
,
690 IN UINTN DescriptorSize
693 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
694 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
695 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
696 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
698 MemoryMapEntry
= MemoryMap
;
699 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
700 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
701 while (MemoryMapEntry
< MemoryMapEnd
) {
702 while (NextMemoryMapEntry
< MemoryMapEnd
) {
703 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
704 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
705 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
706 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
709 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
712 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
713 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
718 Merge adjacent memory map entries if they use the same memory protection policy
720 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
721 the current memory map.
722 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
723 MemoryMap buffer. On input, this is the size of
724 the current memory map. On output,
725 it is the size of new memory map after merge.
726 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
730 MergeMemoryMapForProtectionPolicy (
731 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
732 IN OUT UINTN
*MemoryMapSize
,
733 IN UINTN DescriptorSize
736 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
737 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
738 UINT64 MemoryBlockLength
;
739 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
740 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
743 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
745 MemoryMapEntry
= MemoryMap
;
746 NewMemoryMapEntry
= MemoryMap
;
747 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
748 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
749 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
750 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
753 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
754 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
756 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
757 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
758 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
759 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
760 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
761 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
764 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
767 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
772 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
773 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
776 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
783 Remove exec permissions from all regions whose type is identified by
784 PcdDxeNxMemoryProtectionPolicy.
788 InitializeDxeNxMemoryProtectionPolicy (
794 UINTN DescriptorSize
;
795 UINT32 DescriptorVersion
;
796 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
797 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
798 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
802 EFI_GCD_MAP_ENTRY
*Entry
;
805 // Get the EFI memory map.
810 Status
= gBS
->GetMemoryMap (
817 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
819 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
820 ASSERT (MemoryMap
!= NULL
);
821 Status
= gBS
->GetMemoryMap (
828 if (EFI_ERROR (Status
)) {
829 FreePool (MemoryMap
);
831 } while (Status
== EFI_BUFFER_TOO_SMALL
);
832 ASSERT_EFI_ERROR (Status
);
834 DEBUG((DEBUG_ERROR
, "%a: applying strict permissions to active memory regions\n",
837 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
839 MemoryMapEntry
= MemoryMap
;
840 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
841 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
843 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
844 if (Attributes
!= 0) {
845 SetUefiImageMemoryAttributes (
846 MemoryMapEntry
->PhysicalStart
,
847 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
850 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
852 FreePool (MemoryMap
);
855 // Apply the policy for RAM regions that we know are present and
856 // accessible, but have not been added to the UEFI memory map (yet).
858 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
860 "%a: applying strict permissions to inactive memory regions\n",
863 CoreAcquireGcdMemoryLock ();
865 Link
= mGcdMemorySpaceMap
.ForwardLink
;
866 while (Link
!= &mGcdMemorySpaceMap
) {
868 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
870 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
871 Entry
->EndAddress
< MAX_ADDRESS
&&
872 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
873 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
875 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
876 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
879 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
880 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
883 ASSERT(gCpu
!= NULL
);
884 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
885 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
888 Link
= Link
->ForwardLink
;
890 CoreReleaseGcdMemoryLock ();
896 A notification for CPU_ARCH protocol.
898 @param[in] Event Event whose notification function is being invoked.
899 @param[in] Context Pointer to the notification function's context,
900 which is implementation-dependent.
905 MemoryProtectionCpuArchProtocolNotify (
911 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
912 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
914 EFI_HANDLE
*HandleBuffer
;
917 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
918 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
919 if (EFI_ERROR (Status
)) {
924 // Apply the memory protection policy on non-BScode/RTcode regions.
926 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
927 InitializeDxeNxMemoryProtectionPolicy ();
930 if (mImageProtectionPolicy
== 0) {
934 Status
= gBS
->LocateHandleBuffer (
936 &gEfiLoadedImageProtocolGuid
,
941 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
945 for (Index
= 0; Index
< NoHandles
; Index
++) {
946 Status
= gBS
->HandleProtocol (
948 &gEfiLoadedImageProtocolGuid
,
949 (VOID
**)&LoadedImage
951 if (EFI_ERROR(Status
)) {
954 Status
= gBS
->HandleProtocol (
956 &gEfiLoadedImageDevicePathProtocolGuid
,
957 (VOID
**)&LoadedImageDevicePath
959 if (EFI_ERROR(Status
)) {
960 LoadedImageDevicePath
= NULL
;
963 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
966 CoreCloseEvent (Event
);
971 ExitBootServices Callback function for memory protection.
974 MemoryProtectionExitBootServicesCallback (
978 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
982 // We need remove the RT protection, because RT relocation need write code segment
983 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
985 // Firmware does not own page tables after ExitBootServices(), so the OS would
986 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
987 // delay setting protections on RT code pages until after SetVirtualAddressMap().
988 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
990 if (mImageProtectionPolicy
!= 0) {
991 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
992 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
993 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
999 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1000 order to skip unfixable NULL pointer access issues detected in OptionROM or
1003 @param[in] Event The Event this notify function registered to.
1004 @param[in] Context Pointer to the context data registered to the Event.
1008 DisableNullDetectionAtTheEndOfDxe (
1014 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
1016 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1018 // Disable NULL pointer detection by enabling first 4K page
1020 Status
= CoreGetMemorySpaceDescriptor (0, &Desc
);
1021 ASSERT_EFI_ERROR (Status
);
1023 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
1024 Status
= CoreSetMemorySpaceCapabilities (
1027 Desc
.Capabilities
| EFI_MEMORY_RP
1029 ASSERT_EFI_ERROR (Status
);
1032 Status
= CoreSetMemorySpaceAttributes (
1035 Desc
.Attributes
& ~EFI_MEMORY_RP
1037 ASSERT_EFI_ERROR (Status
);
1039 CoreCloseEvent (Event
);
1040 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1046 Initialize Memory Protection support.
1050 CoreInitializeMemoryProtection (
1056 EFI_EVENT EndOfDxeEvent
;
1059 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1061 InitializeListHead (&mProtectedImageRecordList
);
1064 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1065 // - code regions should have no EFI_MEMORY_XP attribute
1066 // - EfiConventionalMemory and EfiBootServicesData should use the
1069 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1070 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1071 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1072 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1073 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1075 if (mImageProtectionPolicy
!= 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1076 Status
= CoreCreateEvent (
1079 MemoryProtectionCpuArchProtocolNotify
,
1083 ASSERT_EFI_ERROR(Status
);
1086 // Register for protocol notifactions on this event
1088 Status
= CoreRegisterProtocolNotify (
1089 &gEfiCpuArchProtocolGuid
,
1093 ASSERT_EFI_ERROR(Status
);
1097 // Register a callback to disable NULL pointer detection at EndOfDxe
1099 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
))
1101 Status
= CoreCreateEventEx (
1104 DisableNullDetectionAtTheEndOfDxe
,
1106 &gEfiEndOfDxeEventGroupGuid
,
1109 ASSERT_EFI_ERROR (Status
);
1116 Returns whether we are currently executing in SMM mode.
1127 if (gSmmBase2
!= NULL
) {
1128 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1134 Manage memory permission attributes on a memory range, according to the
1135 configured DXE memory protection policy.
1137 @param OldType The old memory type of the range
1138 @param NewType The new memory type of the range
1139 @param Memory The base address of the range
1140 @param Length The size of the range (in bytes)
1142 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1143 are updated in this case
1144 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1145 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1146 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1147 @return other Return value of gCpu->SetMemoryAttributes()
1152 ApplyMemoryProtectionPolicy (
1153 IN EFI_MEMORY_TYPE OldType
,
1154 IN EFI_MEMORY_TYPE NewType
,
1155 IN EFI_PHYSICAL_ADDRESS Memory
,
1159 UINT64 OldAttributes
;
1160 UINT64 NewAttributes
;
1163 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1164 // does not apply to allocations performed in SMM mode.
1171 // If the CPU arch protocol is not installed yet, we cannot manage memory
1172 // permission attributes, and it is the job of the driver that installs this
1173 // protocol to set the permissions on existing allocations.
1180 // Check if a DXE memory protection policy has been configured
1182 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1187 // Update the executable permissions according to the DXE memory
1188 // protection policy, but only if
1189 // - the policy is different between the old and the new type, or
1190 // - this is a newly added region (OldType == EfiMaxMemoryType)
1192 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1194 if (OldType
!= EfiMaxMemoryType
) {
1195 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1196 if (OldAttributes
== NewAttributes
) {
1197 // policy is the same between OldType and NewType
1200 } else if (NewAttributes
== 0) {
1201 // newly added region of a type that does not require protection
1205 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);