X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FUniversal%2FEbc%2FDxe%2FEbcInt.c;fp=MdeModulePkg%2FUniversal%2FEbc%2FDxe%2FEbcInt.c;h=2b647d9bae3792c8a0d29b590d0f95db85bdb88f;hp=0000000000000000000000000000000000000000;hb=d7dec593eaf18c9c2e197e92a4434e21c4f8e9b7;hpb=eb5f1a7fc7fd6f2edb306ca001f19b218abf0d7d diff --git a/MdeModulePkg/Universal/Ebc/Dxe/EbcInt.c b/MdeModulePkg/Universal/Ebc/Dxe/EbcInt.c new file mode 100644 index 0000000000..2b647d9bae --- /dev/null +++ b/MdeModulePkg/Universal/Ebc/Dxe/EbcInt.c @@ -0,0 +1,1172 @@ +/*++ + +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; +} +