]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
MdeModulePkg/Core: allow HeapGuard even before CpuArchProtocol installed
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Misc / MemoryProtection.c
index c36612a1b1f2c1b4f49fe26dfb3501db4aaaa9b2..2f7e490af18feac5fbe9753f9678e1d0c0c30b45 100644 (file)
@@ -19,7 +19,7 @@
 \r
   Once the image is unloaded, the protection is removed automatically.\r
 \r
-Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
 which accompanies this distribution.  The full text of the license may be found at\r
@@ -48,6 +48,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Protocol/SimpleFileSystem.h>\r
 \r
 #include "DxeMain.h"\r
+#include "Mem/HeapGuard.h"\r
 \r
 #define CACHE_ATTRIBUTE_MASK   (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)\r
 #define MEMORY_ATTRIBUTE_MASK  (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)\r
@@ -64,8 +65,18 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #define DO_NOT_PROTECT                         0x00000000\r
 #define PROTECT_IF_ALIGNED_ELSE_ALLOW          0x00000001\r
 \r
+#define MEMORY_TYPE_OS_RESERVED_MIN            0x80000000\r
+#define MEMORY_TYPE_OEM_RESERVED_MIN           0x70000000\r
+\r
+#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
+  ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
+\r
 UINT32   mImageProtectionPolicy;\r
 \r
+extern LIST_ENTRY         mGcdMemorySpaceMap;\r
+\r
+STATIC LIST_ENTRY         mProtectedImageRecordList;\r
+\r
 /**\r
   Sort code section in image record, based upon CodeSegmentBase from low to high.\r
 \r
@@ -230,13 +241,10 @@ SetUefiImageMemoryAttributes (
   Set UEFI image protection attributes.\r
 \r
   @param[in]  ImageRecord    A UEFI image record\r
-  @param[in]  Protect        TRUE:  Protect the UEFI image.\r
-                             FALSE: Unprotect the UEFI image.\r
 **/\r
 VOID\r
 SetUefiImageProtectionAttributes (\r
-  IN IMAGE_PROPERTIES_RECORD     *ImageRecord,\r
-  IN BOOLEAN                     Protect\r
+  IN IMAGE_PROPERTIES_RECORD     *ImageRecord\r
   )\r
 {\r
   IMAGE_PROPERTIES_RECORD_CODE_SECTION      *ImageRecordCodeSection;\r
@@ -245,7 +253,6 @@ SetUefiImageProtectionAttributes (
   LIST_ENTRY                                *ImageRecordCodeSectionList;\r
   UINT64                                    CurrentBase;\r
   UINT64                                    ImageEnd;\r
-  UINT64                                    Attribute;\r
 \r
   ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;\r
 \r
@@ -268,29 +275,19 @@ SetUefiImageProtectionAttributes (
       //\r
       // DATA\r
       //\r
-      if (Protect) {\r
-        Attribute = EFI_MEMORY_XP;\r
-      } else {\r
-        Attribute = 0;\r
-      }\r
       SetUefiImageMemoryAttributes (\r
         CurrentBase,\r
         ImageRecordCodeSection->CodeSegmentBase - CurrentBase,\r
-        Attribute\r
+        EFI_MEMORY_XP\r
         );\r
     }\r
     //\r
     // CODE\r
     //\r
-    if (Protect) {\r
-      Attribute = EFI_MEMORY_RO;\r
-    } else {\r
-      Attribute = 0;\r
-    }\r
     SetUefiImageMemoryAttributes (\r
       ImageRecordCodeSection->CodeSegmentBase,\r
       ImageRecordCodeSection->CodeSegmentSize,\r
-      Attribute\r
+      EFI_MEMORY_RO\r
       );\r
     CurrentBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize;\r
   }\r
@@ -302,15 +299,10 @@ SetUefiImageProtectionAttributes (
     //\r
     // DATA\r
     //\r
-    if (Protect) {\r
-      Attribute = EFI_MEMORY_XP;\r
-    } else {\r
-      Attribute = 0;\r
-    }\r
     SetUefiImageMemoryAttributes (\r
       CurrentBase,\r
       ImageEnd - CurrentBase,\r
-      Attribute\r
+      EFI_MEMORY_XP\r
       );\r
   }\r
   return ;\r
@@ -336,12 +328,12 @@ IsMemoryProtectionSectionAligned (
   switch (MemoryType) {\r
   case EfiRuntimeServicesCode:\r
   case EfiACPIMemoryNVS:\r
-    PageAlignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;\r
+    PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
     break;\r
   case EfiRuntimeServicesData:\r
   case EfiACPIReclaimMemory:\r
     ASSERT (FALSE);\r
-    PageAlignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;\r
+    PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
     break;\r
   case EfiBootServicesCode:\r
   case EfiLoaderCode:\r
@@ -393,18 +385,15 @@ FreeImageRecord (
 }\r
 \r
 /**\r
-  Protect or unprotect UEFI image common function.\r
+  Protect UEFI PE/COFF image.\r
 \r
   @param[in]  LoadedImage              The loaded image protocol\r
   @param[in]  LoadedImageDevicePath    The loaded image device path protocol\r
-  @param[in]  Protect                  TRUE:  Protect the UEFI image.\r
-                                       FALSE: Unprotect the UEFI image.\r
 **/\r
 VOID\r
-ProtectUefiImageCommon (\r
+ProtectUefiImage (\r
   IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,\r
-  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath,\r
-  IN BOOLEAN                     Protect\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath\r
   )\r
 {\r
   VOID                                 *ImageAddress;\r
@@ -533,7 +522,15 @@ ProtectUefiImageCommon (
       Name[7]\r
       ));\r
 \r
-    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {\r
+    //\r
+    // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE\r
+    // can always be mapped read-only, classify a section as a code section only\r
+    // if it has the executable attribute set and the writable attribute cleared.\r
+    //\r
+    // This adheres more closely to the PE/COFF spec, and avoids issues with\r
+    // Linux OS loaders that may consist of a single read/write/execute section.\r
+    //\r
+    if ((Section[Index].Characteristics & (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) {\r
       DEBUG ((DEBUG_VERBOSE, "  VirtualSize          - 0x%08x\n", Section[Index].Misc.VirtualSize));\r
       DEBUG ((DEBUG_VERBOSE, "  VirtualAddress       - 0x%08x\n", Section[Index].VirtualAddress));\r
       DEBUG ((DEBUG_VERBOSE, "  SizeOfRawData        - 0x%08x\n", Section[Index].SizeOfRawData));\r
@@ -564,10 +561,18 @@ ProtectUefiImageCommon (
   }\r
 \r
   if (ImageRecord->CodeSegmentCount == 0) {\r
-    DEBUG ((DEBUG_ERROR, "!!!!!!!!  ProtectUefiImageCommon - CodeSegmentCount is 0  !!!!!!!!\n"));\r
+    //\r
+    // If a UEFI executable consists of a single read+write+exec PE/COFF\r
+    // section, that isn't actually an error. The image can be launched\r
+    // alright, only image protection cannot be applied to it fully.\r
+    //\r
+    // One example that elicits this is (some) Linux kernels (with the EFI stub\r
+    // of course).\r
+    //\r
+    DEBUG ((DEBUG_WARN, "!!!!!!!!  ProtectUefiImageCommon - CodeSegmentCount is 0  !!!!!!!!\n"));\r
     PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);\r
     if (PdbPointer != NULL) {\r
-      DEBUG ((DEBUG_ERROR, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));\r
+      DEBUG ((DEBUG_WARN, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));\r
     }\r
     goto Finish;\r
   }\r
@@ -593,51 +598,374 @@ ProtectUefiImageCommon (
   //\r
   // CPU ARCH present. Update memory attribute directly.\r
   //\r
-  SetUefiImageProtectionAttributes (ImageRecord, Protect);\r
+  SetUefiImageProtectionAttributes (ImageRecord);\r
 \r
   //\r
-  // Clean up\r
+  // Record the image record in the list so we can undo the protections later\r
   //\r
-  FreeImageRecord (ImageRecord);\r
+  InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);\r
 \r
 Finish:\r
   return ;\r
 }\r
 \r
 /**\r
-  Protect UEFI image.\r
+  Unprotect UEFI image.\r
 \r
   @param[in]  LoadedImage              The loaded image protocol\r
   @param[in]  LoadedImageDevicePath    The loaded image device path protocol\r
 **/\r
 VOID\r
-ProtectUefiImage (\r
+UnprotectUefiImage (\r
   IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,\r
   IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath\r
   )\r
 {\r
+  IMAGE_PROPERTIES_RECORD    *ImageRecord;\r
+  LIST_ENTRY                 *ImageRecordLink;\r
+\r
   if (PcdGet32(PcdImageProtectionPolicy) != 0) {\r
-    ProtectUefiImageCommon (LoadedImage, LoadedImageDevicePath, TRUE);\r
+    for (ImageRecordLink = mProtectedImageRecordList.ForwardLink;\r
+         ImageRecordLink != &mProtectedImageRecordList;\r
+         ImageRecordLink = ImageRecordLink->ForwardLink) {\r
+      ImageRecord = CR (\r
+                      ImageRecordLink,\r
+                      IMAGE_PROPERTIES_RECORD,\r
+                      Link,\r
+                      IMAGE_PROPERTIES_RECORD_SIGNATURE\r
+                      );\r
+\r
+      if (ImageRecord->ImageBase == (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase) {\r
+        SetUefiImageMemoryAttributes (ImageRecord->ImageBase,\r
+                                      ImageRecord->ImageSize,\r
+                                      0);\r
+        FreeImageRecord (ImageRecord);\r
+        return;\r
+      }\r
+    }\r
   }\r
 }\r
 \r
 /**\r
-  Unprotect UEFI image.\r
+  Return the EFI memory permission attribute associated with memory\r
+  type 'MemoryType' under the configured DXE memory protection policy.\r
 \r
-  @param[in]  LoadedImage              The loaded image protocol\r
-  @param[in]  LoadedImageDevicePath    The loaded image device path protocol\r
+  @param MemoryType       Memory type.\r
 **/\r
+STATIC\r
+UINT64\r
+GetPermissionAttributeForMemoryType (\r
+  IN EFI_MEMORY_TYPE    MemoryType\r
+  )\r
+{\r
+  UINT64 TestBit;\r
+\r
+  if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {\r
+    TestBit = BIT63;\r
+  } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {\r
+    TestBit = BIT62;\r
+  } else {\r
+    TestBit = LShiftU64 (1, MemoryType);\r
+  }\r
+\r
+  if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {\r
+    return EFI_MEMORY_XP;\r
+  } else {\r
+    return 0;\r
+  }\r
+}\r
+\r
+/**\r
+  Sort memory map entries based upon PhysicalStart, from low to high.\r
+\r
+  @param  MemoryMap              A pointer to the buffer in which firmware places\r
+                                 the current memory map.\r
+  @param  MemoryMapSize          Size, in bytes, of the MemoryMap buffer.\r
+  @param  DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
 VOID\r
-UnprotectUefiImage (\r
-  IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,\r
-  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath\r
+SortMemoryMap (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN UINTN                      MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
   )\r
 {\r
-  if (PcdGet32(PcdImageProtectionPolicy) != 0) {\r
-    ProtectUefiImageCommon (LoadedImage, LoadedImageDevicePath, FALSE);\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  EFI_MEMORY_DESCRIPTOR       TempMemoryMap;\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
+  while (MemoryMapEntry < MemoryMapEnd) {\r
+    while (NextMemoryMapEntry < MemoryMapEnd) {\r
+      if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
+        CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+      }\r
+\r
+      NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+    }\r
+\r
+    MemoryMapEntry      = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NextMemoryMapEntry  = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  }\r
+}\r
+\r
+/**\r
+  Merge adjacent memory map entries if they use the same memory protection policy\r
+\r
+  @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places\r
+                                          the current memory map.\r
+  @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the\r
+                                          MemoryMap buffer. On input, this is the size of\r
+                                          the current memory map.  On output,\r
+                                          it is the size of new memory map after merge.\r
+  @param[in]       DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
+VOID\r
+MergeMemoryMapForProtectionPolicy (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN OUT UINTN                  *MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  UINT64                      MemoryBlockLength;\r
+  EFI_MEMORY_DESCRIPTOR       *NewMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+  UINT64                      Attributes;\r
+\r
+  SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NewMemoryMapEntry = MemoryMap;\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);\r
+  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
+    CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+    NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+\r
+    do {\r
+      MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));\r
+      Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);\r
+\r
+      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
+          Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&\r
+          ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {\r
+        MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        if (NewMemoryMapEntry != MemoryMapEntry) {\r
+          NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        }\r
+\r
+        NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        continue;\r
+      } else {\r
+        MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        break;\r
+      }\r
+    } while (TRUE);\r
+\r
+    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
+  }\r
+\r
+  *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
+\r
+  return ;\r
+}\r
+\r
+\r
+/**\r
+  Remove exec permissions from all regions whose type is identified by\r
+  PcdDxeNxMemoryProtectionPolicy.\r
+**/\r
+STATIC\r
+VOID\r
+InitializeDxeNxMemoryProtectionPolicy (\r
+  VOID\r
+  )\r
+{\r
+  UINTN                             MemoryMapSize;\r
+  UINTN                             MapKey;\r
+  UINTN                             DescriptorSize;\r
+  UINT32                            DescriptorVersion;\r
+  EFI_MEMORY_DESCRIPTOR             *MemoryMap;\r
+  EFI_MEMORY_DESCRIPTOR             *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR             *MemoryMapEnd;\r
+  EFI_STATUS                        Status;\r
+  UINT64                            Attributes;\r
+  LIST_ENTRY                        *Link;\r
+  EFI_GCD_MAP_ENTRY                 *Entry;\r
+  EFI_PEI_HOB_POINTERS              Hob;\r
+  EFI_HOB_MEMORY_ALLOCATION         *MemoryHob;\r
+  EFI_PHYSICAL_ADDRESS              StackBase;\r
+\r
+  //\r
+  // Get the EFI memory map.\r
+  //\r
+  MemoryMapSize = 0;\r
+  MemoryMap     = NULL;\r
+\r
+  Status = gBS->GetMemoryMap (\r
+                  &MemoryMapSize,\r
+                  MemoryMap,\r
+                  &MapKey,\r
+                  &DescriptorSize,\r
+                  &DescriptorVersion\r
+                  );\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+  do {\r
+    MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);\r
+    ASSERT (MemoryMap != NULL);\r
+    Status = gBS->GetMemoryMap (\r
+                    &MemoryMapSize,\r
+                    MemoryMap,\r
+                    &MapKey,\r
+                    &DescriptorSize,\r
+                    &DescriptorVersion\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (MemoryMap);\r
+    }\r
+  } while (Status == EFI_BUFFER_TOO_SMALL);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  StackBase = 0;\r
+  if (PcdGetBool (PcdCpuStackGuard)) {\r
+    //\r
+    // Get the base of stack from Hob.\r
+    //\r
+    Hob.Raw = GetHobList ();\r
+    while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {\r
+      MemoryHob = Hob.MemoryAllocation;\r
+      if (CompareGuid(&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {\r
+        DEBUG ((\r
+          DEBUG_INFO,\r
+          "%a: StackBase = 0x%016lx  StackSize = 0x%016lx\n",\r
+          __FUNCTION__,\r
+          MemoryHob->AllocDescriptor.MemoryBaseAddress,\r
+          MemoryHob->AllocDescriptor.MemoryLength\r
+          ));\r
+\r
+        StackBase = MemoryHob->AllocDescriptor.MemoryBaseAddress;\r
+        //\r
+        // Ensure the base of the stack is page-size aligned.\r
+        //\r
+        ASSERT ((StackBase & EFI_PAGE_MASK) == 0);\r
+        break;\r
+      }\r
+      Hob.Raw = GET_NEXT_HOB (Hob);\r
+    }\r
+\r
+    //\r
+    // Ensure the base of stack can be found from Hob when stack guard is\r
+    // enabled.\r
+    //\r
+    ASSERT (StackBase != 0);\r
+  }\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "%a: applying strict permissions to active memory regions\n",\r
+    __FUNCTION__\r
+    ));\r
+\r
+  MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
+  while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {\r
+\r
+    Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);\r
+    if (Attributes != 0) {\r
+      SetUefiImageMemoryAttributes (\r
+        MemoryMapEntry->PhysicalStart,\r
+        LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),\r
+        Attributes);\r
+\r
+      //\r
+      // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is\r
+      // enabled.\r
+      //\r
+      if (MemoryMapEntry->PhysicalStart == 0 &&\r
+          PcdGet8 (PcdNullPointerDetectionPropertyMask) != 0) {\r
+\r
+        ASSERT (MemoryMapEntry->NumberOfPages > 0);\r
+        SetUefiImageMemoryAttributes (\r
+          0,\r
+          EFI_PAGES_TO_SIZE (1),\r
+          EFI_MEMORY_RP | Attributes);\r
+      }\r
+\r
+      //\r
+      // Add EFI_MEMORY_RP attribute for the first page of the stack if stack\r
+      // guard is enabled.\r
+      //\r
+      if (StackBase != 0 &&\r
+          (StackBase >= MemoryMapEntry->PhysicalStart &&\r
+           StackBase <  MemoryMapEntry->PhysicalStart +\r
+                        LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT)) &&\r
+          PcdGetBool (PcdCpuStackGuard)) {\r
+\r
+        SetUefiImageMemoryAttributes (\r
+          StackBase,\r
+          EFI_PAGES_TO_SIZE (1),\r
+          EFI_MEMORY_RP | Attributes);\r
+      }\r
+\r
+    }\r
+    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  }\r
+  FreePool (MemoryMap);\r
+\r
+  //\r
+  // Apply the policy for RAM regions that we know are present and\r
+  // accessible, but have not been added to the UEFI memory map (yet).\r
+  //\r
+  if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a: applying strict permissions to inactive memory regions\n",\r
+      __FUNCTION__\r
+      ));\r
+\r
+    CoreAcquireGcdMemoryLock ();\r
+\r
+    Link = mGcdMemorySpaceMap.ForwardLink;\r
+    while (Link != &mGcdMemorySpaceMap) {\r
+\r
+      Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
+\r
+      if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
+          Entry->EndAddress < MAX_ADDRESS &&\r
+          (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
+            (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {\r
+\r
+        Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |\r
+                     (Entry->Attributes & CACHE_ATTRIBUTE_MASK);\r
+\r
+        DEBUG ((DEBUG_INFO,\r
+          "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",\r
+          Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,\r
+          Attributes));\r
+\r
+        ASSERT(gCpu != NULL);\r
+        gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,\r
+          Entry->EndAddress - Entry->BaseAddress + 1, Attributes);\r
+      }\r
+\r
+      Link = Link->ForwardLink;\r
+    }\r
+    CoreReleaseGcdMemoryLock ();\r
   }\r
 }\r
 \r
+\r
 /**\r
   A notification for CPU_ARCH protocol.\r
 \r
@@ -666,6 +994,22 @@ MemoryProtectionCpuArchProtocolNotify (
     return;\r
   }\r
 \r
+  //\r
+  // Apply the memory protection policy on non-BScode/RTcode regions.\r
+  //\r
+  if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {\r
+    InitializeDxeNxMemoryProtectionPolicy ();\r
+  }\r
+\r
+  //\r
+  // Call notify function meant for Heap Guard.\r
+  //\r
+  HeapGuardCpuArchProtocolNotify ();\r
+\r
+  if (mImageProtectionPolicy == 0) {\r
+    return;\r
+  }\r
+\r
   Status = gBS->LocateHandleBuffer (\r
                   ByProtocol,\r
                   &gEfiLoadedImageProtocolGuid,\r
@@ -730,6 +1074,53 @@ MemoryProtectionExitBootServicesCallback (
   }\r
 }\r
 \r
+/**\r
+  Disable NULL pointer detection after EndOfDxe. This is a workaround resort in\r
+  order to skip unfixable NULL pointer access issues detected in OptionROM or\r
+  boot loaders.\r
+\r
+  @param[in]  Event     The Event this notify function registered to.\r
+  @param[in]  Context   Pointer to the context data registered to the Event.\r
+**/\r
+VOID\r
+EFIAPI\r
+DisableNullDetectionAtTheEndOfDxe (\r
+  EFI_EVENT                               Event,\r
+  VOID                                    *Context\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Desc;\r
+\r
+  DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));\r
+  //\r
+  // Disable NULL pointer detection by enabling first 4K page\r
+  //\r
+  Status = CoreGetMemorySpaceDescriptor (0, &Desc);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  if ((Desc.Capabilities & EFI_MEMORY_RP) == 0) {\r
+    Status = CoreSetMemorySpaceCapabilities (\r
+              0,\r
+              EFI_PAGE_SIZE,\r
+              Desc.Capabilities | EFI_MEMORY_RP\r
+              );\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  Status = CoreSetMemorySpaceAttributes (\r
+            0,\r
+            EFI_PAGE_SIZE,\r
+            Desc.Attributes & ~EFI_MEMORY_RP\r
+            );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  CoreCloseEvent (Event);\r
+  DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));\r
+\r
+  return;\r
+}\r
+\r
 /**\r
   Initialize Memory Protection support.\r
 **/\r
@@ -741,11 +1132,26 @@ CoreInitializeMemoryProtection (
 {\r
   EFI_STATUS  Status;\r
   EFI_EVENT   Event;\r
+  EFI_EVENT   EndOfDxeEvent;\r
   VOID        *Registration;\r
 \r
   mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);\r
 \r
-  if (mImageProtectionPolicy != 0) {\r
+  InitializeListHead (&mProtectedImageRecordList);\r
+\r
+  //\r
+  // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:\r
+  // - code regions should have no EFI_MEMORY_XP attribute\r
+  // - EfiConventionalMemory and EfiBootServicesData should use the\r
+  //   same attribute\r
+  //\r
+  ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);\r
+  ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);\r
+  ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);\r
+  ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==\r
+          GetPermissionAttributeForMemoryType (EfiConventionalMemory));\r
+\r
+  if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {\r
     Status = CoreCreateEvent (\r
                EVT_NOTIFY_SIGNAL,\r
                TPL_CALLBACK,\r
@@ -765,5 +1171,136 @@ CoreInitializeMemoryProtection (
                );\r
     ASSERT_EFI_ERROR(Status);\r
   }\r
+\r
+  //\r
+  // Register a callback to disable NULL pointer detection at EndOfDxe\r
+  //\r
+  if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7))\r
+       == (BIT0|BIT7)) {\r
+    Status = CoreCreateEventEx (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    DisableNullDetectionAtTheEndOfDxe,\r
+                    NULL,\r
+                    &gEfiEndOfDxeEventGroupGuid,\r
+                    &EndOfDxeEvent\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
   return ;\r
 }\r
+\r
+/**\r
+  Returns whether we are currently executing in SMM mode.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsInSmm (\r
+  VOID\r
+  )\r
+{\r
+  BOOLEAN     InSmm;\r
+\r
+  InSmm = FALSE;\r
+  if (gSmmBase2 != NULL) {\r
+    gSmmBase2->InSmm (gSmmBase2, &InSmm);\r
+  }\r
+  return InSmm;\r
+}\r
+\r
+/**\r
+  Manage memory permission attributes on a memory range, according to the\r
+  configured DXE memory protection policy.\r
+\r
+  @param  OldType           The old memory type of the range\r
+  @param  NewType           The new memory type of the range\r
+  @param  Memory            The base address of the range\r
+  @param  Length            The size of the range (in bytes)\r
+\r
+  @return EFI_SUCCESS       If we are executing in SMM mode. No permission attributes\r
+                            are updated in this case\r
+  @return EFI_SUCCESS       If the the CPU arch protocol is not installed yet\r
+  @return EFI_SUCCESS       If no DXE memory protection policy has been configured\r
+  @return EFI_SUCCESS       If OldType and NewType use the same permission attributes\r
+  @return other             Return value of gCpu->SetMemoryAttributes()\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ApplyMemoryProtectionPolicy (\r
+  IN  EFI_MEMORY_TYPE       OldType,\r
+  IN  EFI_MEMORY_TYPE       NewType,\r
+  IN  EFI_PHYSICAL_ADDRESS  Memory,\r
+  IN  UINT64                Length\r
+  )\r
+{\r
+  UINT64  OldAttributes;\r
+  UINT64  NewAttributes;\r
+\r
+  //\r
+  // The policy configured in PcdDxeNxMemoryProtectionPolicy\r
+  // does not apply to allocations performed in SMM mode.\r
+  //\r
+  if (IsInSmm ()) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // If the CPU arch protocol is not installed yet, we cannot manage memory\r
+  // permission attributes, and it is the job of the driver that installs this\r
+  // protocol to set the permissions on existing allocations.\r
+  //\r
+  if (gCpu == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Check if a DXE memory protection policy has been configured\r
+  //\r
+  if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Don't overwrite Guard pages, which should be the first and/or last page,\r
+  // if any.\r
+  //\r
+  if (IsHeapGuardEnabled ()) {\r
+    if (IsGuardPage (Memory))  {\r
+      Memory += EFI_PAGE_SIZE;\r
+      Length -= EFI_PAGE_SIZE;\r
+      if (Length == 0) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    if (IsGuardPage (Memory + Length - EFI_PAGE_SIZE))  {\r
+      Length -= EFI_PAGE_SIZE;\r
+      if (Length == 0) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the executable permissions according to the DXE memory\r
+  // protection policy, but only if\r
+  // - the policy is different between the old and the new type, or\r
+  // - this is a newly added region (OldType == EfiMaxMemoryType)\r
+  //\r
+  NewAttributes = GetPermissionAttributeForMemoryType (NewType);\r
+\r
+  if (OldType != EfiMaxMemoryType) {\r
+    OldAttributes = GetPermissionAttributeForMemoryType (OldType);\r
+    if (OldAttributes == NewAttributes) {\r
+      // policy is the same between OldType and NewType\r
+      return EFI_SUCCESS;\r
+    }\r
+  } else if (NewAttributes == 0) {\r
+    // newly added region of a type that does not require protection\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);\r
+}\r