]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/PiSmmCore/Dispatcher.c
Enable "Load Module At fixed Address" feature in SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Dispatcher.c
index 9e1a77890079e9f4907d17d4bee60080f17c2fe7..9625eabd9147f29d714ca17f9c69b8e745f9a558 100644 (file)
@@ -122,6 +122,176 @@ FV_FILEPATH_DEVICE_PATH  mFvDevicePath;
 //\r
 EFI_SECURITY_ARCH_PROTOCOL  *mSecurity = NULL;\r
 \r
+//\r
+// The global variable is defined for Loading modules at fixed address feature to track the SMM code\r
+// memory range usage. It is a bit mapped array in which every bit indicates the correspoding \r
+// memory page available or not. \r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED    UINT64                *mSmmCodeMemoryRangeUsageBitMap=NULL;\r
+\r
+/**\r
+  To check memory usage bit map array to figure out if the memory range in which the image will be loaded is available or not. If \r
+  memory range is avaliable, the function will mark the correponding bits to 1 which indicates the memory range is used.\r
+  The function is only invoked when load modules at fixed address feature is enabled. \r
+  \r
+  @param  ImageBase                The base addres the image will be loaded at.\r
+  @param  ImageSize                The size of the image\r
+  \r
+  @retval EFI_SUCCESS              The memory range the image will be loaded in is available\r
+  @retval EFI_NOT_FOUND            The memory range the image will be loaded in is not available\r
+**/\r
+EFI_STATUS\r
+CheckAndMarkFixLoadingMemoryUsageBitMap (\r
+  IN  EFI_PHYSICAL_ADDRESS          ImageBase,\r
+  IN  UINTN                         ImageSize\r
+  )\r
+{\r
+   UINT32                             SmmCodePageNumber;\r
+   UINT64                             SmmCodeSize; \r
+   EFI_PHYSICAL_ADDRESS               SmmCodeBase;\r
+   UINTN                              BaseOffsetPageNumber;\r
+   UINTN                              TopOffsetPageNumber;\r
+   UINTN                              Index;\r
+   //\r
+   // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber\r
+   //\r
+   SmmCodePageNumber = PcdGet32(PcdLoadFixAddressSmmCodePageNumber);\r
+   SmmCodeSize = EFI_PAGES_TO_SIZE (SmmCodePageNumber);\r
+   SmmCodeBase = gLoadModuleAtFixAddressSmramBase;\r
+   \r
+   //\r
+   // If the memory usage bit map is not initialized,  do it. Every bit in the array \r
+   // indicate the status of the corresponding memory page, available or not\r
+   // \r
+   if (mSmmCodeMemoryRangeUsageBitMap == NULL) {\r
+     mSmmCodeMemoryRangeUsageBitMap = AllocateZeroPool(((SmmCodePageNumber / 64) + 1)*sizeof(UINT64));\r
+   }\r
+   //\r
+   // If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND\r
+   //\r
+   if (mSmmCodeMemoryRangeUsageBitMap == NULL) {\r
+     return EFI_NOT_FOUND;\r
+   }\r
+   //\r
+   // see if the memory range for loading the image is in the SMM code range.\r
+   //\r
+   if (SmmCodeBase + SmmCodeSize <  ImageBase + ImageSize || SmmCodeBase >  ImageBase) {\r
+     return EFI_NOT_FOUND;   \r
+   }   \r
+   //\r
+   // Test if the memory is avalaible or not.\r
+   // \r
+   BaseOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES((UINT32)(ImageBase - SmmCodeBase));\r
+   TopOffsetPageNumber  = (UINTN)EFI_SIZE_TO_PAGES((UINT32)(ImageBase + ImageSize - SmmCodeBase));\r
+   for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {\r
+     if ((mSmmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64(1, (Index % 64))) != 0) {\r
+       //\r
+       // This page is already used.\r
+       //\r
+       return EFI_NOT_FOUND;  \r
+     }\r
+   }\r
+   \r
+   //\r
+   // Being here means the memory range is available.  So mark the bits for the memory range\r
+   // \r
+   for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {\r
+     mSmmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64(1, (Index % 64));\r
+   }\r
+   return  EFI_SUCCESS;   \r
+}\r
+/**\r
+  Get the fixed loadding address from image header assigned by build tool. This function only be called \r
+  when Loading module at Fixed address feature enabled.\r
+  \r
+  @param  ImageContext              Pointer to the image context structure that describes the PE/COFF\r
+                                    image that needs to be examined by this function.\r
+  @retval EFI_SUCCESS               An fixed loading address is assigned to this image by build tools .\r
+  @retval EFI_NOT_FOUND             The image has no assigned fixed loadding address.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPeCoffImageFixLoadingAssignedAddress(\r
+  IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext\r
+  )\r
+{\r
+        UINTN                              SectionHeaderOffset;\r
+        EFI_STATUS                         Status;\r
+        EFI_IMAGE_SECTION_HEADER           SectionHeader;\r
+        EFI_IMAGE_OPTIONAL_HEADER_UNION    *ImgHdr;\r
+        EFI_PHYSICAL_ADDRESS               FixLoaddingAddress;\r
+        UINT16                             Index;\r
+        UINTN                              Size; \r
+        UINT16                             NumberOfSections;\r
+        UINT64                             ValueInSectionHeader;\r
+        \r
+        FixLoaddingAddress = 0;\r
+        Status = EFI_NOT_FOUND;\r
+       \r
+        //\r
+   // Get PeHeader pointer\r
+   //\r
+   ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset);\r
+        SectionHeaderOffset = (UINTN)(\r
+                                 ImageContext->PeCoffHeaderOffset +\r
+                                 sizeof (UINT32) +\r
+                                 sizeof (EFI_IMAGE_FILE_HEADER) +\r
+                                 ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader\r
+                                 );\r
+   NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;\r
+     \r
+   //\r
+   // Get base address from the first section header that doesn't point to code section.\r
+   //\r
+   for (Index = 0; Index < NumberOfSections; Index++) {\r
+     //\r
+     // Read section header from file\r
+     //\r
+     Size = sizeof (EFI_IMAGE_SECTION_HEADER);\r
+     Status = ImageContext->ImageRead (\r
+                              ImageContext->Handle,\r
+                              SectionHeaderOffset,\r
+                              &Size,\r
+                              &SectionHeader\r
+                              );\r
+     if (EFI_ERROR (Status)) {\r
+       return Status;\r
+     }\r
+     \r
+     Status = EFI_NOT_FOUND;\r
+     \r
+     if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {\r
+       //\r
+       // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields in the first section header \r
+       // that doesn't point to code section in image header.So there is an assumption that when the feature is enabled,\r
+       // if a module with a loading address assigned by tools, the PointerToRelocations & PointerToLineNumbers fields\r
+       // should not be Zero, or else, these 2 fileds should be set to Zero\r
+       //\r
+       ValueInSectionHeader = ReadUnaligned64((UINT64*)&SectionHeader.PointerToRelocations);\r
+       if (ValueInSectionHeader != 0) {\r
+         //\r
+         // Found first section header that doesn't point to code section in which uild tool saves the\r
+         // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields\r
+         //      \r
+         FixLoaddingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressSmramBase + (INT64)ValueInSectionHeader);\r
+         //\r
+         // Check if the memory range is avaliable.\r
+         //\r
+         Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoaddingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment));\r
+         if (!EFI_ERROR(Status)) {\r
+           //\r
+           // The assigned address is valid. Return the specified loadding address\r
+           //\r
+           ImageContext->ImageAddress = FixLoaddingAddress;\r
+         }\r
+       }\r
+       break;     \r
+     }\r
+     SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);     \r
+   }\r
+   DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n", FixLoaddingAddress, Status));\r
+   return Status;\r
+}\r
 /**\r
   Loads an EFI image into SMRAM.\r
 \r
@@ -258,24 +428,63 @@ SmmLoadImage (
     }\r
     return Status;\r
   }\r
-\r
-  PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);\r
-  DstBuffer = (UINTN)(-1);\r
-  \r
-  Status = SmmAllocatePages (\r
-             AllocateMaxAddress,\r
-             EfiRuntimeServicesCode,\r
-             PageCount,\r
-             &DstBuffer\r
-             );\r
-  if (EFI_ERROR (Status)) {\r
-    if (Buffer != NULL) {\r
-      Status = gBS->FreePool (Buffer);\r
+  //\r
+  // if Loading module at Fixed Address feature is enabled, then  cut out a memory range started from TESG BASE\r
+  // to hold the Smm driver code\r
+  //\r
+  if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {\r
+    //\r
+    // Get the fixed loading address assigned by Build tool\r
+    //\r
+    Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext);\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Since the memory range to load Smm core alreay been cut out, so no need to allocate and free this range\r
+      // following statements is to bypass SmmFreePages\r
+      //\r
+      PageCount = 0;\r
+      DstBuffer = (UINTN)gLoadModuleAtFixAddressSmramBase;   \r
+    } else {\r
+       DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n"));\r
+       //\r
+       // allocate the memory to load the SMM driver\r
+       //\r
+       PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);\r
+       DstBuffer = (UINTN)(-1);\r
+     \r
+       Status = SmmAllocatePages (\r
+                   AllocateMaxAddress,\r
+                   EfiRuntimeServicesCode,\r
+                   PageCount,\r
+                   &DstBuffer\r
+                   );\r
+       if (EFI_ERROR (Status)) {\r
+         if (Buffer != NULL) {\r
+           Status = gBS->FreePool (Buffer);\r
+         } \r
+         return Status;\r
+       }     \r
+      ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
     }\r
-    return Status;\r
+  } else {\r
+     PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);\r
+     DstBuffer = (UINTN)(-1);\r
+     \r
+     Status = SmmAllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiRuntimeServicesCode,\r
+                  PageCount,\r
+                  &DstBuffer\r
+                  );\r
+     if (EFI_ERROR (Status)) {\r
+       if (Buffer != NULL) {\r
+         Status = gBS->FreePool (Buffer);\r
+       }\r
+       return Status;\r
+     }\r
+     \r
+     ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
   }\r
-\r
-  ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
   //\r
   // Align buffer on section boundry\r
   //\r