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
[MAX_EBC_EXCEPTION
+ 1] = {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 if ((mDebugPeriodicCallback
== NULL
) && (PeriodicCallback
== NULL
)) {
409 return EFI_INVALID_PARAMETER
;
411 if ((mDebugPeriodicCallback
!= NULL
) && (PeriodicCallback
!= NULL
)) {
412 return EFI_ALREADY_STARTED
;
415 mDebugPeriodicCallback
= PeriodicCallback
;
422 EbcDebugRegisterExceptionCallback (
423 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
424 IN UINTN ProcessorIndex
,
425 IN EFI_EXCEPTION_CALLBACK ExceptionCallback
,
426 IN EFI_EXCEPTION_TYPE ExceptionType
432 This protocol service is called by the debug agent to register a function
433 for us to call when we detect an exception.
438 This - pointer to the caller's debug support protocol interface
439 PeriodicCallback - pointer to the function to call periodically
447 if ((ExceptionType
< 0) || (ExceptionType
> MAX_EBC_EXCEPTION
)) {
448 return EFI_INVALID_PARAMETER
;
450 if ((mDebugExceptionCallback
[ExceptionType
] == NULL
) && (ExceptionCallback
== NULL
)) {
451 return EFI_INVALID_PARAMETER
;
453 if ((mDebugExceptionCallback
[ExceptionType
] != NULL
) && (ExceptionCallback
!= NULL
)) {
454 return EFI_ALREADY_STARTED
;
456 mDebugExceptionCallback
[ExceptionType
] = ExceptionCallback
;
463 EbcDebugInvalidateInstructionCache (
464 IN EFI_DEBUG_SUPPORT_PROTOCOL
*This
,
465 IN UINTN ProcessorIndex
,
473 This EBC debugger protocol service is called by the debug agent. Required
474 for DebugSupport compliance but is only stubbed out for EBC.
488 EbcDebugSignalException (
489 IN EFI_EXCEPTION_TYPE ExceptionType
,
490 IN EXCEPTION_FLAGS ExceptionFlags
,
497 The VM interpreter calls this function when an exception is detected.
501 VmPtr - pointer to a VM context for passing info to the EFI debugger.
505 EFI_SUCCESS if it returns at all
509 EFI_SYSTEM_CONTEXT_EBC EbcContext
;
510 EFI_SYSTEM_CONTEXT SystemContext
;
511 EFI_STATUS_CODE_VALUE StatusCodeValue
;
514 // Save the exception in the context passed in
516 VmPtr
->ExceptionFlags
|= ExceptionFlags
;
517 VmPtr
->LastException
= ExceptionType
;
519 // If it's a fatal exception, then flag it in the VM context in case an
520 // attached debugger tries to return from it.
522 if (ExceptionFlags
& EXCEPTION_FLAG_FATAL
) {
523 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
526 // Initialize the context structure
528 EbcContext
.R0
= VmPtr
->R
[0];
529 EbcContext
.R1
= VmPtr
->R
[1];
530 EbcContext
.R2
= VmPtr
->R
[2];
531 EbcContext
.R3
= VmPtr
->R
[3];
532 EbcContext
.R4
= VmPtr
->R
[4];
533 EbcContext
.R5
= VmPtr
->R
[5];
534 EbcContext
.R6
= VmPtr
->R
[6];
535 EbcContext
.R7
= VmPtr
->R
[7];
536 EbcContext
.Ip
= (UINT64
) (UINTN
) VmPtr
->Ip
;
537 EbcContext
.Flags
= VmPtr
->Flags
;
538 EbcContext
.ControlFlags
= 0;
539 SystemContext
.SystemContextEbc
= &EbcContext
;
541 // If someone's registered for exception callbacks, then call them.
542 // Otherwise report the status code via the status code API
544 if ((ExceptionType
>= 0) && (ExceptionType
<= MAX_EBC_EXCEPTION
) &&
545 (mDebugExceptionCallback
[ExceptionType
] != NULL
)) {
546 mDebugExceptionCallback
[ExceptionType
] (ExceptionType
, SystemContext
);
549 // Determine if we should report the exception. We report all of them by default,
550 // but if a debugger is attached don't report the breakpoint, debug, and step exceptions.
551 // Note that EXCEPT_EBC_OVERFLOW is never reported by this VM implementation, so is
552 // not included in the switch statement.
555 switch (ExceptionType
) {
556 case EXCEPT_EBC_UNDEFINED
:
557 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_UNDEFINED
;
560 case EXCEPT_EBC_DIVIDE_ERROR
:
561 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_DIVIDE_ERROR
;
564 case EXCEPT_EBC_DEBUG
:
565 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_DEBUG
;
566 Report
= (BOOLEAN
) ((mDebugExceptionCallback
[ExceptionType
] == NULL
) ? TRUE
: FALSE
);
569 case EXCEPT_EBC_BREAKPOINT
:
570 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_BREAKPOINT
;
571 Report
= (BOOLEAN
) ((mDebugExceptionCallback
[ExceptionType
] == NULL
) ? TRUE
: FALSE
);
574 case EXCEPT_EBC_INVALID_OPCODE
:
575 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_INVALID_OPCODE
;
578 case EXCEPT_EBC_STACK_FAULT
:
579 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_STACK_FAULT
;
582 case EXCEPT_EBC_ALIGNMENT_CHECK
:
583 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_ALIGNMENT_CHECK
;
586 case EXCEPT_EBC_INSTRUCTION_ENCODING
:
587 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_INSTRUCTION_ENCODING
;
590 case EXCEPT_EBC_BAD_BREAK
:
591 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_BAD_BREAK
;
594 case EXCEPT_EBC_STEP
:
595 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_EBC_STEP
;
596 Report
= (BOOLEAN
) ((mDebugExceptionCallback
[ExceptionType
] == NULL
) ? TRUE
: FALSE
);
600 StatusCodeValue
= EFI_SOFTWARE_EBC_EXCEPTION
| EFI_SW_EC_NON_SPECIFIC
;
604 // If we determined that we should report the condition, then do so now.
607 REPORT_STATUS_CODE (EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
, StatusCodeValue
);
610 switch (ExceptionType
) {
612 // If ReportStatusCode returned, then for most exceptions we do an assert. The
613 // ExceptionType++ is done simply to force the ASSERT() condition to be met.
614 // For breakpoints, assume a debugger did not insert a software breakpoint
615 // and skip the instruction.
617 case EXCEPT_EBC_BREAKPOINT
:
621 case EXCEPT_EBC_STEP
:
624 case EXCEPT_EBC_UNDEFINED
:
626 ASSERT (ExceptionType
== EXCEPT_EBC_UNDEFINED
);
629 case EXCEPT_EBC_DIVIDE_ERROR
:
631 ASSERT (ExceptionType
== EXCEPT_EBC_DIVIDE_ERROR
);
634 case EXCEPT_EBC_DEBUG
:
636 ASSERT (ExceptionType
== EXCEPT_EBC_DEBUG
);
639 case EXCEPT_EBC_INVALID_OPCODE
:
641 ASSERT (ExceptionType
== EXCEPT_EBC_INVALID_OPCODE
);
644 case EXCEPT_EBC_STACK_FAULT
:
646 ASSERT (ExceptionType
== EXCEPT_EBC_STACK_FAULT
);
649 case EXCEPT_EBC_ALIGNMENT_CHECK
:
651 ASSERT (ExceptionType
== EXCEPT_EBC_ALIGNMENT_CHECK
);
654 case EXCEPT_EBC_INSTRUCTION_ENCODING
:
656 ASSERT (ExceptionType
== EXCEPT_EBC_INSTRUCTION_ENCODING
);
659 case EXCEPT_EBC_BAD_BREAK
:
661 ASSERT (ExceptionType
== EXCEPT_EBC_BAD_BREAK
);
683 The VM interpreter calls this function on a periodic basis to support
684 the EFI debug support protocol.
688 VmPtr - pointer to a VM context for passing info to the debugger.
703 IN EFI_EBC_PROTOCOL
*This
,
704 IN EFI_HANDLE ImageHandle
710 This routine is called by the core when an image is being unloaded from
711 memory. Basically we now have the opportunity to do any necessary cleanup.
712 Typically this will include freeing any memory allocated for thunk-creation.
716 This - protocol instance pointer
717 ImageHandle - handle to the image being unloaded.
721 EFI_INVALID_PARAMETER - the ImageHandle passed in was not found in
722 the internal list of EBC image handles.
723 EFI_STATUS - completed successfully
727 EBC_THUNK_LIST
*ThunkList
;
728 EBC_THUNK_LIST
*NextThunkList
;
729 EBC_IMAGE_LIST
*ImageList
;
730 EBC_IMAGE_LIST
*PrevImageList
;
732 // First go through our list of known image handles and see if we've already
733 // created an image list element for this image handle.
735 PrevImageList
= NULL
;
736 for (ImageList
= mEbcImageList
; ImageList
!= NULL
; ImageList
= ImageList
->Next
) {
737 if (ImageList
->ImageHandle
== ImageHandle
) {
741 // Save the previous so we can connect the lists when we remove this one
743 PrevImageList
= ImageList
;
746 if (ImageList
== NULL
) {
747 return EFI_INVALID_PARAMETER
;
750 // Free up all the thunk buffers and thunks list elements for this image
753 ThunkList
= ImageList
->ThunkList
;
754 while (ThunkList
!= NULL
) {
755 NextThunkList
= ThunkList
->Next
;
756 gBS
->FreePool (ThunkList
->ThunkBuffer
);
757 gBS
->FreePool (ThunkList
);
758 ThunkList
= NextThunkList
;
761 // Now remove this image list element from the chain
763 if (PrevImageList
== NULL
) {
767 mEbcImageList
= ImageList
->Next
;
769 PrevImageList
->Next
= ImageList
->Next
;
772 // Now free up the image list element
774 gBS
->FreePool (ImageList
);
780 IN EFI_HANDLE ImageHandle
,
781 IN VOID
*ThunkBuffer
,
788 Add a thunk to our list of thunks for a given image handle.
789 Also flush the instruction cache since we've written thunk code
790 to memory that will be executed eventually.
794 ImageHandle - the image handle to which the thunk is tied
795 ThunkBuffer - the buffer we've created/allocated
796 ThunkSize - the size of the thunk memory allocated
800 EFI_OUT_OF_RESOURCES - memory allocation failed
801 EFI_SUCCESS - successful completion
805 EBC_THUNK_LIST
*ThunkList
;
806 EBC_IMAGE_LIST
*ImageList
;
810 // It so far so good, then flush the instruction cache
812 if (mEbcICacheFlush
!= NULL
) {
813 Status
= mEbcICacheFlush ((EFI_PHYSICAL_ADDRESS
) (UINTN
) ThunkBuffer
, ThunkSize
);
814 if (EFI_ERROR (Status
)) {
819 // Go through our list of known image handles and see if we've already
820 // created a image list element for this image handle.
822 for (ImageList
= mEbcImageList
; ImageList
!= NULL
; ImageList
= ImageList
->Next
) {
823 if (ImageList
->ImageHandle
== ImageHandle
) {
828 if (ImageList
== NULL
) {
830 // Allocate a new one
832 Status
= gBS
->AllocatePool (
834 sizeof (EBC_IMAGE_LIST
),
837 if (Status
!= EFI_SUCCESS
) {
838 return EFI_OUT_OF_RESOURCES
;
841 ImageList
->ThunkList
= NULL
;
842 ImageList
->ImageHandle
= ImageHandle
;
843 ImageList
->Next
= mEbcImageList
;
844 mEbcImageList
= ImageList
;
847 // Ok, now create a new thunk element to add to the list
849 Status
= gBS
->AllocatePool (
851 sizeof (EBC_THUNK_LIST
),
854 if (Status
!= EFI_SUCCESS
) {
855 return EFI_OUT_OF_RESOURCES
;
858 // Add it to the head of the list
860 ThunkList
->Next
= ImageList
->ThunkList
;
861 ThunkList
->ThunkBuffer
= ThunkBuffer
;
862 ImageList
->ThunkList
= ThunkList
;
869 EbcRegisterICacheFlush (
870 IN EFI_EBC_PROTOCOL
*This
,
871 IN EBC_ICACHE_FLUSH Flush
874 mEbcICacheFlush
= Flush
;
882 IN EFI_EBC_PROTOCOL
*This
,
883 IN OUT UINT64
*Version
886 if (Version
== NULL
) {
887 return EFI_INVALID_PARAMETER
;
890 *Version
= GetVmVersion ();
896 InitEbcVmTestProtocol (
897 IN EFI_HANDLE
*IHandle
903 Produce an EBC VM test protocol that can be used for regression tests.
907 IHandle - handle on which to install the protocol.
911 EFI_OUT_OF_RESOURCES - memory allocation failed
912 EFI_SUCCESS - successful completion
918 EFI_EBC_VM_TEST_PROTOCOL
*EbcVmTestProtocol
;
921 // Allocate memory for the protocol, then fill in the fields
923 Status
= gBS
->AllocatePool (EfiBootServicesData
, sizeof (EFI_EBC_VM_TEST_PROTOCOL
), (VOID
**) &EbcVmTestProtocol
);
924 if (Status
!= EFI_SUCCESS
) {
925 return EFI_OUT_OF_RESOURCES
;
927 EbcVmTestProtocol
->Execute
= (EBC_VM_TEST_EXECUTE
) EbcExecuteInstructions
;
930 EbcVmTestProtocol
->Assemble
= (EBC_VM_TEST_ASM
) EbcVmTestUnsupported
;
931 EbcVmTestProtocol
->Disassemble
= (EBC_VM_TEST_DASM
) EbcVmTestUnsupported
;
935 // Publish the protocol
938 Status
= gBS
->InstallProtocolInterface (&Handle
, &mEfiEbcVmTestProtocolGuid
, EFI_NATIVE_INTERFACE
, EbcVmTestProtocol
);
939 if (EFI_ERROR (Status
)) {
940 gBS
->FreePool (EbcVmTestProtocol
);
946 EbcVmTestUnsupported ()
948 return EFI_UNSUPPORTED
;