MdeModulePkg/DxeCore: invoke the emulator protocol for foreign images

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>
This commit is contained in:
Ard Biesheuvel 2017-03-10 16:50:55 +01:00
parent f72f81777b
commit 57df17fe26
3 changed files with 164 additions and 11 deletions

View File

@ -47,6 +47,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Protocol/TcgService.h> #include <Protocol/TcgService.h>
#include <Protocol/HiiPackageList.h> #include <Protocol/HiiPackageList.h>
#include <Protocol/SmmBase2.h> #include <Protocol/SmmBase2.h>
#include <Protocol/PeCoffImageEmulator.h>
#include <Guid/MemoryTypeInformation.h> #include <Guid/MemoryTypeInformation.h>
#include <Guid/FirmwareFileSystem2.h> #include <Guid/FirmwareFileSystem2.h>
#include <Guid/FirmwareFileSystem3.h> #include <Guid/FirmwareFileSystem3.h>
@ -222,6 +223,8 @@ typedef struct {
UINT16 Machine; UINT16 Machine;
/// EBC Protocol pointer /// EBC Protocol pointer
EFI_EBC_PROTOCOL *Ebc; EFI_EBC_PROTOCOL *Ebc;
/// PE/COFF Image Emulator Protocol pointer
EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *PeCoffEmu;
/// Runtime image list /// Runtime image list
EFI_RUNTIME_IMAGE_ENTRY *RuntimeData; EFI_RUNTIME_IMAGE_ENTRY *RuntimeData;
/// Pointer to Loaded Image Device Path Protocol /// Pointer to Loaded Image Device Path Protocol

View File

@ -156,6 +156,7 @@
gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES
gEfiEbcProtocolGuid ## SOMETIMES_CONSUMES gEfiEbcProtocolGuid ## SOMETIMES_CONSUMES
gEfiSmmBase2ProtocolGuid ## SOMETIMES_CONSUMES gEfiSmmBase2ProtocolGuid ## SOMETIMES_CONSUMES
gEdkiiPeCoffImageEmulatorProtocolGuid ## SOMETIMES_CONSUMES
# Arch Protocols # Arch Protocols
gEfiBdsArchProtocolGuid ## CONSUMES gEfiBdsArchProtocolGuid ## CONSUMES

View File

@ -23,6 +23,15 @@ LOAD_PE32_IMAGE_PRIVATE_DATA mLoadPe32PrivateData = {
} }
}; };
typedef struct {
LIST_ENTRY Link;
EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *Emulator;
UINT16 MachineType;
} EMULATOR_ENTRY;
STATIC LIST_ENTRY mAvailableEmulators;
STATIC EFI_EVENT mPeCoffEmuProtocolRegistrationEvent;
STATIC VOID *mPeCoffEmuProtocolNotifyRegistration;
// //
// This code is needed to build the Image handle for the DXE Core // This code is needed to build the Image handle for the DXE Core
@ -61,6 +70,7 @@ LOADED_IMAGE_PRIVATE_DATA mCorePrivateImage = {
NULL, // JumpContext NULL, // JumpContext
0, // Machine 0, // Machine
NULL, // Ebc NULL, // Ebc
NULL, // PeCoffEmu
NULL, // RuntimeData NULL, // RuntimeData
NULL // LoadedImageDevicePath NULL // LoadedImageDevicePath
}; };
@ -112,6 +122,61 @@ GetMachineTypeName (
return L"<Unknown>"; return L"<Unknown>";
} }
/**
Notification event handler registered by CoreInitializeImageServices () to
keep track of which PE/COFF image emulators are available.
@param Event The Event that is being processed, not used.
@param Context Event Context, not used.
**/
STATIC
VOID
EFIAPI
PeCoffEmuProtocolNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN BufferSize;
EFI_HANDLE EmuHandle;
EMULATOR_ENTRY *Entry;
EmuHandle = NULL;
while (TRUE) {
BufferSize = sizeof (EmuHandle);
Status = CoreLocateHandle (
ByRegisterNotify,
NULL,
mPeCoffEmuProtocolNotifyRegistration,
&BufferSize,
&EmuHandle
);
if (EFI_ERROR (Status)) {
//
// If no more notification events exit
//
return;
}
Entry = AllocateZeroPool (sizeof (*Entry));
ASSERT (Entry != NULL);
Status = CoreHandleProtocol (
EmuHandle,
&gEdkiiPeCoffImageEmulatorProtocolGuid,
(VOID **)&Entry->Emulator
);
ASSERT_EFI_ERROR (Status);
Entry->MachineType = Entry->Emulator->MachineType;
InsertTailList (&mAvailableEmulators, &Entry->Link);
}
}
/** /**
Add the Image Services to EFI Boot Services Table and install the protocol Add the Image Services to EFI Boot Services Table and install the protocol
interfaces for this image. interfaces for this image.
@ -186,6 +251,30 @@ CoreInitializeImageServices (
gDxeCoreImageHandle = Image->Handle; gDxeCoreImageHandle = Image->Handle;
gDxeCoreLoadedImage = &Image->Info; gDxeCoreLoadedImage = &Image->Info;
//
// Create the PE/COFF emulator protocol registration event
//
Status = CoreCreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PeCoffEmuProtocolNotify,
NULL,
&mPeCoffEmuProtocolRegistrationEvent
);
ASSERT_EFI_ERROR(Status);
//
// Register for protocol notifications on this event
//
Status = CoreRegisterProtocolNotify (
&gEdkiiPeCoffImageEmulatorProtocolGuid,
mPeCoffEmuProtocolRegistrationEvent,
&mPeCoffEmuProtocolNotifyRegistration
);
ASSERT_EFI_ERROR(Status);
InitializeListHead (&mAvailableEmulators);
if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) { if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) {
// //
// Export DXE Core PE Loader functionality for backward compatibility. // Export DXE Core PE Loader functionality for backward compatibility.
@ -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)); 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));
return Status; return Status;
} }
/**
Decides whether a PE/COFF image can execute on this system, either natively
or via emulation/interpretation. In the latter case, the PeCoffEmu member
of the LOADED_IMAGE_PRIVATE_DATA struct pointer is populated with a pointer
to the emulator protocol that supports this image.
@param[in, out] Image LOADED_IMAGE_PRIVATE_DATA struct pointer
@retval TRUE The image is supported
@retval FALSE The image is not supported
**/
STATIC
BOOLEAN
CoreIsImageTypeSupported (
IN OUT LOADED_IMAGE_PRIVATE_DATA *Image
)
{
LIST_ENTRY *Link;
EMULATOR_ENTRY *Entry;
for (Link = GetFirstNode (&mAvailableEmulators);
!IsNull (&mAvailableEmulators, Link);
Link = GetNextNode (&mAvailableEmulators, Link)) {
Entry = BASE_CR (Link, EMULATOR_ENTRY, Link);
if (Entry->MachineType != Image->ImageContext.Machine) {
continue;
}
if (Entry->Emulator->IsImageSupported (Entry->Emulator,
Image->ImageContext.ImageType,
Image->Info.FilePath)) {
Image->PeCoffEmu = Entry->Emulator;
return TRUE;
}
}
return EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->ImageContext.Machine) ||
EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Image->ImageContext.Machine);
}
/** /**
Loads, relocates, and invokes a PE/COFF image Loads, relocates, and invokes a PE/COFF image
@ -467,17 +599,16 @@ CoreLoadPeImage (
return Status; return Status;
} }
if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->ImageContext.Machine)) { if (!CoreIsImageTypeSupported (Image)) {
if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Image->ImageContext.Machine)) {
// //
// The PE/COFF loader can support loading image types that can be executed. // The PE/COFF loader can support loading image types that can be executed.
// If we loaded an image type that we can not execute return EFI_UNSUPORTED. // If we loaded an image type that we can not execute return EFI_UNSUPPORTED.
// //
DEBUG ((EFI_D_ERROR, "Image type %s can't be loaded ", GetMachineTypeName(Image->ImageContext.Machine))); DEBUG ((DEBUG_ERROR, "Image type %s can't be loaded on %s UEFI system.\n",
DEBUG ((EFI_D_ERROR, "on %s UEFI system.\n", GetMachineTypeName(mDxeCoreImageMachineType))); GetMachineTypeName (Image->ImageContext.Machine),
GetMachineTypeName (mDxeCoreImageMachineType)));
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
} }
}
// //
// Set EFI memory type based on ImageType // Set EFI memory type based on ImageType
@ -681,6 +812,16 @@ CoreLoadPeImage (
if (EFI_ERROR(Status)) { if (EFI_ERROR(Status)) {
goto Done; goto Done;
} }
} else if (Image->PeCoffEmu != NULL) {
Status = Image->PeCoffEmu->RegisterImage (Image->PeCoffEmu,
Image->ImageBasePage,
EFI_PAGES_TO_SIZE (Image->NumberOfPages),
&Image->EntryPoint);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_LOAD | DEBUG_ERROR,
"CoreLoadPeImage: Failed to register foreign image with emulator.\n"));
goto Done;
}
} }
// //
@ -868,6 +1009,13 @@ CoreUnloadAndCloseImage (
Image->Ebc->UnloadImage (Image->Ebc, Image->Handle); Image->Ebc->UnloadImage (Image->Ebc, Image->Handle);
} }
if (Image->PeCoffEmu != NULL) {
//
// If the PE/COFF Emulator protocol exists we must unregister the image.
//
Image->PeCoffEmu->UnregisterImage (Image->PeCoffEmu, Image->ImageBasePage);
}
// //
// Unload image, free Image->ImageContext->ModHandle // Unload image, free Image->ImageContext->ModHandle
// //
@ -1593,7 +1741,8 @@ CoreStartImage (
// //
// The image to be started must have the machine type supported by DxeCore. // The image to be started must have the machine type supported by DxeCore.
// //
if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->Machine)) { if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->Machine) &&
Image->PeCoffEmu == NULL) {
// //
// Do not ASSERT here, because image might be loaded via EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED // Do not ASSERT here, because image might be loaded via EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED
// But it can not be started. // But it can not be started.