2 UEFI Memory Protection support.
4 If the UEFI image is page aligned, the image code section is set to read only
5 and the image data section is set to non-executable.
7 1) This policy is applied for all UEFI image including boot service driver,
8 runtime driver or application.
9 2) This policy is applied only if the UEFI image meets the page alignment
11 3) This policy is applied only if the Source UEFI image matches the
12 PcdImageProtectionPolicy definition.
13 4) This policy is not applied to the non-PE image region.
15 The DxeCore calls CpuArchProtocol->SetMemoryAttributes() to protect
16 the image. If the CpuArch protocol is not installed yet, the DxeCore
17 enqueues the protection request. Once the CpuArch is installed, the
18 DxeCore dequeues the protection request and applies policy.
20 Once the image is unloaded, the protection is removed automatically.
22 Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
23 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/SimpleFileSystem.h>
50 #include "Mem/HeapGuard.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
;
410 UINT32 ProtectionPolicy
;
412 DEBUG ((DEBUG_INFO
, "ProtectUefiImageCommon - 0x%x\n", LoadedImage
));
413 DEBUG ((DEBUG_INFO
, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
, LoadedImage
->ImageSize
));
419 ProtectionPolicy
= GetUefiImageProtectionPolicy (LoadedImage
, LoadedImageDevicePath
);
420 switch (ProtectionPolicy
) {
423 case PROTECT_IF_ALIGNED_ELSE_ALLOW
:
430 ImageRecord
= AllocateZeroPool (sizeof(*ImageRecord
));
431 if (ImageRecord
== NULL
) {
434 ImageRecord
->Signature
= IMAGE_PROPERTIES_RECORD_SIGNATURE
;
437 // Step 1: record whole region
439 ImageRecord
->ImageBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
;
440 ImageRecord
->ImageSize
= LoadedImage
->ImageSize
;
442 ImageAddress
= LoadedImage
->ImageBase
;
444 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
445 if (PdbPointer
!= NULL
) {
446 DEBUG ((DEBUG_VERBOSE
, " Image - %a\n", PdbPointer
));
450 // Check PE/COFF image
452 DosHdr
= (EFI_IMAGE_DOS_HEADER
*) (UINTN
) ImageAddress
;
453 PeCoffHeaderOffset
= 0;
454 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
) {
455 PeCoffHeaderOffset
= DosHdr
->e_lfanew
;
458 Hdr
.Pe32
= (EFI_IMAGE_NT_HEADERS32
*)((UINT8
*) (UINTN
) ImageAddress
+ PeCoffHeaderOffset
);
459 if (Hdr
.Pe32
->Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
460 DEBUG ((DEBUG_VERBOSE
, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr
.Pe32
->Signature
));
461 // It might be image in SMM.
466 // Get SectionAlignment
468 if (Hdr
.Pe32
->OptionalHeader
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
469 SectionAlignment
= Hdr
.Pe32
->OptionalHeader
.SectionAlignment
;
471 SectionAlignment
= Hdr
.Pe32Plus
->OptionalHeader
.SectionAlignment
;
474 IsAligned
= IsMemoryProtectionSectionAligned (SectionAlignment
, LoadedImage
->ImageCodeType
);
476 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
478 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
479 if (PdbPointer
!= NULL
) {
480 DEBUG ((DEBUG_VERBOSE
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
485 Section
= (EFI_IMAGE_SECTION_HEADER
*) (
486 (UINT8
*) (UINTN
) ImageAddress
+
489 sizeof(EFI_IMAGE_FILE_HEADER
) +
490 Hdr
.Pe32
->FileHeader
.SizeOfOptionalHeader
492 ImageRecord
->CodeSegmentCount
= 0;
493 InitializeListHead (&ImageRecord
->CodeSegmentList
);
494 for (Index
= 0; Index
< Hdr
.Pe32
->FileHeader
.NumberOfSections
; Index
++) {
495 Name
= Section
[Index
].Name
;
498 " Section - '%c%c%c%c%c%c%c%c'\n",
510 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
511 // can always be mapped read-only, classify a section as a code section only
512 // if it has the executable attribute set and the writable attribute cleared.
514 // This adheres more closely to the PE/COFF spec, and avoids issues with
515 // Linux OS loaders that may consist of a single read/write/execute section.
517 if ((Section
[Index
].Characteristics
& (EFI_IMAGE_SCN_MEM_WRITE
| EFI_IMAGE_SCN_MEM_EXECUTE
)) == EFI_IMAGE_SCN_MEM_EXECUTE
) {
518 DEBUG ((DEBUG_VERBOSE
, " VirtualSize - 0x%08x\n", Section
[Index
].Misc
.VirtualSize
));
519 DEBUG ((DEBUG_VERBOSE
, " VirtualAddress - 0x%08x\n", Section
[Index
].VirtualAddress
));
520 DEBUG ((DEBUG_VERBOSE
, " SizeOfRawData - 0x%08x\n", Section
[Index
].SizeOfRawData
));
521 DEBUG ((DEBUG_VERBOSE
, " PointerToRawData - 0x%08x\n", Section
[Index
].PointerToRawData
));
522 DEBUG ((DEBUG_VERBOSE
, " PointerToRelocations - 0x%08x\n", Section
[Index
].PointerToRelocations
));
523 DEBUG ((DEBUG_VERBOSE
, " PointerToLinenumbers - 0x%08x\n", Section
[Index
].PointerToLinenumbers
));
524 DEBUG ((DEBUG_VERBOSE
, " NumberOfRelocations - 0x%08x\n", Section
[Index
].NumberOfRelocations
));
525 DEBUG ((DEBUG_VERBOSE
, " NumberOfLinenumbers - 0x%08x\n", Section
[Index
].NumberOfLinenumbers
));
526 DEBUG ((DEBUG_VERBOSE
, " Characteristics - 0x%08x\n", Section
[Index
].Characteristics
));
529 // Step 2: record code section
531 ImageRecordCodeSection
= AllocatePool (sizeof(*ImageRecordCodeSection
));
532 if (ImageRecordCodeSection
== NULL
) {
535 ImageRecordCodeSection
->Signature
= IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
;
537 ImageRecordCodeSection
->CodeSegmentBase
= (UINTN
)ImageAddress
+ Section
[Index
].VirtualAddress
;
538 ImageRecordCodeSection
->CodeSegmentSize
= ALIGN_VALUE(Section
[Index
].SizeOfRawData
, SectionAlignment
);
540 DEBUG ((DEBUG_VERBOSE
, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection
->CodeSegmentBase
, ImageRecordCodeSection
->CodeSegmentSize
));
542 InsertTailList (&ImageRecord
->CodeSegmentList
, &ImageRecordCodeSection
->Link
);
543 ImageRecord
->CodeSegmentCount
++;
547 if (ImageRecord
->CodeSegmentCount
== 0) {
549 // If a UEFI executable consists of a single read+write+exec PE/COFF
550 // section, that isn't actually an error. The image can be launched
551 // alright, only image protection cannot be applied to it fully.
553 // One example that elicits this is (some) Linux kernels (with the EFI stub
556 DEBUG ((DEBUG_WARN
, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
557 PdbPointer
= PeCoffLoaderGetPdbPointer ((VOID
*) (UINTN
) ImageAddress
);
558 if (PdbPointer
!= NULL
) {
559 DEBUG ((DEBUG_WARN
, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer
));
567 SortImageRecordCodeSection (ImageRecord
);
569 // Check overlap all section in ImageBase/Size
571 if (!IsImageRecordCodeSectionValid (ImageRecord
)) {
572 DEBUG ((DEBUG_ERROR
, "IsImageRecordCodeSectionValid - FAIL\n"));
577 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
578 // Given that the loader always allocates full pages, we know the space after the image is not used.
580 ImageRecord
->ImageSize
= ALIGN_VALUE(LoadedImage
->ImageSize
, EFI_PAGE_SIZE
);
583 // CPU ARCH present. Update memory attribute directly.
585 SetUefiImageProtectionAttributes (ImageRecord
);
588 // Record the image record in the list so we can undo the protections later
590 InsertTailList (&mProtectedImageRecordList
, &ImageRecord
->Link
);
597 Unprotect UEFI image.
599 @param[in] LoadedImage The loaded image protocol
600 @param[in] LoadedImageDevicePath The loaded image device path protocol
604 IN EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
,
605 IN EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
608 IMAGE_PROPERTIES_RECORD
*ImageRecord
;
609 LIST_ENTRY
*ImageRecordLink
;
611 if (PcdGet32(PcdImageProtectionPolicy
) != 0) {
612 for (ImageRecordLink
= mProtectedImageRecordList
.ForwardLink
;
613 ImageRecordLink
!= &mProtectedImageRecordList
;
614 ImageRecordLink
= ImageRecordLink
->ForwardLink
) {
617 IMAGE_PROPERTIES_RECORD
,
619 IMAGE_PROPERTIES_RECORD_SIGNATURE
622 if (ImageRecord
->ImageBase
== (EFI_PHYSICAL_ADDRESS
)(UINTN
)LoadedImage
->ImageBase
) {
623 SetUefiImageMemoryAttributes (ImageRecord
->ImageBase
,
624 ImageRecord
->ImageSize
,
626 FreeImageRecord (ImageRecord
);
634 Return the EFI memory permission attribute associated with memory
635 type 'MemoryType' under the configured DXE memory protection policy.
637 @param MemoryType Memory type.
641 GetPermissionAttributeForMemoryType (
642 IN EFI_MEMORY_TYPE MemoryType
647 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
649 } else if ((UINT32
)MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
652 TestBit
= LShiftU64 (1, MemoryType
);
655 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & TestBit
) != 0) {
656 return EFI_MEMORY_XP
;
663 Sort memory map entries based upon PhysicalStart, from low to high.
665 @param MemoryMap A pointer to the buffer in which firmware places
666 the current memory map.
667 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
668 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
673 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
674 IN UINTN MemoryMapSize
,
675 IN UINTN DescriptorSize
678 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
679 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
680 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
681 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
683 MemoryMapEntry
= MemoryMap
;
684 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
685 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
686 while (MemoryMapEntry
< MemoryMapEnd
) {
687 while (NextMemoryMapEntry
< MemoryMapEnd
) {
688 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
689 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
690 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
691 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof(EFI_MEMORY_DESCRIPTOR
));
694 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
697 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
698 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
703 Merge adjacent memory map entries if they use the same memory protection policy
705 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
706 the current memory map.
707 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
708 MemoryMap buffer. On input, this is the size of
709 the current memory map. On output,
710 it is the size of new memory map after merge.
711 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
715 MergeMemoryMapForProtectionPolicy (
716 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
717 IN OUT UINTN
*MemoryMapSize
,
718 IN UINTN DescriptorSize
721 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
722 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
723 UINT64 MemoryBlockLength
;
724 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
725 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
728 SortMemoryMap (MemoryMap
, *MemoryMapSize
, DescriptorSize
);
730 MemoryMapEntry
= MemoryMap
;
731 NewMemoryMapEntry
= MemoryMap
;
732 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ *MemoryMapSize
);
733 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
734 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof(EFI_MEMORY_DESCRIPTOR
));
735 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
738 MemoryBlockLength
= (UINT64
) (EFI_PAGES_TO_SIZE((UINTN
)MemoryMapEntry
->NumberOfPages
));
739 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
741 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
742 Attributes
== GetPermissionAttributeForMemoryType (NextMemoryMapEntry
->Type
) &&
743 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
)) {
744 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
745 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
746 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
749 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
752 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
757 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
758 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
761 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
768 Remove exec permissions from all regions whose type is identified by
769 PcdDxeNxMemoryProtectionPolicy.
773 InitializeDxeNxMemoryProtectionPolicy (
779 UINTN DescriptorSize
;
780 UINT32 DescriptorVersion
;
781 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
782 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
783 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
787 EFI_GCD_MAP_ENTRY
*Entry
;
788 EFI_PEI_HOB_POINTERS Hob
;
789 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
790 EFI_PHYSICAL_ADDRESS StackBase
;
793 // Get the EFI memory map.
798 Status
= gBS
->GetMemoryMap (
805 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
807 MemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (MemoryMapSize
);
808 ASSERT (MemoryMap
!= NULL
);
809 Status
= gBS
->GetMemoryMap (
816 if (EFI_ERROR (Status
)) {
817 FreePool (MemoryMap
);
819 } while (Status
== EFI_BUFFER_TOO_SMALL
);
820 ASSERT_EFI_ERROR (Status
);
823 if (PcdGetBool (PcdCpuStackGuard
)) {
825 // Get the base of stack from Hob.
827 Hob
.Raw
= GetHobList ();
828 while ((Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION
, Hob
.Raw
)) != NULL
) {
829 MemoryHob
= Hob
.MemoryAllocation
;
830 if (CompareGuid(&gEfiHobMemoryAllocStackGuid
, &MemoryHob
->AllocDescriptor
.Name
)) {
833 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",
835 MemoryHob
->AllocDescriptor
.MemoryBaseAddress
,
836 MemoryHob
->AllocDescriptor
.MemoryLength
839 StackBase
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
;
841 // Ensure the base of the stack is page-size aligned.
843 ASSERT ((StackBase
& EFI_PAGE_MASK
) == 0);
846 Hob
.Raw
= GET_NEXT_HOB (Hob
);
850 // Ensure the base of stack can be found from Hob when stack guard is
853 ASSERT (StackBase
!= 0);
858 "%a: applying strict permissions to active memory regions\n",
862 MergeMemoryMapForProtectionPolicy (MemoryMap
, &MemoryMapSize
, DescriptorSize
);
864 MemoryMapEntry
= MemoryMap
;
865 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) MemoryMap
+ MemoryMapSize
);
866 while ((UINTN
) MemoryMapEntry
< (UINTN
) MemoryMapEnd
) {
868 Attributes
= GetPermissionAttributeForMemoryType (MemoryMapEntry
->Type
);
869 if (Attributes
!= 0) {
870 SetUefiImageMemoryAttributes (
871 MemoryMapEntry
->PhysicalStart
,
872 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
),
876 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
879 if (MemoryMapEntry
->PhysicalStart
== 0 &&
880 PcdGet8 (PcdNullPointerDetectionPropertyMask
) != 0) {
882 ASSERT (MemoryMapEntry
->NumberOfPages
> 0);
883 SetUefiImageMemoryAttributes (
885 EFI_PAGES_TO_SIZE (1),
886 EFI_MEMORY_RP
| Attributes
);
890 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
893 if (StackBase
!= 0 &&
894 (StackBase
>= MemoryMapEntry
->PhysicalStart
&&
895 StackBase
< MemoryMapEntry
->PhysicalStart
+
896 LShiftU64 (MemoryMapEntry
->NumberOfPages
, EFI_PAGE_SHIFT
)) &&
897 PcdGetBool (PcdCpuStackGuard
)) {
899 SetUefiImageMemoryAttributes (
901 EFI_PAGES_TO_SIZE (1),
902 EFI_MEMORY_RP
| Attributes
);
906 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
908 FreePool (MemoryMap
);
911 // Apply the policy for RAM regions that we know are present and
912 // accessible, but have not been added to the UEFI memory map (yet).
914 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory
) != 0) {
917 "%a: applying strict permissions to inactive memory regions\n",
921 CoreAcquireGcdMemoryLock ();
923 Link
= mGcdMemorySpaceMap
.ForwardLink
;
924 while (Link
!= &mGcdMemorySpaceMap
) {
926 Entry
= CR (Link
, EFI_GCD_MAP_ENTRY
, Link
, EFI_GCD_MAP_SIGNATURE
);
928 if (Entry
->GcdMemoryType
== EfiGcdMemoryTypeReserved
&&
929 Entry
->EndAddress
< MAX_ADDRESS
&&
930 (Entry
->Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
931 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
)) {
933 Attributes
= GetPermissionAttributeForMemoryType (EfiConventionalMemory
) |
934 (Entry
->Attributes
& CACHE_ATTRIBUTE_MASK
);
937 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
938 Entry
->BaseAddress
, Entry
->EndAddress
- Entry
->BaseAddress
+ 1,
941 ASSERT(gCpu
!= NULL
);
942 gCpu
->SetMemoryAttributes (gCpu
, Entry
->BaseAddress
,
943 Entry
->EndAddress
- Entry
->BaseAddress
+ 1, Attributes
);
946 Link
= Link
->ForwardLink
;
948 CoreReleaseGcdMemoryLock ();
954 A notification for CPU_ARCH protocol.
956 @param[in] Event Event whose notification function is being invoked.
957 @param[in] Context Pointer to the notification function's context,
958 which is implementation-dependent.
963 MemoryProtectionCpuArchProtocolNotify (
969 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
970 EFI_DEVICE_PATH_PROTOCOL
*LoadedImageDevicePath
;
972 EFI_HANDLE
*HandleBuffer
;
975 DEBUG ((DEBUG_INFO
, "MemoryProtectionCpuArchProtocolNotify:\n"));
976 Status
= CoreLocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&gCpu
);
977 if (EFI_ERROR (Status
)) {
982 // Apply the memory protection policy on non-BScode/RTcode regions.
984 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
985 InitializeDxeNxMemoryProtectionPolicy ();
989 // Call notify function meant for Heap Guard.
991 HeapGuardCpuArchProtocolNotify ();
993 if (mImageProtectionPolicy
== 0) {
997 Status
= gBS
->LocateHandleBuffer (
999 &gEfiLoadedImageProtocolGuid
,
1004 if (EFI_ERROR (Status
) && (NoHandles
== 0)) {
1008 for (Index
= 0; Index
< NoHandles
; Index
++) {
1009 Status
= gBS
->HandleProtocol (
1010 HandleBuffer
[Index
],
1011 &gEfiLoadedImageProtocolGuid
,
1012 (VOID
**)&LoadedImage
1014 if (EFI_ERROR(Status
)) {
1017 Status
= gBS
->HandleProtocol (
1018 HandleBuffer
[Index
],
1019 &gEfiLoadedImageDevicePathProtocolGuid
,
1020 (VOID
**)&LoadedImageDevicePath
1022 if (EFI_ERROR(Status
)) {
1023 LoadedImageDevicePath
= NULL
;
1026 ProtectUefiImage (LoadedImage
, LoadedImageDevicePath
);
1029 CoreCloseEvent (Event
);
1034 ExitBootServices Callback function for memory protection.
1037 MemoryProtectionExitBootServicesCallback (
1041 EFI_RUNTIME_IMAGE_ENTRY
*RuntimeImage
;
1045 // We need remove the RT protection, because RT relocation need write code segment
1046 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1048 // Firmware does not own page tables after ExitBootServices(), so the OS would
1049 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1050 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1051 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1053 if (mImageProtectionPolicy
!= 0) {
1054 for (Link
= gRuntime
->ImageHead
.ForwardLink
; Link
!= &gRuntime
->ImageHead
; Link
= Link
->ForwardLink
) {
1055 RuntimeImage
= BASE_CR (Link
, EFI_RUNTIME_IMAGE_ENTRY
, Link
);
1056 SetUefiImageMemoryAttributes ((UINT64
)(UINTN
)RuntimeImage
->ImageBase
, ALIGN_VALUE(RuntimeImage
->ImageSize
, EFI_PAGE_SIZE
), 0);
1062 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1063 order to skip unfixable NULL pointer access issues detected in OptionROM or
1066 @param[in] Event The Event this notify function registered to.
1067 @param[in] Context Pointer to the context data registered to the Event.
1071 DisableNullDetectionAtTheEndOfDxe (
1077 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
1079 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1081 // Disable NULL pointer detection by enabling first 4K page
1083 Status
= CoreGetMemorySpaceDescriptor (0, &Desc
);
1084 ASSERT_EFI_ERROR (Status
);
1086 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
1087 Status
= CoreSetMemorySpaceCapabilities (
1090 Desc
.Capabilities
| EFI_MEMORY_RP
1092 ASSERT_EFI_ERROR (Status
);
1095 Status
= CoreSetMemorySpaceAttributes (
1098 Desc
.Attributes
& ~EFI_MEMORY_RP
1100 ASSERT_EFI_ERROR (Status
);
1102 CoreCloseEvent (Event
);
1103 DEBUG ((DEBUG_INFO
, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1109 Initialize Memory Protection support.
1113 CoreInitializeMemoryProtection (
1119 EFI_EVENT EndOfDxeEvent
;
1122 mImageProtectionPolicy
= PcdGet32(PcdImageProtectionPolicy
);
1124 InitializeListHead (&mProtectedImageRecordList
);
1127 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1128 // - code regions should have no EFI_MEMORY_XP attribute
1129 // - EfiConventionalMemory and EfiBootServicesData should use the
1132 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode
) & EFI_MEMORY_XP
) == 0);
1133 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode
) & EFI_MEMORY_XP
) == 0);
1134 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode
) & EFI_MEMORY_XP
) == 0);
1135 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData
) ==
1136 GetPermissionAttributeForMemoryType (EfiConventionalMemory
));
1138 if (mImageProtectionPolicy
!= 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) != 0) {
1139 Status
= CoreCreateEvent (
1142 MemoryProtectionCpuArchProtocolNotify
,
1146 ASSERT_EFI_ERROR(Status
);
1149 // Register for protocol notifactions on this event
1151 Status
= CoreRegisterProtocolNotify (
1152 &gEfiCpuArchProtocolGuid
,
1156 ASSERT_EFI_ERROR(Status
);
1160 // Register a callback to disable NULL pointer detection at EndOfDxe
1162 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
))
1164 Status
= CoreCreateEventEx (
1167 DisableNullDetectionAtTheEndOfDxe
,
1169 &gEfiEndOfDxeEventGroupGuid
,
1172 ASSERT_EFI_ERROR (Status
);
1179 Returns whether we are currently executing in SMM mode.
1190 if (gSmmBase2
!= NULL
) {
1191 gSmmBase2
->InSmm (gSmmBase2
, &InSmm
);
1197 Manage memory permission attributes on a memory range, according to the
1198 configured DXE memory protection policy.
1200 @param OldType The old memory type of the range
1201 @param NewType The new memory type of the range
1202 @param Memory The base address of the range
1203 @param Length The size of the range (in bytes)
1205 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1206 are updated in this case
1207 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1208 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1209 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1210 @return other Return value of gCpu->SetMemoryAttributes()
1215 ApplyMemoryProtectionPolicy (
1216 IN EFI_MEMORY_TYPE OldType
,
1217 IN EFI_MEMORY_TYPE NewType
,
1218 IN EFI_PHYSICAL_ADDRESS Memory
,
1222 UINT64 OldAttributes
;
1223 UINT64 NewAttributes
;
1226 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1227 // does not apply to allocations performed in SMM mode.
1234 // If the CPU arch protocol is not installed yet, we cannot manage memory
1235 // permission attributes, and it is the job of the driver that installs this
1236 // protocol to set the permissions on existing allocations.
1243 // Check if a DXE memory protection policy has been configured
1245 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) == 0) {
1250 // Don't overwrite Guard pages, which should be the first and/or last page,
1253 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
)) {
1254 if (IsGuardPage (Memory
)) {
1255 Memory
+= EFI_PAGE_SIZE
;
1256 Length
-= EFI_PAGE_SIZE
;
1262 if (IsGuardPage (Memory
+ Length
- EFI_PAGE_SIZE
)) {
1263 Length
-= EFI_PAGE_SIZE
;
1271 // Update the executable permissions according to the DXE memory
1272 // protection policy, but only if
1273 // - the policy is different between the old and the new type, or
1274 // - this is a newly added region (OldType == EfiMaxMemoryType)
1276 NewAttributes
= GetPermissionAttributeForMemoryType (NewType
);
1278 if (OldType
!= EfiMaxMemoryType
) {
1279 OldAttributes
= GetPermissionAttributeForMemoryType (OldType
);
1280 if (OldAttributes
== NewAttributes
) {
1281 // policy is the same between OldType and NewType
1284 } else if (NewAttributes
== 0) {
1285 // newly added region of a type that does not require protection
1289 return gCpu
->SetMemoryAttributes (gCpu
, Memory
, Length
, NewAttributes
);