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 Contains code that implements the virtual machine.
23 #include "EbcExecute.h"
26 // VM major/minor version
28 #define VM_MAJOR_VERSION 1
29 #define VM_MINOR_VERSION 0
32 // Define some useful data size constants to allow switch statements based on
33 // size of operands or data.
35 #define DATA_SIZE_INVALID 0
37 #define DATA_SIZE_16 2
38 #define DATA_SIZE_32 4
39 #define DATA_SIZE_64 8
40 #define DATA_SIZE_N 48 // 4 or 8
42 // Structure we'll use to dispatch opcodes to execute functions.
45 EFI_STATUS (*ExecuteFunction
) (IN VM_CONTEXT
* VmPtr
);
51 (*DATA_MANIP_EXEC_FUNCTION
) (
52 IN VM_CONTEXT
* VmPtr
,
102 IN VM_CONTEXT
*VmPtr
,
109 IN VM_CONTEXT
*VmPtr
,
116 IN VM_CONTEXT
*VmPtr
,
124 IN VM_CONTEXT
*VmPtr
,
132 IN VM_CONTEXT
*VmPtr
,
139 IN VM_CONTEXT
*VmPtr
,
146 IN VM_CONTEXT
*VmPtr
,
154 IN VM_CONTEXT
*VmPtr
,
161 IN VM_CONTEXT
*VmPtr
,
168 IN VM_CONTEXT
*VmPtr
,
175 IN VM_CONTEXT
*VmPtr
,
182 IN VM_CONTEXT
*VmPtr
,
189 IN VM_CONTEXT
*VmPtr
,
196 IN VM_CONTEXT
*VmPtr
,
203 IN VM_CONTEXT
*VmPtr
,
210 IN VM_CONTEXT
*VmPtr
,
211 IN BOOLEAN IsSignedOperation
215 // Functions that execute VM opcodes
309 ExecuteSignedDataManip (
315 ExecuteUnsignedDataManip (
344 // Data manipulation subfunctions
349 IN VM_CONTEXT
*VmPtr
,
357 IN VM_CONTEXT
*VmPtr
,
365 IN VM_CONTEXT
*VmPtr
,
373 IN VM_CONTEXT
*VmPtr
,
381 IN VM_CONTEXT
*VmPtr
,
389 IN VM_CONTEXT
*VmPtr
,
397 IN VM_CONTEXT
*VmPtr
,
405 IN VM_CONTEXT
*VmPtr
,
413 IN VM_CONTEXT
*VmPtr
,
421 IN VM_CONTEXT
*VmPtr
,
429 IN VM_CONTEXT
*VmPtr
,
437 IN VM_CONTEXT
*VmPtr
,
445 IN VM_CONTEXT
*VmPtr
,
453 IN VM_CONTEXT
*VmPtr
,
461 IN VM_CONTEXT
*VmPtr
,
469 IN VM_CONTEXT
*VmPtr
,
477 IN VM_CONTEXT
*VmPtr
,
485 IN VM_CONTEXT
*VmPtr
,
493 IN VM_CONTEXT
*VmPtr
,
499 // Once we retrieve the operands for the data manipulation instructions,
500 // call these functions to perform the operation.
502 static CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable
[] = {
524 static CONST VM_TABLE_ENTRY mVmOpcodeTable
[] = {
525 { ExecuteBREAK
}, // opcode 0x00
526 { ExecuteJMP
}, // opcode 0x01
527 { ExecuteJMP8
}, // opcode 0x02
528 { ExecuteCALL
}, // opcode 0x03
529 { ExecuteRET
}, // opcode 0x04
530 { ExecuteCMP
}, // opcode 0x05 CMPeq
531 { ExecuteCMP
}, // opcode 0x06 CMPlte
532 { ExecuteCMP
}, // opcode 0x07 CMPgte
533 { ExecuteCMP
}, // opcode 0x08 CMPulte
534 { ExecuteCMP
}, // opcode 0x09 CMPugte
535 { ExecuteUnsignedDataManip
}, // opcode 0x0A NOT
536 { ExecuteSignedDataManip
}, // opcode 0x0B NEG
537 { ExecuteSignedDataManip
}, // opcode 0x0C ADD
538 { ExecuteSignedDataManip
}, // opcode 0x0D SUB
539 { ExecuteSignedDataManip
}, // opcode 0x0E MUL
540 { ExecuteUnsignedDataManip
}, // opcode 0x0F MULU
541 { ExecuteSignedDataManip
}, // opcode 0x10 DIV
542 { ExecuteUnsignedDataManip
}, // opcode 0x11 DIVU
543 { ExecuteSignedDataManip
}, // opcode 0x12 MOD
544 { ExecuteUnsignedDataManip
}, // opcode 0x13 MODU
545 { ExecuteUnsignedDataManip
}, // opcode 0x14 AND
546 { ExecuteUnsignedDataManip
}, // opcode 0x15 OR
547 { ExecuteUnsignedDataManip
}, // opcode 0x16 XOR
548 { ExecuteUnsignedDataManip
}, // opcode 0x17 SHL
549 { ExecuteUnsignedDataManip
}, // opcode 0x18 SHR
550 { ExecuteSignedDataManip
}, // opcode 0x19 ASHR
551 { ExecuteUnsignedDataManip
}, // opcode 0x1A EXTNDB
552 { ExecuteUnsignedDataManip
}, // opcode 0x1B EXTNDW
553 { ExecuteUnsignedDataManip
}, // opcode 0x1C EXTNDD
554 { ExecuteMOVxx
}, // opcode 0x1D MOVBW
555 { ExecuteMOVxx
}, // opcode 0x1E MOVWW
556 { ExecuteMOVxx
}, // opcode 0x1F MOVDW
557 { ExecuteMOVxx
}, // opcode 0x20 MOVQW
558 { ExecuteMOVxx
}, // opcode 0x21 MOVBD
559 { ExecuteMOVxx
}, // opcode 0x22 MOVWD
560 { ExecuteMOVxx
}, // opcode 0x23 MOVDD
561 { ExecuteMOVxx
}, // opcode 0x24 MOVQD
562 { ExecuteMOVsnw
}, // opcode 0x25 MOVsnw
563 { ExecuteMOVsnd
}, // opcode 0x26 MOVsnd
564 { NULL
}, // opcode 0x27
565 { ExecuteMOVxx
}, // opcode 0x28 MOVqq
566 { ExecuteLOADSP
}, // opcode 0x29 LOADSP SP1, R2
567 { ExecuteSTORESP
}, // opcode 0x2A STORESP R1, SP2
568 { ExecutePUSH
}, // opcode 0x2B PUSH {@}R1 [imm16]
569 { ExecutePOP
}, // opcode 0x2C POP {@}R1 [imm16]
570 { ExecuteCMPI
}, // opcode 0x2D CMPIEQ
571 { ExecuteCMPI
}, // opcode 0x2E CMPILTE
572 { ExecuteCMPI
}, // opcode 0x2F CMPIGTE
573 { ExecuteCMPI
}, // opcode 0x30 CMPIULTE
574 { ExecuteCMPI
}, // opcode 0x31 CMPIUGTE
575 { ExecuteMOVxx
}, // opcode 0x32 MOVN
576 { ExecuteMOVxx
}, // opcode 0x33 MOVND
577 { NULL
}, // opcode 0x34
578 { ExecutePUSHn
}, // opcode 0x35
579 { ExecutePOPn
}, // opcode 0x36
580 { ExecuteMOVI
}, // opcode 0x37 - mov immediate data
581 { ExecuteMOVIn
}, // opcode 0x38 - mov immediate natural
582 { ExecuteMOVREL
} // opcode 0x39 - move data relative to PC
586 // Length of JMP instructions, depending on upper two bits of opcode.
588 static CONST UINT8 mJMPLen
[] = { 2, 2, 6, 10 };
591 // Simple Debugger Protocol GUID
593 EFI_GUID mEbcSimpleDebuggerProtocolGuid
= EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID
;
596 EbcExecuteInstructions (
597 IN EFI_EBC_VM_TEST_PROTOCOL
*This
,
598 IN VM_CONTEXT
*VmPtr
,
599 IN OUT UINTN
*InstructionCount
605 Given a pointer to a new VM context, execute one or more instructions. This
606 function is only used for test purposes via the EBC VM test protocol.
610 This - pointer to protocol interface
611 VmPtr - pointer to a VM context
612 InstructionCount - how many instructions to execute. 0 if don't count.
623 UINTN InstructionsLeft
;
624 UINTN SavedInstructionCount
;
626 Status
= EFI_SUCCESS
;
628 if (*InstructionCount
== 0) {
629 InstructionsLeft
= 1;
631 InstructionsLeft
= *InstructionCount
;
634 SavedInstructionCount
= *InstructionCount
;
635 *InstructionCount
= 0;
638 // Index into the opcode table using the opcode byte for this instruction.
639 // This gives you the execute function, which we first test for null, then
640 // call it if it's not null.
642 while (InstructionsLeft
!= 0) {
643 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction
;
644 if (ExecFunc
== (UINTN
) NULL
) {
645 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
646 return EFI_UNSUPPORTED
;
648 mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction (VmPtr
);
649 *InstructionCount
= *InstructionCount
+ 1;
653 // Decrement counter if applicable
655 if (SavedInstructionCount
!= 0) {
671 Execute an EBC image from an entry point or from a published protocol.
675 VmPtr - pointer to prepared VM context.
684 UINT8 StackCorrupted
;
686 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL
*EbcSimpleDebugger
;
688 EbcSimpleDebugger
= NULL
;
689 Status
= EFI_SUCCESS
;
693 // Make sure the magic value has been put on the stack before we got here.
695 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
699 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
702 // Try to get the debug support for EBC
705 Status
= gBS
->LocateProtocol (
706 &mEbcSimpleDebuggerProtocolGuid
,
708 (VOID
**) &EbcSimpleDebugger
710 if (EFI_ERROR (Status
)) {
711 EbcSimpleDebugger
= NULL
;
716 // Save the start IP for debug. For example, if we take an exception we
717 // can print out the location of the exception relative to the entry point,
718 // which could then be used in a disassembly listing to find the problem.
720 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
723 // We'll wait for this flag to know when we're done. The RET
724 // instruction sets it if it runs out of stack.
726 VmPtr
->StopFlags
= 0;
727 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
729 // If we've found a simple debugger protocol, call it
732 if (EbcSimpleDebugger
!= NULL
) {
733 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
738 // Verify the opcode is in range. Otherwise generate an exception.
740 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
741 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
742 Status
= EFI_UNSUPPORTED
;
746 // Use the opcode bits to index into the opcode dispatch table. If the
747 // function pointer is null then generate an exception.
749 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
750 if (ExecFunc
== (UINTN
) NULL
) {
751 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
752 Status
= EFI_UNSUPPORTED
;
756 // The EBC VM is a strongly ordered processor, so perform a fence operation before
757 // and after each instruction is executed.
761 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
766 // If the step flag is set, signal an exception and continue. We don't
767 // clear it here. Assuming the debugger is responsible for clearing it.
769 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
770 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
773 // Make sure stack has not been corrupted. Only report it once though.
775 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
776 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
794 Execute the MOVxx instructions.
798 VmPtr - pointer to a VM context.
807 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
808 MOVqq {@}R1 {Index64}, {@}R2 {Index64}
810 Copies contents of [R2] -> [R1], zero extending where required.
812 First character indicates the size of the move.
813 Second character indicates the size of the index(s).
815 Invalid to have R1 direct with index.
832 Opcode
= GETOPCODE (VmPtr
);
833 OpcMasked
= (UINT8
) (Opcode
& OPCODE_M_OPCODE
);
836 // Get the operands byte so we can get R1 and R2
838 Operands
= GETOPERANDS (VmPtr
);
848 // Determine if we have an index/immediate data. Base instruction size
849 // is 2 (opcode + operands). Add to this size each index specified.
852 if (Opcode
& (OPCODE_M_IMMED_OP1
| OPCODE_M_IMMED_OP2
)) {
854 // Determine size of the index from the opcode. Then get it.
856 if ((OpcMasked
<= OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVNW
)) {
858 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
859 // Get one or both index values.
861 if (Opcode
& OPCODE_M_IMMED_OP1
) {
862 Index16
= VmReadIndex16 (VmPtr
, 2);
863 Index64Op1
= (INT64
) Index16
;
864 Size
+= sizeof (UINT16
);
867 if (Opcode
& OPCODE_M_IMMED_OP2
) {
868 Index16
= VmReadIndex16 (VmPtr
, Size
);
869 Index64Op2
= (INT64
) Index16
;
870 Size
+= sizeof (UINT16
);
872 } else if ((OpcMasked
<= OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVND
)) {
874 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
876 if (Opcode
& OPCODE_M_IMMED_OP1
) {
877 Index32
= VmReadIndex32 (VmPtr
, 2);
878 Index64Op1
= (INT64
) Index32
;
879 Size
+= sizeof (UINT32
);
882 if (Opcode
& OPCODE_M_IMMED_OP2
) {
883 Index32
= VmReadIndex32 (VmPtr
, Size
);
884 Index64Op2
= (INT64
) Index32
;
885 Size
+= sizeof (UINT32
);
887 } else if (OpcMasked
== OPCODE_MOVQQ
) {
889 // MOVqq -- only form with a 64-bit index
891 if (Opcode
& OPCODE_M_IMMED_OP1
) {
892 Index64Op1
= VmReadIndex64 (VmPtr
, 2);
893 Size
+= sizeof (UINT64
);
896 if (Opcode
& OPCODE_M_IMMED_OP2
) {
897 Index64Op2
= VmReadIndex64 (VmPtr
, Size
);
898 Size
+= sizeof (UINT64
);
902 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
904 EbcDebugSignalException (
905 EXCEPT_EBC_INSTRUCTION_ENCODING
,
906 EXCEPTION_FLAG_FATAL
,
909 return EFI_UNSUPPORTED
;
913 // Determine the size of the move, and create a mask for it so we can
914 // clear unused bits.
916 if ((OpcMasked
== OPCODE_MOVBW
) || (OpcMasked
== OPCODE_MOVBD
)) {
917 MoveSize
= DATA_SIZE_8
;
919 } else if ((OpcMasked
== OPCODE_MOVWW
) || (OpcMasked
== OPCODE_MOVWD
)) {
920 MoveSize
= DATA_SIZE_16
;
922 } else if ((OpcMasked
== OPCODE_MOVDW
) || (OpcMasked
== OPCODE_MOVDD
)) {
923 MoveSize
= DATA_SIZE_32
;
924 DataMask
= 0xFFFFFFFF;
925 } else if ((OpcMasked
== OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVQQ
)) {
926 MoveSize
= DATA_SIZE_64
;
927 DataMask
= (UINT64
)~0;
928 } else if ((OpcMasked
== OPCODE_MOVNW
) || (OpcMasked
== OPCODE_MOVND
)) {
929 MoveSize
= DATA_SIZE_N
;
930 DataMask
= (UINT64
)~0 >> (64 - 8 * sizeof (UINTN
));
933 // We were dispatched to this function and we don't recognize the opcode
935 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED
, EXCEPTION_FLAG_FATAL
, VmPtr
);
936 return EFI_UNSUPPORTED
;
939 // Now get the source address
941 if (OPERAND2_INDIRECT (Operands
)) {
943 // Indirect form @R2. Compute address of operand2
945 Source
= (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
);
947 // Now get the data from the source. Always 0-extend and let the compiler
948 // sign-extend where required.
952 Data64
= (UINT64
) (UINT8
) VmReadMem8 (VmPtr
, Source
);
956 Data64
= (UINT64
) (UINT16
) VmReadMem16 (VmPtr
, Source
);
960 Data64
= (UINT64
) (UINT32
) VmReadMem32 (VmPtr
, Source
);
964 Data64
= (UINT64
) VmReadMem64 (VmPtr
, Source
);
968 Data64
= (UINT64
) (UINTN
) VmReadMemN (VmPtr
, Source
);
979 // Not indirect source: MOVxx {@}Rx, Ry [Index]
981 Data64
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
;
983 // Did Operand2 have an index? If so, treat as two signed values since
984 // indexes are signed values.
986 if (Opcode
& OPCODE_M_IMMED_OP2
) {
988 // NOTE: need to find a way to fix this, most likely by changing the VM
989 // implementation to remove the stack gap. To do that, we'd need to
990 // allocate stack space for the VM and actually set the system
991 // stack pointer to the allocated buffer when the VM starts.
993 // Special case -- if someone took the address of a function parameter
994 // then we need to make sure it's not in the stack gap. We can identify
995 // this situation if (Operand2 register == 0) && (Operand2 is direct)
996 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
997 // Situations that to be aware of:
998 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
1000 if ((OPERAND2_REGNUM (Operands
) == 0) &&
1001 (!OPERAND2_INDIRECT (Operands
)) &&
1003 (OPERAND1_REGNUM (Operands
) == 0) &&
1004 (OPERAND1_INDIRECT (Operands
))
1006 Data64
= (UINT64
) ConvertStackAddr (VmPtr
, (UINTN
) (INT64
) Data64
);
1011 // Now write it back
1013 if (OPERAND1_INDIRECT (Operands
)) {
1015 // Reuse the Source variable to now be dest.
1017 Source
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index64Op1
);
1019 // Do the write based on the size
1023 VmWriteMem8 (VmPtr
, Source
, (UINT8
) Data64
);
1027 VmWriteMem16 (VmPtr
, Source
, (UINT16
) Data64
);
1031 VmWriteMem32 (VmPtr
, Source
, (UINT32
) Data64
);
1035 VmWriteMem64 (VmPtr
, Source
, Data64
);
1039 VmWriteMemN (VmPtr
, Source
, (UINTN
) Data64
);
1051 // Make sure we didn't have an index on operand1.
1053 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1054 EbcDebugSignalException (
1055 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1056 EXCEPTION_FLAG_FATAL
,
1059 return EFI_UNSUPPORTED
;
1062 // Direct storage in register. Clear unused bits and store back to
1065 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
& DataMask
;
1068 // Advance the instruction pointer
1077 IN VM_CONTEXT
*VmPtr
1081 Routine Description:
1083 Execute the EBC BREAK instruction
1087 VmPtr - pointer to current VM context
1097 VOID
*EbcEntryPoint
;
1100 UINT64 U64EbcEntryPoint
;
1103 Operands
= GETOPERANDS (VmPtr
);
1106 // Runaway program break. Generate an exception and terminate
1109 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1113 // Get VM version -- return VM revision number in R7
1119 // 16-8 = Major version
1120 // 7-0 = Minor version
1122 VmPtr
->R
[7] = GetVmVersion ();
1126 // Debugger breakpoint
1129 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1131 // See if someone has registered a handler
1133 EbcDebugSignalException (
1134 EXCEPT_EBC_BREAKPOINT
,
1135 EXCEPTION_FLAG_NONE
,
1139 // Don't advance the IP
1141 return EFI_UNSUPPORTED
;
1145 // System call, which there are none, so NOP it.
1151 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1152 // "offset from self" pointer to the EBC entry point.
1153 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1156 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1157 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1158 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1161 // Now create a new thunk
1163 Status
= EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1166 // Finally replace the EBC entry point memory with the thunk address
1168 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1172 // Compiler setting version per value in R7
1175 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1177 // Check compiler version against VM version?
1182 // Unhandled break code. Signal exception.
1185 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1198 IN VM_CONTEXT
*VmPtr
1202 Routine Description:
1203 Execute the JMP instruction
1206 VmPtr - pointer to VM context
1212 JMP64{cs|cc} Immed64
1213 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1216 b0.7 - immediate data present
1217 b0.6 - 1 = 64 bit immediate data
1218 0 = 32 bit immediate data
1219 b1.7 - 1 = conditional
1220 b1.6 1 = CS (condition set)
1221 0 = CC (condition clear)
1222 b1.4 1 = relative address
1223 0 = absolute address
1224 b1.3 1 = operand1 indirect
1231 UINT8 ConditionFlag
;
1238 Operand
= GETOPERANDS (VmPtr
);
1239 Opcode
= GETOPCODE (VmPtr
);
1242 // Get instruction length from the opcode. The upper two bits are used here
1243 // to index into the length array.
1245 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1248 // Decode instruction conditions
1249 // If we haven't met the condition, then simply advance the IP and return.
1251 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1252 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1253 if (Operand
& CONDITION_M_CONDITIONAL
) {
1254 if (CompareSet
!= ConditionFlag
) {
1260 // Check for 64-bit form and do it right away since it's the most
1261 // straight-forward form.
1263 if (Opcode
& OPCODE_M_IMMDATA64
) {
1265 // Double check for immediate-data, which is required. If not there,
1266 // then signal an exception
1268 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1269 EbcDebugSignalException (
1270 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1271 EXCEPTION_FLAG_ERROR
,
1274 return EFI_UNSUPPORTED
;
1277 // 64-bit immediate data is full address. Read the immediate data,
1278 // check for alignment, and jump absolute.
1280 Data64
= VmReadImmed64 (VmPtr
, 2);
1281 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1282 EbcDebugSignalException (
1283 EXCEPT_EBC_ALIGNMENT_CHECK
,
1284 EXCEPTION_FLAG_FATAL
,
1288 return EFI_UNSUPPORTED
;
1292 // Take jump -- relative or absolute
1294 if (Operand
& JMP_M_RELATIVE
) {
1295 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1297 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1304 // Get the index if there is one. May be either an index, or an immediate
1305 // offset depending on indirect operand.
1306 // JMP32 @R1 Index32 -- immediate data is an index
1307 // JMP32 R1 Immed32 -- immedate data is an offset
1309 if (Opcode
& OPCODE_M_IMMDATA
) {
1310 if (OPERAND1_INDIRECT (Operand
)) {
1311 Index32
= VmReadIndex32 (VmPtr
, 2);
1313 Index32
= VmReadImmed32 (VmPtr
, 2);
1319 // Get the register data. If R == 0, then special case where it's ignored.
1321 if (OPERAND1_REGNUM (Operand
) == 0) {
1324 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1329 if (OPERAND1_INDIRECT (Operand
)) {
1331 // Form: JMP32 @Rx {Index32}
1333 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1334 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1335 EbcDebugSignalException (
1336 EXCEPT_EBC_ALIGNMENT_CHECK
,
1337 EXCEPTION_FLAG_FATAL
,
1341 return EFI_UNSUPPORTED
;
1344 if (Operand
& JMP_M_RELATIVE
) {
1345 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1347 VmPtr
->Ip
= (VMIP
) Addr
;
1351 // Form: JMP32 Rx {Immed32}
1353 Addr
= (UINTN
) (Data64
+ Index32
);
1354 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1355 EbcDebugSignalException (
1356 EXCEPT_EBC_ALIGNMENT_CHECK
,
1357 EXCEPTION_FLAG_FATAL
,
1361 return EFI_UNSUPPORTED
;
1364 if (Operand
& JMP_M_RELATIVE
) {
1365 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1367 VmPtr
->Ip
= (VMIP
) Addr
;
1377 IN VM_CONTEXT
*VmPtr
1381 Routine Description:
1382 Execute the EBC JMP8 instruction
1385 VmPtr - pointer to a VM context
1391 JMP8{cs|cc} Offset/2
1396 UINT8 ConditionFlag
;
1401 // Decode instruction.
1403 Opcode
= GETOPCODE (VmPtr
);
1404 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1405 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1408 // If we haven't met the condition, then simply advance the IP and return
1410 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1411 if (CompareSet
!= ConditionFlag
) {
1417 // Get the offset from the instruction stream. It's relative to the
1418 // following instruction, and divided by 2.
1420 Offset
= VmReadImmed8 (VmPtr
, 1);
1422 // Want to check for offset == -2 and then raise an exception?
1424 VmPtr
->Ip
+= (Offset
* 2) + 2;
1431 IN VM_CONTEXT
*VmPtr
1435 Routine Description:
1437 Execute the EBC MOVI
1441 VmPtr - pointer to a VM context
1449 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1451 First variable character specifies the move size
1452 Second variable character specifies size of the immediate data
1454 Sign-extend the immediate data to the size of the operation, and zero-extend
1455 if storing to a register.
1457 Operand1 direct with index/immed is invalid.
1470 // Get the opcode and operands byte so we can get R1 and R2
1472 Opcode
= GETOPCODE (VmPtr
);
1473 Operands
= GETOPERANDS (VmPtr
);
1476 // Get the index (16-bit) if present
1478 if (Operands
& MOVI_M_IMMDATA
) {
1479 Index16
= VmReadIndex16 (VmPtr
, 2);
1486 // Extract the immediate data. Sign-extend always.
1488 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1489 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1491 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1492 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1494 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1495 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1501 EbcDebugSignalException (
1502 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1503 EXCEPTION_FLAG_FATAL
,
1506 return EFI_UNSUPPORTED
;
1509 // Now write back the result
1511 if (!OPERAND1_INDIRECT (Operands
)) {
1513 // Operand1 direct. Make sure it didn't have an index.
1515 if (Operands
& MOVI_M_IMMDATA
) {
1516 EbcDebugSignalException (
1517 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1518 EXCEPTION_FLAG_FATAL
,
1521 return EFI_UNSUPPORTED
;
1524 // Writing directly to a register. Clear unused bits.
1526 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1527 Mask64
= 0x000000FF;
1528 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1529 Mask64
= 0x0000FFFF;
1530 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1531 Mask64
= 0x00000000FFFFFFFF;
1533 Mask64
= (UINT64
)~0;
1536 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1539 // Get the address then write back based on size of the move
1541 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1542 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1543 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1544 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1545 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1546 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1547 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1549 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1553 // Advance the instruction pointer
1562 IN VM_CONTEXT
*VmPtr
1566 Routine Description:
1568 Execute the EBC MOV immediate natural. This instruction moves an immediate
1569 index value into a register or memory location.
1573 VmPtr - pointer to a VM context
1581 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1595 // Get the opcode and operands byte so we can get R1 and R2
1597 Opcode
= GETOPCODE (VmPtr
);
1598 Operands
= GETOPERANDS (VmPtr
);
1601 // Get the operand1 index (16-bit) if present
1603 if (Operands
& MOVI_M_IMMDATA
) {
1604 Index16
= VmReadIndex16 (VmPtr
, 2);
1611 // Extract the immediate data and convert to a 64-bit index.
1613 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1614 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1615 ImmedIndex64
= (INT64
) ImmedIndex16
;
1617 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1618 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1619 ImmedIndex64
= (INT64
) ImmedIndex32
;
1621 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1622 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1628 EbcDebugSignalException (
1629 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1630 EXCEPTION_FLAG_FATAL
,
1633 return EFI_UNSUPPORTED
;
1636 // Now write back the result
1638 if (!OPERAND1_INDIRECT (Operands
)) {
1640 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1643 if (Operands
& MOVI_M_IMMDATA
) {
1644 EbcDebugSignalException (
1645 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1646 EXCEPTION_FLAG_FATAL
,
1649 return EFI_UNSUPPORTED
;
1652 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1657 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1658 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1661 // Advance the instruction pointer
1670 IN VM_CONTEXT
*VmPtr
1674 Routine Description:
1676 Execute the EBC MOVREL instruction.
1677 Dest <- Ip + ImmData
1681 VmPtr - pointer to a VM context
1689 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1702 // Get the opcode and operands byte so we can get R1 and R2
1704 Opcode
= GETOPCODE (VmPtr
);
1705 Operands
= GETOPERANDS (VmPtr
);
1708 // Get the Operand 1 index (16-bit) if present
1710 if (Operands
& MOVI_M_IMMDATA
) {
1711 Index16
= VmReadIndex16 (VmPtr
, 2);
1718 // Get the immediate data.
1720 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1721 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1723 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1724 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1726 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1727 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1733 EbcDebugSignalException (
1734 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1735 EXCEPTION_FLAG_FATAL
,
1738 return EFI_UNSUPPORTED
;
1741 // Compute the value and write back the result
1743 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1744 if (!OPERAND1_INDIRECT (Operands
)) {
1746 // Check for illegal combination of operand1 direct with immediate data
1748 if (Operands
& MOVI_M_IMMDATA
) {
1749 EbcDebugSignalException (
1750 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1751 EXCEPTION_FLAG_FATAL
,
1754 return EFI_UNSUPPORTED
;
1757 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1760 // Get the address = [Rx] + Index16
1761 // Write back the result. Always a natural size write, since
1762 // we're talking addresses here.
1764 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1765 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1768 // Advance the instruction pointer
1777 IN VM_CONTEXT
*VmPtr
1781 Routine Description:
1783 Execute the EBC MOVsnw instruction. This instruction loads a signed
1784 natural value from memory or register to another memory or register. On
1785 32-bit machines, the value gets sign-extended to 64 bits if the destination
1790 VmPtr - pointer to a VM context
1798 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1800 0:7 1=>operand1 index present
1801 0:6 1=>operand2 index present
1813 // Get the opcode and operand bytes
1815 Opcode
= GETOPCODE (VmPtr
);
1816 Operands
= GETOPERANDS (VmPtr
);
1818 Op1Index
= Op2Index
= 0;
1821 // Get the indexes if present.
1824 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1825 if (OPERAND1_INDIRECT (Operands
)) {
1826 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1829 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1831 EbcDebugSignalException (
1832 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1833 EXCEPTION_FLAG_FATAL
,
1836 return EFI_UNSUPPORTED
;
1839 Size
+= sizeof (UINT16
);
1842 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1843 if (OPERAND2_INDIRECT (Operands
)) {
1844 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1846 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1849 Size
+= sizeof (UINT16
);
1852 // Get the data from the source.
1854 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1855 if (OPERAND2_INDIRECT (Operands
)) {
1856 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1859 // Now write back the result.
1861 if (!OPERAND1_INDIRECT (Operands
)) {
1862 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1864 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1867 // Advance the instruction pointer
1876 IN VM_CONTEXT
*VmPtr
1880 Routine Description:
1882 Execute the EBC MOVsnw instruction. This instruction loads a signed
1883 natural value from memory or register to another memory or register. On
1884 32-bit machines, the value gets sign-extended to 64 bits if the destination
1889 VmPtr - pointer to a VM context
1897 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1899 0:7 1=>operand1 index present
1900 0:6 1=>operand2 index present
1912 // Get the opcode and operand bytes
1914 Opcode
= GETOPCODE (VmPtr
);
1915 Operands
= GETOPERANDS (VmPtr
);
1917 Op1Index
= Op2Index
= 0;
1920 // Get the indexes if present.
1923 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1924 if (OPERAND1_INDIRECT (Operands
)) {
1925 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1928 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1930 EbcDebugSignalException (
1931 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1932 EXCEPTION_FLAG_FATAL
,
1935 return EFI_UNSUPPORTED
;
1938 Size
+= sizeof (UINT32
);
1941 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1942 if (OPERAND2_INDIRECT (Operands
)) {
1943 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1945 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1948 Size
+= sizeof (UINT32
);
1951 // Get the data from the source.
1953 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1954 if (OPERAND2_INDIRECT (Operands
)) {
1955 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1958 // Now write back the result.
1960 if (!OPERAND1_INDIRECT (Operands
)) {
1961 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1963 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1966 // Advance the instruction pointer
1975 IN VM_CONTEXT
*VmPtr
1979 Routine Description:
1980 Execute the EBC PUSHn instruction
1983 VmPtr - pointer to a VM context
1989 PUSHn {@}R1 {Index16|Immed16}
1999 // Get opcode and operands
2001 Opcode
= GETOPCODE (VmPtr
);
2002 Operands
= GETOPERANDS (VmPtr
);
2005 // Get index if present
2007 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2008 if (OPERAND1_INDIRECT (Operands
)) {
2009 Index16
= VmReadIndex16 (VmPtr
, 2);
2011 Index16
= VmReadImmed16 (VmPtr
, 2);
2020 // Get the data to push
2022 if (OPERAND1_INDIRECT (Operands
)) {
2023 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2025 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2028 // Adjust the stack down.
2030 VmPtr
->R
[0] -= sizeof (UINTN
);
2031 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2038 IN VM_CONTEXT
*VmPtr
2042 Routine Description:
2043 Execute the EBC PUSH instruction
2046 VmPtr - pointer to a VM context
2052 PUSH[32|64] {@}R1 {Index16|Immed16}
2063 // Get opcode and operands
2065 Opcode
= GETOPCODE (VmPtr
);
2066 Operands
= GETOPERANDS (VmPtr
);
2068 // Get immediate index if present, then advance the IP.
2070 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2071 if (OPERAND1_INDIRECT (Operands
)) {
2072 Index16
= VmReadIndex16 (VmPtr
, 2);
2074 Index16
= VmReadImmed16 (VmPtr
, 2);
2083 // Get the data to push
2085 if (Opcode
& PUSHPOP_M_64
) {
2086 if (OPERAND1_INDIRECT (Operands
)) {
2087 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2089 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2092 // Adjust the stack down, then write back the data
2094 VmPtr
->R
[0] -= sizeof (UINT64
);
2095 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2100 if (OPERAND1_INDIRECT (Operands
)) {
2101 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2103 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2106 // Adjust the stack down and write the data
2108 VmPtr
->R
[0] -= sizeof (UINT32
);
2109 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2118 IN VM_CONTEXT
*VmPtr
2122 Routine Description:
2123 Execute the EBC POPn instruction
2126 VmPtr - pointer to a VM context
2132 POPn {@}R1 {Index16|Immed16}
2142 // Get opcode and operands
2144 Opcode
= GETOPCODE (VmPtr
);
2145 Operands
= GETOPERANDS (VmPtr
);
2147 // Get immediate data if present, and advance the IP
2149 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2150 if (OPERAND1_INDIRECT (Operands
)) {
2151 Index16
= VmReadIndex16 (VmPtr
, 2);
2153 Index16
= VmReadImmed16 (VmPtr
, 2);
2162 // Read the data off the stack, then adjust the stack pointer
2164 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2165 VmPtr
->R
[0] += sizeof (UINTN
);
2167 // Do the write-back
2169 if (OPERAND1_INDIRECT (Operands
)) {
2170 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2172 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2181 IN VM_CONTEXT
*VmPtr
2185 Routine Description:
2186 Execute the EBC POP instruction
2189 VmPtr - pointer to a VM context
2195 POP {@}R1 {Index16|Immed16}
2206 // Get opcode and operands
2208 Opcode
= GETOPCODE (VmPtr
);
2209 Operands
= GETOPERANDS (VmPtr
);
2211 // Get immediate data if present, and advance the IP.
2213 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2214 if (OPERAND1_INDIRECT (Operands
)) {
2215 Index16
= VmReadIndex16 (VmPtr
, 2);
2217 Index16
= VmReadImmed16 (VmPtr
, 2);
2226 // Get the data off the stack, then write it to the appropriate location
2228 if (Opcode
& PUSHPOP_M_64
) {
2230 // Read the data off the stack, then adjust the stack pointer
2232 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2233 VmPtr
->R
[0] += sizeof (UINT64
);
2235 // Do the write-back
2237 if (OPERAND1_INDIRECT (Operands
)) {
2238 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2240 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2244 // 32-bit pop. Read it off the stack and adjust the stack pointer
2246 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2247 VmPtr
->R
[0] += sizeof (UINT32
);
2249 // Do the write-back
2251 if (OPERAND1_INDIRECT (Operands
)) {
2252 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2254 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2264 IN VM_CONTEXT
*VmPtr
2268 Routine Description:
2269 Implements the EBC CALL instruction.
2274 CALL32 {@}R1 {Immed32|Index32}
2276 CALLEX16 {@}R1 {Immed32}
2278 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2281 VmPtr - pointer to a VM context.
2296 // Get opcode and operands
2298 Opcode
= GETOPCODE (VmPtr
);
2299 Operands
= GETOPERANDS (VmPtr
);
2301 // Assign these as well to avoid compiler warnings
2306 FramePtr
= VmPtr
->FramePtr
;
2308 // Determine the instruction size, and get immediate data if present
2310 if (Opcode
& OPCODE_M_IMMDATA
) {
2311 if (Opcode
& OPCODE_M_IMMDATA64
) {
2312 Immed64
= VmReadImmed64 (VmPtr
, 2);
2316 // If register operand is indirect, then the immediate data is an index
2318 if (OPERAND1_INDIRECT (Operands
)) {
2319 Immed32
= VmReadIndex32 (VmPtr
, 2);
2321 Immed32
= VmReadImmed32 (VmPtr
, 2);
2330 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2331 // put our return address and frame pointer on the VM stack.
2333 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2335 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2336 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2338 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2341 // If 64-bit data, then absolute jump only
2343 if (Opcode
& OPCODE_M_IMMDATA64
) {
2345 // Native or EBC call?
2347 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2348 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2351 // Call external function, get the return value, and advance the IP
2353 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2357 // Get the register data. If operand1 == 0, then ignore register and
2358 // take immediate data as relative or absolute address.
2359 // Compiler should take care of upper bits if 32-bit machine.
2361 if (OPERAND1_REGNUM (Operands
) != 0) {
2362 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2365 // Get final address
2367 if (OPERAND1_INDIRECT (Operands
)) {
2368 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2373 // Now determine if external call, and then if relative or absolute
2375 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2377 // EBC call. Relative or absolute? If relative, then it's relative to the
2378 // start of the next instruction.
2380 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2381 VmPtr
->Ip
+= Immed64
+ Size
;
2383 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2387 // Native call. Relative or absolute?
2389 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2390 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2392 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2396 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2407 IN VM_CONTEXT
*VmPtr
2411 Routine Description:
2412 Execute the EBC RET instruction
2415 VmPtr - pointer to a VM context
2426 // If we're at the top of the stack, then simply set the done
2429 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2430 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2433 // Pull the return address off the VM app's stack and set the IP
2436 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2437 EbcDebugSignalException (
2438 EXCEPT_EBC_ALIGNMENT_CHECK
,
2439 EXCEPTION_FLAG_FATAL
,
2444 // Restore the IP and frame pointer from the stack
2446 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2448 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2458 IN VM_CONTEXT
*VmPtr
2462 Routine Description:
2463 Execute the EBC CMP instruction
2466 VmPtr - pointer to a VM context
2472 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2485 // Get opcode and operands
2487 Opcode
= GETOPCODE (VmPtr
);
2488 Operands
= GETOPERANDS (VmPtr
);
2490 // Get the register data we're going to compare to
2492 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2494 // Get immediate data
2496 if (Opcode
& OPCODE_M_IMMDATA
) {
2497 if (OPERAND2_INDIRECT (Operands
)) {
2498 Index16
= VmReadIndex16 (VmPtr
, 2);
2500 Index16
= VmReadImmed16 (VmPtr
, 2);
2511 if (OPERAND2_INDIRECT (Operands
)) {
2512 if (Opcode
& OPCODE_M_64BIT
) {
2513 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2516 // 32-bit operations. 0-extend the values for all cases.
2518 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2521 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2524 // Now do the compare
2527 if (Opcode
& OPCODE_M_64BIT
) {
2531 switch (Opcode
& OPCODE_M_OPCODE
) {
2550 case OPCODE_CMPULTE
:
2551 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2556 case OPCODE_CMPUGTE
:
2557 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2569 switch (Opcode
& OPCODE_M_OPCODE
) {
2571 if ((INT32
) Op1
== (INT32
) Op2
) {
2577 if ((INT32
) Op1
<= (INT32
) Op2
) {
2583 if ((INT32
) Op1
>= (INT32
) Op2
) {
2588 case OPCODE_CMPULTE
:
2589 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2594 case OPCODE_CMPUGTE
:
2595 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2605 // Now set the flag accordingly for the comparison
2608 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2610 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2622 IN VM_CONTEXT
*VmPtr
2626 Routine Description:
2627 Execute the EBC CMPI instruction
2630 VmPtr - pointer to a VM context
2636 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2649 // Get opcode and operands
2651 Opcode
= GETOPCODE (VmPtr
);
2652 Operands
= GETOPERANDS (VmPtr
);
2655 // Get operand1 index if present
2658 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2659 Index16
= VmReadIndex16 (VmPtr
, 2);
2665 // Get operand1 data we're going to compare to
2667 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2668 if (OPERAND1_INDIRECT (Operands
)) {
2670 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2672 if (Opcode
& OPCODE_M_CMPI64
) {
2673 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2675 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2679 // Better not have been an index with direct. That is, CMPI R1 Index,...
2682 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2683 EbcDebugSignalException (
2684 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2685 EXCEPTION_FLAG_ERROR
,
2689 return EFI_UNSUPPORTED
;
2693 // Get immediate data -- 16- or 32-bit sign extended
2695 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2696 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2700 // 16-bit immediate data. Sign extend always.
2702 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2706 // Now do the compare
2709 if (Opcode
& OPCODE_M_CMPI64
) {
2711 // 64 bit comparison
2713 switch (Opcode
& OPCODE_M_OPCODE
) {
2715 if (Op1
== (INT64
) Op2
) {
2720 case OPCODE_CMPILTE
:
2721 if (Op1
<= (INT64
) Op2
) {
2726 case OPCODE_CMPIGTE
:
2727 if (Op1
>= (INT64
) Op2
) {
2732 case OPCODE_CMPIULTE
:
2733 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2738 case OPCODE_CMPIUGTE
:
2739 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2749 // 32-bit comparisons
2751 switch (Opcode
& OPCODE_M_OPCODE
) {
2753 if ((INT32
) Op1
== Op2
) {
2758 case OPCODE_CMPILTE
:
2759 if ((INT32
) Op1
<= Op2
) {
2764 case OPCODE_CMPIGTE
:
2765 if ((INT32
) Op1
>= Op2
) {
2770 case OPCODE_CMPIULTE
:
2771 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2776 case OPCODE_CMPIUGTE
:
2777 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2787 // Now set the flag accordingly for the comparison
2790 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2792 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2804 IN VM_CONTEXT
*VmPtr
,
2810 Routine Description:
2811 Execute the EBC NOT instruction
2814 VmPtr - pointer to a VM context
2815 Op1 - Operand 1 from the instruction
2816 Op2 - Operand 2 from the instruction
2822 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2832 IN VM_CONTEXT
*VmPtr
,
2838 Routine Description:
2839 Execute the EBC NEG instruction
2842 VmPtr - pointer to a VM context
2843 Op1 - Operand 1 from the instruction
2844 Op2 - Operand 2 from the instruction
2850 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2860 IN VM_CONTEXT
*VmPtr
,
2866 Routine Description:
2868 Execute the EBC ADD instruction
2871 VmPtr - pointer to a VM context
2872 Op1 - Operand 1 from the instruction
2873 Op2 - Operand 2 from the instruction
2879 ADD[32|64] {@}R1, {@}R2 {Index16}
2889 IN VM_CONTEXT
*VmPtr
,
2895 Routine Description:
2896 Execute the EBC SUB instruction
2899 VmPtr - pointer to a VM context
2900 Op1 - Operand 1 from the instruction
2901 Op2 - Operand 2 from the instruction
2908 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2912 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2913 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2915 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2922 IN VM_CONTEXT
*VmPtr
,
2928 Routine Description:
2930 Execute the EBC MUL instruction
2933 VmPtr - pointer to a VM context
2934 Op1 - Operand 1 from the instruction
2935 Op2 - Operand 2 from the instruction
2941 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2945 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2946 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2948 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2955 IN VM_CONTEXT
*VmPtr
,
2961 Routine Description:
2962 Execute the EBC MULU instruction
2965 VmPtr - pointer to a VM context
2966 Op1 - Operand 1 from the instruction
2967 Op2 - Operand 2 from the instruction
2970 (unsigned)Op1 * (unsigned)Op2
2973 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2977 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2978 return MultU64x64 (Op1
, Op2
);
2980 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2987 IN VM_CONTEXT
*VmPtr
,
2993 Routine Description:
2995 Execute the EBC DIV instruction
2998 VmPtr - pointer to a VM context
2999 Op1 - Operand 1 from the instruction
3000 Op2 - Operand 2 from the instruction
3006 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
3013 // Check for divide-by-0
3016 EbcDebugSignalException (
3017 EXCEPT_EBC_DIVIDE_ERROR
,
3018 EXCEPTION_FLAG_FATAL
,
3024 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3025 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
3027 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3035 IN VM_CONTEXT
*VmPtr
,
3041 Routine Description:
3042 Execute the EBC DIVU instruction
3045 VmPtr - pointer to a VM context
3046 Op1 - Operand 1 from the instruction
3047 Op2 - Operand 2 from the instruction
3050 (unsigned)Op1 / (unsigned)Op2
3053 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3060 // Check for divide-by-0
3063 EbcDebugSignalException (
3064 EXCEPT_EBC_DIVIDE_ERROR
,
3065 EXCEPTION_FLAG_FATAL
,
3071 // Get the destination register
3073 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3074 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
3076 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3084 IN VM_CONTEXT
*VmPtr
,
3090 Routine Description:
3091 Execute the EBC MOD instruction
3094 VmPtr - pointer to a VM context
3095 Op1 - Operand 1 from the instruction
3096 Op2 - Operand 2 from the instruction
3102 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3109 // Check for divide-by-0
3112 EbcDebugSignalException (
3113 EXCEPT_EBC_DIVIDE_ERROR
,
3114 EXCEPTION_FLAG_FATAL
,
3119 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
3127 IN VM_CONTEXT
*VmPtr
,
3133 Routine Description:
3134 Execute the EBC MODU instruction
3137 VmPtr - pointer to a VM context
3138 Op1 - Operand 1 from the instruction
3139 Op2 - Operand 2 from the instruction
3142 Op1 UNSIGNED_MODULUS Op2
3145 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3152 // Check for divide-by-0
3155 EbcDebugSignalException (
3156 EXCEPT_EBC_DIVIDE_ERROR
,
3157 EXCEPTION_FLAG_FATAL
,
3162 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
3170 IN VM_CONTEXT
*VmPtr
,
3176 Routine Description:
3177 Execute the EBC AND instruction
3180 VmPtr - pointer to a VM context
3181 Op1 - Operand 1 from the instruction
3182 Op2 - Operand 2 from the instruction
3188 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3198 IN VM_CONTEXT
*VmPtr
,
3204 Routine Description:
3205 Execute the EBC OR instruction
3208 VmPtr - pointer to a VM context
3209 Op1 - Operand 1 from the instruction
3210 Op2 - Operand 2 from the instruction
3216 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3226 IN VM_CONTEXT
*VmPtr
,
3232 Routine Description:
3233 Execute the EBC XOR instruction
3236 VmPtr - pointer to a VM context
3237 Op1 - Operand 1 from the instruction
3238 Op2 - Operand 2 from the instruction
3244 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3254 IN VM_CONTEXT
*VmPtr
,
3260 Routine Description:
3262 Execute the EBC SHL shift left instruction
3265 VmPtr - pointer to a VM context
3266 Op1 - Operand 1 from the instruction
3267 Op2 - Operand 2 from the instruction
3273 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3277 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3278 return LShiftU64 (Op1
, (UINTN
)Op2
);
3280 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3287 IN VM_CONTEXT
*VmPtr
,
3293 Routine Description:
3294 Execute the EBC SHR instruction
3297 VmPtr - pointer to a VM context
3298 Op1 - Operand 1 from the instruction
3299 Op2 - Operand 2 from the instruction
3302 Op1 >> Op2 (unsigned operands)
3305 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3309 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3310 return RShiftU64 (Op1
, (UINTN
)Op2
);
3312 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3319 IN VM_CONTEXT
*VmPtr
,
3325 Routine Description:
3326 Execute the EBC ASHR instruction
3329 VmPtr - pointer to a VM context
3330 Op1 - Operand 1 from the instruction
3331 Op2 - Operand 2 from the instruction
3337 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3341 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3342 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3344 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3351 IN VM_CONTEXT
*VmPtr
,
3357 Routine Description:
3358 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3361 VmPtr - pointer to a VM context
3362 Op1 - Operand 1 from the instruction
3363 Op2 - Operand 2 from the instruction
3369 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3377 // Convert to byte, then return as 64-bit signed value to let compiler
3378 // sign-extend the value
3381 Data64
= (INT64
) Data8
;
3383 return (UINT64
) Data64
;
3389 IN VM_CONTEXT
*VmPtr
,
3395 Routine Description:
3396 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3399 VmPtr - pointer to a VM context
3400 Op1 - Operand 1 from the instruction
3401 Op2 - Operand 2 from the instruction
3407 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3415 // Convert to word, then return as 64-bit signed value to let compiler
3416 // sign-extend the value
3418 Data16
= (INT16
) Op2
;
3419 Data64
= (INT64
) Data16
;
3421 return (UINT64
) Data64
;
3424 // Execute the EBC EXTNDD instruction.
3426 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3427 // EXTNDD Dest, Source
3429 // Operation: Dest <- SignExtended((DWORD)Source))
3434 IN VM_CONTEXT
*VmPtr
,
3440 Routine Description:
3441 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3444 VmPtr - pointer to a VM context
3445 Op1 - Operand 1 from the instruction
3446 Op2 - Operand 2 from the instruction
3452 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3460 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3461 // sign-extend the value
3463 Data32
= (INT32
) Op2
;
3464 Data64
= (INT64
) Data32
;
3466 return (UINT64
) Data64
;
3471 ExecuteSignedDataManip (
3472 IN VM_CONTEXT
*VmPtr
3476 // Just call the data manipulation function with a flag indicating this
3477 // is a signed operation.
3479 return ExecuteDataManip (VmPtr
, TRUE
);
3484 ExecuteUnsignedDataManip (
3485 IN VM_CONTEXT
*VmPtr
3489 // Just call the data manipulation function with a flag indicating this
3490 // is not a signed operation.
3492 return ExecuteDataManip (VmPtr
, FALSE
);
3498 IN VM_CONTEXT
*VmPtr
,
3499 IN BOOLEAN IsSignedOp
3503 Routine Description:
3504 Execute all the EBC data manipulation instructions.
3505 Since the EBC data manipulation instructions all have the same basic form,
3506 they can share the code that does the fetch of operands and the write-back
3507 of the result. This function performs the fetch of the operands (even if
3508 both are not needed to be fetched, like NOT instruction), dispatches to the
3509 appropriate subfunction, then writes back the returned result.
3512 VmPtr - pointer to VM context
3518 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3530 // Get opcode and operands
3532 Opcode
= GETOPCODE (VmPtr
);
3533 Operands
= GETOPERANDS (VmPtr
);
3536 // Determine if we have immediate data by the opcode
3538 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3540 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3542 if (OPERAND2_INDIRECT (Operands
)) {
3543 Index16
= VmReadIndex16 (VmPtr
, 2);
3545 Index16
= VmReadImmed16 (VmPtr
, 2);
3554 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3556 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3557 if (OPERAND2_INDIRECT (Operands
)) {
3559 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3561 if (Opcode
& DATAMANIP_M_64
) {
3562 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3565 // Read as signed value where appropriate.
3568 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3570 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3574 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3576 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3578 Op2
= (UINT64
) ((UINT32
) Op2
);
3583 // Get operand1 (destination and sometimes also an actual operand)
3586 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3587 if (OPERAND1_INDIRECT (Operands
)) {
3588 if (Opcode
& DATAMANIP_M_64
) {
3589 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3592 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3594 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3598 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3600 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3602 Op1
= (UINT64
) ((UINT32
) Op1
);
3607 // Dispatch to the computation function
3609 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3610 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3612 EbcDebugSignalException (
3613 EXCEPT_EBC_INVALID_OPCODE
,
3614 EXCEPTION_FLAG_ERROR
,
3618 // Advance and return
3621 return EFI_UNSUPPORTED
;
3623 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3626 // Write back the result.
3628 if (OPERAND1_INDIRECT (Operands
)) {
3629 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3630 if (Opcode
& DATAMANIP_M_64
) {
3631 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3633 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3637 // Storage back to a register. Write back, clearing upper bits (as per
3638 // the specification) if 32-bit operation.
3640 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3641 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3642 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3646 // Advance the instruction pointer
3655 IN VM_CONTEXT
*VmPtr
3659 Routine Description:
3660 Execute the EBC LOADSP instruction
3663 VmPtr - pointer to a VM context
3678 Operands
= GETOPERANDS (VmPtr
);
3683 switch (OPERAND1_REGNUM (Operands
)) {
3689 // Spec states that this instruction will not modify reserved bits in
3690 // the flags register.
3692 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3696 EbcDebugSignalException (
3697 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3698 EXCEPTION_FLAG_WARNING
,
3702 return EFI_UNSUPPORTED
;
3712 IN VM_CONTEXT
*VmPtr
3716 Routine Description:
3717 Execute the EBC STORESP instruction
3720 VmPtr - pointer to a VM context
3726 STORESP Rx, FLAGS|IP
3735 Operands
= GETOPERANDS (VmPtr
);
3740 switch (OPERAND2_REGNUM (Operands
)) {
3746 // Retrieve the value in the flags register, then clear reserved bits
3748 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3752 // Get IP -- address of following instruction
3755 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3759 EbcDebugSignalException (
3760 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3761 EXCEPTION_FLAG_WARNING
,
3765 return EFI_UNSUPPORTED
;
3776 IN VM_CONTEXT
*VmPtr
,
3777 IN UINT32 CodeOffset
3781 Routine Description:
3782 Decode a 16-bit index to determine the offset. Given an index value:
3785 b14:12 - number of bits in this index assigned to natural units (=a)
3786 ba:11 - constant units = C
3787 b0:a - natural units = N
3789 Given this info, the offset can be computed by:
3790 offset = sign_bit * (C + N * sizeof(UINTN))
3792 Max offset is achieved with index = 0x7FFF giving an offset of
3793 0x27B (32-bit machine) or 0x477 (64-bit machine).
3794 Min offset is achieved with index =
3797 VmPtr - pointer to VM context
3798 CodeOffset - offset from IP of the location of the 16-bit index to decode
3813 // First read the index from the code stream
3815 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3818 // Get the mask for N. First get the number of bits from the index.
3820 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3823 // Scale it for 16-bit indexes
3828 // Now using the number of bits, create a mask.
3830 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3833 // Now using the mask, extract N from the lower bits of the index.
3835 N
= (INT16
) (Index
&~Mask
);
3840 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3842 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3847 if (Index
& 0x8000) {
3849 // Do it the hard way to work around a bogus compiler warning
3851 // Offset = -1 * Offset;
3853 Offset
= (INT16
) ((INT32
) Offset
* -1);
3862 IN VM_CONTEXT
*VmPtr
,
3863 IN UINT32 CodeOffset
3867 Routine Description:
3868 Decode a 32-bit index to determine the offset.
3871 VmPtr - pointer to VM context
3872 CodeOffset - offset from IP of the location of the 32-bit index to decode
3875 Converted index per EBC VM specification
3886 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3889 // Get the mask for N. First get the number of bits from the index.
3891 NBits
= (Index
& 0x70000000) >> 28;
3894 // Scale it for 32-bit indexes
3899 // Now using the number of bits, create a mask.
3901 Mask
= (INT32
)~0 << NBits
;
3904 // Now using the mask, extract N from the lower bits of the index.
3911 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3913 Offset
= N
* sizeof (UINTN
) + C
;
3918 if (Index
& 0x80000000) {
3919 Offset
= Offset
* -1;
3928 IN VM_CONTEXT
*VmPtr
,
3929 IN UINT32 CodeOffset
3933 Routine Description:
3934 Decode a 64-bit index to determine the offset.
3937 VmPtr - pointer to VM context
3938 CodeOffset - offset from IP of the location of the 64-bit index to decode
3941 Converted index per EBC VM specification
3952 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3955 // Get the mask for N. First get the number of bits from the index.
3957 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3960 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3962 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3965 // Now using the number of bits, create a mask.
3967 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3970 // Now using the mask, extract N from the lower bits of the index.
3977 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3979 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3984 if (Index
& 0x8000000000000000ULL
) {
3985 Offset
= MultS64x64 (Offset
, -1);
3994 IN VM_CONTEXT
*VmPtr
,
4000 Routine Description:
4001 The following VmWriteMem? routines are called by the EBC data
4002 movement instructions that write to memory. Since these writes
4003 may be to the stack, which looks like (high address on top) this,
4005 [EBC entry point arguments]
4009 we need to detect all attempts to write to the EBC entry point argument
4010 stack area and adjust the address (which will initially point into the
4011 VM stack) to point into the EBC entry point arguments.
4014 VmPtr - pointer to a VM context
4015 Addr - adddress to write to
4016 Data - value to write to Addr
4024 // Convert the address if it's in the stack gap
4026 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4027 *(UINT8
*) Addr
= Data
;
4034 IN VM_CONTEXT
*VmPtr
,
4042 // Convert the address if it's in the stack gap
4044 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4047 // Do a simple write if aligned
4049 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4050 *(UINT16
*) Addr
= Data
;
4053 // Write as two bytes
4056 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4061 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4074 IN VM_CONTEXT
*VmPtr
,
4082 // Convert the address if it's in the stack gap
4084 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4087 // Do a simple write if aligned
4089 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4090 *(UINT32
*) Addr
= Data
;
4093 // Write as two words
4096 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4101 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4113 IN VM_CONTEXT
*VmPtr
,
4122 // Convert the address if it's in the stack gap
4124 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4127 // Do a simple write if aligned
4129 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4130 *(UINT64
*) Addr
= Data
;
4133 // Write as two 32-bit words
4136 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4141 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4142 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4154 IN VM_CONTEXT
*VmPtr
,
4162 Status
= EFI_SUCCESS
;
4165 // Convert the address if it's in the stack gap
4167 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4170 // Do a simple write if aligned
4172 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4173 *(UINTN
*) Addr
= Data
;
4175 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4177 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4179 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4189 IN VM_CONTEXT
*VmPtr
,
4194 Routine Description:
4196 The following VmReadImmed routines are called by the EBC execute
4197 functions to read EBC immediate values from the code stream.
4198 Since we can't assume alignment, each tries to read in the biggest
4199 chunks size available, but will revert to smaller reads if necessary.
4202 VmPtr - pointer to a VM context
4203 Offset - offset from IP of the code bytes to read.
4206 Signed data of the requested size from the specified address.
4211 // Simply return the data in flat memory space
4213 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4219 IN VM_CONTEXT
*VmPtr
,
4224 // Read direct if aligned
4226 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4227 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4230 // All code word reads should be aligned
4232 EbcDebugSignalException (
4233 EXCEPT_EBC_ALIGNMENT_CHECK
,
4234 EXCEPTION_FLAG_WARNING
,
4239 // Return unaligned data
4241 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4247 IN VM_CONTEXT
*VmPtr
,
4254 // Read direct if aligned
4256 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4257 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4260 // Return unaligned data
4262 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4263 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4270 IN VM_CONTEXT
*VmPtr
,
4279 // Read direct if aligned
4281 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4282 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4285 // Return unaligned data.
4287 Ptr
= (UINT8
*) &Data64
;
4288 Data32
= VmReadCode32 (VmPtr
, Offset
);
4289 *(UINT32
*) Ptr
= Data32
;
4290 Ptr
+= sizeof (Data32
);
4291 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4292 *(UINT32
*) Ptr
= Data32
;
4299 IN VM_CONTEXT
*VmPtr
,
4304 Routine Description:
4305 The following VmReadCode() routines provide the ability to read raw
4306 unsigned data from the code stream.
4309 VmPtr - pointer to VM context
4310 Offset - offset from current IP to the raw data to read.
4313 The raw unsigned 16-bit value from the code stream.
4318 // Read direct if aligned
4320 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4321 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4324 // All code word reads should be aligned
4326 EbcDebugSignalException (
4327 EXCEPT_EBC_ALIGNMENT_CHECK
,
4328 EXCEPTION_FLAG_WARNING
,
4333 // Return unaligned data
4335 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4341 IN VM_CONTEXT
*VmPtr
,
4347 // Read direct if aligned
4349 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4350 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4353 // Return unaligned data
4355 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4356 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4363 IN VM_CONTEXT
*VmPtr
,
4372 // Read direct if aligned
4374 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4375 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4378 // Return unaligned data.
4380 Ptr
= (UINT8
*) &Data64
;
4381 Data32
= VmReadCode32 (VmPtr
, Offset
);
4382 *(UINT32
*) Ptr
= Data32
;
4383 Ptr
+= sizeof (Data32
);
4384 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4385 *(UINT32
*) Ptr
= Data32
;
4392 IN VM_CONTEXT
*VmPtr
,
4397 // Convert the address if it's in the stack gap
4399 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4401 // Simply return the data in flat memory space
4403 return * (UINT8
*) Addr
;
4409 IN VM_CONTEXT
*VmPtr
,
4414 // Convert the address if it's in the stack gap
4416 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4418 // Read direct if aligned
4420 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4421 return * (UINT16
*) Addr
;
4424 // Return unaligned data
4426 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4432 IN VM_CONTEXT
*VmPtr
,
4439 // Convert the address if it's in the stack gap
4441 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4443 // Read direct if aligned
4445 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4446 return * (UINT32
*) Addr
;
4449 // Return unaligned data
4451 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4452 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4459 IN VM_CONTEXT
*VmPtr
,
4467 // Convert the address if it's in the stack gap
4469 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4472 // Read direct if aligned
4474 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4475 return * (UINT64
*) Addr
;
4478 // Return unaligned data. Assume little endian.
4480 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4481 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4482 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4489 IN VM_CONTEXT
*VmPtr
,
4494 Routine Description:
4496 Given an address that EBC is going to read from or write to, return
4497 an appropriate address that accounts for a gap in the stack.
4499 The stack for this application looks like this (high addr on top)
4500 [EBC entry point arguments]
4504 The EBC assumes that its arguments are at the top of its stack, which
4505 is where the VM stack is really. Therefore if the EBC does memory
4506 accesses into the VM stack area, then we need to convert the address
4507 to point to the EBC entry point arguments area. Do this here.
4511 VmPtr - pointer to VM context
4512 Addr - address of interest
4516 The unchanged address if it's not in the VM stack region. Otherwise,
4517 adjust for the stack gap and return the modified address.
4521 if ((Addr
>= VmPtr
->LowStackTop
) && (Addr
< VmPtr
->HighStackBottom
)) {
4523 // In the stack gap -- now make sure it's not in the VM itself, which
4524 // would be the case if it's accessing VM register contents.
4526 if ((Addr
< (UINTN
) VmPtr
) || (Addr
> (UINTN
) VmPtr
+ sizeof (VM_CONTEXT
))) {
4527 VmPtr
->LastAddrConverted
= Addr
;
4528 VmPtr
->LastAddrConvertedValue
= Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4529 return Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4539 IN VM_CONTEXT
*VmPtr
,
4544 Routine Description:
4545 Read a natural value from memory. May or may not be aligned.
4548 VmPtr - current VM context
4549 Addr - the address to read from
4552 The natural value at address Addr.
4561 // Convert the address if it's in the stack gap
4563 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4565 // Read direct if aligned
4567 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4568 return * (UINTN
*) Addr
;
4571 // Return unaligned data
4574 FromPtr
= (UINT8
*) Addr
;
4575 ToPtr
= (UINT8
*) &Data
;
4577 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4591 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));