407aece80786715e7265fc0ff53ea8d3126f8829
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Misc / MemoryProtection.c
1 /** @file
2 UEFI Memory Protection support.
3
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.
6
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
10 requirement.
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.
14
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.
19
20 Once the image is unloaded, the protection is removed automatically.
21
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
27
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.
30
31 **/
32
33 #include <PiDxe.h>
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>
41
42 #include <Guid/EventGroup.h>
43 #include <Guid/MemoryAttributesTable.h>
44 #include <Guid/PropertiesTable.h>
45
46 #include <Protocol/FirmwareVolume2.h>
47 #include <Protocol/BlockIo.h>
48 #include <Protocol/SimpleFileSystem.h>
49
50 #include "DxeMain.h"
51 #include "Mem/HeapGuard.h"
52
53 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)
54 #define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)
55
56 //
57 // Image type definitions
58 //
59 #define IMAGE_UNKNOWN 0x00000001
60 #define IMAGE_FROM_FV 0x00000002
61
62 //
63 // Protection policy bit definition
64 //
65 #define DO_NOT_PROTECT 0x00000000
66 #define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001
67
68 #define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
69 #define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
70
71 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
72 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
73
74 UINT32 mImageProtectionPolicy;
75
76 extern LIST_ENTRY mGcdMemorySpaceMap;
77
78 STATIC LIST_ENTRY mProtectedImageRecordList;
79
80 /**
81 Sort code section in image record, based upon CodeSegmentBase from low to high.
82
83 @param ImageRecord image record to be sorted
84 **/
85 VOID
86 SortImageRecordCodeSection (
87 IN IMAGE_PROPERTIES_RECORD *ImageRecord
88 );
89
90 /**
91 Check if code section in image record is valid.
92
93 @param ImageRecord image record to be checked
94
95 @retval TRUE image record is valid
96 @retval FALSE image record is invalid
97 **/
98 BOOLEAN
99 IsImageRecordCodeSectionValid (
100 IN IMAGE_PROPERTIES_RECORD *ImageRecord
101 );
102
103 /**
104 Get the image type.
105
106 @param[in] File This is a pointer to the device path of the file that is
107 being dispatched.
108
109 @return UINT32 Image Type
110 **/
111 UINT32
112 GetImageType (
113 IN CONST EFI_DEVICE_PATH_PROTOCOL *File
114 )
115 {
116 EFI_STATUS Status;
117 EFI_HANDLE DeviceHandle;
118 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
119
120 if (File == NULL) {
121 return IMAGE_UNKNOWN;
122 }
123
124 //
125 // First check to see if File is from a Firmware Volume
126 //
127 DeviceHandle = NULL;
128 TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
129 Status = gBS->LocateDevicePath (
130 &gEfiFirmwareVolume2ProtocolGuid,
131 &TempDevicePath,
132 &DeviceHandle
133 );
134 if (!EFI_ERROR (Status)) {
135 Status = gBS->OpenProtocol (
136 DeviceHandle,
137 &gEfiFirmwareVolume2ProtocolGuid,
138 NULL,
139 NULL,
140 NULL,
141 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
142 );
143 if (!EFI_ERROR (Status)) {
144 return IMAGE_FROM_FV;
145 }
146 }
147 return IMAGE_UNKNOWN;
148 }
149
150 /**
151 Get UEFI image protection policy based upon image type.
152
153 @param[in] ImageType The UEFI image type
154
155 @return UEFI image protection policy
156 **/
157 UINT32
158 GetProtectionPolicyFromImageType (
159 IN UINT32 ImageType
160 )
161 {
162 if ((ImageType & mImageProtectionPolicy) == 0) {
163 return DO_NOT_PROTECT;
164 } else {
165 return PROTECT_IF_ALIGNED_ELSE_ALLOW;
166 }
167 }
168
169 /**
170 Get UEFI image protection policy based upon loaded image device path.
171
172 @param[in] LoadedImage The loaded image protocol
173 @param[in] LoadedImageDevicePath The loaded image device path protocol
174
175 @return UEFI image protection policy
176 **/
177 UINT32
178 GetUefiImageProtectionPolicy (
179 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
180 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
181 )
182 {
183 BOOLEAN InSmm;
184 UINT32 ImageType;
185 UINT32 ProtectionPolicy;
186
187 //
188 // Check SMM
189 //
190 InSmm = FALSE;
191 if (gSmmBase2 != NULL) {
192 gSmmBase2->InSmm (gSmmBase2, &InSmm);
193 }
194 if (InSmm) {
195 return FALSE;
196 }
197
198 //
199 // Check DevicePath
200 //
201 if (LoadedImage == gDxeCoreLoadedImage) {
202 ImageType = IMAGE_FROM_FV;
203 } else {
204 ImageType = GetImageType (LoadedImageDevicePath);
205 }
206 ProtectionPolicy = GetProtectionPolicyFromImageType (ImageType);
207 return ProtectionPolicy;
208 }
209
210
211 /**
212 Set UEFI image memory attributes.
213
214 @param[in] BaseAddress Specified start address
215 @param[in] Length Specified length
216 @param[in] Attributes Specified attributes
217 **/
218 VOID
219 SetUefiImageMemoryAttributes (
220 IN UINT64 BaseAddress,
221 IN UINT64 Length,
222 IN UINT64 Attributes
223 )
224 {
225 EFI_STATUS Status;
226 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
227 UINT64 FinalAttributes;
228
229 Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor);
230 ASSERT_EFI_ERROR(Status);
231
232 FinalAttributes = (Descriptor.Attributes & CACHE_ATTRIBUTE_MASK) | (Attributes & MEMORY_ATTRIBUTE_MASK);
233
234 DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes));
235
236 ASSERT(gCpu != NULL);
237 gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
238 }
239
240 /**
241 Set UEFI image protection attributes.
242
243 @param[in] ImageRecord A UEFI image record
244 **/
245 VOID
246 SetUefiImageProtectionAttributes (
247 IN IMAGE_PROPERTIES_RECORD *ImageRecord
248 )
249 {
250 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
251 LIST_ENTRY *ImageRecordCodeSectionLink;
252 LIST_ENTRY *ImageRecordCodeSectionEndLink;
253 LIST_ENTRY *ImageRecordCodeSectionList;
254 UINT64 CurrentBase;
255 UINT64 ImageEnd;
256
257 ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;
258
259 CurrentBase = ImageRecord->ImageBase;
260 ImageEnd = ImageRecord->ImageBase + ImageRecord->ImageSize;
261
262 ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink;
263 ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList;
264 while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) {
265 ImageRecordCodeSection = CR (
266 ImageRecordCodeSectionLink,
267 IMAGE_PROPERTIES_RECORD_CODE_SECTION,
268 Link,
269 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
270 );
271 ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink;
272
273 ASSERT (CurrentBase <= ImageRecordCodeSection->CodeSegmentBase);
274 if (CurrentBase < ImageRecordCodeSection->CodeSegmentBase) {
275 //
276 // DATA
277 //
278 SetUefiImageMemoryAttributes (
279 CurrentBase,
280 ImageRecordCodeSection->CodeSegmentBase - CurrentBase,
281 EFI_MEMORY_XP
282 );
283 }
284 //
285 // CODE
286 //
287 SetUefiImageMemoryAttributes (
288 ImageRecordCodeSection->CodeSegmentBase,
289 ImageRecordCodeSection->CodeSegmentSize,
290 EFI_MEMORY_RO
291 );
292 CurrentBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize;
293 }
294 //
295 // Last DATA
296 //
297 ASSERT (CurrentBase <= ImageEnd);
298 if (CurrentBase < ImageEnd) {
299 //
300 // DATA
301 //
302 SetUefiImageMemoryAttributes (
303 CurrentBase,
304 ImageEnd - CurrentBase,
305 EFI_MEMORY_XP
306 );
307 }
308 return ;
309 }
310
311 /**
312 Return if the PE image section is aligned.
313
314 @param[in] SectionAlignment PE/COFF section alignment
315 @param[in] MemoryType PE/COFF image memory type
316
317 @retval TRUE The PE image section is aligned.
318 @retval FALSE The PE image section is not aligned.
319 **/
320 BOOLEAN
321 IsMemoryProtectionSectionAligned (
322 IN UINT32 SectionAlignment,
323 IN EFI_MEMORY_TYPE MemoryType
324 )
325 {
326 UINT32 PageAlignment;
327
328 switch (MemoryType) {
329 case EfiRuntimeServicesCode:
330 case EfiACPIMemoryNVS:
331 PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
332 break;
333 case EfiRuntimeServicesData:
334 case EfiACPIReclaimMemory:
335 ASSERT (FALSE);
336 PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
337 break;
338 case EfiBootServicesCode:
339 case EfiLoaderCode:
340 case EfiReservedMemoryType:
341 PageAlignment = EFI_PAGE_SIZE;
342 break;
343 default:
344 ASSERT (FALSE);
345 PageAlignment = EFI_PAGE_SIZE;
346 break;
347 }
348
349 if ((SectionAlignment & (PageAlignment - 1)) != 0) {
350 return FALSE;
351 } else {
352 return TRUE;
353 }
354 }
355
356 /**
357 Free Image record.
358
359 @param[in] ImageRecord A UEFI image record
360 **/
361 VOID
362 FreeImageRecord (
363 IN IMAGE_PROPERTIES_RECORD *ImageRecord
364 )
365 {
366 LIST_ENTRY *CodeSegmentListHead;
367 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
368
369 CodeSegmentListHead = &ImageRecord->CodeSegmentList;
370 while (!IsListEmpty (CodeSegmentListHead)) {
371 ImageRecordCodeSection = CR (
372 CodeSegmentListHead->ForwardLink,
373 IMAGE_PROPERTIES_RECORD_CODE_SECTION,
374 Link,
375 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
376 );
377 RemoveEntryList (&ImageRecordCodeSection->Link);
378 FreePool (ImageRecordCodeSection);
379 }
380
381 if (ImageRecord->Link.ForwardLink != NULL) {
382 RemoveEntryList (&ImageRecord->Link);
383 }
384 FreePool (ImageRecord);
385 }
386
387 /**
388 Protect UEFI PE/COFF image.
389
390 @param[in] LoadedImage The loaded image protocol
391 @param[in] LoadedImageDevicePath The loaded image device path protocol
392 **/
393 VOID
394 ProtectUefiImage (
395 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
396 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
397 )
398 {
399 VOID *ImageAddress;
400 EFI_IMAGE_DOS_HEADER *DosHdr;
401 UINT32 PeCoffHeaderOffset;
402 UINT32 SectionAlignment;
403 EFI_IMAGE_SECTION_HEADER *Section;
404 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
405 UINT8 *Name;
406 UINTN Index;
407 IMAGE_PROPERTIES_RECORD *ImageRecord;
408 CHAR8 *PdbPointer;
409 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
410 UINT16 Magic;
411 BOOLEAN IsAligned;
412 UINT32 ProtectionPolicy;
413
414 DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
415 DEBUG ((DEBUG_INFO, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
416
417 if (gCpu == NULL) {
418 return ;
419 }
420
421 ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
422 switch (ProtectionPolicy) {
423 case DO_NOT_PROTECT:
424 return ;
425 case PROTECT_IF_ALIGNED_ELSE_ALLOW:
426 break;
427 default:
428 ASSERT(FALSE);
429 return ;
430 }
431
432 ImageRecord = AllocateZeroPool (sizeof(*ImageRecord));
433 if (ImageRecord == NULL) {
434 return ;
435 }
436 ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE;
437
438 //
439 // Step 1: record whole region
440 //
441 ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;
442 ImageRecord->ImageSize = LoadedImage->ImageSize;
443
444 ImageAddress = LoadedImage->ImageBase;
445
446 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
447 if (PdbPointer != NULL) {
448 DEBUG ((DEBUG_VERBOSE, " Image - %a\n", PdbPointer));
449 }
450
451 //
452 // Check PE/COFF image
453 //
454 DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress;
455 PeCoffHeaderOffset = 0;
456 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
457 PeCoffHeaderOffset = DosHdr->e_lfanew;
458 }
459
460 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset);
461 if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
462 DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
463 // It might be image in SMM.
464 goto Finish;
465 }
466
467 //
468 // Get SectionAlignment
469 //
470 if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
471 //
472 // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
473 // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
474 // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
475 // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
476 //
477 Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
478 } else {
479 //
480 // Get the magic value from the PE/COFF Optional Header
481 //
482 Magic = Hdr.Pe32->OptionalHeader.Magic;
483 }
484 if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
485 SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
486 } else {
487 SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
488 }
489
490 IsAligned = IsMemoryProtectionSectionAligned (SectionAlignment, LoadedImage->ImageCodeType);
491 if (!IsAligned) {
492 DEBUG ((DEBUG_VERBOSE, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
493 SectionAlignment));
494 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
495 if (PdbPointer != NULL) {
496 DEBUG ((DEBUG_VERBOSE, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));
497 }
498 goto Finish;
499 }
500
501 Section = (EFI_IMAGE_SECTION_HEADER *) (
502 (UINT8 *) (UINTN) ImageAddress +
503 PeCoffHeaderOffset +
504 sizeof(UINT32) +
505 sizeof(EFI_IMAGE_FILE_HEADER) +
506 Hdr.Pe32->FileHeader.SizeOfOptionalHeader
507 );
508 ImageRecord->CodeSegmentCount = 0;
509 InitializeListHead (&ImageRecord->CodeSegmentList);
510 for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
511 Name = Section[Index].Name;
512 DEBUG ((
513 DEBUG_VERBOSE,
514 " Section - '%c%c%c%c%c%c%c%c'\n",
515 Name[0],
516 Name[1],
517 Name[2],
518 Name[3],
519 Name[4],
520 Name[5],
521 Name[6],
522 Name[7]
523 ));
524
525 //
526 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
527 // can always be mapped read-only, classify a section as a code section only
528 // if it has the executable attribute set and the writable attribute cleared.
529 //
530 // This adheres more closely to the PE/COFF spec, and avoids issues with
531 // Linux OS loaders that may consist of a single read/write/execute section.
532 //
533 if ((Section[Index].Characteristics & (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) {
534 DEBUG ((DEBUG_VERBOSE, " VirtualSize - 0x%08x\n", Section[Index].Misc.VirtualSize));
535 DEBUG ((DEBUG_VERBOSE, " VirtualAddress - 0x%08x\n", Section[Index].VirtualAddress));
536 DEBUG ((DEBUG_VERBOSE, " SizeOfRawData - 0x%08x\n", Section[Index].SizeOfRawData));
537 DEBUG ((DEBUG_VERBOSE, " PointerToRawData - 0x%08x\n", Section[Index].PointerToRawData));
538 DEBUG ((DEBUG_VERBOSE, " PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations));
539 DEBUG ((DEBUG_VERBOSE, " PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers));
540 DEBUG ((DEBUG_VERBOSE, " NumberOfRelocations - 0x%08x\n", Section[Index].NumberOfRelocations));
541 DEBUG ((DEBUG_VERBOSE, " NumberOfLinenumbers - 0x%08x\n", Section[Index].NumberOfLinenumbers));
542 DEBUG ((DEBUG_VERBOSE, " Characteristics - 0x%08x\n", Section[Index].Characteristics));
543
544 //
545 // Step 2: record code section
546 //
547 ImageRecordCodeSection = AllocatePool (sizeof(*ImageRecordCodeSection));
548 if (ImageRecordCodeSection == NULL) {
549 return ;
550 }
551 ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE;
552
553 ImageRecordCodeSection->CodeSegmentBase = (UINTN)ImageAddress + Section[Index].VirtualAddress;
554 ImageRecordCodeSection->CodeSegmentSize = ALIGN_VALUE(Section[Index].SizeOfRawData, SectionAlignment);
555
556 DEBUG ((DEBUG_VERBOSE, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection->CodeSegmentBase, ImageRecordCodeSection->CodeSegmentSize));
557
558 InsertTailList (&ImageRecord->CodeSegmentList, &ImageRecordCodeSection->Link);
559 ImageRecord->CodeSegmentCount++;
560 }
561 }
562
563 if (ImageRecord->CodeSegmentCount == 0) {
564 //
565 // If a UEFI executable consists of a single read+write+exec PE/COFF
566 // section, that isn't actually an error. The image can be launched
567 // alright, only image protection cannot be applied to it fully.
568 //
569 // One example that elicits this is (some) Linux kernels (with the EFI stub
570 // of course).
571 //
572 DEBUG ((DEBUG_WARN, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
573 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
574 if (PdbPointer != NULL) {
575 DEBUG ((DEBUG_WARN, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));
576 }
577 goto Finish;
578 }
579
580 //
581 // Final
582 //
583 SortImageRecordCodeSection (ImageRecord);
584 //
585 // Check overlap all section in ImageBase/Size
586 //
587 if (!IsImageRecordCodeSectionValid (ImageRecord)) {
588 DEBUG ((DEBUG_ERROR, "IsImageRecordCodeSectionValid - FAIL\n"));
589 goto Finish;
590 }
591
592 //
593 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
594 // Given that the loader always allocates full pages, we know the space after the image is not used.
595 //
596 ImageRecord->ImageSize = ALIGN_VALUE(LoadedImage->ImageSize, EFI_PAGE_SIZE);
597
598 //
599 // CPU ARCH present. Update memory attribute directly.
600 //
601 SetUefiImageProtectionAttributes (ImageRecord);
602
603 //
604 // Record the image record in the list so we can undo the protections later
605 //
606 InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);
607
608 Finish:
609 return ;
610 }
611
612 /**
613 Unprotect UEFI image.
614
615 @param[in] LoadedImage The loaded image protocol
616 @param[in] LoadedImageDevicePath The loaded image device path protocol
617 **/
618 VOID
619 UnprotectUefiImage (
620 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
621 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
622 )
623 {
624 IMAGE_PROPERTIES_RECORD *ImageRecord;
625 LIST_ENTRY *ImageRecordLink;
626
627 if (PcdGet32(PcdImageProtectionPolicy) != 0) {
628 for (ImageRecordLink = mProtectedImageRecordList.ForwardLink;
629 ImageRecordLink != &mProtectedImageRecordList;
630 ImageRecordLink = ImageRecordLink->ForwardLink) {
631 ImageRecord = CR (
632 ImageRecordLink,
633 IMAGE_PROPERTIES_RECORD,
634 Link,
635 IMAGE_PROPERTIES_RECORD_SIGNATURE
636 );
637
638 if (ImageRecord->ImageBase == (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase) {
639 SetUefiImageMemoryAttributes (ImageRecord->ImageBase,
640 ImageRecord->ImageSize,
641 0);
642 FreeImageRecord (ImageRecord);
643 return;
644 }
645 }
646 }
647 }
648
649 /**
650 Return the EFI memory permission attribute associated with memory
651 type 'MemoryType' under the configured DXE memory protection policy.
652
653 @param MemoryType Memory type.
654 **/
655 STATIC
656 UINT64
657 GetPermissionAttributeForMemoryType (
658 IN EFI_MEMORY_TYPE MemoryType
659 )
660 {
661 UINT64 TestBit;
662
663 if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
664 TestBit = BIT63;
665 } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
666 TestBit = BIT62;
667 } else {
668 TestBit = LShiftU64 (1, MemoryType);
669 }
670
671 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {
672 return EFI_MEMORY_XP;
673 } else {
674 return 0;
675 }
676 }
677
678 /**
679 Sort memory map entries based upon PhysicalStart, from low to high.
680
681 @param MemoryMap A pointer to the buffer in which firmware places
682 the current memory map.
683 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
684 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
685 **/
686 STATIC
687 VOID
688 SortMemoryMap (
689 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
690 IN UINTN MemoryMapSize,
691 IN UINTN DescriptorSize
692 )
693 {
694 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
695 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
696 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
697 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
698
699 MemoryMapEntry = MemoryMap;
700 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
701 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
702 while (MemoryMapEntry < MemoryMapEnd) {
703 while (NextMemoryMapEntry < MemoryMapEnd) {
704 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
705 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
706 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
707 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
708 }
709
710 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
711 }
712
713 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
714 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
715 }
716 }
717
718 /**
719 Merge adjacent memory map entries if they use the same memory protection policy
720
721 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
722 the current memory map.
723 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
724 MemoryMap buffer. On input, this is the size of
725 the current memory map. On output,
726 it is the size of new memory map after merge.
727 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
728 **/
729 STATIC
730 VOID
731 MergeMemoryMapForProtectionPolicy (
732 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
733 IN OUT UINTN *MemoryMapSize,
734 IN UINTN DescriptorSize
735 )
736 {
737 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
738 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
739 UINT64 MemoryBlockLength;
740 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
741 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
742 UINT64 Attributes;
743
744 SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);
745
746 MemoryMapEntry = MemoryMap;
747 NewMemoryMapEntry = MemoryMap;
748 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
749 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
750 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
751 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
752
753 do {
754 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
755 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
756
757 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
758 Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&
759 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
760 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
761 if (NewMemoryMapEntry != MemoryMapEntry) {
762 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
763 }
764
765 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
766 continue;
767 } else {
768 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
769 break;
770 }
771 } while (TRUE);
772
773 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
774 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
775 }
776
777 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
778
779 return ;
780 }
781
782
783 /**
784 Remove exec permissions from all regions whose type is identified by
785 PcdDxeNxMemoryProtectionPolicy.
786 **/
787 STATIC
788 VOID
789 InitializeDxeNxMemoryProtectionPolicy (
790 VOID
791 )
792 {
793 UINTN MemoryMapSize;
794 UINTN MapKey;
795 UINTN DescriptorSize;
796 UINT32 DescriptorVersion;
797 EFI_MEMORY_DESCRIPTOR *MemoryMap;
798 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
799 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
800 EFI_STATUS Status;
801 UINT64 Attributes;
802 LIST_ENTRY *Link;
803 EFI_GCD_MAP_ENTRY *Entry;
804 EFI_PEI_HOB_POINTERS Hob;
805 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
806 EFI_PHYSICAL_ADDRESS StackBase;
807
808 //
809 // Get the EFI memory map.
810 //
811 MemoryMapSize = 0;
812 MemoryMap = NULL;
813
814 Status = gBS->GetMemoryMap (
815 &MemoryMapSize,
816 MemoryMap,
817 &MapKey,
818 &DescriptorSize,
819 &DescriptorVersion
820 );
821 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
822 do {
823 MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
824 ASSERT (MemoryMap != NULL);
825 Status = gBS->GetMemoryMap (
826 &MemoryMapSize,
827 MemoryMap,
828 &MapKey,
829 &DescriptorSize,
830 &DescriptorVersion
831 );
832 if (EFI_ERROR (Status)) {
833 FreePool (MemoryMap);
834 }
835 } while (Status == EFI_BUFFER_TOO_SMALL);
836 ASSERT_EFI_ERROR (Status);
837
838 StackBase = 0;
839 if (PcdGetBool (PcdCpuStackGuard)) {
840 //
841 // Get the base of stack from Hob.
842 //
843 Hob.Raw = GetHobList ();
844 while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
845 MemoryHob = Hob.MemoryAllocation;
846 if (CompareGuid(&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {
847 DEBUG ((
848 DEBUG_INFO,
849 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",
850 __FUNCTION__,
851 MemoryHob->AllocDescriptor.MemoryBaseAddress,
852 MemoryHob->AllocDescriptor.MemoryLength
853 ));
854
855 StackBase = MemoryHob->AllocDescriptor.MemoryBaseAddress;
856 //
857 // Ensure the base of the stack is page-size aligned.
858 //
859 ASSERT ((StackBase & EFI_PAGE_MASK) == 0);
860 break;
861 }
862 Hob.Raw = GET_NEXT_HOB (Hob);
863 }
864
865 //
866 // Ensure the base of stack can be found from Hob when stack guard is
867 // enabled.
868 //
869 ASSERT (StackBase != 0);
870 }
871
872 DEBUG ((
873 DEBUG_INFO,
874 "%a: applying strict permissions to active memory regions\n",
875 __FUNCTION__
876 ));
877
878 MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);
879
880 MemoryMapEntry = MemoryMap;
881 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
882 while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
883
884 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
885 if (Attributes != 0) {
886 SetUefiImageMemoryAttributes (
887 MemoryMapEntry->PhysicalStart,
888 LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),
889 Attributes);
890
891 //
892 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is
893 // enabled.
894 //
895 if (MemoryMapEntry->PhysicalStart == 0 &&
896 PcdGet8 (PcdNullPointerDetectionPropertyMask) != 0) {
897
898 ASSERT (MemoryMapEntry->NumberOfPages > 0);
899 SetUefiImageMemoryAttributes (
900 0,
901 EFI_PAGES_TO_SIZE (1),
902 EFI_MEMORY_RP | Attributes);
903 }
904
905 //
906 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack
907 // guard is enabled.
908 //
909 if (StackBase != 0 &&
910 (StackBase >= MemoryMapEntry->PhysicalStart &&
911 StackBase < MemoryMapEntry->PhysicalStart +
912 LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT)) &&
913 PcdGetBool (PcdCpuStackGuard)) {
914
915 SetUefiImageMemoryAttributes (
916 StackBase,
917 EFI_PAGES_TO_SIZE (1),
918 EFI_MEMORY_RP | Attributes);
919 }
920
921 }
922 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
923 }
924 FreePool (MemoryMap);
925
926 //
927 // Apply the policy for RAM regions that we know are present and
928 // accessible, but have not been added to the UEFI memory map (yet).
929 //
930 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {
931 DEBUG ((
932 DEBUG_INFO,
933 "%a: applying strict permissions to inactive memory regions\n",
934 __FUNCTION__
935 ));
936
937 CoreAcquireGcdMemoryLock ();
938
939 Link = mGcdMemorySpaceMap.ForwardLink;
940 while (Link != &mGcdMemorySpaceMap) {
941
942 Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
943
944 if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&
945 Entry->EndAddress < MAX_ADDRESS &&
946 (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
947 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {
948
949 Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |
950 (Entry->Attributes & CACHE_ATTRIBUTE_MASK);
951
952 DEBUG ((DEBUG_INFO,
953 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
954 Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,
955 Attributes));
956
957 ASSERT(gCpu != NULL);
958 gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,
959 Entry->EndAddress - Entry->BaseAddress + 1, Attributes);
960 }
961
962 Link = Link->ForwardLink;
963 }
964 CoreReleaseGcdMemoryLock ();
965 }
966 }
967
968
969 /**
970 A notification for CPU_ARCH protocol.
971
972 @param[in] Event Event whose notification function is being invoked.
973 @param[in] Context Pointer to the notification function's context,
974 which is implementation-dependent.
975
976 **/
977 VOID
978 EFIAPI
979 MemoryProtectionCpuArchProtocolNotify (
980 IN EFI_EVENT Event,
981 IN VOID *Context
982 )
983 {
984 EFI_STATUS Status;
985 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
986 EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath;
987 UINTN NoHandles;
988 EFI_HANDLE *HandleBuffer;
989 UINTN Index;
990
991 DEBUG ((DEBUG_INFO, "MemoryProtectionCpuArchProtocolNotify:\n"));
992 Status = CoreLocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
993 if (EFI_ERROR (Status)) {
994 return;
995 }
996
997 //
998 // Apply the memory protection policy on non-BScode/RTcode regions.
999 //
1000 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
1001 InitializeDxeNxMemoryProtectionPolicy ();
1002 }
1003
1004 if (mImageProtectionPolicy == 0) {
1005 return;
1006 }
1007
1008 Status = gBS->LocateHandleBuffer (
1009 ByProtocol,
1010 &gEfiLoadedImageProtocolGuid,
1011 NULL,
1012 &NoHandles,
1013 &HandleBuffer
1014 );
1015 if (EFI_ERROR (Status) && (NoHandles == 0)) {
1016 return ;
1017 }
1018
1019 for (Index = 0; Index < NoHandles; Index++) {
1020 Status = gBS->HandleProtocol (
1021 HandleBuffer[Index],
1022 &gEfiLoadedImageProtocolGuid,
1023 (VOID **)&LoadedImage
1024 );
1025 if (EFI_ERROR(Status)) {
1026 continue;
1027 }
1028 Status = gBS->HandleProtocol (
1029 HandleBuffer[Index],
1030 &gEfiLoadedImageDevicePathProtocolGuid,
1031 (VOID **)&LoadedImageDevicePath
1032 );
1033 if (EFI_ERROR(Status)) {
1034 LoadedImageDevicePath = NULL;
1035 }
1036
1037 ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
1038 }
1039
1040 CoreCloseEvent (Event);
1041 return;
1042 }
1043
1044 /**
1045 ExitBootServices Callback function for memory protection.
1046 **/
1047 VOID
1048 MemoryProtectionExitBootServicesCallback (
1049 VOID
1050 )
1051 {
1052 EFI_RUNTIME_IMAGE_ENTRY *RuntimeImage;
1053 LIST_ENTRY *Link;
1054
1055 //
1056 // We need remove the RT protection, because RT relocation need write code segment
1057 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
1058 //
1059 // Firmware does not own page tables after ExitBootServices(), so the OS would
1060 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
1061 // delay setting protections on RT code pages until after SetVirtualAddressMap().
1062 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
1063 //
1064 if (mImageProtectionPolicy != 0) {
1065 for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {
1066 RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
1067 SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE(RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);
1068 }
1069 }
1070 }
1071
1072 /**
1073 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
1074 order to skip unfixable NULL pointer access issues detected in OptionROM or
1075 boot loaders.
1076
1077 @param[in] Event The Event this notify function registered to.
1078 @param[in] Context Pointer to the context data registered to the Event.
1079 **/
1080 VOID
1081 EFIAPI
1082 DisableNullDetectionAtTheEndOfDxe (
1083 EFI_EVENT Event,
1084 VOID *Context
1085 )
1086 {
1087 EFI_STATUS Status;
1088 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc;
1089
1090 DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
1091 //
1092 // Disable NULL pointer detection by enabling first 4K page
1093 //
1094 Status = CoreGetMemorySpaceDescriptor (0, &Desc);
1095 ASSERT_EFI_ERROR (Status);
1096
1097 if ((Desc.Capabilities & EFI_MEMORY_RP) == 0) {
1098 Status = CoreSetMemorySpaceCapabilities (
1099 0,
1100 EFI_PAGE_SIZE,
1101 Desc.Capabilities | EFI_MEMORY_RP
1102 );
1103 ASSERT_EFI_ERROR (Status);
1104 }
1105
1106 Status = CoreSetMemorySpaceAttributes (
1107 0,
1108 EFI_PAGE_SIZE,
1109 Desc.Attributes & ~EFI_MEMORY_RP
1110 );
1111 ASSERT_EFI_ERROR (Status);
1112
1113 CoreCloseEvent (Event);
1114 DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
1115
1116 return;
1117 }
1118
1119 /**
1120 Initialize Memory Protection support.
1121 **/
1122 VOID
1123 EFIAPI
1124 CoreInitializeMemoryProtection (
1125 VOID
1126 )
1127 {
1128 EFI_STATUS Status;
1129 EFI_EVENT Event;
1130 EFI_EVENT EndOfDxeEvent;
1131 VOID *Registration;
1132
1133 mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);
1134
1135 InitializeListHead (&mProtectedImageRecordList);
1136
1137 //
1138 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1139 // - code regions should have no EFI_MEMORY_XP attribute
1140 // - EfiConventionalMemory and EfiBootServicesData should use the
1141 // same attribute
1142 //
1143 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);
1144 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);
1145 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);
1146 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
1147 GetPermissionAttributeForMemoryType (EfiConventionalMemory));
1148
1149 if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
1150 Status = CoreCreateEvent (
1151 EVT_NOTIFY_SIGNAL,
1152 TPL_CALLBACK,
1153 MemoryProtectionCpuArchProtocolNotify,
1154 NULL,
1155 &Event
1156 );
1157 ASSERT_EFI_ERROR(Status);
1158
1159 //
1160 // Register for protocol notifactions on this event
1161 //
1162 Status = CoreRegisterProtocolNotify (
1163 &gEfiCpuArchProtocolGuid,
1164 Event,
1165 &Registration
1166 );
1167 ASSERT_EFI_ERROR(Status);
1168 }
1169
1170 //
1171 // Register a callback to disable NULL pointer detection at EndOfDxe
1172 //
1173 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7))
1174 == (BIT0|BIT7)) {
1175 Status = CoreCreateEventEx (
1176 EVT_NOTIFY_SIGNAL,
1177 TPL_NOTIFY,
1178 DisableNullDetectionAtTheEndOfDxe,
1179 NULL,
1180 &gEfiEndOfDxeEventGroupGuid,
1181 &EndOfDxeEvent
1182 );
1183 ASSERT_EFI_ERROR (Status);
1184 }
1185
1186 return ;
1187 }
1188
1189 /**
1190 Returns whether we are currently executing in SMM mode.
1191 **/
1192 STATIC
1193 BOOLEAN
1194 IsInSmm (
1195 VOID
1196 )
1197 {
1198 BOOLEAN InSmm;
1199
1200 InSmm = FALSE;
1201 if (gSmmBase2 != NULL) {
1202 gSmmBase2->InSmm (gSmmBase2, &InSmm);
1203 }
1204 return InSmm;
1205 }
1206
1207 /**
1208 Manage memory permission attributes on a memory range, according to the
1209 configured DXE memory protection policy.
1210
1211 @param OldType The old memory type of the range
1212 @param NewType The new memory type of the range
1213 @param Memory The base address of the range
1214 @param Length The size of the range (in bytes)
1215
1216 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1217 are updated in this case
1218 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1219 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1220 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1221 @return other Return value of gCpu->SetMemoryAttributes()
1222
1223 **/
1224 EFI_STATUS
1225 EFIAPI
1226 ApplyMemoryProtectionPolicy (
1227 IN EFI_MEMORY_TYPE OldType,
1228 IN EFI_MEMORY_TYPE NewType,
1229 IN EFI_PHYSICAL_ADDRESS Memory,
1230 IN UINT64 Length
1231 )
1232 {
1233 UINT64 OldAttributes;
1234 UINT64 NewAttributes;
1235
1236 //
1237 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1238 // does not apply to allocations performed in SMM mode.
1239 //
1240 if (IsInSmm ()) {
1241 return EFI_SUCCESS;
1242 }
1243
1244 //
1245 // If the CPU arch protocol is not installed yet, we cannot manage memory
1246 // permission attributes, and it is the job of the driver that installs this
1247 // protocol to set the permissions on existing allocations.
1248 //
1249 if (gCpu == NULL) {
1250 return EFI_SUCCESS;
1251 }
1252
1253 //
1254 // Check if a DXE memory protection policy has been configured
1255 //
1256 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {
1257 return EFI_SUCCESS;
1258 }
1259
1260 //
1261 // Don't overwrite Guard pages, which should be the first and/or last page,
1262 // if any.
1263 //
1264 if (IsHeapGuardEnabled ()) {
1265 if (IsGuardPage (Memory)) {
1266 Memory += EFI_PAGE_SIZE;
1267 Length -= EFI_PAGE_SIZE;
1268 if (Length == 0) {
1269 return EFI_SUCCESS;
1270 }
1271 }
1272
1273 if (IsGuardPage (Memory + Length - EFI_PAGE_SIZE)) {
1274 Length -= EFI_PAGE_SIZE;
1275 if (Length == 0) {
1276 return EFI_SUCCESS;
1277 }
1278 }
1279 }
1280
1281 //
1282 // Update the executable permissions according to the DXE memory
1283 // protection policy, but only if
1284 // - the policy is different between the old and the new type, or
1285 // - this is a newly added region (OldType == EfiMaxMemoryType)
1286 //
1287 NewAttributes = GetPermissionAttributeForMemoryType (NewType);
1288
1289 if (OldType != EfiMaxMemoryType) {
1290 OldAttributes = GetPermissionAttributeForMemoryType (OldType);
1291 if (OldAttributes == NewAttributes) {
1292 // policy is the same between OldType and NewType
1293 return EFI_SUCCESS;
1294 }
1295 } else if (NewAttributes == 0) {
1296 // newly added region of a type that does not require protection
1297 return EFI_SUCCESS;
1298 }
1299
1300 return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
1301 }