mirror of https://github.com/acidanthera/audk.git
933 lines
24 KiB
C
933 lines
24 KiB
C
/*++
|
|
|
|
Copyright (c) 2006, Intel Corporation
|
|
All rights reserved. This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
Module Name:
|
|
|
|
EbcInt.c
|
|
|
|
Abstract:
|
|
|
|
Top level module for the EBC virtual machine implementation.
|
|
Provides auxilliary support routines for the VM. That is, routines
|
|
that are not particularly related to VM execution of EBC instructions.
|
|
|
|
--*/
|
|
|
|
#include "EbcInt.h"
|
|
#include "EbcExecute.h"
|
|
|
|
//
|
|
// We'll keep track of all thunks we create in a linked list. Each
|
|
// thunk is tied to an image handle, so we have a linked list of
|
|
// image handles, with each having a linked list of thunks allocated
|
|
// to that image handle.
|
|
//
|
|
typedef struct _EBC_THUNK_LIST {
|
|
VOID *ThunkBuffer;
|
|
struct _EBC_THUNK_LIST *Next;
|
|
} EBC_THUNK_LIST;
|
|
|
|
typedef struct _EBC_IMAGE_LIST {
|
|
struct _EBC_IMAGE_LIST *Next;
|
|
EFI_HANDLE ImageHandle;
|
|
EBC_THUNK_LIST *ThunkList;
|
|
} EBC_IMAGE_LIST;
|
|
|
|
//
|
|
// Function prototypes
|
|
//
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeEbcDriver (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcUnloadImage (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EFI_HANDLE ImageHandle
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcCreateThunk (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN VOID *EbcEntryPoint,
|
|
OUT VOID **Thunk
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcGetVersion (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN OUT UINT64 *Version
|
|
);
|
|
|
|
//
|
|
// These two functions and the GUID are used to produce an EBC test protocol.
|
|
// This functionality is definitely not required for execution.
|
|
//
|
|
STATIC
|
|
EFI_STATUS
|
|
InitEbcVmTestProtocol (
|
|
IN EFI_HANDLE *Handle
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EbcVmTestUnsupported (
|
|
VOID
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcRegisterICacheFlush (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EBC_ICACHE_FLUSH Flush
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugGetMaximumProcessorIndex (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
OUT UINTN *MaxProcessorIndex
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugRegisterPeriodicCallback (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN EFI_PERIODIC_CALLBACK PeriodicCallback
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugRegisterExceptionCallback (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN EFI_EXCEPTION_CALLBACK ExceptionCallback,
|
|
IN EFI_EXCEPTION_TYPE ExceptionType
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugInvalidateInstructionCache (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN VOID *Start,
|
|
IN UINT64 Length
|
|
);
|
|
|
|
//
|
|
// We have one linked list of image handles for the whole world. Since
|
|
// there should only be one interpreter, make them global. They must
|
|
// also be global since the execution of an EBC image does not provide
|
|
// a This pointer.
|
|
//
|
|
static EBC_IMAGE_LIST *mEbcImageList = NULL;
|
|
|
|
//
|
|
// Callback function to flush the icache after thunk creation
|
|
//
|
|
static EBC_ICACHE_FLUSH mEbcICacheFlush;
|
|
|
|
//
|
|
// These get set via calls by the debug agent
|
|
//
|
|
static EFI_PERIODIC_CALLBACK mDebugPeriodicCallback = NULL;
|
|
static EFI_EXCEPTION_CALLBACK mDebugExceptionCallback = NULL;
|
|
static EFI_GUID mEfiEbcVmTestProtocolGuid = EFI_EBC_VM_TEST_PROTOCOL_GUID;
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeEbcDriver (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the VM EFI interface. Allocates memory for the VM interface
|
|
and registers the VM protocol.
|
|
|
|
Arguments:
|
|
|
|
ImageHandle - EFI image handle.
|
|
SystemTable - Pointer to the EFI system table.
|
|
|
|
Returns:
|
|
Standard EFI status code.
|
|
|
|
--*/
|
|
{
|
|
EFI_EBC_PROTOCOL *EbcProtocol;
|
|
EFI_EBC_PROTOCOL *OldEbcProtocol;
|
|
EFI_STATUS Status;
|
|
EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumHandles;
|
|
UINTN Index;
|
|
BOOLEAN Installed;
|
|
|
|
//
|
|
// Allocate memory for our protocol. Then fill in the blanks.
|
|
//
|
|
Status = gBS->AllocatePool (
|
|
EfiBootServicesData,
|
|
sizeof (EFI_EBC_PROTOCOL),
|
|
(VOID **) &EbcProtocol
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
EbcProtocol->CreateThunk = EbcCreateThunk;
|
|
EbcProtocol->UnloadImage = EbcUnloadImage;
|
|
EbcProtocol->RegisterICacheFlush = EbcRegisterICacheFlush;
|
|
EbcProtocol->GetVersion = EbcGetVersion;
|
|
mEbcICacheFlush = NULL;
|
|
|
|
//
|
|
// Find any already-installed EBC protocols and uninstall them
|
|
//
|
|
Installed = FALSE;
|
|
HandleBuffer = NULL;
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiEbcProtocolGuid,
|
|
NULL,
|
|
&NumHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (Status == EFI_SUCCESS) {
|
|
//
|
|
// Loop through the handles
|
|
//
|
|
for (Index = 0; Index < NumHandles; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiEbcProtocolGuid,
|
|
(VOID **) &OldEbcProtocol
|
|
);
|
|
if (Status == EFI_SUCCESS) {
|
|
if (gBS->ReinstallProtocolInterface (
|
|
HandleBuffer[Index],
|
|
&gEfiEbcProtocolGuid,
|
|
OldEbcProtocol,
|
|
EbcProtocol
|
|
) == EFI_SUCCESS) {
|
|
Installed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HandleBuffer != NULL) {
|
|
gBS->FreePool (HandleBuffer);
|
|
HandleBuffer = NULL;
|
|
}
|
|
//
|
|
// Add the protocol so someone can locate us if we haven't already.
|
|
//
|
|
if (!Installed) {
|
|
Status = gBS->InstallProtocolInterface (
|
|
&ImageHandle,
|
|
&gEfiEbcProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
EbcProtocol
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (EbcProtocol);
|
|
return Status;
|
|
}
|
|
}
|
|
//
|
|
// Allocate memory for our debug protocol. Then fill in the blanks.
|
|
//
|
|
Status = gBS->AllocatePool (
|
|
EfiBootServicesData,
|
|
sizeof (EFI_DEBUG_SUPPORT_PROTOCOL),
|
|
(VOID **) &EbcDebugProtocol
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
EbcDebugProtocol->Isa = IsaEbc;
|
|
EbcDebugProtocol->GetMaximumProcessorIndex = EbcDebugGetMaximumProcessorIndex;
|
|
EbcDebugProtocol->RegisterPeriodicCallback = EbcDebugRegisterPeriodicCallback;
|
|
EbcDebugProtocol->RegisterExceptionCallback = EbcDebugRegisterExceptionCallback;
|
|
EbcDebugProtocol->InvalidateInstructionCache = EbcDebugInvalidateInstructionCache;
|
|
|
|
//
|
|
// Add the protocol so the debug agent can find us
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&ImageHandle,
|
|
&gEfiDebugSupportProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
EbcDebugProtocol
|
|
);
|
|
//
|
|
// This is recoverable, so free the memory and continue.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (EbcDebugProtocol);
|
|
}
|
|
//
|
|
// Produce a VM test interface protocol. Not required for execution.
|
|
//
|
|
DEBUG_CODE_BEGIN ();
|
|
InitEbcVmTestProtocol (&ImageHandle);
|
|
DEBUG_CODE_END ();
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcCreateThunk (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN VOID *EbcEntryPoint,
|
|
OUT VOID **Thunk
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the top-level routine plugged into the EBC protocol. Since thunks
|
|
are very processor-specific, from here we dispatch directly to the very
|
|
processor-specific routine EbcCreateThunks().
|
|
|
|
Arguments:
|
|
|
|
This - protocol instance pointer
|
|
ImageHandle - handle to the image. The EBC interpreter may use this to keep
|
|
track of any resource allocations performed in loading and
|
|
executing the image.
|
|
EbcEntryPoint - the entry point for the image (as defined in the file header)
|
|
Thunk - pointer to thunk pointer where the address of the created
|
|
thunk is returned.
|
|
|
|
Returns:
|
|
|
|
EFI_STATUS
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = EbcCreateThunks (
|
|
ImageHandle,
|
|
EbcEntryPoint,
|
|
Thunk,
|
|
FLAG_THUNK_ENTRY_POINT
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugGetMaximumProcessorIndex (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
OUT UINTN *MaxProcessorIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This EBC debugger protocol service is called by the debug agent
|
|
|
|
Arguments:
|
|
|
|
This - pointer to the caller's debug support protocol interface
|
|
MaxProcessorIndex - pointer to a caller allocated UINTN in which the maximum
|
|
processor index is returned.
|
|
|
|
Returns:
|
|
|
|
Standard EFI_STATUS
|
|
|
|
--*/
|
|
{
|
|
*MaxProcessorIndex = 0;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugRegisterPeriodicCallback (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN EFI_PERIODIC_CALLBACK PeriodicCallback
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This protocol service is called by the debug agent to register a function
|
|
for us to call on a periodic basis.
|
|
|
|
|
|
Arguments:
|
|
|
|
This - pointer to the caller's debug support protocol interface
|
|
PeriodicCallback - pointer to the function to call periodically
|
|
|
|
Returns:
|
|
|
|
Always EFI_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
mDebugPeriodicCallback = PeriodicCallback;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugRegisterExceptionCallback (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN EFI_EXCEPTION_CALLBACK ExceptionCallback,
|
|
IN EFI_EXCEPTION_TYPE ExceptionType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This protocol service is called by the debug agent to register a function
|
|
for us to call when we detect an exception.
|
|
|
|
|
|
Arguments:
|
|
|
|
This - pointer to the caller's debug support protocol interface
|
|
PeriodicCallback - pointer to the function to call periodically
|
|
|
|
Returns:
|
|
|
|
Always EFI_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
mDebugExceptionCallback = ExceptionCallback;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcDebugInvalidateInstructionCache (
|
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
|
|
IN UINTN ProcessorIndex,
|
|
IN VOID *Start,
|
|
IN UINT64 Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This EBC debugger protocol service is called by the debug agent. Required
|
|
for DebugSupport compliance but is only stubbed out for EBC.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EbcDebugSignalException (
|
|
IN EFI_EXCEPTION_TYPE ExceptionType,
|
|
IN EXCEPTION_FLAGS ExceptionFlags,
|
|
IN VM_CONTEXT *VmPtr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The VM interpreter calls this function when an exception is detected.
|
|
|
|
Arguments:
|
|
|
|
VmPtr - pointer to a VM context for passing info to the EFI debugger.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS if it returns at all
|
|
|
|
--*/
|
|
{
|
|
EFI_SYSTEM_CONTEXT_EBC EbcContext;
|
|
EFI_SYSTEM_CONTEXT SystemContext;
|
|
EFI_STATUS_CODE_VALUE StatusCodeValue;
|
|
BOOLEAN Report;
|
|
//
|
|
// Save the exception in the context passed in
|
|
//
|
|
VmPtr->ExceptionFlags |= ExceptionFlags;
|
|
VmPtr->LastException = ExceptionType;
|
|
//
|
|
// If it's a fatal exception, then flag it in the VM context in case an
|
|
// attached debugger tries to return from it.
|
|
//
|
|
if (ExceptionFlags & EXCEPTION_FLAG_FATAL) {
|
|
VmPtr->StopFlags |= STOPFLAG_APP_DONE;
|
|
}
|
|
//
|
|
// Initialize the context structure
|
|
//
|
|
EbcContext.R0 = VmPtr->R[0];
|
|
EbcContext.R1 = VmPtr->R[1];
|
|
EbcContext.R2 = VmPtr->R[2];
|
|
EbcContext.R3 = VmPtr->R[3];
|
|
EbcContext.R4 = VmPtr->R[4];
|
|
EbcContext.R5 = VmPtr->R[5];
|
|
EbcContext.R6 = VmPtr->R[6];
|
|
EbcContext.R7 = VmPtr->R[7];
|
|
EbcContext.Ip = (UINT64) (UINTN) VmPtr->Ip;
|
|
EbcContext.Flags = VmPtr->Flags;
|
|
SystemContext.SystemContextEbc = &EbcContext;
|
|
//
|
|
// If someone's registered for exception callbacks, then call them.
|
|
// Otherwise report the status code via the status code API
|
|
//
|
|
if (mDebugExceptionCallback != NULL) {
|
|
mDebugExceptionCallback (ExceptionType, SystemContext);
|
|
}
|
|
//
|
|
// Determine if we should report the exception. We report all of them by default,
|
|
// but if a debugger is attached don't report the breakpoint, debug, and step exceptions.
|
|
// Note that EXCEPT_EBC_OVERFLOW is never reported by this VM implementation, so is
|
|
// not included in the switch statement.
|
|
//
|
|
Report = TRUE;
|
|
switch (ExceptionType) {
|
|
case EXCEPT_EBC_UNDEFINED:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_UNDEFINED;
|
|
break;
|
|
|
|
case EXCEPT_EBC_DIVIDE_ERROR:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_DIVIDE_ERROR;
|
|
break;
|
|
|
|
case EXCEPT_EBC_DEBUG:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_DEBUG;
|
|
Report = (BOOLEAN) ((mDebugExceptionCallback == NULL) ? TRUE : FALSE);
|
|
break;
|
|
|
|
case EXCEPT_EBC_BREAKPOINT:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_BREAKPOINT;
|
|
Report = (BOOLEAN) ((mDebugExceptionCallback == NULL) ? TRUE : FALSE);
|
|
break;
|
|
|
|
case EXCEPT_EBC_INVALID_OPCODE:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_INVALID_OPCODE;
|
|
break;
|
|
|
|
case EXCEPT_EBC_STACK_FAULT:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_STACK_FAULT;
|
|
break;
|
|
|
|
case EXCEPT_EBC_ALIGNMENT_CHECK:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_ALIGNMENT_CHECK;
|
|
break;
|
|
|
|
case EXCEPT_EBC_INSTRUCTION_ENCODING:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_INSTRUCTION_ENCODING;
|
|
break;
|
|
|
|
case EXCEPT_EBC_BAD_BREAK:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_BAD_BREAK;
|
|
break;
|
|
|
|
case EXCEPT_EBC_STEP:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_EBC_STEP;
|
|
Report = (BOOLEAN) ((mDebugExceptionCallback == NULL) ? TRUE : FALSE);
|
|
break;
|
|
|
|
default:
|
|
StatusCodeValue = EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_NON_SPECIFIC;
|
|
break;
|
|
}
|
|
//
|
|
// If we determined that we should report the condition, then do so now.
|
|
//
|
|
if (Report) {
|
|
REPORT_STATUS_CODE (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED, StatusCodeValue);
|
|
}
|
|
|
|
switch (ExceptionType) {
|
|
//
|
|
// If ReportStatusCode returned, then for most exceptions we do an assert. The
|
|
// ExceptionType++ is done simply to force the ASSERT() condition to be met.
|
|
// For breakpoints, assume a debugger did not insert a software breakpoint
|
|
// and skip the instruction.
|
|
//
|
|
case EXCEPT_EBC_BREAKPOINT:
|
|
VmPtr->Ip += 2;
|
|
break;
|
|
|
|
case EXCEPT_EBC_STEP:
|
|
break;
|
|
|
|
case EXCEPT_EBC_UNDEFINED:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_UNDEFINED);
|
|
break;
|
|
|
|
case EXCEPT_EBC_DIVIDE_ERROR:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_DIVIDE_ERROR);
|
|
break;
|
|
|
|
case EXCEPT_EBC_DEBUG:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_DEBUG);
|
|
break;
|
|
|
|
case EXCEPT_EBC_INVALID_OPCODE:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_INVALID_OPCODE);
|
|
break;
|
|
|
|
case EXCEPT_EBC_STACK_FAULT:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_STACK_FAULT);
|
|
break;
|
|
|
|
case EXCEPT_EBC_ALIGNMENT_CHECK:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_ALIGNMENT_CHECK);
|
|
break;
|
|
|
|
case EXCEPT_EBC_INSTRUCTION_ENCODING:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_INSTRUCTION_ENCODING);
|
|
break;
|
|
|
|
case EXCEPT_EBC_BAD_BREAK:
|
|
ExceptionType++;
|
|
ASSERT (ExceptionType == EXCEPT_EBC_BAD_BREAK);
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Unknown
|
|
//
|
|
ASSERT (0);
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EbcDebugPeriodic (
|
|
IN VM_CONTEXT *VmPtr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The VM interpreter calls this function on a periodic basis to support
|
|
the EFI debug support protocol.
|
|
|
|
Arguments:
|
|
|
|
VmPtr - pointer to a VM context for passing info to the debugger.
|
|
|
|
Returns:
|
|
|
|
Standard EFI status.
|
|
|
|
--*/
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcUnloadImage (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the core when an image is being unloaded from
|
|
memory. Basically we now have the opportunity to do any necessary cleanup.
|
|
Typically this will include freeing any memory allocated for thunk-creation.
|
|
|
|
Arguments:
|
|
|
|
This - protocol instance pointer
|
|
ImageHandle - handle to the image being unloaded.
|
|
|
|
Returns:
|
|
|
|
EFI_INVALID_PARAMETER - the ImageHandle passed in was not found in
|
|
the internal list of EBC image handles.
|
|
EFI_STATUS - completed successfully
|
|
|
|
--*/
|
|
{
|
|
EBC_THUNK_LIST *ThunkList;
|
|
EBC_THUNK_LIST *NextThunkList;
|
|
EBC_IMAGE_LIST *ImageList;
|
|
EBC_IMAGE_LIST *PrevImageList;
|
|
//
|
|
// First go through our list of known image handles and see if we've already
|
|
// created an image list element for this image handle.
|
|
//
|
|
PrevImageList = NULL;
|
|
for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) {
|
|
if (ImageList->ImageHandle == ImageHandle) {
|
|
break;
|
|
}
|
|
//
|
|
// Save the previous so we can connect the lists when we remove this one
|
|
//
|
|
PrevImageList = ImageList;
|
|
}
|
|
|
|
if (ImageList == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// Free up all the thunk buffers and thunks list elements for this image
|
|
// handle.
|
|
//
|
|
ThunkList = ImageList->ThunkList;
|
|
while (ThunkList != NULL) {
|
|
NextThunkList = ThunkList->Next;
|
|
gBS->FreePool (ThunkList->ThunkBuffer);
|
|
gBS->FreePool (ThunkList);
|
|
ThunkList = NextThunkList;
|
|
}
|
|
//
|
|
// Now remove this image list element from the chain
|
|
//
|
|
if (PrevImageList == NULL) {
|
|
//
|
|
// Remove from head
|
|
//
|
|
mEbcImageList = ImageList->Next;
|
|
} else {
|
|
PrevImageList->Next = ImageList->Next;
|
|
}
|
|
//
|
|
// Now free up the image list element
|
|
//
|
|
gBS->FreePool (ImageList);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EbcAddImageThunk (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN VOID *ThunkBuffer,
|
|
IN UINT32 ThunkSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a thunk to our list of thunks for a given image handle.
|
|
Also flush the instruction cache since we've written thunk code
|
|
to memory that will be executed eventually.
|
|
|
|
Arguments:
|
|
|
|
ImageHandle - the image handle to which the thunk is tied
|
|
ThunkBuffer - the buffer we've created/allocated
|
|
ThunkSize - the size of the thunk memory allocated
|
|
|
|
Returns:
|
|
|
|
EFI_OUT_OF_RESOURCES - memory allocation failed
|
|
EFI_SUCCESS - successful completion
|
|
|
|
--*/
|
|
{
|
|
EBC_THUNK_LIST *ThunkList;
|
|
EBC_IMAGE_LIST *ImageList;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// It so far so good, then flush the instruction cache
|
|
//
|
|
if (mEbcICacheFlush != NULL) {
|
|
Status = mEbcICacheFlush ((EFI_PHYSICAL_ADDRESS) (UINTN) ThunkBuffer, ThunkSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
//
|
|
// Go through our list of known image handles and see if we've already
|
|
// created a image list element for this image handle.
|
|
//
|
|
for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) {
|
|
if (ImageList->ImageHandle == ImageHandle) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ImageList == NULL) {
|
|
//
|
|
// Allocate a new one
|
|
//
|
|
Status = gBS->AllocatePool (
|
|
EfiBootServicesData,
|
|
sizeof (EBC_IMAGE_LIST),
|
|
(VOID **) &ImageList
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ImageList->ThunkList = NULL;
|
|
ImageList->ImageHandle = ImageHandle;
|
|
ImageList->Next = mEbcImageList;
|
|
mEbcImageList = ImageList;
|
|
}
|
|
//
|
|
// Ok, now create a new thunk element to add to the list
|
|
//
|
|
Status = gBS->AllocatePool (
|
|
EfiBootServicesData,
|
|
sizeof (EBC_THUNK_LIST),
|
|
(VOID **) &ThunkList
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Add it to the head of the list
|
|
//
|
|
ThunkList->Next = ImageList->ThunkList;
|
|
ThunkList->ThunkBuffer = ThunkBuffer;
|
|
ImageList->ThunkList = ThunkList;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcRegisterICacheFlush (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN EBC_ICACHE_FLUSH Flush
|
|
)
|
|
{
|
|
mEbcICacheFlush = Flush;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EbcGetVersion (
|
|
IN EFI_EBC_PROTOCOL *This,
|
|
IN OUT UINT64 *Version
|
|
)
|
|
{
|
|
if (Version == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Version = GetVmVersion ();
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InitEbcVmTestProtocol (
|
|
IN EFI_HANDLE *IHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Produce an EBC VM test protocol that can be used for regression tests.
|
|
|
|
Arguments:
|
|
|
|
IHandle - handle on which to install the protocol.
|
|
|
|
Returns:
|
|
|
|
EFI_OUT_OF_RESOURCES - memory allocation failed
|
|
EFI_SUCCESS - successful completion
|
|
|
|
--*/
|
|
{
|
|
EFI_HANDLE Handle;
|
|
EFI_STATUS Status;
|
|
EFI_EBC_VM_TEST_PROTOCOL *EbcVmTestProtocol;
|
|
|
|
//
|
|
// Allocate memory for the protocol, then fill in the fields
|
|
//
|
|
Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_EBC_VM_TEST_PROTOCOL), (VOID **) &EbcVmTestProtocol);
|
|
if (Status != EFI_SUCCESS) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
EbcVmTestProtocol->Execute = (EBC_VM_TEST_EXECUTE) EbcExecuteInstructions;
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
EbcVmTestProtocol->Assemble = (EBC_VM_TEST_ASM) EbcVmTestUnsupported;
|
|
EbcVmTestProtocol->Disassemble = (EBC_VM_TEST_DASM) EbcVmTestUnsupported;
|
|
DEBUG_CODE_END ();
|
|
|
|
//
|
|
// Publish the protocol
|
|
//
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (&Handle, &mEfiEbcVmTestProtocolGuid, EFI_NATIVE_INTERFACE, EbcVmTestProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (EbcVmTestProtocol);
|
|
}
|
|
return Status;
|
|
}
|
|
STATIC
|
|
EFI_STATUS
|
|
EbcVmTestUnsupported ()
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|