Implement the new EDK2 PE/COFF image emulator protocol so that we can remove the EBC specific handling in the DXE core and other places in the core code. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Hao Wu <hao.a.wu@intel.com>
		
			
				
	
	
		
			1543 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1543 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Top level module for the EBC virtual machine implementation.
 | |
|   Provides auxiliary support routines for the VM. That is, routines
 | |
|   that are not particularly related to VM execution of EBC instructions.
 | |
| 
 | |
| Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "EbcInt.h"
 | |
| #include "EbcExecute.h"
 | |
| #include "EbcDebuggerHook.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 EBC_THUNK_LIST;
 | |
| struct _EBC_THUNK_LIST {
 | |
|   VOID            *ThunkBuffer;
 | |
|   EBC_THUNK_LIST  *Next;
 | |
| };
 | |
| 
 | |
| typedef struct _EBC_IMAGE_LIST EBC_IMAGE_LIST;
 | |
| struct _EBC_IMAGE_LIST {
 | |
|   EBC_IMAGE_LIST  *Next;
 | |
|   EFI_HANDLE      ImageHandle;
 | |
|   EBC_THUNK_LIST  *ThunkList;
 | |
| };
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  ImageHandle           Handle of image for which the thunk is being
 | |
|                                 created.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER The ImageHandle passed in was not found in the
 | |
|                                 internal list of EBC image handles.
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcUnloadImage (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EFI_HANDLE         ImageHandle
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   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().
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  ImageHandle           Handle of image for which the thunk is being
 | |
|                                 created. The EBC interpreter may use this to
 | |
|                                 keep track of any resource allocations
 | |
|                                 performed in loading and executing the image.
 | |
|   @param  EbcEntryPoint         Address of the actual EBC entry point or
 | |
|                                 protocol service the thunk should call.
 | |
|   @param  Thunk                 Returned pointer to a thunk created.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_INVALID_PARAMETER Image entry point is not 2-byte aligned.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Memory could not be allocated for the thunk.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcCreateThunk (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EFI_HANDLE         ImageHandle,
 | |
|   IN VOID               *EbcEntryPoint,
 | |
|   OUT VOID              **Thunk
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Called to get the version of the interpreter.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  Version               Pointer to where to store the returned version
 | |
|                                 of the interpreter.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_INVALID_PARAMETER Version pointer is NULL.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcGetVersion (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN OUT UINT64         *Version
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   To install default Callback function for the VM interpreter.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval Others                Some error occurs when creating periodic event.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitializeEbcCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @param  InterruptType          Interrupt type.
 | |
|   @param  SystemContext          EBC system context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| CommonEbcExceptionHandler (
 | |
|   IN EFI_EXCEPTION_TYPE   InterruptType,
 | |
|   IN EFI_SYSTEM_CONTEXT   SystemContext
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   The periodic callback function for EBC VM interpreter, which is used
 | |
|   to support the EFI debug support protocol.
 | |
| 
 | |
|   @param  Event                  The Periodic Callback Event.
 | |
|   @param  Context                It should be the address of VM_CONTEXT pointer.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| EbcPeriodicNotifyFunction (
 | |
|   IN EFI_EVENT     Event,
 | |
|   IN VOID          *Context
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   The VM interpreter calls this function on a periodic basis to support
 | |
|   the EFI debug support protocol.
 | |
| 
 | |
|   @param  VmPtr                  Pointer to a VM context for passing info to the
 | |
|                                  debugger.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The function completed successfully.
 | |
| 
 | |
| **/
 | |
| 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.
 | |
| //
 | |
| /**
 | |
|   Produces an EBC VM test protocol that can be used for regression tests.
 | |
| 
 | |
|   @param  IHandle                Handle on which to install the protocol.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
 | |
|   @retval EFI_SUCCESS            The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| InitEbcVmTestProtocol (
 | |
|   IN EFI_HANDLE     *IHandle
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Returns the EFI_UNSUPPORTED Status.
 | |
| 
 | |
|   @return EFI_UNSUPPORTED  This function always return EFI_UNSUPPORTED status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcVmTestUnsupported (
 | |
|   VOID
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Registers a callback function that the EBC interpreter calls to flush the
 | |
|   processor instruction cache following creation of thunks.
 | |
| 
 | |
|   @param  This        A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  Flush       Pointer to a function of type EBC_ICACH_FLUSH.
 | |
| 
 | |
|   @retval EFI_SUCCESS The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcRegisterICacheFlush (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EBC_ICACHE_FLUSH   Flush
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This EBC debugger protocol service is called by the debug agent
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the
 | |
|                                 maximum supported processor index is returned.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugGetMaximumProcessorIndex (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
 | |
|   OUT UINTN                              *MaxProcessorIndex
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This protocol service is called by the debug agent to register a function
 | |
|   for us to call on a periodic basis.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  PeriodicCallback      A pointer to a function of type
 | |
|                                 PERIODIC_CALLBACK that is the main periodic
 | |
|                                 entry point of the debug agent. It receives as a
 | |
|                                 parameter a pointer to the full context of the
 | |
|                                 interrupted execution thread.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a
 | |
|                                 callback function was previously registered.
 | |
|   @retval EFI_INVALID_PARAMETER Null PeriodicCallback parameter when no
 | |
|                                 callback function was previously registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugRegisterPeriodicCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This,
 | |
|   IN UINTN                       ProcessorIndex,
 | |
|   IN EFI_PERIODIC_CALLBACK       PeriodicCallback
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This protocol service is called by the debug agent to register a function
 | |
|   for us to call when we detect an exception.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  ExceptionCallback     A pointer to a function of type
 | |
|                                 EXCEPTION_CALLBACK that is called when the
 | |
|                                 processor exception specified by ExceptionType
 | |
|                                 occurs. Passing NULL unregisters any previously
 | |
|                                 registered function associated with
 | |
|                                 ExceptionType.
 | |
|   @param  ExceptionType         Specifies which processor exception to hook.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_ALREADY_STARTED   Non-NULL ExceptionCallback parameter when a
 | |
|                                 callback function was previously registered.
 | |
|   @retval EFI_INVALID_PARAMETER ExceptionType parameter is negative or exceeds
 | |
|                                 MAX_EBC_EXCEPTION.
 | |
|   @retval EFI_INVALID_PARAMETER Null ExceptionCallback parameter when no
 | |
|                                 callback function was previously registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugRegisterExceptionCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This,
 | |
|   IN UINTN                       ProcessorIndex,
 | |
|   IN EFI_EXCEPTION_CALLBACK      ExceptionCallback,
 | |
|   IN EFI_EXCEPTION_TYPE          ExceptionType
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This EBC debugger protocol service is called by the debug agent.  Required
 | |
|   for DebugSupport compliance but is only stubbed out for EBC.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  Start                 StartSpecifies the physical base of the memory
 | |
|                                 range to be invalidated.
 | |
|   @param  Length                Specifies the minimum number of bytes in the
 | |
|                                 processor's instruction cache to invalidate.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| 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.
 | |
| //
 | |
| EBC_IMAGE_LIST         *mEbcImageList = NULL;
 | |
| 
 | |
| //
 | |
| // Callback function to flush the icache after thunk creation
 | |
| //
 | |
| EBC_ICACHE_FLUSH       mEbcICacheFlush;
 | |
| 
 | |
| //
 | |
| // These get set via calls by the debug agent
 | |
| //
 | |
| EFI_PERIODIC_CALLBACK  mDebugPeriodicCallback = NULL;
 | |
| EFI_EXCEPTION_CALLBACK mDebugExceptionCallback[MAX_EBC_EXCEPTION + 1] = {NULL};
 | |
| 
 | |
| VOID                   *mStackBuffer[MAX_STACK_NUM];
 | |
| EFI_HANDLE             mStackBufferIndex[MAX_STACK_NUM];
 | |
| UINTN                  mStackNum = 0;
 | |
| 
 | |
| //
 | |
| // Event for Periodic callback
 | |
| //
 | |
| EFI_EVENT              mEbcPeriodicEvent;
 | |
| VM_CONTEXT             *mVmPtr = NULL;
 | |
| 
 | |
| /**
 | |
|   Check whether the emulator supports executing a certain PE/COFF image
 | |
| 
 | |
|   @param[in] This         This pointer for EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL
 | |
|                           structure
 | |
|   @param[in] ImageType    Whether the image is an application, a boot time
 | |
|                           driver or a runtime driver.
 | |
|   @param[in] DevicePath   Path to device where the image originated
 | |
|                           (e.g., a PCI option ROM)
 | |
| 
 | |
|   @retval TRUE            The image is supported by the emulator
 | |
|   @retval FALSE           The image is not supported by the emulator.
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| EbcIsImageSupported (
 | |
|   IN  EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL    *This,
 | |
|   IN  UINT16                                  ImageType,
 | |
|   IN  EFI_DEVICE_PATH_PROTOCOL                *DevicePath   OPTIONAL
 | |
|   )
 | |
| {
 | |
|   if (ImageType != EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION &&
 | |
|       ImageType != EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) {
 | |
|     return FALSE;
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Register a supported PE/COFF image with the emulator. After this call
 | |
|   completes successfully, the PE/COFF image may be started as usual, and
 | |
|   it is the responsibility of the emulator implementation that any branch
 | |
|   into the code section of the image (including returns from functions called
 | |
|   from the foreign code) is executed as if it were running on the machine
 | |
|   type it was built for.
 | |
| 
 | |
|   @param[in]      This          This pointer for
 | |
|                                 EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL structure
 | |
|   @param[in]      ImageBase     The base address in memory of the PE/COFF image
 | |
|   @param[in]      ImageSize     The size in memory of the PE/COFF image
 | |
|   @param[in,out]  EntryPoint    The entry point of the PE/COFF image. Passed by
 | |
|                                 reference so that the emulator may modify it.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The image was registered with the emulator and
 | |
|                                 can be started as usual.
 | |
|   @retval other                 The image could not be registered.
 | |
| 
 | |
|   If the PE/COFF machine type or image type are not supported by the emulator,
 | |
|   then ASSERT().
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcRegisterImage (
 | |
|   IN      EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL    *This,
 | |
|   IN      EFI_PHYSICAL_ADDRESS                    ImageBase,
 | |
|   IN      UINT64                                  ImageSize,
 | |
|   IN  OUT EFI_IMAGE_ENTRY_POINT                   *EntryPoint
 | |
|   )
 | |
| {
 | |
|   DEBUG_CODE_BEGIN ();
 | |
|     PE_COFF_LOADER_IMAGE_CONTEXT  ImageContext;
 | |
|     EFI_STATUS                    Status;
 | |
| 
 | |
|     ZeroMem (&ImageContext, sizeof (ImageContext));
 | |
| 
 | |
|     ImageContext.Handle    = (VOID *)(UINTN)ImageBase;
 | |
|     ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
 | |
| 
 | |
|     Status = PeCoffLoaderGetImageInfo (&ImageContext);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     ASSERT (ImageContext.Machine == EFI_IMAGE_MACHINE_EBC);
 | |
|     ASSERT (ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION ||
 | |
|             ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER);
 | |
|   DEBUG_CODE_END ();
 | |
| 
 | |
|   EbcRegisterICacheFlush (NULL,
 | |
|     (EBC_ICACHE_FLUSH)InvalidateInstructionCacheRange);
 | |
| 
 | |
|   return EbcCreateThunk (NULL, (VOID *)(UINTN)ImageBase,
 | |
|            (VOID *)(UINTN)*EntryPoint, (VOID **)EntryPoint);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Unregister a PE/COFF image that has been registered with the emulator.
 | |
|   This should be done before the image is unloaded from memory.
 | |
| 
 | |
|   @param[in] This         This pointer for EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL
 | |
|                           structure
 | |
|   @param[in] ImageBase    The base address in memory of the PE/COFF image
 | |
| 
 | |
|   @retval EFI_SUCCESS     The image was unregistered with the emulator.
 | |
|   @retval other           Image could not be unloaded.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcUnregisterImage (
 | |
|   IN  EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL    *This,
 | |
|   IN  EFI_PHYSICAL_ADDRESS                    ImageBase
 | |
|   )
 | |
| {
 | |
|   return EbcUnloadImage (NULL, (VOID *)(UINTN)ImageBase);
 | |
| }
 | |
| 
 | |
| STATIC EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL mPeCoffEmuProtocol = {
 | |
|   EbcIsImageSupported,
 | |
|   EbcRegisterImage,
 | |
|   EbcUnregisterImage,
 | |
|   EDKII_PECOFF_IMAGE_EMULATOR_VERSION,
 | |
|   EFI_IMAGE_MACHINE_EBC
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Initializes the VM EFI interface.  Allocates memory for the VM interface
 | |
|   and registers the VM protocol.
 | |
| 
 | |
|   @param  ImageHandle            EFI image handle.
 | |
|   @param  SystemTable            Pointer to the EFI system table.
 | |
| 
 | |
|   @return Standard EFI status code.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitializeEbcDriver (
 | |
|   IN EFI_HANDLE           ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE     *SystemTable
 | |
|   )
 | |
| {
 | |
|   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->InstallMultipleProtocolInterfaces (
 | |
|                     &ImageHandle,
 | |
|                     &gEfiEbcProtocolGuid, EbcProtocol,
 | |
|                     &gEdkiiPeCoffImageEmulatorProtocolGuid, &mPeCoffEmuProtocol,
 | |
|                     NULL
 | |
|                     );
 | |
|     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 ();
 | |
| 
 | |
|   EbcDebuggerHookInit (ImageHandle, EbcDebugProtocol);
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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().
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  ImageHandle           Handle of image for which the thunk is being
 | |
|                                 created. The EBC interpreter may use this to
 | |
|                                 keep track of any resource allocations
 | |
|                                 performed in loading and executing the image.
 | |
|   @param  EbcEntryPoint         Address of the actual EBC entry point or
 | |
|                                 protocol service the thunk should call.
 | |
|   @param  Thunk                 Returned pointer to a thunk created.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_INVALID_PARAMETER Image entry point is not 2-byte aligned.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Memory could not be allocated for the thunk.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcCreateThunk (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EFI_HANDLE         ImageHandle,
 | |
|   IN VOID               *EbcEntryPoint,
 | |
|   OUT VOID              **Thunk
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = EbcCreateThunks (
 | |
|             ImageHandle,
 | |
|             EbcEntryPoint,
 | |
|             Thunk,
 | |
|             FLAG_THUNK_ENTRY_POINT
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This EBC debugger protocol service is called by the debug agent
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the
 | |
|                                 maximum supported processor index is returned.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugGetMaximumProcessorIndex (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
 | |
|   OUT UINTN                              *MaxProcessorIndex
 | |
|   )
 | |
| {
 | |
|   *MaxProcessorIndex = 0;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This protocol service is called by the debug agent to register a function
 | |
|   for us to call on a periodic basis.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  PeriodicCallback      A pointer to a function of type
 | |
|                                 PERIODIC_CALLBACK that is the main periodic
 | |
|                                 entry point of the debug agent. It receives as a
 | |
|                                 parameter a pointer to the full context of the
 | |
|                                 interrupted execution thread.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a
 | |
|                                 callback function was previously registered.
 | |
|   @retval EFI_INVALID_PARAMETER Null PeriodicCallback parameter when no
 | |
|                                 callback function was previously registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugRegisterPeriodicCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This,
 | |
|   IN UINTN                       ProcessorIndex,
 | |
|   IN EFI_PERIODIC_CALLBACK       PeriodicCallback
 | |
|   )
 | |
| {
 | |
|   if ((mDebugPeriodicCallback == NULL) && (PeriodicCallback == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   if ((mDebugPeriodicCallback != NULL) && (PeriodicCallback != NULL)) {
 | |
|     return EFI_ALREADY_STARTED;
 | |
|   }
 | |
| 
 | |
|   mDebugPeriodicCallback = PeriodicCallback;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This protocol service is called by the debug agent to register a function
 | |
|   for us to call when we detect an exception.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  ExceptionCallback     A pointer to a function of type
 | |
|                                 EXCEPTION_CALLBACK that is called when the
 | |
|                                 processor exception specified by ExceptionType
 | |
|                                 occurs. Passing NULL unregisters any previously
 | |
|                                 registered function associated with
 | |
|                                 ExceptionType.
 | |
|   @param  ExceptionType         Specifies which processor exception to hook.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_ALREADY_STARTED   Non-NULL ExceptionCallback parameter when a
 | |
|                                 callback function was previously registered.
 | |
|   @retval EFI_INVALID_PARAMETER ExceptionType parameter is negative or exceeds
 | |
|                                 MAX_EBC_EXCEPTION.
 | |
|   @retval EFI_INVALID_PARAMETER Null ExceptionCallback parameter when no
 | |
|                                 callback function was previously registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugRegisterExceptionCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This,
 | |
|   IN UINTN                       ProcessorIndex,
 | |
|   IN EFI_EXCEPTION_CALLBACK      ExceptionCallback,
 | |
|   IN EFI_EXCEPTION_TYPE          ExceptionType
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This EBC debugger protocol service is called by the debug agent.  Required
 | |
|   for DebugSupport compliance but is only stubbed out for EBC.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
|   @param  ProcessorIndex        Specifies which processor the callback function
 | |
|                                 applies to.
 | |
|   @param  Start                 StartSpecifies the physical base of the memory
 | |
|                                 range to be invalidated.
 | |
|   @param  Length                Specifies the minimum number of bytes in the
 | |
|                                 processor's instruction cache to invalidate.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugInvalidateInstructionCache (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
 | |
|   IN UINTN                               ProcessorIndex,
 | |
|   IN VOID                                *Start,
 | |
|   IN UINT64                              Length
 | |
|   )
 | |
| {
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The VM interpreter calls this function when an exception is detected.
 | |
| 
 | |
|   @param  ExceptionType          Specifies the processor exception detected.
 | |
|   @param  ExceptionFlags         Specifies the exception context.
 | |
|   @param  VmPtr                  Pointer to a VM context for passing info to the
 | |
|                                  EFI debugger.
 | |
| 
 | |
|   @retval EFI_SUCCESS            This function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EbcDebugSignalException (
 | |
|   IN EFI_EXCEPTION_TYPE                   ExceptionType,
 | |
|   IN EXCEPTION_FLAGS                      ExceptionFlags,
 | |
|   IN VM_CONTEXT                           *VmPtr
 | |
|   )
 | |
| {
 | |
|   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 = (UINTN) 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) != 0) {
 | |
|     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                   = (UINT64) VmPtr->Gpr[0];
 | |
|     EbcContext.R1                   = (UINT64) VmPtr->Gpr[1];
 | |
|     EbcContext.R2                   = (UINT64) VmPtr->Gpr[2];
 | |
|     EbcContext.R3                   = (UINT64) VmPtr->Gpr[3];
 | |
|     EbcContext.R4                   = (UINT64) VmPtr->Gpr[4];
 | |
|     EbcContext.R5                   = (UINT64) VmPtr->Gpr[5];
 | |
|     EbcContext.R6                   = (UINT64) VmPtr->Gpr[6];
 | |
|     EbcContext.R7                   = (UINT64) VmPtr->Gpr[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->Gpr[0]  = EbcContext.R0;
 | |
|     VmPtr->Gpr[1]  = EbcContext.R1;
 | |
|     VmPtr->Gpr[2]  = EbcContext.R2;
 | |
|     VmPtr->Gpr[3]  = EbcContext.R3;
 | |
|     VmPtr->Gpr[4]  = EbcContext.R4;
 | |
|     VmPtr->Gpr[5]  = EbcContext.R5;
 | |
|     VmPtr->Gpr[6]  = EbcContext.R6;
 | |
|     VmPtr->Gpr[7]  = EbcContext.R7;
 | |
|     VmPtr->Ip    = (VMIP)(UINTN)EbcContext.Ip;
 | |
|     VmPtr->Flags = EbcContext.Flags;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   To install default Callback function for the VM interpreter.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL
 | |
|                                 instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval Others                Some error occurs when creating periodic event.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitializeEbcCallback (
 | |
|   IN EFI_DEBUG_SUPPORT_PROTOCOL  *This
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @param  InterruptType          Interrupt type.
 | |
|   @param  SystemContext          EBC system context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| CommonEbcExceptionHandler (
 | |
|   IN EFI_EXCEPTION_TYPE   InterruptType,
 | |
|   IN EFI_SYSTEM_CONTEXT   SystemContext
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // We print debug information to let user know what happen.
 | |
|   //
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "EBC Interrupter Version - 0x%016lx\n",
 | |
|     (UINT64) (((VM_MAJOR_VERSION & 0xFFFF) << 16) | ((VM_MINOR_VERSION & 0xFFFF)))
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "Exception Type - 0x%016lx\n",
 | |
|     (UINT64)(UINTN)InterruptType
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  R0 - 0x%016lx, R1 - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->R0,
 | |
|     SystemContext.SystemContextEbc->R1
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  R2 - 0x%016lx, R3 - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->R2,
 | |
|     SystemContext.SystemContextEbc->R3
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  R4 - 0x%016lx, R5 - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->R4,
 | |
|     SystemContext.SystemContextEbc->R5
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  R6 - 0x%016lx, R7 - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->R6,
 | |
|     SystemContext.SystemContextEbc->R7
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  Flags - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->Flags
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  ControlFlags - 0x%016lx\n",
 | |
|     SystemContext.SystemContextEbc->ControlFlags
 | |
|     ));
 | |
|   DEBUG ((
 | |
|     EFI_D_ERROR,
 | |
|     "  Ip - 0x%016lx\n\n",
 | |
|     SystemContext.SystemContextEbc->Ip
 | |
|     ));
 | |
| 
 | |
|   //
 | |
|   // We deadloop here to make it easy to debug this issue.
 | |
|   //
 | |
|   CpuDeadLoop ();
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The periodic callback function for EBC VM interpreter, which is used
 | |
|   to support the EFI debug support protocol.
 | |
| 
 | |
|   @param  Event                  The Periodic Callback Event.
 | |
|   @param  Context                It should be the address of VM_CONTEXT pointer.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| EbcPeriodicNotifyFunction (
 | |
|   IN EFI_EVENT     Event,
 | |
|   IN VOID          *Context
 | |
|   )
 | |
| {
 | |
|   VM_CONTEXT *VmPtr;
 | |
| 
 | |
|   VmPtr = *(VM_CONTEXT **)Context;
 | |
| 
 | |
|   if (VmPtr != NULL) {
 | |
|     EbcDebugPeriodic (VmPtr);
 | |
|   }
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The VM interpreter calls this function on a periodic basis to support
 | |
|   the EFI debug support protocol.
 | |
| 
 | |
|   @param  VmPtr                  Pointer to a VM context for passing info to the
 | |
|                                  debugger.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcDebugPeriodic (
 | |
|   IN VM_CONTEXT *VmPtr
 | |
|   )
 | |
| {
 | |
|   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                   = (UINT64) VmPtr->Gpr[0];
 | |
|     EbcContext.R1                   = (UINT64) VmPtr->Gpr[1];
 | |
|     EbcContext.R2                   = (UINT64) VmPtr->Gpr[2];
 | |
|     EbcContext.R3                   = (UINT64) VmPtr->Gpr[3];
 | |
|     EbcContext.R4                   = (UINT64) VmPtr->Gpr[4];
 | |
|     EbcContext.R5                   = (UINT64) VmPtr->Gpr[5];
 | |
|     EbcContext.R6                   = (UINT64) VmPtr->Gpr[6];
 | |
|     EbcContext.R7                   = (UINT64) VmPtr->Gpr[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->Gpr[0]  = EbcContext.R0;
 | |
|     VmPtr->Gpr[1]  = EbcContext.R1;
 | |
|     VmPtr->Gpr[2]  = EbcContext.R2;
 | |
|     VmPtr->Gpr[3]  = EbcContext.R3;
 | |
|     VmPtr->Gpr[4]  = EbcContext.R4;
 | |
|     VmPtr->Gpr[5]  = EbcContext.R5;
 | |
|     VmPtr->Gpr[6]  = EbcContext.R6;
 | |
|     VmPtr->Gpr[7]  = EbcContext.R7;
 | |
|     VmPtr->Ip    = (VMIP)(UINTN)EbcContext.Ip;
 | |
|     VmPtr->Flags = EbcContext.Flags;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  ImageHandle           Handle of image for which the thunk is being
 | |
|                                 created.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER The ImageHandle passed in was not found in the
 | |
|                                 internal list of EBC image handles.
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcUnloadImage (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EFI_HANDLE         ImageHandle
 | |
|   )
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   EbcDebuggerHookEbcUnloadImage (ImageHandle);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @param  ImageHandle            The image handle to which the thunk is tied.
 | |
|   @param  ThunkBuffer            The buffer that has been created/allocated.
 | |
|   @param  ThunkSize              The size of the thunk memory allocated.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
 | |
|   @retval EFI_SUCCESS            The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EbcAddImageThunk (
 | |
|   IN EFI_HANDLE      ImageHandle,
 | |
|   IN VOID            *ThunkBuffer,
 | |
|   IN UINT32          ThunkSize
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Registers a callback function that the EBC interpreter calls to flush the
 | |
|   processor instruction cache following creation of thunks.
 | |
| 
 | |
|   @param  This        A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  Flush       Pointer to a function of type EBC_ICACH_FLUSH.
 | |
| 
 | |
|   @retval EFI_SUCCESS The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcRegisterICacheFlush (
 | |
|   IN EFI_EBC_PROTOCOL   *This,
 | |
|   IN EBC_ICACHE_FLUSH   Flush
 | |
|   )
 | |
| {
 | |
|   mEbcICacheFlush = Flush;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Called to get the version of the interpreter.
 | |
| 
 | |
|   @param  This                  A pointer to the EFI_EBC_PROTOCOL instance.
 | |
|   @param  Version               Pointer to where to store the returned version
 | |
|                                 of the interpreter.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully.
 | |
|   @retval EFI_INVALID_PARAMETER Version pointer is NULL.
 | |
| 
 | |
| **/
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns the stack index and buffer assosicated with the Handle parameter.
 | |
| 
 | |
|   @param  Handle                The EFI handle as the index to the EBC stack.
 | |
|   @param  StackBuffer           A pointer to hold the returned stack buffer.
 | |
|   @param  BufferIndex           A pointer to hold the returned stack index.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES  The Handle parameter does not correspond to any
 | |
|                                 existing EBC stack.
 | |
|   @retval EFI_SUCCESS           The stack index and buffer were found and
 | |
|                                 returned to the caller.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| GetEBCStack(
 | |
|   IN  EFI_HANDLE Handle,
 | |
|   OUT VOID       **StackBuffer,
 | |
|   OUT 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns from the EBC stack by stack Index.
 | |
| 
 | |
|   @param  Index        Specifies which EBC stack to return from.
 | |
| 
 | |
|   @retval EFI_SUCCESS  The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReturnEBCStack(
 | |
|   IN UINTN Index
 | |
|   )
 | |
| {
 | |
|   mStackBufferIndex[Index] = NULL;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns from the EBC stack associated with the Handle parameter.
 | |
| 
 | |
|   @param  Handle      Specifies the EFI handle to find the EBC stack with.
 | |
| 
 | |
|   @retval EFI_SUCCESS The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReturnEBCStackByHandle(
 | |
|   IN 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Allocates memory to hold all the EBC stacks.
 | |
| 
 | |
|   @retval EFI_SUCCESS          The EBC stacks were allocated successfully.
 | |
|   @retval EFI_OUT_OF_RESOURCES Not enough memory available for EBC stacks.
 | |
| 
 | |
| **/
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Free all EBC stacks allocated before.
 | |
| 
 | |
|   @retval EFI_SUCCESS   All the EBC stacks were freed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FreeEBCStack(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINTN Index;
 | |
|   for (Index = 0; Index < mStackNum; Index ++) {
 | |
|     FreePool(mStackBuffer[Index]);
 | |
|   }
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Produces an EBC VM test protocol that can be used for regression tests.
 | |
| 
 | |
|   @param  IHandle                Handle on which to install the protocol.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
 | |
|   @retval EFI_SUCCESS            The function completed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| InitEbcVmTestProtocol (
 | |
|   IN EFI_HANDLE     *IHandle
 | |
|   )
 | |
| {
 | |
|   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, &gEfiEbcVmTestProtocolGuid, EFI_NATIVE_INTERFACE, EbcVmTestProtocol);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (EbcVmTestProtocol);
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Returns the EFI_UNSUPPORTED Status.
 | |
| 
 | |
|   @return EFI_UNSUPPORTED  This function always return EFI_UNSUPPORTED status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EbcVmTestUnsupported (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   return EFI_UNSUPPORTED;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Allocates a buffer of type EfiBootServicesCode.
 | |
| 
 | |
|   @param  AllocationSize        The number of bytes to allocate.
 | |
| 
 | |
|   @return A pointer to the allocated buffer or NULL if allocation fails.
 | |
| 
 | |
| **/
 | |
| VOID *
 | |
| EFIAPI
 | |
| EbcAllocatePoolForThunk (
 | |
|   IN UINTN  AllocationSize
 | |
|   )
 | |
| {
 | |
|   VOID        *Buffer;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = gBS->AllocatePool (EfiBootServicesCode, AllocationSize, &Buffer);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return NULL;
 | |
|   }
 | |
|   return Buffer;
 | |
| }
 |