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
;
689 EbcSimpleDebugger
= NULL
;
690 Status
= EFI_SUCCESS
;
694 // Make sure the magic value has been put on the stack before we got here.
696 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
700 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
703 // Try to get the debug support for EBC
706 Status
= gBS
->LocateProtocol (
707 &mEbcSimpleDebuggerProtocolGuid
,
709 (VOID
**) &EbcSimpleDebugger
711 if (EFI_ERROR (Status
)) {
712 EbcSimpleDebugger
= NULL
;
717 // Save the start IP for debug. For example, if we take an exception we
718 // can print out the location of the exception relative to the entry point,
719 // which could then be used in a disassembly listing to find the problem.
721 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
724 // We'll wait for this flag to know when we're done. The RET
725 // instruction sets it if it runs out of stack.
727 VmPtr
->StopFlags
= 0;
728 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
730 // If we've found a simple debugger protocol, call it
733 if (EbcSimpleDebugger
!= NULL
) {
734 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
739 // Verify the opcode is in range. Otherwise generate an exception.
741 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
742 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
743 Status
= EFI_UNSUPPORTED
;
747 // Use the opcode bits to index into the opcode dispatch table. If the
748 // function pointer is null then generate an exception.
750 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
751 if (ExecFunc
== (UINTN
) NULL
) {
752 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
753 Status
= EFI_UNSUPPORTED
;
757 // The EBC VM is a strongly ordered processor, so perform a fence operation before
758 // and after each instruction is executed.
762 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
767 // If the step flag is set, signal an exception and continue. We don't
768 // clear it here. Assuming the debugger is responsible for clearing it.
770 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
771 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
774 // Make sure stack has not been corrupted. Only report it once though.
776 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
777 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
796 Execute the MOVxx instructions.
800 VmPtr - pointer to a VM context.
809 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
810 MOVqq {@}R1 {Index64}, {@}R2 {Index64}
812 Copies contents of [R2] -> [R1], zero extending where required.
814 First character indicates the size of the move.
815 Second character indicates the size of the index(s).
817 Invalid to have R1 direct with index.
834 Opcode
= GETOPCODE (VmPtr
);
835 OpcMasked
= (UINT8
) (Opcode
& OPCODE_M_OPCODE
);
838 // Get the operands byte so we can get R1 and R2
840 Operands
= GETOPERANDS (VmPtr
);
850 // Determine if we have an index/immediate data. Base instruction size
851 // is 2 (opcode + operands). Add to this size each index specified.
854 if (Opcode
& (OPCODE_M_IMMED_OP1
| OPCODE_M_IMMED_OP2
)) {
856 // Determine size of the index from the opcode. Then get it.
858 if ((OpcMasked
<= OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVNW
)) {
860 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
861 // Get one or both index values.
863 if (Opcode
& OPCODE_M_IMMED_OP1
) {
864 Index16
= VmReadIndex16 (VmPtr
, 2);
865 Index64Op1
= (INT64
) Index16
;
866 Size
+= sizeof (UINT16
);
869 if (Opcode
& OPCODE_M_IMMED_OP2
) {
870 Index16
= VmReadIndex16 (VmPtr
, Size
);
871 Index64Op2
= (INT64
) Index16
;
872 Size
+= sizeof (UINT16
);
874 } else if ((OpcMasked
<= OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVND
)) {
876 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
878 if (Opcode
& OPCODE_M_IMMED_OP1
) {
879 Index32
= VmReadIndex32 (VmPtr
, 2);
880 Index64Op1
= (INT64
) Index32
;
881 Size
+= sizeof (UINT32
);
884 if (Opcode
& OPCODE_M_IMMED_OP2
) {
885 Index32
= VmReadIndex32 (VmPtr
, Size
);
886 Index64Op2
= (INT64
) Index32
;
887 Size
+= sizeof (UINT32
);
889 } else if (OpcMasked
== OPCODE_MOVQQ
) {
891 // MOVqq -- only form with a 64-bit index
893 if (Opcode
& OPCODE_M_IMMED_OP1
) {
894 Index64Op1
= VmReadIndex64 (VmPtr
, 2);
895 Size
+= sizeof (UINT64
);
898 if (Opcode
& OPCODE_M_IMMED_OP2
) {
899 Index64Op2
= VmReadIndex64 (VmPtr
, Size
);
900 Size
+= sizeof (UINT64
);
904 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
906 EbcDebugSignalException (
907 EXCEPT_EBC_INSTRUCTION_ENCODING
,
908 EXCEPTION_FLAG_FATAL
,
911 return EFI_UNSUPPORTED
;
915 // Determine the size of the move, and create a mask for it so we can
916 // clear unused bits.
918 if ((OpcMasked
== OPCODE_MOVBW
) || (OpcMasked
== OPCODE_MOVBD
)) {
919 MoveSize
= DATA_SIZE_8
;
921 } else if ((OpcMasked
== OPCODE_MOVWW
) || (OpcMasked
== OPCODE_MOVWD
)) {
922 MoveSize
= DATA_SIZE_16
;
924 } else if ((OpcMasked
== OPCODE_MOVDW
) || (OpcMasked
== OPCODE_MOVDD
)) {
925 MoveSize
= DATA_SIZE_32
;
926 DataMask
= 0xFFFFFFFF;
927 } else if ((OpcMasked
== OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVQQ
)) {
928 MoveSize
= DATA_SIZE_64
;
929 DataMask
= (UINT64
)~0;
930 } else if ((OpcMasked
== OPCODE_MOVNW
) || (OpcMasked
== OPCODE_MOVND
)) {
931 MoveSize
= DATA_SIZE_N
;
932 DataMask
= (UINT64
)~0 >> (64 - 8 * sizeof (UINTN
));
935 // We were dispatched to this function and we don't recognize the opcode
937 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED
, EXCEPTION_FLAG_FATAL
, VmPtr
);
938 return EFI_UNSUPPORTED
;
941 // Now get the source address
943 if (OPERAND2_INDIRECT (Operands
)) {
945 // Indirect form @R2. Compute address of operand2
947 Source
= (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
);
949 // Now get the data from the source. Always 0-extend and let the compiler
950 // sign-extend where required.
954 Data64
= (UINT64
) (UINT8
) VmReadMem8 (VmPtr
, Source
);
958 Data64
= (UINT64
) (UINT16
) VmReadMem16 (VmPtr
, Source
);
962 Data64
= (UINT64
) (UINT32
) VmReadMem32 (VmPtr
, Source
);
966 Data64
= (UINT64
) VmReadMem64 (VmPtr
, Source
);
970 Data64
= (UINT64
) (UINTN
) VmReadMemN (VmPtr
, Source
);
981 // Not indirect source: MOVxx {@}Rx, Ry [Index]
983 Data64
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
;
985 // Did Operand2 have an index? If so, treat as two signed values since
986 // indexes are signed values.
988 if (Opcode
& OPCODE_M_IMMED_OP2
) {
990 // NOTE: need to find a way to fix this, most likely by changing the VM
991 // implementation to remove the stack gap. To do that, we'd need to
992 // allocate stack space for the VM and actually set the system
993 // stack pointer to the allocated buffer when the VM starts.
995 // Special case -- if someone took the address of a function parameter
996 // then we need to make sure it's not in the stack gap. We can identify
997 // this situation if (Operand2 register == 0) && (Operand2 is direct)
998 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
999 // Situations that to be aware of:
1000 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
1002 if ((OPERAND2_REGNUM (Operands
) == 0) &&
1003 (!OPERAND2_INDIRECT (Operands
)) &&
1005 (OPERAND1_REGNUM (Operands
) == 0) &&
1006 (OPERAND1_INDIRECT (Operands
))
1008 Data64
= (UINT64
) ConvertStackAddr (VmPtr
, (UINTN
) (INT64
) Data64
);
1013 // Now write it back
1015 if (OPERAND1_INDIRECT (Operands
)) {
1017 // Reuse the Source variable to now be dest.
1019 Source
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index64Op1
);
1021 // Do the write based on the size
1025 VmWriteMem8 (VmPtr
, Source
, (UINT8
) Data64
);
1029 VmWriteMem16 (VmPtr
, Source
, (UINT16
) Data64
);
1033 VmWriteMem32 (VmPtr
, Source
, (UINT32
) Data64
);
1037 VmWriteMem64 (VmPtr
, Source
, Data64
);
1041 VmWriteMemN (VmPtr
, Source
, (UINTN
) Data64
);
1053 // Make sure we didn't have an index on operand1.
1055 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1056 EbcDebugSignalException (
1057 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1058 EXCEPTION_FLAG_FATAL
,
1061 return EFI_UNSUPPORTED
;
1064 // Direct storage in register. Clear unused bits and store back to
1067 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
& DataMask
;
1070 // Advance the instruction pointer
1079 IN VM_CONTEXT
*VmPtr
1083 Routine Description:
1085 Execute the EBC BREAK instruction
1089 VmPtr - pointer to current VM context
1099 VOID
*EbcEntryPoint
;
1101 UINT64 U64EbcEntryPoint
;
1104 Operands
= GETOPERANDS (VmPtr
);
1107 // Runaway program break. Generate an exception and terminate
1110 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1114 // Get VM version -- return VM revision number in R7
1120 // 16-8 = Major version
1121 // 7-0 = Minor version
1123 VmPtr
->R
[7] = GetVmVersion ();
1127 // Debugger breakpoint
1130 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1132 // See if someone has registered a handler
1134 EbcDebugSignalException (
1135 EXCEPT_EBC_BREAKPOINT
,
1136 EXCEPTION_FLAG_NONE
,
1140 // Don't advance the IP
1142 return EFI_UNSUPPORTED
;
1146 // System call, which there are none, so NOP it.
1152 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1153 // "offset from self" pointer to the EBC entry point.
1154 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1157 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1158 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1159 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1162 // Now create a new thunk
1164 EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1167 // Finally replace the EBC entry point memory with the thunk address
1169 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1173 // Compiler setting version per value in R7
1176 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1178 // Check compiler version against VM version?
1183 // Unhandled break code. Signal exception.
1186 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1199 IN VM_CONTEXT
*VmPtr
1203 Routine Description:
1204 Execute the JMP instruction
1207 VmPtr - pointer to VM context
1213 JMP64{cs|cc} Immed64
1214 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1217 b0.7 - immediate data present
1218 b0.6 - 1 = 64 bit immediate data
1219 0 = 32 bit immediate data
1220 b1.7 - 1 = conditional
1221 b1.6 1 = CS (condition set)
1222 0 = CC (condition clear)
1223 b1.4 1 = relative address
1224 0 = absolute address
1225 b1.3 1 = operand1 indirect
1232 UINT8 ConditionFlag
;
1239 Operand
= GETOPERANDS (VmPtr
);
1240 Opcode
= GETOPCODE (VmPtr
);
1243 // Get instruction length from the opcode. The upper two bits are used here
1244 // to index into the length array.
1246 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1249 // Decode instruction conditions
1250 // If we haven't met the condition, then simply advance the IP and return.
1252 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1253 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1254 if (Operand
& CONDITION_M_CONDITIONAL
) {
1255 if (CompareSet
!= ConditionFlag
) {
1261 // Check for 64-bit form and do it right away since it's the most
1262 // straight-forward form.
1264 if (Opcode
& OPCODE_M_IMMDATA64
) {
1266 // Double check for immediate-data, which is required. If not there,
1267 // then signal an exception
1269 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1270 EbcDebugSignalException (
1271 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1272 EXCEPTION_FLAG_ERROR
,
1275 return EFI_UNSUPPORTED
;
1278 // 64-bit immediate data is full address. Read the immediate data,
1279 // check for alignment, and jump absolute.
1281 Data64
= VmReadImmed64 (VmPtr
, 2);
1282 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1283 EbcDebugSignalException (
1284 EXCEPT_EBC_ALIGNMENT_CHECK
,
1285 EXCEPTION_FLAG_FATAL
,
1289 return EFI_UNSUPPORTED
;
1293 // Take jump -- relative or absolute
1295 if (Operand
& JMP_M_RELATIVE
) {
1296 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1298 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1305 // Get the index if there is one. May be either an index, or an immediate
1306 // offset depending on indirect operand.
1307 // JMP32 @R1 Index32 -- immediate data is an index
1308 // JMP32 R1 Immed32 -- immedate data is an offset
1310 if (Opcode
& OPCODE_M_IMMDATA
) {
1311 if (OPERAND1_INDIRECT (Operand
)) {
1312 Index32
= VmReadIndex32 (VmPtr
, 2);
1314 Index32
= VmReadImmed32 (VmPtr
, 2);
1320 // Get the register data. If R == 0, then special case where it's ignored.
1322 if (OPERAND1_REGNUM (Operand
) == 0) {
1325 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1330 if (OPERAND1_INDIRECT (Operand
)) {
1332 // Form: JMP32 @Rx {Index32}
1334 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1335 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1336 EbcDebugSignalException (
1337 EXCEPT_EBC_ALIGNMENT_CHECK
,
1338 EXCEPTION_FLAG_FATAL
,
1342 return EFI_UNSUPPORTED
;
1345 if (Operand
& JMP_M_RELATIVE
) {
1346 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1348 VmPtr
->Ip
= (VMIP
) Addr
;
1352 // Form: JMP32 Rx {Immed32}
1354 Addr
= (UINTN
) (Data64
+ Index32
);
1355 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1356 EbcDebugSignalException (
1357 EXCEPT_EBC_ALIGNMENT_CHECK
,
1358 EXCEPTION_FLAG_FATAL
,
1362 return EFI_UNSUPPORTED
;
1365 if (Operand
& JMP_M_RELATIVE
) {
1366 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1368 VmPtr
->Ip
= (VMIP
) Addr
;
1378 IN VM_CONTEXT
*VmPtr
1382 Routine Description:
1383 Execute the EBC JMP8 instruction
1386 VmPtr - pointer to a VM context
1392 JMP8{cs|cc} Offset/2
1397 UINT8 ConditionFlag
;
1402 // Decode instruction.
1404 Opcode
= GETOPCODE (VmPtr
);
1405 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1406 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1409 // If we haven't met the condition, then simply advance the IP and return
1411 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1412 if (CompareSet
!= ConditionFlag
) {
1418 // Get the offset from the instruction stream. It's relative to the
1419 // following instruction, and divided by 2.
1421 Offset
= VmReadImmed8 (VmPtr
, 1);
1423 // Want to check for offset == -2 and then raise an exception?
1425 VmPtr
->Ip
+= (Offset
* 2) + 2;
1432 IN VM_CONTEXT
*VmPtr
1436 Routine Description:
1438 Execute the EBC MOVI
1442 VmPtr - pointer to a VM context
1450 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1452 First variable character specifies the move size
1453 Second variable character specifies size of the immediate data
1455 Sign-extend the immediate data to the size of the operation, and zero-extend
1456 if storing to a register.
1458 Operand1 direct with index/immed is invalid.
1471 // Get the opcode and operands byte so we can get R1 and R2
1473 Opcode
= GETOPCODE (VmPtr
);
1474 Operands
= GETOPERANDS (VmPtr
);
1477 // Get the index (16-bit) if present
1479 if (Operands
& MOVI_M_IMMDATA
) {
1480 Index16
= VmReadIndex16 (VmPtr
, 2);
1487 // Extract the immediate data. Sign-extend always.
1489 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1490 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1492 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1493 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1495 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1496 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1502 EbcDebugSignalException (
1503 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1504 EXCEPTION_FLAG_FATAL
,
1507 return EFI_UNSUPPORTED
;
1510 // Now write back the result
1512 if (!OPERAND1_INDIRECT (Operands
)) {
1514 // Operand1 direct. Make sure it didn't have an index.
1516 if (Operands
& MOVI_M_IMMDATA
) {
1517 EbcDebugSignalException (
1518 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1519 EXCEPTION_FLAG_FATAL
,
1522 return EFI_UNSUPPORTED
;
1525 // Writing directly to a register. Clear unused bits.
1527 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1528 Mask64
= 0x000000FF;
1529 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1530 Mask64
= 0x0000FFFF;
1531 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1532 Mask64
= 0x00000000FFFFFFFF;
1534 Mask64
= (UINT64
)~0;
1537 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1540 // Get the address then write back based on size of the move
1542 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1543 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1544 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1545 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1546 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1547 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1548 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1550 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1554 // Advance the instruction pointer
1563 IN VM_CONTEXT
*VmPtr
1567 Routine Description:
1569 Execute the EBC MOV immediate natural. This instruction moves an immediate
1570 index value into a register or memory location.
1574 VmPtr - pointer to a VM context
1582 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1596 // Get the opcode and operands byte so we can get R1 and R2
1598 Opcode
= GETOPCODE (VmPtr
);
1599 Operands
= GETOPERANDS (VmPtr
);
1602 // Get the operand1 index (16-bit) if present
1604 if (Operands
& MOVI_M_IMMDATA
) {
1605 Index16
= VmReadIndex16 (VmPtr
, 2);
1612 // Extract the immediate data and convert to a 64-bit index.
1614 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1615 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1616 ImmedIndex64
= (INT64
) ImmedIndex16
;
1618 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1619 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1620 ImmedIndex64
= (INT64
) ImmedIndex32
;
1622 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1623 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1629 EbcDebugSignalException (
1630 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1631 EXCEPTION_FLAG_FATAL
,
1634 return EFI_UNSUPPORTED
;
1637 // Now write back the result
1639 if (!OPERAND1_INDIRECT (Operands
)) {
1641 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1644 if (Operands
& MOVI_M_IMMDATA
) {
1645 EbcDebugSignalException (
1646 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1647 EXCEPTION_FLAG_FATAL
,
1650 return EFI_UNSUPPORTED
;
1653 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1658 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1659 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1662 // Advance the instruction pointer
1671 IN VM_CONTEXT
*VmPtr
1675 Routine Description:
1677 Execute the EBC MOVREL instruction.
1678 Dest <- Ip + ImmData
1682 VmPtr - pointer to a VM context
1690 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1703 // Get the opcode and operands byte so we can get R1 and R2
1705 Opcode
= GETOPCODE (VmPtr
);
1706 Operands
= GETOPERANDS (VmPtr
);
1709 // Get the Operand 1 index (16-bit) if present
1711 if (Operands
& MOVI_M_IMMDATA
) {
1712 Index16
= VmReadIndex16 (VmPtr
, 2);
1719 // Get the immediate data.
1721 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1722 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1724 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1725 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1727 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1728 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1734 EbcDebugSignalException (
1735 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1736 EXCEPTION_FLAG_FATAL
,
1739 return EFI_UNSUPPORTED
;
1742 // Compute the value and write back the result
1744 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1745 if (!OPERAND1_INDIRECT (Operands
)) {
1747 // Check for illegal combination of operand1 direct with immediate data
1749 if (Operands
& MOVI_M_IMMDATA
) {
1750 EbcDebugSignalException (
1751 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1752 EXCEPTION_FLAG_FATAL
,
1755 return EFI_UNSUPPORTED
;
1758 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1761 // Get the address = [Rx] + Index16
1762 // Write back the result. Always a natural size write, since
1763 // we're talking addresses here.
1765 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1766 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1769 // Advance the instruction pointer
1778 IN VM_CONTEXT
*VmPtr
1782 Routine Description:
1784 Execute the EBC MOVsnw instruction. This instruction loads a signed
1785 natural value from memory or register to another memory or register. On
1786 32-bit machines, the value gets sign-extended to 64 bits if the destination
1791 VmPtr - pointer to a VM context
1799 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1801 0:7 1=>operand1 index present
1802 0:6 1=>operand2 index present
1814 // Get the opcode and operand bytes
1816 Opcode
= GETOPCODE (VmPtr
);
1817 Operands
= GETOPERANDS (VmPtr
);
1819 Op1Index
= Op2Index
= 0;
1822 // Get the indexes if present.
1825 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1826 if (OPERAND1_INDIRECT (Operands
)) {
1827 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1830 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1832 EbcDebugSignalException (
1833 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1834 EXCEPTION_FLAG_FATAL
,
1837 return EFI_UNSUPPORTED
;
1840 Size
+= sizeof (UINT16
);
1843 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1844 if (OPERAND2_INDIRECT (Operands
)) {
1845 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1847 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1850 Size
+= sizeof (UINT16
);
1853 // Get the data from the source.
1855 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1856 if (OPERAND2_INDIRECT (Operands
)) {
1857 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1860 // Now write back the result.
1862 if (!OPERAND1_INDIRECT (Operands
)) {
1863 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1865 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1868 // Advance the instruction pointer
1877 IN VM_CONTEXT
*VmPtr
1881 Routine Description:
1883 Execute the EBC MOVsnw instruction. This instruction loads a signed
1884 natural value from memory or register to another memory or register. On
1885 32-bit machines, the value gets sign-extended to 64 bits if the destination
1890 VmPtr - pointer to a VM context
1898 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1900 0:7 1=>operand1 index present
1901 0:6 1=>operand2 index present
1913 // Get the opcode and operand bytes
1915 Opcode
= GETOPCODE (VmPtr
);
1916 Operands
= GETOPERANDS (VmPtr
);
1918 Op1Index
= Op2Index
= 0;
1921 // Get the indexes if present.
1924 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1925 if (OPERAND1_INDIRECT (Operands
)) {
1926 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1929 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1931 EbcDebugSignalException (
1932 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1933 EXCEPTION_FLAG_FATAL
,
1936 return EFI_UNSUPPORTED
;
1939 Size
+= sizeof (UINT32
);
1942 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1943 if (OPERAND2_INDIRECT (Operands
)) {
1944 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1946 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1949 Size
+= sizeof (UINT32
);
1952 // Get the data from the source.
1954 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1955 if (OPERAND2_INDIRECT (Operands
)) {
1956 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1959 // Now write back the result.
1961 if (!OPERAND1_INDIRECT (Operands
)) {
1962 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1964 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1967 // Advance the instruction pointer
1976 IN VM_CONTEXT
*VmPtr
1980 Routine Description:
1981 Execute the EBC PUSHn instruction
1984 VmPtr - pointer to a VM context
1990 PUSHn {@}R1 {Index16|Immed16}
2000 // Get opcode and operands
2002 Opcode
= GETOPCODE (VmPtr
);
2003 Operands
= GETOPERANDS (VmPtr
);
2006 // Get index if present
2008 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2009 if (OPERAND1_INDIRECT (Operands
)) {
2010 Index16
= VmReadIndex16 (VmPtr
, 2);
2012 Index16
= VmReadImmed16 (VmPtr
, 2);
2021 // Get the data to push
2023 if (OPERAND1_INDIRECT (Operands
)) {
2024 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2026 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2029 // Adjust the stack down.
2031 VmPtr
->R
[0] -= sizeof (UINTN
);
2032 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2039 IN VM_CONTEXT
*VmPtr
2043 Routine Description:
2044 Execute the EBC PUSH instruction
2047 VmPtr - pointer to a VM context
2053 PUSH[32|64] {@}R1 {Index16|Immed16}
2064 // Get opcode and operands
2066 Opcode
= GETOPCODE (VmPtr
);
2067 Operands
= GETOPERANDS (VmPtr
);
2069 // Get immediate index if present, then advance the IP.
2071 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2072 if (OPERAND1_INDIRECT (Operands
)) {
2073 Index16
= VmReadIndex16 (VmPtr
, 2);
2075 Index16
= VmReadImmed16 (VmPtr
, 2);
2084 // Get the data to push
2086 if (Opcode
& PUSHPOP_M_64
) {
2087 if (OPERAND1_INDIRECT (Operands
)) {
2088 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2090 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2093 // Adjust the stack down, then write back the data
2095 VmPtr
->R
[0] -= sizeof (UINT64
);
2096 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2101 if (OPERAND1_INDIRECT (Operands
)) {
2102 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2104 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2107 // Adjust the stack down and write the data
2109 VmPtr
->R
[0] -= sizeof (UINT32
);
2110 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2119 IN VM_CONTEXT
*VmPtr
2123 Routine Description:
2124 Execute the EBC POPn instruction
2127 VmPtr - pointer to a VM context
2133 POPn {@}R1 {Index16|Immed16}
2143 // Get opcode and operands
2145 Opcode
= GETOPCODE (VmPtr
);
2146 Operands
= GETOPERANDS (VmPtr
);
2148 // Get immediate data if present, and advance the IP
2150 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2151 if (OPERAND1_INDIRECT (Operands
)) {
2152 Index16
= VmReadIndex16 (VmPtr
, 2);
2154 Index16
= VmReadImmed16 (VmPtr
, 2);
2163 // Read the data off the stack, then adjust the stack pointer
2165 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2166 VmPtr
->R
[0] += sizeof (UINTN
);
2168 // Do the write-back
2170 if (OPERAND1_INDIRECT (Operands
)) {
2171 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2173 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2182 IN VM_CONTEXT
*VmPtr
2186 Routine Description:
2187 Execute the EBC POP instruction
2190 VmPtr - pointer to a VM context
2196 POP {@}R1 {Index16|Immed16}
2207 // Get opcode and operands
2209 Opcode
= GETOPCODE (VmPtr
);
2210 Operands
= GETOPERANDS (VmPtr
);
2212 // Get immediate data if present, and advance the IP.
2214 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2215 if (OPERAND1_INDIRECT (Operands
)) {
2216 Index16
= VmReadIndex16 (VmPtr
, 2);
2218 Index16
= VmReadImmed16 (VmPtr
, 2);
2227 // Get the data off the stack, then write it to the appropriate location
2229 if (Opcode
& PUSHPOP_M_64
) {
2231 // Read the data off the stack, then adjust the stack pointer
2233 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2234 VmPtr
->R
[0] += sizeof (UINT64
);
2236 // Do the write-back
2238 if (OPERAND1_INDIRECT (Operands
)) {
2239 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2241 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2245 // 32-bit pop. Read it off the stack and adjust the stack pointer
2247 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2248 VmPtr
->R
[0] += sizeof (UINT32
);
2250 // Do the write-back
2252 if (OPERAND1_INDIRECT (Operands
)) {
2253 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2255 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2265 IN VM_CONTEXT
*VmPtr
2269 Routine Description:
2270 Implements the EBC CALL instruction.
2275 CALL32 {@}R1 {Immed32|Index32}
2277 CALLEX16 {@}R1 {Immed32}
2279 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2282 VmPtr - pointer to a VM context.
2297 // Get opcode and operands
2299 Opcode
= GETOPCODE (VmPtr
);
2300 Operands
= GETOPERANDS (VmPtr
);
2302 // Assign these as well to avoid compiler warnings
2307 FramePtr
= VmPtr
->FramePtr
;
2309 // Determine the instruction size, and get immediate data if present
2311 if (Opcode
& OPCODE_M_IMMDATA
) {
2312 if (Opcode
& OPCODE_M_IMMDATA64
) {
2313 Immed64
= VmReadImmed64 (VmPtr
, 2);
2317 // If register operand is indirect, then the immediate data is an index
2319 if (OPERAND1_INDIRECT (Operands
)) {
2320 Immed32
= VmReadIndex32 (VmPtr
, 2);
2322 Immed32
= VmReadImmed32 (VmPtr
, 2);
2331 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2332 // put our return address and frame pointer on the VM stack.
2334 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2336 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2337 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2339 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2342 // If 64-bit data, then absolute jump only
2344 if (Opcode
& OPCODE_M_IMMDATA64
) {
2346 // Native or EBC call?
2348 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2349 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2352 // Call external function, get the return value, and advance the IP
2354 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2358 // Get the register data. If operand1 == 0, then ignore register and
2359 // take immediate data as relative or absolute address.
2360 // Compiler should take care of upper bits if 32-bit machine.
2362 if (OPERAND1_REGNUM (Operands
) != 0) {
2363 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2366 // Get final address
2368 if (OPERAND1_INDIRECT (Operands
)) {
2369 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2374 // Now determine if external call, and then if relative or absolute
2376 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2378 // EBC call. Relative or absolute? If relative, then it's relative to the
2379 // start of the next instruction.
2381 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2382 VmPtr
->Ip
+= Immed64
+ Size
;
2384 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2388 // Native call. Relative or absolute?
2390 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2391 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2393 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2397 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2408 IN VM_CONTEXT
*VmPtr
2412 Routine Description:
2413 Execute the EBC RET instruction
2416 VmPtr - pointer to a VM context
2427 // If we're at the top of the stack, then simply set the done
2430 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2431 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2434 // Pull the return address off the VM app's stack and set the IP
2437 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2438 EbcDebugSignalException (
2439 EXCEPT_EBC_ALIGNMENT_CHECK
,
2440 EXCEPTION_FLAG_FATAL
,
2445 // Restore the IP and frame pointer from the stack
2447 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2449 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2459 IN VM_CONTEXT
*VmPtr
2463 Routine Description:
2464 Execute the EBC CMP instruction
2467 VmPtr - pointer to a VM context
2473 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2486 // Get opcode and operands
2488 Opcode
= GETOPCODE (VmPtr
);
2489 Operands
= GETOPERANDS (VmPtr
);
2491 // Get the register data we're going to compare to
2493 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2495 // Get immediate data
2497 if (Opcode
& OPCODE_M_IMMDATA
) {
2498 if (OPERAND2_INDIRECT (Operands
)) {
2499 Index16
= VmReadIndex16 (VmPtr
, 2);
2501 Index16
= VmReadImmed16 (VmPtr
, 2);
2512 if (OPERAND2_INDIRECT (Operands
)) {
2513 if (Opcode
& OPCODE_M_64BIT
) {
2514 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2517 // 32-bit operations. 0-extend the values for all cases.
2519 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2522 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2525 // Now do the compare
2528 if (Opcode
& OPCODE_M_64BIT
) {
2532 switch (Opcode
& OPCODE_M_OPCODE
) {
2551 case OPCODE_CMPULTE
:
2552 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2557 case OPCODE_CMPUGTE
:
2558 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2570 switch (Opcode
& OPCODE_M_OPCODE
) {
2572 if ((INT32
) Op1
== (INT32
) Op2
) {
2578 if ((INT32
) Op1
<= (INT32
) Op2
) {
2584 if ((INT32
) Op1
>= (INT32
) Op2
) {
2589 case OPCODE_CMPULTE
:
2590 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2595 case OPCODE_CMPUGTE
:
2596 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2606 // Now set the flag accordingly for the comparison
2609 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2611 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2623 IN VM_CONTEXT
*VmPtr
2627 Routine Description:
2628 Execute the EBC CMPI instruction
2631 VmPtr - pointer to a VM context
2637 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2650 // Get opcode and operands
2652 Opcode
= GETOPCODE (VmPtr
);
2653 Operands
= GETOPERANDS (VmPtr
);
2656 // Get operand1 index if present
2659 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2660 Index16
= VmReadIndex16 (VmPtr
, 2);
2666 // Get operand1 data we're going to compare to
2668 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2669 if (OPERAND1_INDIRECT (Operands
)) {
2671 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2673 if (Opcode
& OPCODE_M_CMPI64
) {
2674 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2676 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2680 // Better not have been an index with direct. That is, CMPI R1 Index,...
2683 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2684 EbcDebugSignalException (
2685 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2686 EXCEPTION_FLAG_ERROR
,
2690 return EFI_UNSUPPORTED
;
2694 // Get immediate data -- 16- or 32-bit sign extended
2696 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2697 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2701 // 16-bit immediate data. Sign extend always.
2703 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2707 // Now do the compare
2710 if (Opcode
& OPCODE_M_CMPI64
) {
2712 // 64 bit comparison
2714 switch (Opcode
& OPCODE_M_OPCODE
) {
2716 if (Op1
== (INT64
) Op2
) {
2721 case OPCODE_CMPILTE
:
2722 if (Op1
<= (INT64
) Op2
) {
2727 case OPCODE_CMPIGTE
:
2728 if (Op1
>= (INT64
) Op2
) {
2733 case OPCODE_CMPIULTE
:
2734 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2739 case OPCODE_CMPIUGTE
:
2740 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2750 // 32-bit comparisons
2752 switch (Opcode
& OPCODE_M_OPCODE
) {
2754 if ((INT32
) Op1
== Op2
) {
2759 case OPCODE_CMPILTE
:
2760 if ((INT32
) Op1
<= Op2
) {
2765 case OPCODE_CMPIGTE
:
2766 if ((INT32
) Op1
>= Op2
) {
2771 case OPCODE_CMPIULTE
:
2772 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2777 case OPCODE_CMPIUGTE
:
2778 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2788 // Now set the flag accordingly for the comparison
2791 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2793 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2805 IN VM_CONTEXT
*VmPtr
,
2811 Routine Description:
2812 Execute the EBC NOT instruction
2815 VmPtr - pointer to a VM context
2816 Op1 - Operand 1 from the instruction
2817 Op2 - Operand 2 from the instruction
2823 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2833 IN VM_CONTEXT
*VmPtr
,
2839 Routine Description:
2840 Execute the EBC NEG instruction
2843 VmPtr - pointer to a VM context
2844 Op1 - Operand 1 from the instruction
2845 Op2 - Operand 2 from the instruction
2851 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2861 IN VM_CONTEXT
*VmPtr
,
2867 Routine Description:
2869 Execute the EBC ADD instruction
2872 VmPtr - pointer to a VM context
2873 Op1 - Operand 1 from the instruction
2874 Op2 - Operand 2 from the instruction
2880 ADD[32|64] {@}R1, {@}R2 {Index16}
2890 IN VM_CONTEXT
*VmPtr
,
2896 Routine Description:
2897 Execute the EBC SUB instruction
2900 VmPtr - pointer to a VM context
2901 Op1 - Operand 1 from the instruction
2902 Op2 - Operand 2 from the instruction
2909 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2913 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2914 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2916 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2923 IN VM_CONTEXT
*VmPtr
,
2929 Routine Description:
2931 Execute the EBC MUL instruction
2934 VmPtr - pointer to a VM context
2935 Op1 - Operand 1 from the instruction
2936 Op2 - Operand 2 from the instruction
2942 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2946 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2947 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2949 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2956 IN VM_CONTEXT
*VmPtr
,
2962 Routine Description:
2963 Execute the EBC MULU instruction
2966 VmPtr - pointer to a VM context
2967 Op1 - Operand 1 from the instruction
2968 Op2 - Operand 2 from the instruction
2971 (unsigned)Op1 * (unsigned)Op2
2974 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2978 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2979 return MultU64x64 (Op1
, Op2
);
2981 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2988 IN VM_CONTEXT
*VmPtr
,
2994 Routine Description:
2996 Execute the EBC DIV instruction
2999 VmPtr - pointer to a VM context
3000 Op1 - Operand 1 from the instruction
3001 Op2 - Operand 2 from the instruction
3007 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
3014 // Check for divide-by-0
3017 EbcDebugSignalException (
3018 EXCEPT_EBC_DIVIDE_ERROR
,
3019 EXCEPTION_FLAG_FATAL
,
3025 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3026 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
3028 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3036 IN VM_CONTEXT
*VmPtr
,
3042 Routine Description:
3043 Execute the EBC DIVU instruction
3046 VmPtr - pointer to a VM context
3047 Op1 - Operand 1 from the instruction
3048 Op2 - Operand 2 from the instruction
3051 (unsigned)Op1 / (unsigned)Op2
3054 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3061 // Check for divide-by-0
3064 EbcDebugSignalException (
3065 EXCEPT_EBC_DIVIDE_ERROR
,
3066 EXCEPTION_FLAG_FATAL
,
3072 // Get the destination register
3074 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3075 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
3077 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3085 IN VM_CONTEXT
*VmPtr
,
3091 Routine Description:
3092 Execute the EBC MOD instruction
3095 VmPtr - pointer to a VM context
3096 Op1 - Operand 1 from the instruction
3097 Op2 - Operand 2 from the instruction
3103 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3110 // Check for divide-by-0
3113 EbcDebugSignalException (
3114 EXCEPT_EBC_DIVIDE_ERROR
,
3115 EXCEPTION_FLAG_FATAL
,
3120 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
3128 IN VM_CONTEXT
*VmPtr
,
3134 Routine Description:
3135 Execute the EBC MODU instruction
3138 VmPtr - pointer to a VM context
3139 Op1 - Operand 1 from the instruction
3140 Op2 - Operand 2 from the instruction
3143 Op1 UNSIGNED_MODULUS Op2
3146 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3153 // Check for divide-by-0
3156 EbcDebugSignalException (
3157 EXCEPT_EBC_DIVIDE_ERROR
,
3158 EXCEPTION_FLAG_FATAL
,
3163 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
3171 IN VM_CONTEXT
*VmPtr
,
3177 Routine Description:
3178 Execute the EBC AND instruction
3181 VmPtr - pointer to a VM context
3182 Op1 - Operand 1 from the instruction
3183 Op2 - Operand 2 from the instruction
3189 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3199 IN VM_CONTEXT
*VmPtr
,
3205 Routine Description:
3206 Execute the EBC OR instruction
3209 VmPtr - pointer to a VM context
3210 Op1 - Operand 1 from the instruction
3211 Op2 - Operand 2 from the instruction
3217 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3227 IN VM_CONTEXT
*VmPtr
,
3233 Routine Description:
3234 Execute the EBC XOR instruction
3237 VmPtr - pointer to a VM context
3238 Op1 - Operand 1 from the instruction
3239 Op2 - Operand 2 from the instruction
3245 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3255 IN VM_CONTEXT
*VmPtr
,
3261 Routine Description:
3263 Execute the EBC SHL shift left instruction
3266 VmPtr - pointer to a VM context
3267 Op1 - Operand 1 from the instruction
3268 Op2 - Operand 2 from the instruction
3274 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3278 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3279 return LShiftU64 (Op1
, (UINTN
)Op2
);
3281 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3288 IN VM_CONTEXT
*VmPtr
,
3294 Routine Description:
3295 Execute the EBC SHR instruction
3298 VmPtr - pointer to a VM context
3299 Op1 - Operand 1 from the instruction
3300 Op2 - Operand 2 from the instruction
3303 Op1 >> Op2 (unsigned operands)
3306 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3310 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3311 return RShiftU64 (Op1
, (UINTN
)Op2
);
3313 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3320 IN VM_CONTEXT
*VmPtr
,
3326 Routine Description:
3327 Execute the EBC ASHR instruction
3330 VmPtr - pointer to a VM context
3331 Op1 - Operand 1 from the instruction
3332 Op2 - Operand 2 from the instruction
3338 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3342 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3343 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3345 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3352 IN VM_CONTEXT
*VmPtr
,
3358 Routine Description:
3359 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3362 VmPtr - pointer to a VM context
3363 Op1 - Operand 1 from the instruction
3364 Op2 - Operand 2 from the instruction
3370 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3378 // Convert to byte, then return as 64-bit signed value to let compiler
3379 // sign-extend the value
3382 Data64
= (INT64
) Data8
;
3384 return (UINT64
) Data64
;
3390 IN VM_CONTEXT
*VmPtr
,
3396 Routine Description:
3397 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3400 VmPtr - pointer to a VM context
3401 Op1 - Operand 1 from the instruction
3402 Op2 - Operand 2 from the instruction
3408 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3416 // Convert to word, then return as 64-bit signed value to let compiler
3417 // sign-extend the value
3419 Data16
= (INT16
) Op2
;
3420 Data64
= (INT64
) Data16
;
3422 return (UINT64
) Data64
;
3425 // Execute the EBC EXTNDD instruction.
3427 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3428 // EXTNDD Dest, Source
3430 // Operation: Dest <- SignExtended((DWORD)Source))
3435 IN VM_CONTEXT
*VmPtr
,
3441 Routine Description:
3442 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3445 VmPtr - pointer to a VM context
3446 Op1 - Operand 1 from the instruction
3447 Op2 - Operand 2 from the instruction
3453 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3461 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3462 // sign-extend the value
3464 Data32
= (INT32
) Op2
;
3465 Data64
= (INT64
) Data32
;
3467 return (UINT64
) Data64
;
3472 ExecuteSignedDataManip (
3473 IN VM_CONTEXT
*VmPtr
3477 // Just call the data manipulation function with a flag indicating this
3478 // is a signed operation.
3480 return ExecuteDataManip (VmPtr
, TRUE
);
3485 ExecuteUnsignedDataManip (
3486 IN VM_CONTEXT
*VmPtr
3490 // Just call the data manipulation function with a flag indicating this
3491 // is not a signed operation.
3493 return ExecuteDataManip (VmPtr
, FALSE
);
3499 IN VM_CONTEXT
*VmPtr
,
3500 IN BOOLEAN IsSignedOp
3504 Routine Description:
3505 Execute all the EBC data manipulation instructions.
3506 Since the EBC data manipulation instructions all have the same basic form,
3507 they can share the code that does the fetch of operands and the write-back
3508 of the result. This function performs the fetch of the operands (even if
3509 both are not needed to be fetched, like NOT instruction), dispatches to the
3510 appropriate subfunction, then writes back the returned result.
3513 VmPtr - pointer to VM context
3519 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3531 // Get opcode and operands
3533 Opcode
= GETOPCODE (VmPtr
);
3534 Operands
= GETOPERANDS (VmPtr
);
3537 // Determine if we have immediate data by the opcode
3539 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3541 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3543 if (OPERAND2_INDIRECT (Operands
)) {
3544 Index16
= VmReadIndex16 (VmPtr
, 2);
3546 Index16
= VmReadImmed16 (VmPtr
, 2);
3555 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3557 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3558 if (OPERAND2_INDIRECT (Operands
)) {
3560 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3562 if (Opcode
& DATAMANIP_M_64
) {
3563 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3566 // Read as signed value where appropriate.
3569 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3571 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3575 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3577 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3579 Op2
= (UINT64
) ((UINT32
) Op2
);
3584 // Get operand1 (destination and sometimes also an actual operand)
3587 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3588 if (OPERAND1_INDIRECT (Operands
)) {
3589 if (Opcode
& DATAMANIP_M_64
) {
3590 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3593 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3595 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3599 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3601 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3603 Op1
= (UINT64
) ((UINT32
) Op1
);
3608 // Dispatch to the computation function
3610 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3611 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3613 EbcDebugSignalException (
3614 EXCEPT_EBC_INVALID_OPCODE
,
3615 EXCEPTION_FLAG_ERROR
,
3619 // Advance and return
3622 return EFI_UNSUPPORTED
;
3624 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3627 // Write back the result.
3629 if (OPERAND1_INDIRECT (Operands
)) {
3630 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3631 if (Opcode
& DATAMANIP_M_64
) {
3632 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3634 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3638 // Storage back to a register. Write back, clearing upper bits (as per
3639 // the specification) if 32-bit operation.
3641 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3642 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3643 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3647 // Advance the instruction pointer
3656 IN VM_CONTEXT
*VmPtr
3660 Routine Description:
3661 Execute the EBC LOADSP instruction
3664 VmPtr - pointer to a VM context
3679 Operands
= GETOPERANDS (VmPtr
);
3684 switch (OPERAND1_REGNUM (Operands
)) {
3690 // Spec states that this instruction will not modify reserved bits in
3691 // the flags register.
3693 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3697 EbcDebugSignalException (
3698 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3699 EXCEPTION_FLAG_WARNING
,
3703 return EFI_UNSUPPORTED
;
3713 IN VM_CONTEXT
*VmPtr
3717 Routine Description:
3718 Execute the EBC STORESP instruction
3721 VmPtr - pointer to a VM context
3727 STORESP Rx, FLAGS|IP
3736 Operands
= GETOPERANDS (VmPtr
);
3741 switch (OPERAND2_REGNUM (Operands
)) {
3747 // Retrieve the value in the flags register, then clear reserved bits
3749 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3753 // Get IP -- address of following instruction
3756 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3760 EbcDebugSignalException (
3761 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3762 EXCEPTION_FLAG_WARNING
,
3766 return EFI_UNSUPPORTED
;
3777 IN VM_CONTEXT
*VmPtr
,
3778 IN UINT32 CodeOffset
3782 Routine Description:
3783 Decode a 16-bit index to determine the offset. Given an index value:
3786 b14:12 - number of bits in this index assigned to natural units (=a)
3787 ba:11 - constant units = C
3788 b0:a - natural units = N
3790 Given this info, the offset can be computed by:
3791 offset = sign_bit * (C + N * sizeof(UINTN))
3793 Max offset is achieved with index = 0x7FFF giving an offset of
3794 0x27B (32-bit machine) or 0x477 (64-bit machine).
3795 Min offset is achieved with index =
3798 VmPtr - pointer to VM context
3799 CodeOffset - offset from IP of the location of the 16-bit index to decode
3814 // First read the index from the code stream
3816 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3819 // Get the mask for N. First get the number of bits from the index.
3821 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3824 // Scale it for 16-bit indexes
3829 // Now using the number of bits, create a mask.
3831 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3834 // Now using the mask, extract N from the lower bits of the index.
3836 N
= (INT16
) (Index
&~Mask
);
3841 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3843 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3848 if (Index
& 0x8000) {
3850 // Do it the hard way to work around a bogus compiler warning
3852 // Offset = -1 * Offset;
3854 Offset
= (INT16
) ((INT32
) Offset
* -1);
3863 IN VM_CONTEXT
*VmPtr
,
3864 IN UINT32 CodeOffset
3868 Routine Description:
3869 Decode a 32-bit index to determine the offset.
3872 VmPtr - pointer to VM context
3873 CodeOffset - offset from IP of the location of the 32-bit index to decode
3876 Converted index per EBC VM specification
3887 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3890 // Get the mask for N. First get the number of bits from the index.
3892 NBits
= (Index
& 0x70000000) >> 28;
3895 // Scale it for 32-bit indexes
3900 // Now using the number of bits, create a mask.
3902 Mask
= (INT32
)~0 << NBits
;
3905 // Now using the mask, extract N from the lower bits of the index.
3912 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3914 Offset
= N
* sizeof (UINTN
) + C
;
3919 if (Index
& 0x80000000) {
3920 Offset
= Offset
* -1;
3929 IN VM_CONTEXT
*VmPtr
,
3930 IN UINT32 CodeOffset
3934 Routine Description:
3935 Decode a 64-bit index to determine the offset.
3938 VmPtr - pointer to VM context
3939 CodeOffset - offset from IP of the location of the 64-bit index to decode
3942 Converted index per EBC VM specification
3953 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3956 // Get the mask for N. First get the number of bits from the index.
3958 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3961 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3963 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3966 // Now using the number of bits, create a mask.
3968 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3971 // Now using the mask, extract N from the lower bits of the index.
3978 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3980 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3985 if (Index
& 0x8000000000000000ULL
) {
3986 Offset
= MultS64x64 (Offset
, -1);
3995 IN VM_CONTEXT
*VmPtr
,
4001 Routine Description:
4002 The following VmWriteMem? routines are called by the EBC data
4003 movement instructions that write to memory. Since these writes
4004 may be to the stack, which looks like (high address on top) this,
4006 [EBC entry point arguments]
4010 we need to detect all attempts to write to the EBC entry point argument
4011 stack area and adjust the address (which will initially point into the
4012 VM stack) to point into the EBC entry point arguments.
4015 VmPtr - pointer to a VM context
4016 Addr - adddress to write to
4017 Data - value to write to Addr
4025 // Convert the address if it's in the stack gap
4027 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4028 *(UINT8
*) Addr
= Data
;
4035 IN VM_CONTEXT
*VmPtr
,
4043 // Convert the address if it's in the stack gap
4045 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4048 // Do a simple write if aligned
4050 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4051 *(UINT16
*) Addr
= Data
;
4054 // Write as two bytes
4057 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4062 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4075 IN VM_CONTEXT
*VmPtr
,
4083 // Convert the address if it's in the stack gap
4085 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4088 // Do a simple write if aligned
4090 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4091 *(UINT32
*) Addr
= Data
;
4094 // Write as two words
4097 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4102 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4114 IN VM_CONTEXT
*VmPtr
,
4123 // Convert the address if it's in the stack gap
4125 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4128 // Do a simple write if aligned
4130 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4131 *(UINT64
*) Addr
= Data
;
4134 // Write as two 32-bit words
4137 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4142 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4143 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4155 IN VM_CONTEXT
*VmPtr
,
4163 Status
= EFI_SUCCESS
;
4166 // Convert the address if it's in the stack gap
4168 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4171 // Do a simple write if aligned
4173 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4174 *(UINTN
*) Addr
= Data
;
4176 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4178 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4180 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4190 IN VM_CONTEXT
*VmPtr
,
4195 Routine Description:
4197 The following VmReadImmed routines are called by the EBC execute
4198 functions to read EBC immediate values from the code stream.
4199 Since we can't assume alignment, each tries to read in the biggest
4200 chunks size available, but will revert to smaller reads if necessary.
4203 VmPtr - pointer to a VM context
4204 Offset - offset from IP of the code bytes to read.
4207 Signed data of the requested size from the specified address.
4212 // Simply return the data in flat memory space
4214 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4220 IN VM_CONTEXT
*VmPtr
,
4225 // Read direct if aligned
4227 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4228 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4231 // All code word reads should be aligned
4233 EbcDebugSignalException (
4234 EXCEPT_EBC_ALIGNMENT_CHECK
,
4235 EXCEPTION_FLAG_WARNING
,
4240 // Return unaligned data
4242 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4248 IN VM_CONTEXT
*VmPtr
,
4255 // Read direct if aligned
4257 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4258 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4261 // Return unaligned data
4263 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4264 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4271 IN VM_CONTEXT
*VmPtr
,
4280 // Read direct if aligned
4282 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4283 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4286 // Return unaligned data.
4288 Ptr
= (UINT8
*) &Data64
;
4289 Data32
= VmReadCode32 (VmPtr
, Offset
);
4290 *(UINT32
*) Ptr
= Data32
;
4291 Ptr
+= sizeof (Data32
);
4292 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4293 *(UINT32
*) Ptr
= Data32
;
4300 IN VM_CONTEXT
*VmPtr
,
4305 Routine Description:
4306 The following VmReadCode() routines provide the ability to read raw
4307 unsigned data from the code stream.
4310 VmPtr - pointer to VM context
4311 Offset - offset from current IP to the raw data to read.
4314 The raw unsigned 16-bit value from the code stream.
4319 // Read direct if aligned
4321 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4322 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4325 // All code word reads should be aligned
4327 EbcDebugSignalException (
4328 EXCEPT_EBC_ALIGNMENT_CHECK
,
4329 EXCEPTION_FLAG_WARNING
,
4334 // Return unaligned data
4336 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4342 IN VM_CONTEXT
*VmPtr
,
4348 // Read direct if aligned
4350 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4351 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4354 // Return unaligned data
4356 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4357 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4364 IN VM_CONTEXT
*VmPtr
,
4373 // Read direct if aligned
4375 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4376 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4379 // Return unaligned data.
4381 Ptr
= (UINT8
*) &Data64
;
4382 Data32
= VmReadCode32 (VmPtr
, Offset
);
4383 *(UINT32
*) Ptr
= Data32
;
4384 Ptr
+= sizeof (Data32
);
4385 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4386 *(UINT32
*) Ptr
= Data32
;
4393 IN VM_CONTEXT
*VmPtr
,
4398 // Convert the address if it's in the stack gap
4400 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4402 // Simply return the data in flat memory space
4404 return * (UINT8
*) Addr
;
4410 IN VM_CONTEXT
*VmPtr
,
4415 // Convert the address if it's in the stack gap
4417 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4419 // Read direct if aligned
4421 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4422 return * (UINT16
*) Addr
;
4425 // Return unaligned data
4427 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4433 IN VM_CONTEXT
*VmPtr
,
4440 // Convert the address if it's in the stack gap
4442 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4444 // Read direct if aligned
4446 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4447 return * (UINT32
*) Addr
;
4450 // Return unaligned data
4452 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4453 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4460 IN VM_CONTEXT
*VmPtr
,
4468 // Convert the address if it's in the stack gap
4470 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4473 // Read direct if aligned
4475 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4476 return * (UINT64
*) Addr
;
4479 // Return unaligned data. Assume little endian.
4481 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4482 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4483 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4490 IN VM_CONTEXT
*VmPtr
,
4495 Routine Description:
4497 Given an address that EBC is going to read from or write to, return
4498 an appropriate address that accounts for a gap in the stack.
4500 The stack for this application looks like this (high addr on top)
4501 [EBC entry point arguments]
4505 The EBC assumes that its arguments are at the top of its stack, which
4506 is where the VM stack is really. Therefore if the EBC does memory
4507 accesses into the VM stack area, then we need to convert the address
4508 to point to the EBC entry point arguments area. Do this here.
4512 VmPtr - pointer to VM context
4513 Addr - address of interest
4517 The unchanged address if it's not in the VM stack region. Otherwise,
4518 adjust for the stack gap and return the modified address.
4522 if ((Addr
>= VmPtr
->LowStackTop
) && (Addr
< VmPtr
->HighStackBottom
)) {
4524 // In the stack gap -- now make sure it's not in the VM itself, which
4525 // would be the case if it's accessing VM register contents.
4527 if ((Addr
< (UINTN
) VmPtr
) || (Addr
> (UINTN
) VmPtr
+ sizeof (VM_CONTEXT
))) {
4528 VmPtr
->LastAddrConverted
= Addr
;
4529 VmPtr
->LastAddrConvertedValue
= Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4530 return Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4540 IN VM_CONTEXT
*VmPtr
,
4545 Routine Description:
4546 Read a natural value from memory. May or may not be aligned.
4549 VmPtr - current VM context
4550 Addr - the address to read from
4553 The natural value at address Addr.
4562 // Convert the address if it's in the stack gap
4564 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4566 // Read direct if aligned
4568 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4569 return * (UINTN
*) Addr
;
4572 // Return unaligned data
4575 FromPtr
= (UINT8
*) Addr
;
4576 ToPtr
= (UINT8
*) &Data
;
4578 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4592 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));