MdeModulePkg/DxeCore: invoke the emulator protocol for foreign images
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Fri, 10 Mar 2017 15:50:55 +0000 (16:50 +0100)
committerArd Biesheuvel <ard.biesheuvel@linaro.org>
Mon, 15 Apr 2019 01:37:12 +0000 (18:37 -0700)
When encountering PE/COFF images that cannot be supported natively,
attempt to locate an instance of the PE/COFF image emulator protocol,
and if it supports the image, proceed with loading it and register it
with the emulator.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Hao Wu <hao.a.wu@intel.com>
MdeModulePkg/Core/Dxe/DxeMain.h
MdeModulePkg/Core/Dxe/DxeMain.inf
MdeModulePkg/Core/Dxe/Image/Image.c

index 966c285..bbb424b 100644 (file)
@@ -47,6 +47,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Protocol/TcgService.h>\r
 #include <Protocol/HiiPackageList.h>\r
 #include <Protocol/SmmBase2.h>\r
+#include <Protocol/PeCoffImageEmulator.h>\r
 #include <Guid/MemoryTypeInformation.h>\r
 #include <Guid/FirmwareFileSystem2.h>\r
 #include <Guid/FirmwareFileSystem3.h>\r
@@ -222,6 +223,8 @@ typedef struct {
   UINT16                      Machine;\r
   /// EBC Protocol pointer\r
   EFI_EBC_PROTOCOL            *Ebc;\r
+  /// PE/COFF Image Emulator Protocol pointer\r
+  EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL  *PeCoffEmu;\r
   /// Runtime image list\r
   EFI_RUNTIME_IMAGE_ENTRY     *RuntimeData;\r
   /// Pointer to Loaded Image Device Path Protocol\r
index 113b09a..d4b3912 100644 (file)
   gEfiHiiPackageListProtocolGuid                ## SOMETIMES_PRODUCES\r
   gEfiEbcProtocolGuid                           ## SOMETIMES_CONSUMES\r
   gEfiSmmBase2ProtocolGuid                      ## SOMETIMES_CONSUMES\r
+  gEdkiiPeCoffImageEmulatorProtocolGuid         ## SOMETIMES_CONSUMES\r
 \r
   # Arch Protocols\r
   gEfiBdsArchProtocolGuid                       ## CONSUMES\r
index 0d0ba5b..7ff1dfe 100644 (file)
@@ -23,6 +23,15 @@ LOAD_PE32_IMAGE_PRIVATE_DATA  mLoadPe32PrivateData = {
   }\r
 };\r
 \r
+typedef struct {\r
+  LIST_ENTRY                            Link;\r
+  EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL  *Emulator;\r
+  UINT16                                MachineType;\r
+} EMULATOR_ENTRY;\r
+\r
+STATIC LIST_ENTRY                       mAvailableEmulators;\r
+STATIC EFI_EVENT                        mPeCoffEmuProtocolRegistrationEvent;\r
+STATIC VOID                             *mPeCoffEmuProtocolNotifyRegistration;\r
 \r
 //\r
 // This code is needed to build the Image handle for the DXE Core\r
@@ -61,6 +70,7 @@ LOADED_IMAGE_PRIVATE_DATA mCorePrivateImage  = {
   NULL,                       // JumpContext\r
   0,                          // Machine\r
   NULL,                       // Ebc\r
+  NULL,                       // PeCoffEmu\r
   NULL,                       // RuntimeData\r
   NULL                        // LoadedImageDevicePath\r
 };\r
@@ -113,6 +123,61 @@ GetMachineTypeName (
 }\r
 \r
 /**\r
+  Notification event handler registered by CoreInitializeImageServices () to\r
+  keep track of which PE/COFF image emulators are available.\r
+\r
+  @param  Event          The Event that is being processed, not used.\r
+  @param  Context        Event Context, not used.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+PeCoffEmuProtocolNotify (\r
+  IN  EFI_EVENT       Event,\r
+  IN  VOID            *Context\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UINTN               BufferSize;\r
+  EFI_HANDLE          EmuHandle;\r
+  EMULATOR_ENTRY      *Entry;\r
+\r
+  EmuHandle = NULL;\r
+\r
+  while (TRUE) {\r
+    BufferSize = sizeof (EmuHandle);\r
+    Status = CoreLocateHandle (\r
+               ByRegisterNotify,\r
+               NULL,\r
+               mPeCoffEmuProtocolNotifyRegistration,\r
+               &BufferSize,\r
+               &EmuHandle\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // If no more notification events exit\r
+      //\r
+      return;\r
+    }\r
+\r
+    Entry = AllocateZeroPool (sizeof (*Entry));\r
+    ASSERT (Entry != NULL);\r
+\r
+    Status = CoreHandleProtocol (\r
+               EmuHandle,\r
+               &gEdkiiPeCoffImageEmulatorProtocolGuid,\r
+               (VOID **)&Entry->Emulator\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    Entry->MachineType = Entry->Emulator->MachineType;\r
+\r
+    InsertTailList (&mAvailableEmulators, &Entry->Link);\r
+  }\r
+}\r
+\r
+/**\r
   Add the Image Services to EFI Boot Services Table and install the protocol\r
   interfaces for this image.\r
 \r
@@ -186,6 +251,30 @@ CoreInitializeImageServices (
   gDxeCoreImageHandle = Image->Handle;\r
   gDxeCoreLoadedImage = &Image->Info;\r
 \r
+  //\r
+  // Create the PE/COFF emulator protocol registration event\r
+  //\r
+  Status = CoreCreateEvent (\r
+             EVT_NOTIFY_SIGNAL,\r
+             TPL_CALLBACK,\r
+             PeCoffEmuProtocolNotify,\r
+             NULL,\r
+             &mPeCoffEmuProtocolRegistrationEvent\r
+             );\r
+  ASSERT_EFI_ERROR(Status);\r
+\r
+  //\r
+  // Register for protocol notifications on this event\r
+  //\r
+  Status = CoreRegisterProtocolNotify (\r
+             &gEdkiiPeCoffImageEmulatorProtocolGuid,\r
+             mPeCoffEmuProtocolRegistrationEvent,\r
+             &mPeCoffEmuProtocolNotifyRegistration\r
+             );\r
+  ASSERT_EFI_ERROR(Status);\r
+\r
+  InitializeListHead (&mAvailableEmulators);\r
+\r
   if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) {\r
     //\r
     // Export DXE Core PE Loader functionality for backward compatibility.\r
@@ -419,6 +508,49 @@ GetPeCoffImageFixLoadingAssignedAddress(
    DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address 0x%11p. Status = %r \n", (VOID *)(UINTN)(ImageContext->ImageAddress), Status));\r
    return Status;\r
 }\r
+\r
+/**\r
+  Decides whether a PE/COFF image can execute on this system, either natively\r
+  or via emulation/interpretation. In the latter case, the PeCoffEmu member\r
+  of the LOADED_IMAGE_PRIVATE_DATA struct pointer is populated with a pointer\r
+  to the emulator protocol that supports this image.\r
+\r
+  @param[in, out]   Image         LOADED_IMAGE_PRIVATE_DATA struct pointer\r
+\r
+  @retval           TRUE          The image is supported\r
+  @retval           FALSE         The image is not supported\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+CoreIsImageTypeSupported (\r
+  IN OUT LOADED_IMAGE_PRIVATE_DATA  *Image\r
+  )\r
+{\r
+  LIST_ENTRY                        *Link;\r
+  EMULATOR_ENTRY                    *Entry;\r
+\r
+  for (Link = GetFirstNode (&mAvailableEmulators);\r
+       !IsNull (&mAvailableEmulators, Link);\r
+       Link = GetNextNode (&mAvailableEmulators, Link)) {\r
+\r
+    Entry = BASE_CR (Link, EMULATOR_ENTRY, Link);\r
+    if (Entry->MachineType != Image->ImageContext.Machine) {\r
+      continue;\r
+    }\r
+\r
+    if (Entry->Emulator->IsImageSupported (Entry->Emulator,\r
+                           Image->ImageContext.ImageType,\r
+                           Image->Info.FilePath)) {\r
+      Image->PeCoffEmu = Entry->Emulator;\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->ImageContext.Machine) ||\r
+         EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Image->ImageContext.Machine);\r
+}\r
+\r
 /**\r
   Loads, relocates, and invokes a PE/COFF image\r
 \r
@@ -467,16 +599,15 @@ CoreLoadPeImage (
     return Status;\r
   }\r
 \r
-  if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->ImageContext.Machine)) {\r
-    if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Image->ImageContext.Machine)) {\r
-      //\r
-      // The PE/COFF loader can support loading image types that can be executed.\r
-      // If we loaded an image type that we can not execute return EFI_UNSUPORTED.\r
-      //\r
-      DEBUG ((EFI_D_ERROR, "Image type %s can't be loaded ", GetMachineTypeName(Image->ImageContext.Machine)));\r
-      DEBUG ((EFI_D_ERROR, "on %s UEFI system.\n", GetMachineTypeName(mDxeCoreImageMachineType)));\r
-      return EFI_UNSUPPORTED;\r
-    }\r
+  if (!CoreIsImageTypeSupported (Image)) {\r
+    //\r
+    // The PE/COFF loader can support loading image types that can be executed.\r
+    // If we loaded an image type that we can not execute return EFI_UNSUPPORTED.\r
+    //\r
+    DEBUG ((DEBUG_ERROR, "Image type %s can't be loaded on %s UEFI system.\n",\r
+      GetMachineTypeName (Image->ImageContext.Machine),\r
+      GetMachineTypeName (mDxeCoreImageMachineType)));\r
+    return EFI_UNSUPPORTED;\r
   }\r
 \r
   //\r
@@ -681,6 +812,16 @@ CoreLoadPeImage (
     if (EFI_ERROR(Status)) {\r
       goto Done;\r
     }\r
+  } else if (Image->PeCoffEmu != NULL) {\r
+    Status = Image->PeCoffEmu->RegisterImage (Image->PeCoffEmu,\r
+                                 Image->ImageBasePage,\r
+                                 EFI_PAGES_TO_SIZE (Image->NumberOfPages),\r
+                                 &Image->EntryPoint);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_LOAD | DEBUG_ERROR,\r
+        "CoreLoadPeImage: Failed to register foreign image with emulator.\n"));\r
+      goto Done;\r
+    }\r
   }\r
 \r
   //\r
@@ -868,6 +1009,13 @@ CoreUnloadAndCloseImage (
     Image->Ebc->UnloadImage (Image->Ebc, Image->Handle);\r
   }\r
 \r
+  if (Image->PeCoffEmu != NULL) {\r
+    //\r
+    // If the PE/COFF Emulator protocol exists we must unregister the image.\r
+    //\r
+    Image->PeCoffEmu->UnregisterImage (Image->PeCoffEmu, Image->ImageBasePage);\r
+  }\r
+\r
   //\r
   // Unload image, free Image->ImageContext->ModHandle\r
   //\r
@@ -1593,7 +1741,8 @@ CoreStartImage (
   //\r
   // The image to be started must have the machine type supported by DxeCore.\r
   //\r
-  if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->Machine)) {\r
+  if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->Machine) &&\r
+      Image->PeCoffEmu == NULL) {\r
     //\r
     // Do not ASSERT here, because image might be loaded via EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED\r
     // But it can not be started.\r