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