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
GetImageType(IN CONST EFI_DEVICE_PATH_PROTOCOL * File)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
GetProtectionPolicyFromImageType(IN UINT32 ImageType)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
GetUefiImageProtectionPolicy(IN EFI_LOADED_IMAGE_PROTOCOL * LoadedImage,IN EFI_DEVICE_PATH_PROTOCOL * LoadedImageDevicePath)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
SetUefiImageMemoryAttributes(IN UINT64 BaseAddress,IN UINT64 Length,IN UINT64 Attributes)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
SetUefiImageProtectionAttributes(IN IMAGE_PROPERTIES_RECORD * ImageRecord)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
IsMemoryProtectionSectionAligned(IN UINT32 SectionAlignment,IN EFI_MEMORY_TYPE MemoryType)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
FreeImageRecord(IN IMAGE_PROPERTIES_RECORD * ImageRecord)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
ProtectUefiImage(IN EFI_LOADED_IMAGE_PROTOCOL * LoadedImage,IN EFI_DEVICE_PATH_PROTOCOL * LoadedImageDevicePath)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
UnprotectUefiImage(IN EFI_LOADED_IMAGE_PROTOCOL * LoadedImage,IN EFI_DEVICE_PATH_PROTOCOL * LoadedImageDevicePath)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
GetPermissionAttributeForMemoryType(IN EFI_MEMORY_TYPE MemoryType)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
SortMemoryMap(IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,IN UINTN MemoryMapSize,IN UINTN DescriptorSize)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
MergeMemoryMapForProtectionPolicy(IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,IN OUT UINTN * MemoryMapSize,IN UINTN DescriptorSize)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
InitializeDxeNxMemoryProtectionPolicy(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
MemoryProtectionCpuArchProtocolNotify(IN EFI_EVENT Event,IN VOID * Context)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
MemoryProtectionExitBootServicesCallback(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
DisableNullDetectionAtTheEndOfDxe(EFI_EVENT Event,VOID * Context)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
CoreInitializeMemoryProtection(VOID)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
IsInSmm(VOID)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
ApplyMemoryProtectionPolicy(IN EFI_MEMORY_TYPE OldType,IN EFI_MEMORY_TYPE NewType,IN EFI_PHYSICAL_ADDRESS Memory,IN UINT64 Length)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 }
1285