]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
MdeModulePkg: remove PE/COFF header workaround for ELILO on IPF
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Misc / MemoryProtection.c
index 7689c794a8ad22601965303e4dfce255e3923c09..fa8f8fe91ac7a8e9f7f5e9805bf2ac63c77f4d5e 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
@@ -44,10 +44,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Guid/PropertiesTable.h>\r
 \r
 #include <Protocol/FirmwareVolume2.h>\r
-#include <Protocol/BlockIo.h>\r
 #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
@@ -74,6 +74,8 @@ UINT32   mImageProtectionPolicy;
 \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
@@ -238,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
@@ -253,7 +252,6 @@ SetUefiImageProtectionAttributes (
   LIST_ENTRY                                *ImageRecordCodeSectionList;\r
   UINT64                                    CurrentBase;\r
   UINT64                                    ImageEnd;\r
-  UINT64                                    Attribute;\r
 \r
   ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;\r
 \r
@@ -276,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
@@ -310,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
@@ -401,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
@@ -426,7 +406,6 @@ ProtectUefiImageCommon (
   IMAGE_PROPERTIES_RECORD              *ImageRecord;\r
   CHAR8                                *PdbPointer;\r
   IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;\r
-  UINT16                               Magic;\r
   BOOLEAN                              IsAligned;\r
   UINT32                               ProtectionPolicy;\r
 \r
@@ -486,21 +465,7 @@ ProtectUefiImageCommon (
   //\r
   // Get SectionAlignment\r
   //\r
-  if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
-    //\r
-    // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value\r
-    //       in the PE/COFF Header. If the MachineType is Itanium(IA64) and the\r
-    //       Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC\r
-    //       then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC\r
-    //\r
-    Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r
-  } else {\r
-    //\r
-    // Get the magic value from the PE/COFF Optional Header\r
-    //\r
-    Magic = Hdr.Pe32->OptionalHeader.Magic;\r
-  }\r
-  if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+  if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
     SectionAlignment  = Hdr.Pe32->OptionalHeader.SectionAlignment;\r
   } else {\r
     SectionAlignment  = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;\r
@@ -580,10 +545,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
@@ -609,34 +582,17 @@ 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
-\r
-  @param[in]  LoadedImage              The loaded image protocol\r
-  @param[in]  LoadedImageDevicePath    The loaded image device path protocol\r
-**/\r
-VOID\r
-ProtectUefiImage (\r
-  IN EFI_LOADED_IMAGE_PROTOCOL   *LoadedImage,\r
-  IN EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath\r
-  )\r
-{\r
-  if (PcdGet32(PcdImageProtectionPolicy) != 0) {\r
-    ProtectUefiImageCommon (LoadedImage, LoadedImageDevicePath, TRUE);\r
-  }\r
-}\r
-\r
 /**\r
   Unprotect UEFI image.\r
 \r
@@ -649,8 +605,28 @@ UnprotectUefiImage (
   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, FALSE);\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
@@ -809,6 +785,9 @@ InitializeDxeNxMemoryProtectionPolicy (
   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
@@ -840,8 +819,45 @@ InitializeDxeNxMemoryProtectionPolicy (
   } 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
+  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
@@ -855,6 +871,37 @@ InitializeDxeNxMemoryProtectionPolicy (
         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
@@ -865,9 +912,11 @@ InitializeDxeNxMemoryProtectionPolicy (
   // accessible, but have not been added to the UEFI memory map (yet).\r
   //\r
   if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {\r
-    DEBUG((DEBUG_ERROR,\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
       "%a: applying strict permissions to inactive memory regions\n",\r
-      __FUNCTION__));\r
+      __FUNCTION__\r
+      ));\r
 \r
     CoreAcquireGcdMemoryLock ();\r
 \r
@@ -936,6 +985,11 @@ MemoryProtectionCpuArchProtocolNotify (
     InitializeDxeNxMemoryProtectionPolicy ();\r
   }\r
 \r
+  //\r
+  // Call notify function meant for Heap Guard.\r
+  //\r
+  HeapGuardCpuArchProtocolNotify ();\r
+\r
   if (mImageProtectionPolicy == 0) {\r
     return;\r
   }\r
@@ -1004,6 +1058,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
@@ -1015,10 +1116,13 @@ CoreInitializeMemoryProtection (
 {\r
   EFI_STATUS  Status;\r
   EFI_EVENT   Event;\r
+  EFI_EVENT   EndOfDxeEvent;\r
   VOID        *Registration;\r
 \r
   mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);\r
 \r
+  InitializeListHead (&mProtectedImageRecordList);\r
+\r
   //\r
   // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:\r
   // - code regions should have no EFI_MEMORY_XP attribute\r
@@ -1051,6 +1155,23 @@ 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
@@ -1125,6 +1246,27 @@ ApplyMemoryProtectionPolicy (
     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