mirror of https://github.com/acidanthera/audk.git
1173 lines
28 KiB
C
1173 lines
28 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;
|
||
|
|
||
|
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
|
||
|
);
|
||
|
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
InitializeEbcCallback (
|
||
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This
|
||
|
);
|
||
|
|
||
|
STATIC
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
CommonEbcExceptionHandler (
|
||
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
||
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
||
|
);
|
||
|
|
||
|
STATIC
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
EbcPeriodicNotifyFunction (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EbcDebugPeriodic (
|
||
|
IN VM_CONTEXT *VmPtr
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// 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[MAX_EBC_EXCEPTION + 1] = {NULL};
|
||
|
static EFI_GUID mEfiEbcVmTestProtocolGuid = EFI_EBC_VM_TEST_PROTOCOL_GUID;
|
||
|
|
||
|
static VOID* mStackBuffer[MAX_STACK_NUM];
|
||
|
static EFI_HANDLE mStackBufferIndex[MAX_STACK_NUM];
|
||
|
static UINTN mStackNum = 0;
|
||
|
|
||
|
//
|
||
|
// Event for Periodic callback
|
||
|
//
|
||
|
static EFI_EVENT mEbcPeriodicEvent;
|
||
|
VM_CONTEXT *mVmPtr = NULL;
|
||
|
|
||
|
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;
|
||
|
|
||
|
EbcProtocol = NULL;
|
||
|
EbcDebugProtocol = NULL;
|
||
|
|
||
|
//
|
||
|
// Allocate memory for our protocol. Then fill in the blanks.
|
||
|
//
|
||
|
EbcProtocol = AllocatePool (sizeof (EFI_EBC_PROTOCOL));
|
||
|
|
||
|
if (EbcProtocol == NULL) {
|
||
|
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) {
|
||
|
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)) {
|
||
|
FreePool (EbcProtocol);
|
||
|
return Status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Status = InitEBCStack();
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate memory for our debug protocol. Then fill in the blanks.
|
||
|
//
|
||
|
EbcDebugProtocol = AllocatePool (sizeof (EFI_DEBUG_SUPPORT_PROTOCOL));
|
||
|
|
||
|
if (EbcDebugProtocol == NULL) {
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
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)) {
|
||
|
FreePool (EbcDebugProtocol);
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
//
|
||
|
// Install EbcDebugSupport Protocol Successfully
|
||
|
// Now we need to initialize the Ebc default Callback
|
||
|
//
|
||
|
Status = InitializeEbcCallback (EbcDebugProtocol);
|
||
|
|
||
|
//
|
||
|
// Produce a VM test interface protocol. Not required for execution.
|
||
|
//
|
||
|
DEBUG_CODE_BEGIN ();
|
||
|
InitEbcVmTestProtocol (&ImageHandle);
|
||
|
DEBUG_CODE_END ();
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
FreeEBCStack();
|
||
|
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) {
|
||
|
gBS->UninstallProtocolInterface (
|
||
|
HandleBuffer[Index],
|
||
|
&gEfiEbcProtocolGuid,
|
||
|
OldEbcProtocol
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (HandleBuffer != NULL) {
|
||
|
FreePool (HandleBuffer);
|
||
|
HandleBuffer = NULL;
|
||
|
}
|
||
|
|
||
|
FreePool (EbcProtocol);
|
||
|
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ((mDebugPeriodicCallback == NULL) && (PeriodicCallback == NULL)) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
if ((mDebugPeriodicCallback != NULL) && (PeriodicCallback != NULL)) {
|
||
|
return EFI_ALREADY_STARTED;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
ExceptionCallback - pointer to the function to the exception
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Always EFI_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ((ExceptionType < 0) || (ExceptionType > MAX_EBC_EXCEPTION)) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
if ((mDebugExceptionCallback[ExceptionType] == NULL) && (ExceptionCallback == NULL)) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
if ((mDebugExceptionCallback[ExceptionType] != NULL) && (ExceptionCallback != NULL)) {
|
||
|
return EFI_ALREADY_STARTED;
|
||
|
}
|
||
|
mDebugExceptionCallback[ExceptionType] = 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;
|
||
|
|
||
|
ASSERT ((ExceptionType >= 0) && (ExceptionType <= MAX_EBC_EXCEPTION));
|
||
|
//
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If someone's registered for exception callbacks, then call them.
|
||
|
//
|
||
|
// EBC driver will register default exception callback to report the
|
||
|
// status code via the status code API
|
||
|
//
|
||
|
if (mDebugExceptionCallback[ExceptionType] != NULL) {
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
EbcContext.ControlFlags = 0;
|
||
|
SystemContext.SystemContextEbc = &EbcContext;
|
||
|
|
||
|
mDebugExceptionCallback[ExceptionType] (ExceptionType, SystemContext);
|
||
|
//
|
||
|
// Restore the context structure and continue to execute
|
||
|
//
|
||
|
VmPtr->R[0] = EbcContext.R0;
|
||
|
VmPtr->R[1] = EbcContext.R1;
|
||
|
VmPtr->R[2] = EbcContext.R2;
|
||
|
VmPtr->R[3] = EbcContext.R3;
|
||
|
VmPtr->R[4] = EbcContext.R4;
|
||
|
VmPtr->R[5] = EbcContext.R5;
|
||
|
VmPtr->R[6] = EbcContext.R6;
|
||
|
VmPtr->R[7] = EbcContext.R7;
|
||
|
VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip;
|
||
|
VmPtr->Flags = EbcContext.Flags;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
InitializeEbcCallback (
|
||
|
IN EFI_DEBUG_SUPPORT_PROTOCOL *This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
To install default Callback function for the VM interpreter.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
This - pointer to the instance of DebugSupport protocol
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
INTN Index;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
//
|
||
|
// For ExceptionCallback
|
||
|
//
|
||
|
for (Index = 0; Index <= MAX_EBC_EXCEPTION; Index++) {
|
||
|
EbcDebugRegisterExceptionCallback (
|
||
|
This,
|
||
|
0,
|
||
|
CommonEbcExceptionHandler,
|
||
|
Index
|
||
|
);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For PeriodicCallback
|
||
|
//
|
||
|
Status = gBS->CreateEvent (
|
||
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
||
|
TPL_NOTIFY,
|
||
|
EbcPeriodicNotifyFunction,
|
||
|
&mVmPtr,
|
||
|
&mEbcPeriodicEvent
|
||
|
);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
Status = gBS->SetTimer (
|
||
|
mEbcPeriodicEvent,
|
||
|
TimerPeriodic,
|
||
|
EBC_VM_PERIODIC_CALLBACK_RATE
|
||
|
);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
STATIC
|
||
|
VOID
|
||
|
CommonEbcExceptionHandler (
|
||
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
||
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
The default Exception Callback for the VM interpreter.
|
||
|
In this function, we report status code, and print debug information
|
||
|
about EBC_CONTEXT, then dead loop.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
InterruptType - Interrupt type.
|
||
|
SystemContext - EBC system context.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// We deadloop here to make it easy to debug this issue.
|
||
|
//
|
||
|
ASSERT (FALSE);
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
STATIC
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
EbcPeriodicNotifyFunction (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
The periodic callback function for EBC VM interpreter, which is used
|
||
|
to support the EFI debug support protocol.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Event - The Periodic Callback Event.
|
||
|
Context - It should be the address of VM_CONTEXT pointer.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
VM_CONTEXT *VmPtr;
|
||
|
|
||
|
VmPtr = *(VM_CONTEXT **)Context;
|
||
|
|
||
|
if (VmPtr != NULL) {
|
||
|
EbcDebugPeriodic (VmPtr);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
STATIC
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
EFI_SYSTEM_CONTEXT_EBC EbcContext;
|
||
|
EFI_SYSTEM_CONTEXT SystemContext;
|
||
|
|
||
|
//
|
||
|
// If someone's registered for periodic callbacks, then call them.
|
||
|
//
|
||
|
if (mDebugPeriodicCallback != NULL) {
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
EbcContext.ControlFlags = 0;
|
||
|
SystemContext.SystemContextEbc = &EbcContext;
|
||
|
|
||
|
mDebugPeriodicCallback (SystemContext);
|
||
|
|
||
|
//
|
||
|
// Restore the context structure and continue to execute
|
||
|
//
|
||
|
VmPtr->R[0] = EbcContext.R0;
|
||
|
VmPtr->R[1] = EbcContext.R1;
|
||
|
VmPtr->R[2] = EbcContext.R2;
|
||
|
VmPtr->R[3] = EbcContext.R3;
|
||
|
VmPtr->R[4] = EbcContext.R4;
|
||
|
VmPtr->R[5] = EbcContext.R5;
|
||
|
VmPtr->R[6] = EbcContext.R6;
|
||
|
VmPtr->R[7] = EbcContext.R7;
|
||
|
VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip;
|
||
|
VmPtr->Flags = EbcContext.Flags;
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
//
|
||
|
ReturnEBCStackByHandle(ImageHandle);
|
||
|
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;
|
||
|
FreePool (ThunkList->ThunkBuffer);
|
||
|
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
|
||
|
//
|
||
|
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
|
||
|
//
|
||
|
ImageList = AllocatePool (sizeof (EBC_IMAGE_LIST));
|
||
|
|
||
|
if (ImageList == NULL) {
|
||
|
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
|
||
|
//
|
||
|
ThunkList = AllocatePool (sizeof (EBC_THUNK_LIST));
|
||
|
|
||
|
if (ThunkList == NULL) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
GetEBCStack(
|
||
|
EFI_HANDLE Handle,
|
||
|
VOID **StackBuffer,
|
||
|
UINTN *BufferIndex
|
||
|
)
|
||
|
{
|
||
|
UINTN Index;
|
||
|
EFI_TPL OldTpl;
|
||
|
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
|
||
|
for (Index = 0; Index < mStackNum; Index ++) {
|
||
|
if (mStackBufferIndex[Index] == NULL) {
|
||
|
mStackBufferIndex[Index] = Handle;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
gBS->RestoreTPL(OldTpl);
|
||
|
if (Index == mStackNum) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
*BufferIndex = Index;
|
||
|
*StackBuffer = mStackBuffer[Index];
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
ReturnEBCStack(
|
||
|
UINTN Index
|
||
|
)
|
||
|
{
|
||
|
mStackBufferIndex[Index] =NULL;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
ReturnEBCStackByHandle(
|
||
|
EFI_HANDLE Handle
|
||
|
)
|
||
|
{
|
||
|
UINTN Index;
|
||
|
for (Index = 0; Index < mStackNum; Index ++) {
|
||
|
if (mStackBufferIndex[Index] == Handle) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (Index == mStackNum) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
mStackBufferIndex[Index] = NULL;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
InitEBCStack (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
for (mStackNum = 0; mStackNum < MAX_STACK_NUM; mStackNum ++) {
|
||
|
mStackBuffer[mStackNum] = AllocatePool(STACK_POOL_SIZE);
|
||
|
mStackBufferIndex[mStackNum] = NULL;
|
||
|
if (mStackBuffer[mStackNum] == NULL) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (mStackNum == 0) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
FreeEBCStack(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
UINTN Index;
|
||
|
for (Index = 0; Index < mStackNum; Index ++) {
|
||
|
FreePool(mStackBuffer[Index]);
|
||
|
}
|
||
|
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
|
||
|
//
|
||
|
EbcVmTestProtocol = AllocatePool (sizeof (EFI_EBC_VM_TEST_PROTOCOL));
|
||
|
if (EbcVmTestProtocol == NULL) {
|
||
|
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)) {
|
||
|
FreePool (EbcVmTestProtocol);
|
||
|
}
|
||
|
return Status;
|
||
|
}
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
EbcVmTestUnsupported ()
|
||
|
{
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
|