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/HiiPackageList.h>
#include <Protocol/SmmBase2.h>
#include <Protocol/PeCoffImageEmulator.h>
#include <Guid/MemoryTypeInformation.h>
#include <Guid/FirmwareFileSystem2.h>
#include <Guid/FirmwareFileSystem3.h>
@ -222,6 +223,8 @@ typedef struct {
UINT16 Machine;
/// EBC Protocol pointer
EFI_EBC_PROTOCOL *Ebc;
/// PE/COFF Image Emulator Protocol pointer
EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *PeCoffEmu;
/// Runtime image list
EFI_RUNTIME_IMAGE_ENTRY *RuntimeData;
/// Pointer to Loaded Image Device Path Protocol

View File

@ -156,6 +156,7 @@
gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES
gEfiEbcProtocolGuid ## SOMETIMES_CONSUMES
gEfiSmmBase2ProtocolGuid ## SOMETIMES_CONSUMES
gEdkiiPeCoffImageEmulatorProtocolGuid ## SOMETIMES_CONSUMES
# Arch Protocols
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
@ -61,6 +70,7 @@ LOADED_IMAGE_PRIVATE_DATA mCorePrivateImage = {
NULL, // JumpContext
0, // Machine
NULL, // Ebc
NULL, // PeCoffEmu
NULL, // RuntimeData
NULL // LoadedImageDevicePath
};
@ -112,6 +122,61 @@ GetMachineTypeName (
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
interfaces for this image.
@ -186,6 +251,30 @@ CoreInitializeImageServices (
gDxeCoreImageHandle = Image->Handle;
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)) {
//
// 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));
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
@ -467,17 +599,16 @@ CoreLoadPeImage (
return Status;
}
if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Image->ImageContext.Machine)) {
if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Image->ImageContext.Machine)) {
if (!CoreIsImageTypeSupported (Image)) {
//
// 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 ((EFI_D_ERROR, "on %s UEFI system.\n", GetMachineTypeName(mDxeCoreImageMachineType)));
DEBUG ((DEBUG_ERROR, "Image type %s can't be loaded on %s UEFI system.\n",
GetMachineTypeName (Image->ImageContext.Machine),
GetMachineTypeName (mDxeCoreImageMachineType)));
return EFI_UNSUPPORTED;
}
}
//
// Set EFI memory type based on ImageType
@ -681,6 +812,16 @@ CoreLoadPeImage (
if (EFI_ERROR(Status)) {
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);
}
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
//
@ -1593,7 +1741,8 @@ CoreStartImage (
//
// 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
// But it can not be started.