]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
MdeModulePkg: Fix unix style of EOL
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Misc / MemoryProtection.c
index 46d88463d417b476e7d476c2440b341e1815a060..21a52d0af55a6d94814d7859e168082054fc3ba1 100644 (file)
@@ -64,8 +64,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 +240,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 +252,6 @@ SetUefiImageProtectionAttributes (
   LIST_ENTRY                                *ImageRecordCodeSectionList;\r
   UINT64                                    CurrentBase;\r
   UINT64                                    ImageEnd;\r
-  UINT64                                    Attribute;\r
 \r
   ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;\r
 \r
@@ -268,29 +274,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 +298,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 +327,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 +384,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
@@ -572,10 +560,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
@@ -601,51 +597,301 @@ 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
+\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
+  DEBUG((DEBUG_ERROR, "%a: applying strict permissions to active memory regions\n",\r
+    __FUNCTION__));\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
+    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((DEBUG_ERROR,\r
+      "%a: applying strict permissions to inactive memory regions\n",\r
+      __FUNCTION__));\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
@@ -674,6 +920,17 @@ 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
+  if (mImageProtectionPolicy == 0) {\r
+    return;\r
+  }\r
+\r
   Status = gBS->LocateHandleBuffer (\r
                   ByProtocol,\r
                   &gEfiLoadedImageProtocolGuid,\r
@@ -738,6 +995,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
@@ -749,11 +1053,29 @@ 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
+  // - heap guard should not be enabled for the same type of memory\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
+  ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPoolType)) == 0);\r
+  ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPageType)) == 0);\r
+\r
+  if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {\r
     Status = CoreCreateEvent (\r
                EVT_NOTIFY_SIGNAL,\r
                TPL_CALLBACK,\r
@@ -773,5 +1095,115 @@ 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
+  // 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