--- /dev/null
+/*++\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+ EbcInt.c\r
+\r
+Abstract:\r
+\r
+ Top level module for the EBC virtual machine implementation.\r
+ Provides auxilliary support routines for the VM. That is, routines\r
+ that are not particularly related to VM execution of EBC instructions.\r
+\r
+--*/\r
+\r
+#include "EbcInt.h"\r
+#include "EbcExecute.h"\r
+\r
+//\r
+// We'll keep track of all thunks we create in a linked list. Each\r
+// thunk is tied to an image handle, so we have a linked list of\r
+// image handles, with each having a linked list of thunks allocated\r
+// to that image handle.\r
+//\r
+typedef struct _EBC_THUNK_LIST {\r
+ VOID *ThunkBuffer;\r
+ struct _EBC_THUNK_LIST *Next;\r
+} EBC_THUNK_LIST;\r
+\r
+typedef struct _EBC_IMAGE_LIST {\r
+ struct _EBC_IMAGE_LIST *Next;\r
+ EFI_HANDLE ImageHandle;\r
+ EBC_THUNK_LIST *ThunkList;\r
+} EBC_IMAGE_LIST;\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcUnloadImage (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EFI_HANDLE ImageHandle\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcCreateThunk (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN VOID *EbcEntryPoint,\r
+ OUT VOID **Thunk\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcGetVersion (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN OUT UINT64 *Version\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeEbcCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This\r
+ );\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+CommonEbcExceptionHandler (\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ IN EFI_SYSTEM_CONTEXT SystemContext\r
+ );\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+EbcPeriodicNotifyFunction (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugPeriodic (\r
+ IN VM_CONTEXT *VmPtr\r
+ );\r
+\r
+//\r
+// These two functions and the GUID are used to produce an EBC test protocol.\r
+// This functionality is definitely not required for execution.\r
+//\r
+STATIC\r
+EFI_STATUS\r
+InitEbcVmTestProtocol (\r
+ IN EFI_HANDLE *Handle\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EbcVmTestUnsupported (\r
+ VOID\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcRegisterICacheFlush (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EBC_ICACHE_FLUSH Flush\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugGetMaximumProcessorIndex (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ OUT UINTN *MaxProcessorIndex\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugRegisterPeriodicCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN EFI_PERIODIC_CALLBACK PeriodicCallback\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugRegisterExceptionCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN EFI_EXCEPTION_CALLBACK ExceptionCallback,\r
+ IN EFI_EXCEPTION_TYPE ExceptionType\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugInvalidateInstructionCache (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN VOID *Start,\r
+ IN UINT64 Length\r
+ );\r
+\r
+//\r
+// We have one linked list of image handles for the whole world. Since\r
+// there should only be one interpreter, make them global. They must\r
+// also be global since the execution of an EBC image does not provide\r
+// a This pointer.\r
+//\r
+static EBC_IMAGE_LIST *mEbcImageList = NULL;\r
+\r
+//\r
+// Callback function to flush the icache after thunk creation\r
+//\r
+static EBC_ICACHE_FLUSH mEbcICacheFlush;\r
+\r
+//\r
+// These get set via calls by the debug agent\r
+//\r
+static EFI_PERIODIC_CALLBACK mDebugPeriodicCallback = NULL;\r
+static EFI_EXCEPTION_CALLBACK mDebugExceptionCallback[MAX_EBC_EXCEPTION + 1] = {NULL};\r
+static EFI_GUID mEfiEbcVmTestProtocolGuid = EFI_EBC_VM_TEST_PROTOCOL_GUID;\r
+\r
+static VOID* mStackBuffer[MAX_STACK_NUM];\r
+static EFI_HANDLE mStackBufferIndex[MAX_STACK_NUM];\r
+static UINTN mStackNum = 0;\r
+\r
+//\r
+// Event for Periodic callback\r
+//\r
+static EFI_EVENT mEbcPeriodicEvent;\r
+VM_CONTEXT *mVmPtr = NULL;\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeEbcDriver (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Initializes the VM EFI interface. Allocates memory for the VM interface\r
+ and registers the VM protocol.\r
+\r
+Arguments:\r
+\r
+ ImageHandle - EFI image handle.\r
+ SystemTable - Pointer to the EFI system table.\r
+\r
+Returns:\r
+ Standard EFI status code.\r
+\r
+--*/\r
+{\r
+ EFI_EBC_PROTOCOL *EbcProtocol;\r
+ EFI_EBC_PROTOCOL *OldEbcProtocol;\r
+ EFI_STATUS Status;\r
+ EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN NumHandles;\r
+ UINTN Index;\r
+ BOOLEAN Installed;\r
+\r
+ EbcProtocol = NULL;\r
+ EbcDebugProtocol = NULL;\r
+\r
+ //\r
+ // Allocate memory for our protocol. Then fill in the blanks.\r
+ //\r
+ EbcProtocol = AllocatePool (sizeof (EFI_EBC_PROTOCOL));\r
+\r
+ if (EbcProtocol == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ EbcProtocol->CreateThunk = EbcCreateThunk;\r
+ EbcProtocol->UnloadImage = EbcUnloadImage;\r
+ EbcProtocol->RegisterICacheFlush = EbcRegisterICacheFlush;\r
+ EbcProtocol->GetVersion = EbcGetVersion;\r
+ mEbcICacheFlush = NULL;\r
+\r
+ //\r
+ // Find any already-installed EBC protocols and uninstall them\r
+ //\r
+ Installed = FALSE;\r
+ HandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiEbcProtocolGuid,\r
+ NULL,\r
+ &NumHandles,\r
+ &HandleBuffer\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // Loop through the handles\r
+ //\r
+ for (Index = 0; Index < NumHandles; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[Index],\r
+ &gEfiEbcProtocolGuid,\r
+ (VOID **) &OldEbcProtocol\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ if (gBS->ReinstallProtocolInterface (\r
+ HandleBuffer[Index],\r
+ &gEfiEbcProtocolGuid,\r
+ OldEbcProtocol,\r
+ EbcProtocol\r
+ ) == EFI_SUCCESS) {\r
+ Installed = TRUE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (HandleBuffer != NULL) {\r
+ FreePool (HandleBuffer);\r
+ HandleBuffer = NULL;\r
+ }\r
+ //\r
+ // Add the protocol so someone can locate us if we haven't already.\r
+ //\r
+ if (!Installed) {\r
+ Status = gBS->InstallProtocolInterface (\r
+ &ImageHandle,\r
+ &gEfiEbcProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ EbcProtocol\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (EbcProtocol);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ Status = InitEBCStack();\r
+ if (EFI_ERROR(Status)) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ //\r
+ // Allocate memory for our debug protocol. Then fill in the blanks.\r
+ //\r
+ EbcDebugProtocol = AllocatePool (sizeof (EFI_DEBUG_SUPPORT_PROTOCOL));\r
+\r
+ if (EbcDebugProtocol == NULL) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ EbcDebugProtocol->Isa = IsaEbc;\r
+ EbcDebugProtocol->GetMaximumProcessorIndex = EbcDebugGetMaximumProcessorIndex;\r
+ EbcDebugProtocol->RegisterPeriodicCallback = EbcDebugRegisterPeriodicCallback;\r
+ EbcDebugProtocol->RegisterExceptionCallback = EbcDebugRegisterExceptionCallback;\r
+ EbcDebugProtocol->InvalidateInstructionCache = EbcDebugInvalidateInstructionCache;\r
+\r
+ //\r
+ // Add the protocol so the debug agent can find us\r
+ //\r
+ Status = gBS->InstallProtocolInterface (\r
+ &ImageHandle,\r
+ &gEfiDebugSupportProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ EbcDebugProtocol\r
+ );\r
+ //\r
+ // This is recoverable, so free the memory and continue.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (EbcDebugProtocol);\r
+ goto ErrorExit;\r
+ }\r
+ //\r
+ // Install EbcDebugSupport Protocol Successfully\r
+ // Now we need to initialize the Ebc default Callback\r
+ //\r
+ Status = InitializeEbcCallback (EbcDebugProtocol);\r
+\r
+ //\r
+ // Produce a VM test interface protocol. Not required for execution.\r
+ //\r
+ DEBUG_CODE_BEGIN ();\r
+ InitEbcVmTestProtocol (&ImageHandle);\r
+ DEBUG_CODE_END ();\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+ FreeEBCStack();\r
+ HandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiEbcProtocolGuid,\r
+ NULL,\r
+ &NumHandles,\r
+ &HandleBuffer\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // Loop through the handles\r
+ //\r
+ for (Index = 0; Index < NumHandles; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[Index],\r
+ &gEfiEbcProtocolGuid,\r
+ (VOID **) &OldEbcProtocol\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ gBS->UninstallProtocolInterface (\r
+ HandleBuffer[Index],\r
+ &gEfiEbcProtocolGuid,\r
+ OldEbcProtocol\r
+ );\r
+ }\r
+ }\r
+ }\r
+\r
+ if (HandleBuffer != NULL) {\r
+ FreePool (HandleBuffer);\r
+ HandleBuffer = NULL;\r
+ }\r
+\r
+ FreePool (EbcProtocol);\r
+\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcCreateThunk (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN VOID *EbcEntryPoint,\r
+ OUT VOID **Thunk\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This is the top-level routine plugged into the EBC protocol. Since thunks\r
+ are very processor-specific, from here we dispatch directly to the very\r
+ processor-specific routine EbcCreateThunks().\r
+\r
+Arguments:\r
+\r
+ This - protocol instance pointer\r
+ ImageHandle - handle to the image. The EBC interpreter may use this to keep\r
+ track of any resource allocations performed in loading and\r
+ executing the image.\r
+ EbcEntryPoint - the entry point for the image (as defined in the file header)\r
+ Thunk - pointer to thunk pointer where the address of the created\r
+ thunk is returned.\r
+\r
+Returns:\r
+\r
+ EFI_STATUS\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EbcCreateThunks (\r
+ ImageHandle,\r
+ EbcEntryPoint,\r
+ Thunk,\r
+ FLAG_THUNK_ENTRY_POINT\r
+ );\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugGetMaximumProcessorIndex (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ OUT UINTN *MaxProcessorIndex\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This EBC debugger protocol service is called by the debug agent\r
+\r
+Arguments:\r
+\r
+ This - pointer to the caller's debug support protocol interface\r
+ MaxProcessorIndex - pointer to a caller allocated UINTN in which the maximum\r
+ processor index is returned.\r
+\r
+Returns:\r
+\r
+ Standard EFI_STATUS\r
+\r
+--*/\r
+{\r
+ *MaxProcessorIndex = 0;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugRegisterPeriodicCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN EFI_PERIODIC_CALLBACK PeriodicCallback\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This protocol service is called by the debug agent to register a function\r
+ for us to call on a periodic basis.\r
+\r
+\r
+Arguments:\r
+\r
+ This - pointer to the caller's debug support protocol interface\r
+ PeriodicCallback - pointer to the function to call periodically\r
+\r
+Returns:\r
+\r
+ Always EFI_SUCCESS\r
+\r
+--*/\r
+{\r
+ if ((mDebugPeriodicCallback == NULL) && (PeriodicCallback == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((mDebugPeriodicCallback != NULL) && (PeriodicCallback != NULL)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ mDebugPeriodicCallback = PeriodicCallback;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugRegisterExceptionCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN EFI_EXCEPTION_CALLBACK ExceptionCallback,\r
+ IN EFI_EXCEPTION_TYPE ExceptionType\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This protocol service is called by the debug agent to register a function\r
+ for us to call when we detect an exception.\r
+\r
+\r
+Arguments:\r
+\r
+ This - pointer to the caller's debug support protocol interface\r
+ ExceptionCallback - pointer to the function to the exception\r
+\r
+Returns:\r
+\r
+ Always EFI_SUCCESS\r
+\r
+--*/\r
+{\r
+ if ((ExceptionType < 0) || (ExceptionType > MAX_EBC_EXCEPTION)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((mDebugExceptionCallback[ExceptionType] == NULL) && (ExceptionCallback == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((mDebugExceptionCallback[ExceptionType] != NULL) && (ExceptionCallback != NULL)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+ mDebugExceptionCallback[ExceptionType] = ExceptionCallback;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcDebugInvalidateInstructionCache (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ IN VOID *Start,\r
+ IN UINT64 Length\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This EBC debugger protocol service is called by the debug agent. Required\r
+ for DebugSupport compliance but is only stubbed out for EBC.\r
+\r
+Arguments:\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS\r
+\r
+--*/\r
+{\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EbcDebugSignalException (\r
+ IN EFI_EXCEPTION_TYPE ExceptionType,\r
+ IN EXCEPTION_FLAGS ExceptionFlags,\r
+ IN VM_CONTEXT *VmPtr\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ The VM interpreter calls this function when an exception is detected.\r
+\r
+Arguments:\r
+\r
+ VmPtr - pointer to a VM context for passing info to the EFI debugger.\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS if it returns at all\r
+\r
+--*/\r
+{\r
+ EFI_SYSTEM_CONTEXT_EBC EbcContext;\r
+ EFI_SYSTEM_CONTEXT SystemContext;\r
+\r
+ ASSERT ((ExceptionType >= 0) && (ExceptionType <= MAX_EBC_EXCEPTION));\r
+ //\r
+ // Save the exception in the context passed in\r
+ //\r
+ VmPtr->ExceptionFlags |= ExceptionFlags;\r
+ VmPtr->LastException = ExceptionType;\r
+ //\r
+ // If it's a fatal exception, then flag it in the VM context in case an\r
+ // attached debugger tries to return from it.\r
+ //\r
+ if (ExceptionFlags & EXCEPTION_FLAG_FATAL) {\r
+ VmPtr->StopFlags |= STOPFLAG_APP_DONE;\r
+ }\r
+\r
+ //\r
+ // If someone's registered for exception callbacks, then call them.\r
+ //\r
+ // EBC driver will register default exception callback to report the\r
+ // status code via the status code API\r
+ //\r
+ if (mDebugExceptionCallback[ExceptionType] != NULL) {\r
+\r
+ //\r
+ // Initialize the context structure\r
+ //\r
+ EbcContext.R0 = VmPtr->R[0];\r
+ EbcContext.R1 = VmPtr->R[1];\r
+ EbcContext.R2 = VmPtr->R[2];\r
+ EbcContext.R3 = VmPtr->R[3];\r
+ EbcContext.R4 = VmPtr->R[4];\r
+ EbcContext.R5 = VmPtr->R[5];\r
+ EbcContext.R6 = VmPtr->R[6];\r
+ EbcContext.R7 = VmPtr->R[7];\r
+ EbcContext.Ip = (UINT64)(UINTN)VmPtr->Ip;\r
+ EbcContext.Flags = VmPtr->Flags;\r
+ EbcContext.ControlFlags = 0;\r
+ SystemContext.SystemContextEbc = &EbcContext;\r
+\r
+ mDebugExceptionCallback[ExceptionType] (ExceptionType, SystemContext);\r
+ //\r
+ // Restore the context structure and continue to execute\r
+ //\r
+ VmPtr->R[0] = EbcContext.R0;\r
+ VmPtr->R[1] = EbcContext.R1;\r
+ VmPtr->R[2] = EbcContext.R2;\r
+ VmPtr->R[3] = EbcContext.R3;\r
+ VmPtr->R[4] = EbcContext.R4;\r
+ VmPtr->R[5] = EbcContext.R5;\r
+ VmPtr->R[6] = EbcContext.R6;\r
+ VmPtr->R[7] = EbcContext.R7;\r
+ VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip;\r
+ VmPtr->Flags = EbcContext.Flags;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+InitializeEbcCallback (\r
+ IN EFI_DEBUG_SUPPORT_PROTOCOL *This\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ To install default Callback function for the VM interpreter.\r
+\r
+Arguments:\r
+\r
+ This - pointer to the instance of DebugSupport protocol\r
+\r
+Returns:\r
+\r
+ None\r
+\r
+--*/\r
+{\r
+ INTN Index;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // For ExceptionCallback\r
+ //\r
+ for (Index = 0; Index <= MAX_EBC_EXCEPTION; Index++) {\r
+ EbcDebugRegisterExceptionCallback (\r
+ This,\r
+ 0,\r
+ CommonEbcExceptionHandler,\r
+ Index\r
+ );\r
+ }\r
+\r
+ //\r
+ // For PeriodicCallback\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ EbcPeriodicNotifyFunction,\r
+ &mVmPtr,\r
+ &mEbcPeriodicEvent\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->SetTimer (\r
+ mEbcPeriodicEvent,\r
+ TimerPeriodic,\r
+ EBC_VM_PERIODIC_CALLBACK_RATE\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+VOID\r
+CommonEbcExceptionHandler (\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ IN EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ The default Exception Callback for the VM interpreter.\r
+ In this function, we report status code, and print debug information\r
+ about EBC_CONTEXT, then dead loop.\r
+\r
+Arguments:\r
+\r
+ InterruptType - Interrupt type.\r
+ SystemContext - EBC system context.\r
+\r
+Returns:\r
+\r
+ None\r
+\r
+--*/\r
+{\r
+ //\r
+ // We deadloop here to make it easy to debug this issue.\r
+ //\r
+ ASSERT (FALSE);\r
+\r
+ return ;\r
+}\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+EbcPeriodicNotifyFunction (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ The periodic callback function for EBC VM interpreter, which is used\r
+ to support the EFI debug support protocol.\r
+\r
+Arguments:\r
+\r
+ Event - The Periodic Callback Event.\r
+ Context - It should be the address of VM_CONTEXT pointer.\r
+\r
+Returns:\r
+\r
+ None.\r
+\r
+--*/\r
+{\r
+ VM_CONTEXT *VmPtr;\r
+\r
+ VmPtr = *(VM_CONTEXT **)Context;\r
+\r
+ if (VmPtr != NULL) {\r
+ EbcDebugPeriodic (VmPtr);\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EbcDebugPeriodic (\r
+ IN VM_CONTEXT *VmPtr\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ The VM interpreter calls this function on a periodic basis to support\r
+ the EFI debug support protocol.\r
+\r
+Arguments:\r
+\r
+ VmPtr - pointer to a VM context for passing info to the debugger.\r
+\r
+Returns:\r
+\r
+ Standard EFI status.\r
+\r
+--*/\r
+{\r
+ EFI_SYSTEM_CONTEXT_EBC EbcContext;\r
+ EFI_SYSTEM_CONTEXT SystemContext;\r
+\r
+ //\r
+ // If someone's registered for periodic callbacks, then call them.\r
+ //\r
+ if (mDebugPeriodicCallback != NULL) {\r
+\r
+ //\r
+ // Initialize the context structure\r
+ //\r
+ EbcContext.R0 = VmPtr->R[0];\r
+ EbcContext.R1 = VmPtr->R[1];\r
+ EbcContext.R2 = VmPtr->R[2];\r
+ EbcContext.R3 = VmPtr->R[3];\r
+ EbcContext.R4 = VmPtr->R[4];\r
+ EbcContext.R5 = VmPtr->R[5];\r
+ EbcContext.R6 = VmPtr->R[6];\r
+ EbcContext.R7 = VmPtr->R[7];\r
+ EbcContext.Ip = (UINT64)(UINTN)VmPtr->Ip;\r
+ EbcContext.Flags = VmPtr->Flags;\r
+ EbcContext.ControlFlags = 0;\r
+ SystemContext.SystemContextEbc = &EbcContext;\r
+\r
+ mDebugPeriodicCallback (SystemContext);\r
+\r
+ //\r
+ // Restore the context structure and continue to execute\r
+ //\r
+ VmPtr->R[0] = EbcContext.R0;\r
+ VmPtr->R[1] = EbcContext.R1;\r
+ VmPtr->R[2] = EbcContext.R2;\r
+ VmPtr->R[3] = EbcContext.R3;\r
+ VmPtr->R[4] = EbcContext.R4;\r
+ VmPtr->R[5] = EbcContext.R5;\r
+ VmPtr->R[6] = EbcContext.R6;\r
+ VmPtr->R[7] = EbcContext.R7;\r
+ VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip;\r
+ VmPtr->Flags = EbcContext.Flags;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcUnloadImage (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This routine is called by the core when an image is being unloaded from\r
+ memory. Basically we now have the opportunity to do any necessary cleanup.\r
+ Typically this will include freeing any memory allocated for thunk-creation.\r
+\r
+Arguments:\r
+\r
+ This - protocol instance pointer\r
+ ImageHandle - handle to the image being unloaded.\r
+\r
+Returns:\r
+\r
+ EFI_INVALID_PARAMETER - the ImageHandle passed in was not found in\r
+ the internal list of EBC image handles.\r
+ EFI_STATUS - completed successfully\r
+\r
+--*/\r
+{\r
+ EBC_THUNK_LIST *ThunkList;\r
+ EBC_THUNK_LIST *NextThunkList;\r
+ EBC_IMAGE_LIST *ImageList;\r
+ EBC_IMAGE_LIST *PrevImageList;\r
+ //\r
+ // First go through our list of known image handles and see if we've already\r
+ // created an image list element for this image handle.\r
+ //\r
+ ReturnEBCStackByHandle(ImageHandle);\r
+ PrevImageList = NULL;\r
+ for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) {\r
+ if (ImageList->ImageHandle == ImageHandle) {\r
+ break;\r
+ }\r
+ //\r
+ // Save the previous so we can connect the lists when we remove this one\r
+ //\r
+ PrevImageList = ImageList;\r
+ }\r
+\r
+ if (ImageList == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // Free up all the thunk buffers and thunks list elements for this image\r
+ // handle.\r
+ //\r
+ ThunkList = ImageList->ThunkList;\r
+ while (ThunkList != NULL) {\r
+ NextThunkList = ThunkList->Next;\r
+ FreePool (ThunkList->ThunkBuffer);\r
+ FreePool (ThunkList);\r
+ ThunkList = NextThunkList;\r
+ }\r
+ //\r
+ // Now remove this image list element from the chain\r
+ //\r
+ if (PrevImageList == NULL) {\r
+ //\r
+ // Remove from head\r
+ //\r
+ mEbcImageList = ImageList->Next;\r
+ } else {\r
+ PrevImageList->Next = ImageList->Next;\r
+ }\r
+ //\r
+ // Now free up the image list element\r
+ //\r
+ FreePool (ImageList);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EbcAddImageThunk (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN VOID *ThunkBuffer,\r
+ IN UINT32 ThunkSize\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Add a thunk to our list of thunks for a given image handle.\r
+ Also flush the instruction cache since we've written thunk code\r
+ to memory that will be executed eventually.\r
+\r
+Arguments:\r
+\r
+ ImageHandle - the image handle to which the thunk is tied\r
+ ThunkBuffer - the buffer we've created/allocated\r
+ ThunkSize - the size of the thunk memory allocated\r
+\r
+Returns:\r
+\r
+ EFI_OUT_OF_RESOURCES - memory allocation failed\r
+ EFI_SUCCESS - successful completion\r
+\r
+--*/\r
+{\r
+ EBC_THUNK_LIST *ThunkList;\r
+ EBC_IMAGE_LIST *ImageList;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // It so far so good, then flush the instruction cache\r
+ //\r
+ if (mEbcICacheFlush != NULL) {\r
+ Status = mEbcICacheFlush ((EFI_PHYSICAL_ADDRESS) (UINTN) ThunkBuffer, ThunkSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ //\r
+ // Go through our list of known image handles and see if we've already\r
+ // created a image list element for this image handle.\r
+ //\r
+ for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) {\r
+ if (ImageList->ImageHandle == ImageHandle) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (ImageList == NULL) {\r
+ //\r
+ // Allocate a new one\r
+ //\r
+ ImageList = AllocatePool (sizeof (EBC_IMAGE_LIST));\r
+\r
+ if (ImageList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ImageList->ThunkList = NULL;\r
+ ImageList->ImageHandle = ImageHandle;\r
+ ImageList->Next = mEbcImageList;\r
+ mEbcImageList = ImageList;\r
+ }\r
+ //\r
+ // Ok, now create a new thunk element to add to the list\r
+ //\r
+ ThunkList = AllocatePool (sizeof (EBC_THUNK_LIST));\r
+\r
+ if (ThunkList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Add it to the head of the list\r
+ //\r
+ ThunkList->Next = ImageList->ThunkList;\r
+ ThunkList->ThunkBuffer = ThunkBuffer;\r
+ ImageList->ThunkList = ThunkList;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcRegisterICacheFlush (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN EBC_ICACHE_FLUSH Flush\r
+ )\r
+{\r
+ mEbcICacheFlush = Flush;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EbcGetVersion (\r
+ IN EFI_EBC_PROTOCOL *This,\r
+ IN OUT UINT64 *Version\r
+ )\r
+{\r
+ if (Version == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *Version = GetVmVersion ();\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+GetEBCStack(\r
+ EFI_HANDLE Handle,\r
+ VOID **StackBuffer,\r
+ UINTN *BufferIndex\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_TPL OldTpl;\r
+ OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);\r
+ for (Index = 0; Index < mStackNum; Index ++) {\r
+ if (mStackBufferIndex[Index] == NULL) {\r
+ mStackBufferIndex[Index] = Handle;\r
+ break;\r
+ }\r
+ }\r
+ gBS->RestoreTPL(OldTpl);\r
+ if (Index == mStackNum) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ *BufferIndex = Index;\r
+ *StackBuffer = mStackBuffer[Index];\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+ReturnEBCStack(\r
+ UINTN Index\r
+ )\r
+{\r
+ mStackBufferIndex[Index] =NULL;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+ReturnEBCStackByHandle(\r
+ EFI_HANDLE Handle\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < mStackNum; Index ++) {\r
+ if (mStackBufferIndex[Index] == Handle) {\r
+ break;\r
+ }\r
+ }\r
+ if (Index == mStackNum) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ mStackBufferIndex[Index] = NULL;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+InitEBCStack (\r
+ VOID\r
+ )\r
+{\r
+ for (mStackNum = 0; mStackNum < MAX_STACK_NUM; mStackNum ++) {\r
+ mStackBuffer[mStackNum] = AllocatePool(STACK_POOL_SIZE);\r
+ mStackBufferIndex[mStackNum] = NULL;\r
+ if (mStackBuffer[mStackNum] == NULL) {\r
+ break;\r
+ }\r
+ }\r
+ if (mStackNum == 0) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+FreeEBCStack(\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < mStackNum; Index ++) {\r
+ FreePool(mStackBuffer[Index]);\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+STATIC\r
+EFI_STATUS\r
+InitEbcVmTestProtocol (\r
+ IN EFI_HANDLE *IHandle\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Produce an EBC VM test protocol that can be used for regression tests.\r
+\r
+Arguments:\r
+\r
+ IHandle - handle on which to install the protocol.\r
+\r
+Returns:\r
+\r
+ EFI_OUT_OF_RESOURCES - memory allocation failed\r
+ EFI_SUCCESS - successful completion\r
+\r
+--*/\r
+{\r
+ EFI_HANDLE Handle;\r
+ EFI_STATUS Status;\r
+ EFI_EBC_VM_TEST_PROTOCOL *EbcVmTestProtocol;\r
+\r
+ //\r
+ // Allocate memory for the protocol, then fill in the fields\r
+ //\r
+ EbcVmTestProtocol = AllocatePool (sizeof (EFI_EBC_VM_TEST_PROTOCOL));\r
+ if (EbcVmTestProtocol == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ EbcVmTestProtocol->Execute = (EBC_VM_TEST_EXECUTE) EbcExecuteInstructions;\r
+\r
+ DEBUG_CODE_BEGIN ();\r
+ EbcVmTestProtocol->Assemble = (EBC_VM_TEST_ASM) EbcVmTestUnsupported;\r
+ EbcVmTestProtocol->Disassemble = (EBC_VM_TEST_DASM) EbcVmTestUnsupported;\r
+ DEBUG_CODE_END ();\r
+\r
+ //\r
+ // Publish the protocol\r
+ //\r
+ Handle = NULL;\r
+ Status = gBS->InstallProtocolInterface (&Handle, &mEfiEbcVmTestProtocolGuid, EFI_NATIVE_INTERFACE, EbcVmTestProtocol);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (EbcVmTestProtocol);\r
+ }\r
+ return Status;\r
+}\r
+STATIC\r
+EFI_STATUS\r
+EbcVmTestUnsupported ()\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r