3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Top level module for the EBC virtual machine implementation.
19 Provides auxilliary support routines for the VM. That is, routines
20 that are not particularly related to VM execution of EBC instructions.
25 #include "EbcExecute.h"
28 // We'll keep track of all thunks we create in a linked list. Each
29 // thunk is tied to an image handle, so we have a linked list of
30 // image handles, with each having a linked list of thunks allocated
31 // to that image handle.
33 typedef struct _EBC_THUNK_LIST
{
35 struct _EBC_THUNK_LIST
*Next
;
38 typedef struct _EBC_IMAGE_LIST
{
39 struct _EBC_IMAGE_LIST
*Next
;
40 EFI_HANDLE ImageHandle
;
41 EBC_THUNK_LIST
*ThunkList
;
45 // Function prototypes
50 IN EFI_HANDLE ImageHandle
,
51 IN EFI_SYSTEM_TABLE
*SystemTable
58 IN EFI_EBC_PROTOCOL
*This
,
59 IN EFI_HANDLE ImageHandle
66 IN EFI_EBC_PROTOCOL
*This
,
67 IN EFI_HANDLE ImageHandle
,
68 IN VOID
*EbcEntryPoint
,
76 IN EFI_EBC_PROTOCOL
*This
,
77 IN OUT UINT64
*Version
81 // These two functions and the GUID are used to produce an EBC test protocol.
82 // This functionality is definitely not required for execution.
86 InitEbcVmTestProtocol (
92 EbcVmTestUnsupported (
99 EbcRegisterICacheFlush (
100 IN EFI_EBC_PROTOCOL
*This
,
101 IN EBC_ICACHE_FLUSH Flush
107 EbcDebugGetMaximumProcessorIndex (
108 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
109 OUT UINTN
*MaxProcessorIndex
115 EbcDebugRegisterPeriodicCallback (
116 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
117 IN UINTN ProcessorIndex
,
118 IN EFI_PERIODIC_CALLBACK PeriodicCallback
124 EbcDebugRegisterExceptionCallback (
125 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
126 IN UINTN ProcessorIndex
,
127 IN EFI_EXCEPTION_CALLBACK ExceptionCallback
,
128 IN EFI_EXCEPTION_TYPE ExceptionType
134 EbcDebugInvalidateInstructionCache (
135 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
136 IN UINTN ProcessorIndex
,
142 // We have one linked list of image handles for the whole world. Since
143 // there should only be one interpreter, make them global. They must
144 // also be global since the execution of an EBC image does not provide
147 static EBC_IMAGE_LIST
*mEbcImageList
= NULL
;
150 // Callback function to flush the icache after thunk creation
152 static EBC_ICACHE_FLUSH mEbcICacheFlush
;
155 // These get set via calls by the debug agent
157 static EFI_PERIODIC_CALLBACK mDebugPeriodicCallback
= NULL
;
158 static EFI_EXCEPTION_CALLBACK mDebugExceptionCallback
= NULL
;
159 static EFI_GUID mEfiEbcVmTestProtocolGuid
= EFI_EBC_VM_TEST_PROTOCOL_GUID
;
163 InitializeEbcDriver (
164 IN EFI_HANDLE ImageHandle
,
165 IN EFI_SYSTEM_TABLE
*SystemTable
171 Initializes the VM EFI interface. Allocates memory for the VM interface
172 and registers the VM protocol.
176 ImageHandle - EFI image handle.
177 SystemTable - Pointer to the EFI system table.
180 Standard EFI status code.
184 EFI_EBC_PROTOCOL
*EbcProtocol
;
185 EFI_EBC_PROTOCOL
*OldEbcProtocol
;
187 EFI_DEBUG_SUPPORT_PROTOCOL
*EbcDebugProtocol
;
188 EFI_HANDLE
*HandleBuffer
;
194 // Allocate memory for our protocol. Then fill in the blanks.
196 Status
= gBS
->AllocatePool (
198 sizeof (EFI_EBC_PROTOCOL
),
199 (VOID
**) &EbcProtocol
201 if (Status
!= EFI_SUCCESS
) {
202 return EFI_OUT_OF_RESOURCES
;
205 EbcProtocol
->CreateThunk
= EbcCreateThunk
;
206 EbcProtocol
->UnloadImage
= EbcUnloadImage
;
207 EbcProtocol
->RegisterICacheFlush
= EbcRegisterICacheFlush
;
208 EbcProtocol
->GetVersion
= EbcGetVersion
;
209 mEbcICacheFlush
= NULL
;
212 // Find any already-installed EBC protocols and uninstall them
216 Status
= gBS
->LocateHandleBuffer (
218 &gEfiEbcProtocolGuid
,
223 if (Status
== EFI_SUCCESS
) {
225 // Loop through the handles
227 for (Index
= 0; Index
< NumHandles
; Index
++) {
228 Status
= gBS
->HandleProtocol (
230 &gEfiEbcProtocolGuid
,
231 (VOID
**) &OldEbcProtocol
233 if (Status
== EFI_SUCCESS
) {
234 if (gBS
->ReinstallProtocolInterface (
236 &gEfiEbcProtocolGuid
,
246 if (HandleBuffer
!= NULL
) {
247 gBS
->FreePool (HandleBuffer
);
251 // Add the protocol so someone can locate us if we haven't already.
254 Status
= gBS
->InstallProtocolInterface (
256 &gEfiEbcProtocolGuid
,
257 EFI_NATIVE_INTERFACE
,
260 if (EFI_ERROR (Status
)) {
261 gBS
->FreePool (EbcProtocol
);
266 // Allocate memory for our debug protocol. Then fill in the blanks.
268 Status
= gBS
->AllocatePool (
270 sizeof (EFI_DEBUG_SUPPORT_PROTOCOL
),
271 (VOID
**) &EbcDebugProtocol
273 if (Status
!= EFI_SUCCESS
) {
274 return EFI_OUT_OF_RESOURCES
;
277 EbcDebugProtocol
->Isa
= IsaEbc
;
278 EbcDebugProtocol
->GetMaximumProcessorIndex
= EbcDebugGetMaximumProcessorIndex
;
279 EbcDebugProtocol
->RegisterPeriodicCallback
= EbcDebugRegisterPeriodicCallback
;
280 EbcDebugProtocol
->RegisterExceptionCallback
= EbcDebugRegisterExceptionCallback
;
281 EbcDebugProtocol
->InvalidateInstructionCache
= EbcDebugInvalidateInstructionCache
;
284 // Add the protocol so the debug agent can find us
286 Status
= gBS
->InstallProtocolInterface (
288 &gEfiDebugSupportProtocolGuid
,
289 EFI_NATIVE_INTERFACE
,
293 // This is recoverable, so free the memory and continue.
295 if (EFI_ERROR (Status
)) {
296 gBS
->FreePool (EbcDebugProtocol
);
299 // Produce a VM test interface protocol. Not required for execution.
302 InitEbcVmTestProtocol (&ImageHandle
);
312 IN EFI_EBC_PROTOCOL
*This
,
313 IN EFI_HANDLE ImageHandle
,
314 IN VOID
*EbcEntryPoint
,
321 This is the top-level routine plugged into the EBC protocol. Since thunks
322 are very processor-specific, from here we dispatch directly to the very
323 processor-specific routine EbcCreateThunks().
327 This - protocol instance pointer
328 ImageHandle - handle to the image. The EBC interpreter may use this to keep
329 track of any resource allocations performed in loading and
331 EbcEntryPoint - the entry point for the image (as defined in the file header)
332 Thunk - pointer to thunk pointer where the address of the created
343 Status
= EbcCreateThunks (
347 FLAG_THUNK_ENTRY_POINT
355 EbcDebugGetMaximumProcessorIndex (
356 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
357 OUT UINTN
*MaxProcessorIndex
363 This EBC debugger protocol service is called by the debug agent
367 This - pointer to the caller's debug support protocol interface
368 MaxProcessorIndex - pointer to a caller allocated UINTN in which the maximum
369 processor index is returned.
377 *MaxProcessorIndex
= 0;
384 EbcDebugRegisterPeriodicCallback (
385 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
386 IN UINTN ProcessorIndex
,
387 IN EFI_PERIODIC_CALLBACK PeriodicCallback
393 This protocol service is called by the debug agent to register a function
394 for us to call on a periodic basis.
399 This - pointer to the caller's debug support protocol interface
400 PeriodicCallback - pointer to the function to call periodically
408 mDebugPeriodicCallback
= PeriodicCallback
;
415 EbcDebugRegisterExceptionCallback (
416 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
417 IN UINTN ProcessorIndex
,
418 IN EFI_EXCEPTION_CALLBACK ExceptionCallback
,
419 IN EFI_EXCEPTION_TYPE ExceptionType
425 This protocol service is called by the debug agent to register a function
426 for us to call when we detect an exception.
431 This - pointer to the caller's debug support protocol interface
432 PeriodicCallback - pointer to the function to call periodically
440 mDebugExceptionCallback
= ExceptionCallback
;
447 EbcDebugInvalidateInstructionCache (
448 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
449 IN UINTN ProcessorIndex
,
457 This EBC debugger protocol service is called by the debug agent. Required
458 for DebugSupport compliance but is only stubbed out for EBC.
472 EbcDebugSignalException (
473 IN EFI_EXCEPTION_TYPE ExceptionType
,
474 IN EXCEPTION_FLAGS ExceptionFlags
,
481 The VM interpreter calls this function when an exception is detected.
485 VmPtr - pointer to a VM context for passing info to the EFI debugger.
489 EFI_SUCCESS if it returns at all
493 EFI_SYSTEM_CONTEXT_EBC EbcContext
;
494 EFI_SYSTEM_CONTEXT SystemContext
;
495 EFI_STATUS_CODE_VALUE StatusCodeValue
;
498 // Save the exception in the context passed in
500 VmPtr
->ExceptionFlags
|= ExceptionFlags
;
501 VmPtr
->LastException
= ExceptionType
;
503 // If it's a fatal exception, then flag it in the VM context in case an
504 // attached debugger tries to return from it.
506 if (ExceptionFlags
& EXCEPTION_FLAG_FATAL
) {
507 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
510 // Initialize the context structure
512 EbcContext
.R0
= VmPtr
->R
[0];
513 EbcContext
.R1
= VmPtr
->R
[1];
514 EbcContext
.R2
= VmPtr
->R
[2];
515 EbcContext
.R3
= VmPtr
->R
[3];
516 EbcContext
.R4
= VmPtr
->R
[4];
517 EbcContext
.R5
= VmPtr
->R
[5];
518 EbcContext
.R6
= VmPtr
->R
[6];
519 EbcContext
.R7
= VmPtr
->R
[7];
520 EbcContext
.Ip
= (UINT64
) (UINTN
) VmPtr
->Ip
;
521 EbcContext
.Flags
= VmPtr
->Flags
;
522 SystemContext
.SystemContextEbc
= &EbcContext
;
524 // If someone's registered for exception callbacks, then call them.
525 // Otherwise report the status code via the status code API
527 if (mDebugExceptionCallback
!= NULL
) {
528 mDebugExceptionCallback (ExceptionType
, SystemContext
);
531 // Determine if we should report the exception. We report all of them by default,
532 // but if a debugger is attached don't report the breakpoint, debug, and step exceptions.
533 // Note that EXCEPT_EBC_OVERFLOW is never reported by this VM implementation, so is
534 // not included in the switch statement.
537 switch (ExceptionType
) {
538 case EXCEPT_EBC_UNDEFINED
:
539 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_UNDEFINED
;
542 case EXCEPT_EBC_DIVIDE_ERROR
:
543 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_DIVIDE_ERROR
;
546 case EXCEPT_EBC_DEBUG
:
547 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_DEBUG
;
548 Report
= (BOOLEAN
) ((mDebugExceptionCallback
== NULL
) ? TRUE
: FALSE
);
551 case EXCEPT_EBC_BREAKPOINT
:
552 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_BREAKPOINT
;
553 Report
= (BOOLEAN
) ((mDebugExceptionCallback
== NULL
) ? TRUE
: FALSE
);
556 case EXCEPT_EBC_INVALID_OPCODE
:
557 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_INVALID_OPCODE
;
560 case EXCEPT_EBC_STACK_FAULT
:
561 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_STACK_FAULT
;
564 case EXCEPT_EBC_ALIGNMENT_CHECK
:
565 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_ALIGNMENT_CHECK
;
568 case EXCEPT_EBC_INSTRUCTION_ENCODING
:
569 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_INSTRUCTION_ENCODING
;
572 case EXCEPT_EBC_BAD_BREAK
:
573 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_BAD_BREAK
;
576 case EXCEPT_EBC_STEP
:
577 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_EBC_STEP
;
578 Report
= (BOOLEAN
) ((mDebugExceptionCallback
== NULL
) ? TRUE
: FALSE
);
582 StatusCodeValue
= EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_NON_SPECIFIC
;
586 // If we determined that we should report the condition, then do so now.
589 REPORT_STATUS_CODE (EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
, StatusCodeValue
);
592 switch (ExceptionType
) {
594 // If ReportStatusCode returned, then for most exceptions we do an assert. The
595 // ExceptionType++ is done simply to force the ASSERT() condition to be met.
596 // For breakpoints, assume a debugger did not insert a software breakpoint
597 // and skip the instruction.
599 case EXCEPT_EBC_BREAKPOINT
:
603 case EXCEPT_EBC_STEP
:
606 case EXCEPT_EBC_UNDEFINED
:
608 ASSERT (ExceptionType
== EXCEPT_EBC_UNDEFINED
);
611 case EXCEPT_EBC_DIVIDE_ERROR
:
613 ASSERT (ExceptionType
== EXCEPT_EBC_DIVIDE_ERROR
);
616 case EXCEPT_EBC_DEBUG
:
618 ASSERT (ExceptionType
== EXCEPT_EBC_DEBUG
);
621 case EXCEPT_EBC_INVALID_OPCODE
:
623 ASSERT (ExceptionType
== EXCEPT_EBC_INVALID_OPCODE
);
626 case EXCEPT_EBC_STACK_FAULT
:
628 ASSERT (ExceptionType
== EXCEPT_EBC_STACK_FAULT
);
631 case EXCEPT_EBC_ALIGNMENT_CHECK
:
633 ASSERT (ExceptionType
== EXCEPT_EBC_ALIGNMENT_CHECK
);
636 case EXCEPT_EBC_INSTRUCTION_ENCODING
:
638 ASSERT (ExceptionType
== EXCEPT_EBC_INSTRUCTION_ENCODING
);
641 case EXCEPT_EBC_BAD_BREAK
:
643 ASSERT (ExceptionType
== EXCEPT_EBC_BAD_BREAK
);
665 The VM interpreter calls this function on a periodic basis to support
666 the EFI debug support protocol.
670 VmPtr - pointer to a VM context for passing info to the debugger.
685 IN EFI_EBC_PROTOCOL
*This
,
686 IN EFI_HANDLE ImageHandle
692 This routine is called by the core when an image is being unloaded from
693 memory. Basically we now have the opportunity to do any necessary cleanup.
694 Typically this will include freeing any memory allocated for thunk-creation.
698 This - protocol instance pointer
699 ImageHandle - handle to the image being unloaded.
703 EFI_INVALID_PARAMETER - the ImageHandle passed in was not found in
704 the internal list of EBC image handles.
705 EFI_STATUS - completed successfully
709 EBC_THUNK_LIST
*ThunkList
;
710 EBC_THUNK_LIST
*NextThunkList
;
711 EBC_IMAGE_LIST
*ImageList
;
712 EBC_IMAGE_LIST
*PrevImageList
;
714 // First go through our list of known image handles and see if we've already
715 // created an image list element for this image handle.
717 PrevImageList
= NULL
;
718 for (ImageList
= mEbcImageList
; ImageList
!= NULL
; ImageList
= ImageList
->Next
) {
719 if (ImageList
->ImageHandle
== ImageHandle
) {
723 // Save the previous so we can connect the lists when we remove this one
725 PrevImageList
= ImageList
;
728 if (ImageList
== NULL
) {
729 return EFI_INVALID_PARAMETER
;
732 // Free up all the thunk buffers and thunks list elements for this image
735 ThunkList
= ImageList
->ThunkList
;
736 while (ThunkList
!= NULL
) {
737 NextThunkList
= ThunkList
->Next
;
738 gBS
->FreePool (ThunkList
->ThunkBuffer
);
739 gBS
->FreePool (ThunkList
);
740 ThunkList
= NextThunkList
;
743 // Now remove this image list element from the chain
745 if (PrevImageList
== NULL
) {
749 mEbcImageList
= ImageList
->Next
;
751 PrevImageList
->Next
= ImageList
->Next
;
754 // Now free up the image list element
756 gBS
->FreePool (ImageList
);
762 IN EFI_HANDLE ImageHandle
,
763 IN VOID
*ThunkBuffer
,
770 Add a thunk to our list of thunks for a given image handle.
771 Also flush the instruction cache since we've written thunk code
772 to memory that will be executed eventually.
776 ImageHandle - the image handle to which the thunk is tied
777 ThunkBuffer - the buffer we've created/allocated
778 ThunkSize - the size of the thunk memory allocated
782 EFI_OUT_OF_RESOURCES - memory allocation failed
783 EFI_SUCCESS - successful completion
787 EBC_THUNK_LIST
*ThunkList
;
788 EBC_IMAGE_LIST
*ImageList
;
792 // It so far so good, then flush the instruction cache
794 if (mEbcICacheFlush
!= NULL
) {
795 Status
= mEbcICacheFlush ((EFI_PHYSICAL_ADDRESS
) (UINTN
) ThunkBuffer
, ThunkSize
);
796 if (EFI_ERROR (Status
)) {
801 // Go through our list of known image handles and see if we've already
802 // created a image list element for this image handle.
804 for (ImageList
= mEbcImageList
; ImageList
!= NULL
; ImageList
= ImageList
->Next
) {
805 if (ImageList
->ImageHandle
== ImageHandle
) {
810 if (ImageList
== NULL
) {
812 // Allocate a new one
814 Status
= gBS
->AllocatePool (
816 sizeof (EBC_IMAGE_LIST
),
819 if (Status
!= EFI_SUCCESS
) {
820 return EFI_OUT_OF_RESOURCES
;
823 ImageList
->ThunkList
= NULL
;
824 ImageList
->ImageHandle
= ImageHandle
;
825 ImageList
->Next
= mEbcImageList
;
826 mEbcImageList
= ImageList
;
829 // Ok, now create a new thunk element to add to the list
831 Status
= gBS
->AllocatePool (
833 sizeof (EBC_THUNK_LIST
),
836 if (Status
!= EFI_SUCCESS
) {
837 return EFI_OUT_OF_RESOURCES
;
840 // Add it to the head of the list
842 ThunkList
->Next
= ImageList
->ThunkList
;
843 ThunkList
->ThunkBuffer
= ThunkBuffer
;
844 ImageList
->ThunkList
= ThunkList
;
851 EbcRegisterICacheFlush (
852 IN EFI_EBC_PROTOCOL
*This
,
853 IN EBC_ICACHE_FLUSH Flush
856 mEbcICacheFlush
= Flush
;
864 IN EFI_EBC_PROTOCOL
*This
,
865 IN OUT UINT64
*Version
868 if (Version
== NULL
) {
869 return EFI_INVALID_PARAMETER
;
872 *Version
= GetVmVersion ();
878 InitEbcVmTestProtocol (
879 IN EFI_HANDLE
*IHandle
885 Produce an EBC VM test protocol that can be used for regression tests.
889 IHandle - handle on which to install the protocol.
893 EFI_OUT_OF_RESOURCES - memory allocation failed
894 EFI_SUCCESS - successful completion
900 EFI_EBC_VM_TEST_PROTOCOL
*EbcVmTestProtocol
;
903 // Allocate memory for the protocol, then fill in the fields
905 Status
= gBS
->AllocatePool (EfiBootServicesData
, sizeof (EFI_EBC_VM_TEST_PROTOCOL
), (VOID
**) &EbcVmTestProtocol
);
906 if (Status
!= EFI_SUCCESS
) {
907 return EFI_OUT_OF_RESOURCES
;
909 EbcVmTestProtocol
->Execute
= (EBC_VM_TEST_EXECUTE
) EbcExecuteInstructions
;
912 EbcVmTestProtocol
->Assemble
= (EBC_VM_TEST_ASM
) EbcVmTestUnsupported
;
913 EbcVmTestProtocol
->Disassemble
= (EBC_VM_TEST_DASM
) EbcVmTestUnsupported
;
917 // Publish the protocol
920 Status
= gBS
->InstallProtocolInterface (&Handle
, &mEfiEbcVmTestProtocolGuid
, EFI_NATIVE_INTERFACE
, EbcVmTestProtocol
);
921 if (EFI_ERROR (Status
)) {
922 gBS
->FreePool (EbcVmTestProtocol
);
928 EbcVmTestUnsupported ()
930 return EFI_UNSUPPORTED
;