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
;
1102 UINT64 U64EbcEntryPoint
;
1105 Operands
= GETOPERANDS (VmPtr
);
1108 // Runaway program break. Generate an exception and terminate
1111 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1115 // Get VM version -- return VM revision number in R7
1121 // 16-8 = Major version
1122 // 7-0 = Minor version
1124 VmPtr
->R
[7] = GetVmVersion ();
1128 // Debugger breakpoint
1131 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1133 // See if someone has registered a handler
1135 EbcDebugSignalException (
1136 EXCEPT_EBC_BREAKPOINT
,
1137 EXCEPTION_FLAG_NONE
,
1141 // Don't advance the IP
1143 return EFI_UNSUPPORTED
;
1147 // System call, which there are none, so NOP it.
1153 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1154 // "offset from self" pointer to the EBC entry point.
1155 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1158 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1159 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1160 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1163 // Now create a new thunk
1165 Status
= EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1168 // Finally replace the EBC entry point memory with the thunk address
1170 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1174 // Compiler setting version per value in R7
1177 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1179 // Check compiler version against VM version?
1184 // Unhandled break code. Signal exception.
1187 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1200 IN VM_CONTEXT
*VmPtr
1204 Routine Description:
1205 Execute the JMP instruction
1208 VmPtr - pointer to VM context
1214 JMP64{cs|cc} Immed64
1215 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1218 b0.7 - immediate data present
1219 b0.6 - 1 = 64 bit immediate data
1220 0 = 32 bit immediate data
1221 b1.7 - 1 = conditional
1222 b1.6 1 = CS (condition set)
1223 0 = CC (condition clear)
1224 b1.4 1 = relative address
1225 0 = absolute address
1226 b1.3 1 = operand1 indirect
1233 UINT8 ConditionFlag
;
1240 Operand
= GETOPERANDS (VmPtr
);
1241 Opcode
= GETOPCODE (VmPtr
);
1244 // Get instruction length from the opcode. The upper two bits are used here
1245 // to index into the length array.
1247 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1250 // Decode instruction conditions
1251 // If we haven't met the condition, then simply advance the IP and return.
1253 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1254 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1255 if (Operand
& CONDITION_M_CONDITIONAL
) {
1256 if (CompareSet
!= ConditionFlag
) {
1262 // Check for 64-bit form and do it right away since it's the most
1263 // straight-forward form.
1265 if (Opcode
& OPCODE_M_IMMDATA64
) {
1267 // Double check for immediate-data, which is required. If not there,
1268 // then signal an exception
1270 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1271 EbcDebugSignalException (
1272 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1273 EXCEPTION_FLAG_ERROR
,
1276 return EFI_UNSUPPORTED
;
1279 // 64-bit immediate data is full address. Read the immediate data,
1280 // check for alignment, and jump absolute.
1282 Data64
= VmReadImmed64 (VmPtr
, 2);
1283 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1284 EbcDebugSignalException (
1285 EXCEPT_EBC_ALIGNMENT_CHECK
,
1286 EXCEPTION_FLAG_FATAL
,
1290 return EFI_UNSUPPORTED
;
1294 // Take jump -- relative or absolute
1296 if (Operand
& JMP_M_RELATIVE
) {
1297 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1299 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1306 // Get the index if there is one. May be either an index, or an immediate
1307 // offset depending on indirect operand.
1308 // JMP32 @R1 Index32 -- immediate data is an index
1309 // JMP32 R1 Immed32 -- immedate data is an offset
1311 if (Opcode
& OPCODE_M_IMMDATA
) {
1312 if (OPERAND1_INDIRECT (Operand
)) {
1313 Index32
= VmReadIndex32 (VmPtr
, 2);
1315 Index32
= VmReadImmed32 (VmPtr
, 2);
1321 // Get the register data. If R == 0, then special case where it's ignored.
1323 if (OPERAND1_REGNUM (Operand
) == 0) {
1326 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1331 if (OPERAND1_INDIRECT (Operand
)) {
1333 // Form: JMP32 @Rx {Index32}
1335 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1336 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1337 EbcDebugSignalException (
1338 EXCEPT_EBC_ALIGNMENT_CHECK
,
1339 EXCEPTION_FLAG_FATAL
,
1343 return EFI_UNSUPPORTED
;
1346 if (Operand
& JMP_M_RELATIVE
) {
1347 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1349 VmPtr
->Ip
= (VMIP
) Addr
;
1353 // Form: JMP32 Rx {Immed32}
1355 Addr
= (UINTN
) (Data64
+ Index32
);
1356 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1357 EbcDebugSignalException (
1358 EXCEPT_EBC_ALIGNMENT_CHECK
,
1359 EXCEPTION_FLAG_FATAL
,
1363 return EFI_UNSUPPORTED
;
1366 if (Operand
& JMP_M_RELATIVE
) {
1367 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1369 VmPtr
->Ip
= (VMIP
) Addr
;
1379 IN VM_CONTEXT
*VmPtr
1383 Routine Description:
1384 Execute the EBC JMP8 instruction
1387 VmPtr - pointer to a VM context
1393 JMP8{cs|cc} Offset/2
1398 UINT8 ConditionFlag
;
1403 // Decode instruction.
1405 Opcode
= GETOPCODE (VmPtr
);
1406 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1407 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1410 // If we haven't met the condition, then simply advance the IP and return
1412 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1413 if (CompareSet
!= ConditionFlag
) {
1419 // Get the offset from the instruction stream. It's relative to the
1420 // following instruction, and divided by 2.
1422 Offset
= VmReadImmed8 (VmPtr
, 1);
1424 // Want to check for offset == -2 and then raise an exception?
1426 VmPtr
->Ip
+= (Offset
* 2) + 2;
1433 IN VM_CONTEXT
*VmPtr
1437 Routine Description:
1439 Execute the EBC MOVI
1443 VmPtr - pointer to a VM context
1451 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1453 First variable character specifies the move size
1454 Second variable character specifies size of the immediate data
1456 Sign-extend the immediate data to the size of the operation, and zero-extend
1457 if storing to a register.
1459 Operand1 direct with index/immed is invalid.
1472 // Get the opcode and operands byte so we can get R1 and R2
1474 Opcode
= GETOPCODE (VmPtr
);
1475 Operands
= GETOPERANDS (VmPtr
);
1478 // Get the index (16-bit) if present
1480 if (Operands
& MOVI_M_IMMDATA
) {
1481 Index16
= VmReadIndex16 (VmPtr
, 2);
1488 // Extract the immediate data. Sign-extend always.
1490 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1491 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1493 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1494 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1496 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1497 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1503 EbcDebugSignalException (
1504 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1505 EXCEPTION_FLAG_FATAL
,
1508 return EFI_UNSUPPORTED
;
1511 // Now write back the result
1513 if (!OPERAND1_INDIRECT (Operands
)) {
1515 // Operand1 direct. Make sure it didn't have an index.
1517 if (Operands
& MOVI_M_IMMDATA
) {
1518 EbcDebugSignalException (
1519 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1520 EXCEPTION_FLAG_FATAL
,
1523 return EFI_UNSUPPORTED
;
1526 // Writing directly to a register. Clear unused bits.
1528 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1529 Mask64
= 0x000000FF;
1530 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1531 Mask64
= 0x0000FFFF;
1532 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1533 Mask64
= 0x00000000FFFFFFFF;
1535 Mask64
= (UINT64
)~0;
1538 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1541 // Get the address then write back based on size of the move
1543 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1544 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1545 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1546 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1547 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1548 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1549 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1551 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1555 // Advance the instruction pointer
1564 IN VM_CONTEXT
*VmPtr
1568 Routine Description:
1570 Execute the EBC MOV immediate natural. This instruction moves an immediate
1571 index value into a register or memory location.
1575 VmPtr - pointer to a VM context
1583 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1597 // Get the opcode and operands byte so we can get R1 and R2
1599 Opcode
= GETOPCODE (VmPtr
);
1600 Operands
= GETOPERANDS (VmPtr
);
1603 // Get the operand1 index (16-bit) if present
1605 if (Operands
& MOVI_M_IMMDATA
) {
1606 Index16
= VmReadIndex16 (VmPtr
, 2);
1613 // Extract the immediate data and convert to a 64-bit index.
1615 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1616 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1617 ImmedIndex64
= (INT64
) ImmedIndex16
;
1619 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1620 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1621 ImmedIndex64
= (INT64
) ImmedIndex32
;
1623 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1624 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1630 EbcDebugSignalException (
1631 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1632 EXCEPTION_FLAG_FATAL
,
1635 return EFI_UNSUPPORTED
;
1638 // Now write back the result
1640 if (!OPERAND1_INDIRECT (Operands
)) {
1642 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1645 if (Operands
& MOVI_M_IMMDATA
) {
1646 EbcDebugSignalException (
1647 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1648 EXCEPTION_FLAG_FATAL
,
1651 return EFI_UNSUPPORTED
;
1654 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1659 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1660 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1663 // Advance the instruction pointer
1672 IN VM_CONTEXT
*VmPtr
1676 Routine Description:
1678 Execute the EBC MOVREL instruction.
1679 Dest <- Ip + ImmData
1683 VmPtr - pointer to a VM context
1691 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1704 // Get the opcode and operands byte so we can get R1 and R2
1706 Opcode
= GETOPCODE (VmPtr
);
1707 Operands
= GETOPERANDS (VmPtr
);
1710 // Get the Operand 1 index (16-bit) if present
1712 if (Operands
& MOVI_M_IMMDATA
) {
1713 Index16
= VmReadIndex16 (VmPtr
, 2);
1720 // Get the immediate data.
1722 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1723 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1725 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1726 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1728 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1729 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1735 EbcDebugSignalException (
1736 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1737 EXCEPTION_FLAG_FATAL
,
1740 return EFI_UNSUPPORTED
;
1743 // Compute the value and write back the result
1745 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1746 if (!OPERAND1_INDIRECT (Operands
)) {
1748 // Check for illegal combination of operand1 direct with immediate data
1750 if (Operands
& MOVI_M_IMMDATA
) {
1751 EbcDebugSignalException (
1752 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1753 EXCEPTION_FLAG_FATAL
,
1756 return EFI_UNSUPPORTED
;
1759 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1762 // Get the address = [Rx] + Index16
1763 // Write back the result. Always a natural size write, since
1764 // we're talking addresses here.
1766 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1767 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1770 // Advance the instruction pointer
1779 IN VM_CONTEXT
*VmPtr
1783 Routine Description:
1785 Execute the EBC MOVsnw instruction. This instruction loads a signed
1786 natural value from memory or register to another memory or register. On
1787 32-bit machines, the value gets sign-extended to 64 bits if the destination
1792 VmPtr - pointer to a VM context
1800 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1802 0:7 1=>operand1 index present
1803 0:6 1=>operand2 index present
1815 // Get the opcode and operand bytes
1817 Opcode
= GETOPCODE (VmPtr
);
1818 Operands
= GETOPERANDS (VmPtr
);
1820 Op1Index
= Op2Index
= 0;
1823 // Get the indexes if present.
1826 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1827 if (OPERAND1_INDIRECT (Operands
)) {
1828 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1831 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1833 EbcDebugSignalException (
1834 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1835 EXCEPTION_FLAG_FATAL
,
1838 return EFI_UNSUPPORTED
;
1841 Size
+= sizeof (UINT16
);
1844 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1845 if (OPERAND2_INDIRECT (Operands
)) {
1846 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1848 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1851 Size
+= sizeof (UINT16
);
1854 // Get the data from the source.
1856 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1857 if (OPERAND2_INDIRECT (Operands
)) {
1858 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1861 // Now write back the result.
1863 if (!OPERAND1_INDIRECT (Operands
)) {
1864 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1866 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1869 // Advance the instruction pointer
1878 IN VM_CONTEXT
*VmPtr
1882 Routine Description:
1884 Execute the EBC MOVsnw instruction. This instruction loads a signed
1885 natural value from memory or register to another memory or register. On
1886 32-bit machines, the value gets sign-extended to 64 bits if the destination
1891 VmPtr - pointer to a VM context
1899 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1901 0:7 1=>operand1 index present
1902 0:6 1=>operand2 index present
1914 // Get the opcode and operand bytes
1916 Opcode
= GETOPCODE (VmPtr
);
1917 Operands
= GETOPERANDS (VmPtr
);
1919 Op1Index
= Op2Index
= 0;
1922 // Get the indexes if present.
1925 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1926 if (OPERAND1_INDIRECT (Operands
)) {
1927 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1930 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1932 EbcDebugSignalException (
1933 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1934 EXCEPTION_FLAG_FATAL
,
1937 return EFI_UNSUPPORTED
;
1940 Size
+= sizeof (UINT32
);
1943 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1944 if (OPERAND2_INDIRECT (Operands
)) {
1945 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1947 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1950 Size
+= sizeof (UINT32
);
1953 // Get the data from the source.
1955 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1956 if (OPERAND2_INDIRECT (Operands
)) {
1957 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1960 // Now write back the result.
1962 if (!OPERAND1_INDIRECT (Operands
)) {
1963 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1965 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1968 // Advance the instruction pointer
1977 IN VM_CONTEXT
*VmPtr
1981 Routine Description:
1982 Execute the EBC PUSHn instruction
1985 VmPtr - pointer to a VM context
1991 PUSHn {@}R1 {Index16|Immed16}
2001 // Get opcode and operands
2003 Opcode
= GETOPCODE (VmPtr
);
2004 Operands
= GETOPERANDS (VmPtr
);
2007 // Get index if present
2009 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2010 if (OPERAND1_INDIRECT (Operands
)) {
2011 Index16
= VmReadIndex16 (VmPtr
, 2);
2013 Index16
= VmReadImmed16 (VmPtr
, 2);
2022 // Get the data to push
2024 if (OPERAND1_INDIRECT (Operands
)) {
2025 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2027 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2030 // Adjust the stack down.
2032 VmPtr
->R
[0] -= sizeof (UINTN
);
2033 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2040 IN VM_CONTEXT
*VmPtr
2044 Routine Description:
2045 Execute the EBC PUSH instruction
2048 VmPtr - pointer to a VM context
2054 PUSH[32|64] {@}R1 {Index16|Immed16}
2065 // Get opcode and operands
2067 Opcode
= GETOPCODE (VmPtr
);
2068 Operands
= GETOPERANDS (VmPtr
);
2070 // Get immediate index if present, then advance the IP.
2072 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2073 if (OPERAND1_INDIRECT (Operands
)) {
2074 Index16
= VmReadIndex16 (VmPtr
, 2);
2076 Index16
= VmReadImmed16 (VmPtr
, 2);
2085 // Get the data to push
2087 if (Opcode
& PUSHPOP_M_64
) {
2088 if (OPERAND1_INDIRECT (Operands
)) {
2089 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2091 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2094 // Adjust the stack down, then write back the data
2096 VmPtr
->R
[0] -= sizeof (UINT64
);
2097 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2102 if (OPERAND1_INDIRECT (Operands
)) {
2103 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2105 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2108 // Adjust the stack down and write the data
2110 VmPtr
->R
[0] -= sizeof (UINT32
);
2111 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2120 IN VM_CONTEXT
*VmPtr
2124 Routine Description:
2125 Execute the EBC POPn instruction
2128 VmPtr - pointer to a VM context
2134 POPn {@}R1 {Index16|Immed16}
2144 // Get opcode and operands
2146 Opcode
= GETOPCODE (VmPtr
);
2147 Operands
= GETOPERANDS (VmPtr
);
2149 // Get immediate data if present, and advance the IP
2151 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2152 if (OPERAND1_INDIRECT (Operands
)) {
2153 Index16
= VmReadIndex16 (VmPtr
, 2);
2155 Index16
= VmReadImmed16 (VmPtr
, 2);
2164 // Read the data off the stack, then adjust the stack pointer
2166 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2167 VmPtr
->R
[0] += sizeof (UINTN
);
2169 // Do the write-back
2171 if (OPERAND1_INDIRECT (Operands
)) {
2172 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2174 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2183 IN VM_CONTEXT
*VmPtr
2187 Routine Description:
2188 Execute the EBC POP instruction
2191 VmPtr - pointer to a VM context
2197 POP {@}R1 {Index16|Immed16}
2208 // Get opcode and operands
2210 Opcode
= GETOPCODE (VmPtr
);
2211 Operands
= GETOPERANDS (VmPtr
);
2213 // Get immediate data if present, and advance the IP.
2215 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2216 if (OPERAND1_INDIRECT (Operands
)) {
2217 Index16
= VmReadIndex16 (VmPtr
, 2);
2219 Index16
= VmReadImmed16 (VmPtr
, 2);
2228 // Get the data off the stack, then write it to the appropriate location
2230 if (Opcode
& PUSHPOP_M_64
) {
2232 // Read the data off the stack, then adjust the stack pointer
2234 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2235 VmPtr
->R
[0] += sizeof (UINT64
);
2237 // Do the write-back
2239 if (OPERAND1_INDIRECT (Operands
)) {
2240 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2242 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2246 // 32-bit pop. Read it off the stack and adjust the stack pointer
2248 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2249 VmPtr
->R
[0] += sizeof (UINT32
);
2251 // Do the write-back
2253 if (OPERAND1_INDIRECT (Operands
)) {
2254 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2256 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2266 IN VM_CONTEXT
*VmPtr
2270 Routine Description:
2271 Implements the EBC CALL instruction.
2276 CALL32 {@}R1 {Immed32|Index32}
2278 CALLEX16 {@}R1 {Immed32}
2280 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2283 VmPtr - pointer to a VM context.
2298 // Get opcode and operands
2300 Opcode
= GETOPCODE (VmPtr
);
2301 Operands
= GETOPERANDS (VmPtr
);
2303 // Assign these as well to avoid compiler warnings
2308 FramePtr
= VmPtr
->FramePtr
;
2310 // Determine the instruction size, and get immediate data if present
2312 if (Opcode
& OPCODE_M_IMMDATA
) {
2313 if (Opcode
& OPCODE_M_IMMDATA64
) {
2314 Immed64
= VmReadImmed64 (VmPtr
, 2);
2318 // If register operand is indirect, then the immediate data is an index
2320 if (OPERAND1_INDIRECT (Operands
)) {
2321 Immed32
= VmReadIndex32 (VmPtr
, 2);
2323 Immed32
= VmReadImmed32 (VmPtr
, 2);
2332 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2333 // put our return address and frame pointer on the VM stack.
2335 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2337 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2338 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2340 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2343 // If 64-bit data, then absolute jump only
2345 if (Opcode
& OPCODE_M_IMMDATA64
) {
2347 // Native or EBC call?
2349 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2350 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2353 // Call external function, get the return value, and advance the IP
2355 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2359 // Get the register data. If operand1 == 0, then ignore register and
2360 // take immediate data as relative or absolute address.
2361 // Compiler should take care of upper bits if 32-bit machine.
2363 if (OPERAND1_REGNUM (Operands
) != 0) {
2364 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2367 // Get final address
2369 if (OPERAND1_INDIRECT (Operands
)) {
2370 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2375 // Now determine if external call, and then if relative or absolute
2377 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2379 // EBC call. Relative or absolute? If relative, then it's relative to the
2380 // start of the next instruction.
2382 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2383 VmPtr
->Ip
+= Immed64
+ Size
;
2385 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2389 // Native call. Relative or absolute?
2391 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2392 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2394 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2398 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2409 IN VM_CONTEXT
*VmPtr
2413 Routine Description:
2414 Execute the EBC RET instruction
2417 VmPtr - pointer to a VM context
2428 // If we're at the top of the stack, then simply set the done
2431 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2432 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2435 // Pull the return address off the VM app's stack and set the IP
2438 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2439 EbcDebugSignalException (
2440 EXCEPT_EBC_ALIGNMENT_CHECK
,
2441 EXCEPTION_FLAG_FATAL
,
2446 // Restore the IP and frame pointer from the stack
2448 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2450 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2460 IN VM_CONTEXT
*VmPtr
2464 Routine Description:
2465 Execute the EBC CMP instruction
2468 VmPtr - pointer to a VM context
2474 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2487 // Get opcode and operands
2489 Opcode
= GETOPCODE (VmPtr
);
2490 Operands
= GETOPERANDS (VmPtr
);
2492 // Get the register data we're going to compare to
2494 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2496 // Get immediate data
2498 if (Opcode
& OPCODE_M_IMMDATA
) {
2499 if (OPERAND2_INDIRECT (Operands
)) {
2500 Index16
= VmReadIndex16 (VmPtr
, 2);
2502 Index16
= VmReadImmed16 (VmPtr
, 2);
2513 if (OPERAND2_INDIRECT (Operands
)) {
2514 if (Opcode
& OPCODE_M_64BIT
) {
2515 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2518 // 32-bit operations. 0-extend the values for all cases.
2520 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2523 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2526 // Now do the compare
2529 if (Opcode
& OPCODE_M_64BIT
) {
2533 switch (Opcode
& OPCODE_M_OPCODE
) {
2552 case OPCODE_CMPULTE
:
2553 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2558 case OPCODE_CMPUGTE
:
2559 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2571 switch (Opcode
& OPCODE_M_OPCODE
) {
2573 if ((INT32
) Op1
== (INT32
) Op2
) {
2579 if ((INT32
) Op1
<= (INT32
) Op2
) {
2585 if ((INT32
) Op1
>= (INT32
) Op2
) {
2590 case OPCODE_CMPULTE
:
2591 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2596 case OPCODE_CMPUGTE
:
2597 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2607 // Now set the flag accordingly for the comparison
2610 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2612 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2624 IN VM_CONTEXT
*VmPtr
2628 Routine Description:
2629 Execute the EBC CMPI instruction
2632 VmPtr - pointer to a VM context
2638 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2651 // Get opcode and operands
2653 Opcode
= GETOPCODE (VmPtr
);
2654 Operands
= GETOPERANDS (VmPtr
);
2657 // Get operand1 index if present
2660 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2661 Index16
= VmReadIndex16 (VmPtr
, 2);
2667 // Get operand1 data we're going to compare to
2669 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2670 if (OPERAND1_INDIRECT (Operands
)) {
2672 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2674 if (Opcode
& OPCODE_M_CMPI64
) {
2675 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2677 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2681 // Better not have been an index with direct. That is, CMPI R1 Index,...
2684 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2685 EbcDebugSignalException (
2686 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2687 EXCEPTION_FLAG_ERROR
,
2691 return EFI_UNSUPPORTED
;
2695 // Get immediate data -- 16- or 32-bit sign extended
2697 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2698 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2702 // 16-bit immediate data. Sign extend always.
2704 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2708 // Now do the compare
2711 if (Opcode
& OPCODE_M_CMPI64
) {
2713 // 64 bit comparison
2715 switch (Opcode
& OPCODE_M_OPCODE
) {
2717 if (Op1
== (INT64
) Op2
) {
2722 case OPCODE_CMPILTE
:
2723 if (Op1
<= (INT64
) Op2
) {
2728 case OPCODE_CMPIGTE
:
2729 if (Op1
>= (INT64
) Op2
) {
2734 case OPCODE_CMPIULTE
:
2735 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2740 case OPCODE_CMPIUGTE
:
2741 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2751 // 32-bit comparisons
2753 switch (Opcode
& OPCODE_M_OPCODE
) {
2755 if ((INT32
) Op1
== Op2
) {
2760 case OPCODE_CMPILTE
:
2761 if ((INT32
) Op1
<= Op2
) {
2766 case OPCODE_CMPIGTE
:
2767 if ((INT32
) Op1
>= Op2
) {
2772 case OPCODE_CMPIULTE
:
2773 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2778 case OPCODE_CMPIUGTE
:
2779 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2789 // Now set the flag accordingly for the comparison
2792 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2794 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2806 IN VM_CONTEXT
*VmPtr
,
2812 Routine Description:
2813 Execute the EBC NOT instruction
2816 VmPtr - pointer to a VM context
2817 Op1 - Operand 1 from the instruction
2818 Op2 - Operand 2 from the instruction
2824 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2834 IN VM_CONTEXT
*VmPtr
,
2840 Routine Description:
2841 Execute the EBC NEG instruction
2844 VmPtr - pointer to a VM context
2845 Op1 - Operand 1 from the instruction
2846 Op2 - Operand 2 from the instruction
2852 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2862 IN VM_CONTEXT
*VmPtr
,
2868 Routine Description:
2870 Execute the EBC ADD instruction
2873 VmPtr - pointer to a VM context
2874 Op1 - Operand 1 from the instruction
2875 Op2 - Operand 2 from the instruction
2881 ADD[32|64] {@}R1, {@}R2 {Index16}
2891 IN VM_CONTEXT
*VmPtr
,
2897 Routine Description:
2898 Execute the EBC SUB instruction
2901 VmPtr - pointer to a VM context
2902 Op1 - Operand 1 from the instruction
2903 Op2 - Operand 2 from the instruction
2910 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2914 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2915 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2917 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2924 IN VM_CONTEXT
*VmPtr
,
2930 Routine Description:
2932 Execute the EBC MUL instruction
2935 VmPtr - pointer to a VM context
2936 Op1 - Operand 1 from the instruction
2937 Op2 - Operand 2 from the instruction
2943 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2947 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2948 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2950 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2957 IN VM_CONTEXT
*VmPtr
,
2963 Routine Description:
2964 Execute the EBC MULU instruction
2967 VmPtr - pointer to a VM context
2968 Op1 - Operand 1 from the instruction
2969 Op2 - Operand 2 from the instruction
2972 (unsigned)Op1 * (unsigned)Op2
2975 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2979 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2980 return MultU64x64 (Op1
, Op2
);
2982 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2989 IN VM_CONTEXT
*VmPtr
,
2995 Routine Description:
2997 Execute the EBC DIV instruction
3000 VmPtr - pointer to a VM context
3001 Op1 - Operand 1 from the instruction
3002 Op2 - Operand 2 from the instruction
3008 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
3015 // Check for divide-by-0
3018 EbcDebugSignalException (
3019 EXCEPT_EBC_DIVIDE_ERROR
,
3020 EXCEPTION_FLAG_FATAL
,
3026 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3027 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
3029 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3037 IN VM_CONTEXT
*VmPtr
,
3043 Routine Description:
3044 Execute the EBC DIVU instruction
3047 VmPtr - pointer to a VM context
3048 Op1 - Operand 1 from the instruction
3049 Op2 - Operand 2 from the instruction
3052 (unsigned)Op1 / (unsigned)Op2
3055 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3062 // Check for divide-by-0
3065 EbcDebugSignalException (
3066 EXCEPT_EBC_DIVIDE_ERROR
,
3067 EXCEPTION_FLAG_FATAL
,
3073 // Get the destination register
3075 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3076 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
3078 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3086 IN VM_CONTEXT
*VmPtr
,
3092 Routine Description:
3093 Execute the EBC MOD instruction
3096 VmPtr - pointer to a VM context
3097 Op1 - Operand 1 from the instruction
3098 Op2 - Operand 2 from the instruction
3104 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3111 // Check for divide-by-0
3114 EbcDebugSignalException (
3115 EXCEPT_EBC_DIVIDE_ERROR
,
3116 EXCEPTION_FLAG_FATAL
,
3121 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
3129 IN VM_CONTEXT
*VmPtr
,
3135 Routine Description:
3136 Execute the EBC MODU instruction
3139 VmPtr - pointer to a VM context
3140 Op1 - Operand 1 from the instruction
3141 Op2 - Operand 2 from the instruction
3144 Op1 UNSIGNED_MODULUS Op2
3147 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3154 // Check for divide-by-0
3157 EbcDebugSignalException (
3158 EXCEPT_EBC_DIVIDE_ERROR
,
3159 EXCEPTION_FLAG_FATAL
,
3164 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
3172 IN VM_CONTEXT
*VmPtr
,
3178 Routine Description:
3179 Execute the EBC AND instruction
3182 VmPtr - pointer to a VM context
3183 Op1 - Operand 1 from the instruction
3184 Op2 - Operand 2 from the instruction
3190 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3200 IN VM_CONTEXT
*VmPtr
,
3206 Routine Description:
3207 Execute the EBC OR instruction
3210 VmPtr - pointer to a VM context
3211 Op1 - Operand 1 from the instruction
3212 Op2 - Operand 2 from the instruction
3218 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3228 IN VM_CONTEXT
*VmPtr
,
3234 Routine Description:
3235 Execute the EBC XOR instruction
3238 VmPtr - pointer to a VM context
3239 Op1 - Operand 1 from the instruction
3240 Op2 - Operand 2 from the instruction
3246 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3256 IN VM_CONTEXT
*VmPtr
,
3262 Routine Description:
3264 Execute the EBC SHL shift left instruction
3267 VmPtr - pointer to a VM context
3268 Op1 - Operand 1 from the instruction
3269 Op2 - Operand 2 from the instruction
3275 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3279 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3280 return LShiftU64 (Op1
, (UINTN
)Op2
);
3282 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3289 IN VM_CONTEXT
*VmPtr
,
3295 Routine Description:
3296 Execute the EBC SHR instruction
3299 VmPtr - pointer to a VM context
3300 Op1 - Operand 1 from the instruction
3301 Op2 - Operand 2 from the instruction
3304 Op1 >> Op2 (unsigned operands)
3307 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3311 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3312 return RShiftU64 (Op1
, (UINTN
)Op2
);
3314 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3321 IN VM_CONTEXT
*VmPtr
,
3327 Routine Description:
3328 Execute the EBC ASHR instruction
3331 VmPtr - pointer to a VM context
3332 Op1 - Operand 1 from the instruction
3333 Op2 - Operand 2 from the instruction
3339 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3343 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3344 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3346 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3353 IN VM_CONTEXT
*VmPtr
,
3359 Routine Description:
3360 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3363 VmPtr - pointer to a VM context
3364 Op1 - Operand 1 from the instruction
3365 Op2 - Operand 2 from the instruction
3371 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3379 // Convert to byte, then return as 64-bit signed value to let compiler
3380 // sign-extend the value
3383 Data64
= (INT64
) Data8
;
3385 return (UINT64
) Data64
;
3391 IN VM_CONTEXT
*VmPtr
,
3397 Routine Description:
3398 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3401 VmPtr - pointer to a VM context
3402 Op1 - Operand 1 from the instruction
3403 Op2 - Operand 2 from the instruction
3409 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3417 // Convert to word, then return as 64-bit signed value to let compiler
3418 // sign-extend the value
3420 Data16
= (INT16
) Op2
;
3421 Data64
= (INT64
) Data16
;
3423 return (UINT64
) Data64
;
3426 // Execute the EBC EXTNDD instruction.
3428 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3429 // EXTNDD Dest, Source
3431 // Operation: Dest <- SignExtended((DWORD)Source))
3436 IN VM_CONTEXT
*VmPtr
,
3442 Routine Description:
3443 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3446 VmPtr - pointer to a VM context
3447 Op1 - Operand 1 from the instruction
3448 Op2 - Operand 2 from the instruction
3454 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3462 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3463 // sign-extend the value
3465 Data32
= (INT32
) Op2
;
3466 Data64
= (INT64
) Data32
;
3468 return (UINT64
) Data64
;
3473 ExecuteSignedDataManip (
3474 IN VM_CONTEXT
*VmPtr
3478 // Just call the data manipulation function with a flag indicating this
3479 // is a signed operation.
3481 return ExecuteDataManip (VmPtr
, TRUE
);
3486 ExecuteUnsignedDataManip (
3487 IN VM_CONTEXT
*VmPtr
3491 // Just call the data manipulation function with a flag indicating this
3492 // is not a signed operation.
3494 return ExecuteDataManip (VmPtr
, FALSE
);
3500 IN VM_CONTEXT
*VmPtr
,
3501 IN BOOLEAN IsSignedOp
3505 Routine Description:
3506 Execute all the EBC data manipulation instructions.
3507 Since the EBC data manipulation instructions all have the same basic form,
3508 they can share the code that does the fetch of operands and the write-back
3509 of the result. This function performs the fetch of the operands (even if
3510 both are not needed to be fetched, like NOT instruction), dispatches to the
3511 appropriate subfunction, then writes back the returned result.
3514 VmPtr - pointer to VM context
3520 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3532 // Get opcode and operands
3534 Opcode
= GETOPCODE (VmPtr
);
3535 Operands
= GETOPERANDS (VmPtr
);
3538 // Determine if we have immediate data by the opcode
3540 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3542 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3544 if (OPERAND2_INDIRECT (Operands
)) {
3545 Index16
= VmReadIndex16 (VmPtr
, 2);
3547 Index16
= VmReadImmed16 (VmPtr
, 2);
3556 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3558 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3559 if (OPERAND2_INDIRECT (Operands
)) {
3561 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3563 if (Opcode
& DATAMANIP_M_64
) {
3564 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3567 // Read as signed value where appropriate.
3570 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3572 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3576 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3578 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3580 Op2
= (UINT64
) ((UINT32
) Op2
);
3585 // Get operand1 (destination and sometimes also an actual operand)
3588 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3589 if (OPERAND1_INDIRECT (Operands
)) {
3590 if (Opcode
& DATAMANIP_M_64
) {
3591 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3594 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3596 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3600 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3602 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3604 Op1
= (UINT64
) ((UINT32
) Op1
);
3609 // Dispatch to the computation function
3611 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3612 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3614 EbcDebugSignalException (
3615 EXCEPT_EBC_INVALID_OPCODE
,
3616 EXCEPTION_FLAG_ERROR
,
3620 // Advance and return
3623 return EFI_UNSUPPORTED
;
3625 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3628 // Write back the result.
3630 if (OPERAND1_INDIRECT (Operands
)) {
3631 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3632 if (Opcode
& DATAMANIP_M_64
) {
3633 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3635 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3639 // Storage back to a register. Write back, clearing upper bits (as per
3640 // the specification) if 32-bit operation.
3642 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3643 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3644 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3648 // Advance the instruction pointer
3657 IN VM_CONTEXT
*VmPtr
3661 Routine Description:
3662 Execute the EBC LOADSP instruction
3665 VmPtr - pointer to a VM context
3680 Operands
= GETOPERANDS (VmPtr
);
3685 switch (OPERAND1_REGNUM (Operands
)) {
3691 // Spec states that this instruction will not modify reserved bits in
3692 // the flags register.
3694 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3698 EbcDebugSignalException (
3699 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3700 EXCEPTION_FLAG_WARNING
,
3704 return EFI_UNSUPPORTED
;
3714 IN VM_CONTEXT
*VmPtr
3718 Routine Description:
3719 Execute the EBC STORESP instruction
3722 VmPtr - pointer to a VM context
3728 STORESP Rx, FLAGS|IP
3737 Operands
= GETOPERANDS (VmPtr
);
3742 switch (OPERAND2_REGNUM (Operands
)) {
3748 // Retrieve the value in the flags register, then clear reserved bits
3750 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3754 // Get IP -- address of following instruction
3757 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3761 EbcDebugSignalException (
3762 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3763 EXCEPTION_FLAG_WARNING
,
3767 return EFI_UNSUPPORTED
;
3778 IN VM_CONTEXT
*VmPtr
,
3779 IN UINT32 CodeOffset
3783 Routine Description:
3784 Decode a 16-bit index to determine the offset. Given an index value:
3787 b14:12 - number of bits in this index assigned to natural units (=a)
3788 ba:11 - constant units = C
3789 b0:a - natural units = N
3791 Given this info, the offset can be computed by:
3792 offset = sign_bit * (C + N * sizeof(UINTN))
3794 Max offset is achieved with index = 0x7FFF giving an offset of
3795 0x27B (32-bit machine) or 0x477 (64-bit machine).
3796 Min offset is achieved with index =
3799 VmPtr - pointer to VM context
3800 CodeOffset - offset from IP of the location of the 16-bit index to decode
3815 // First read the index from the code stream
3817 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3820 // Get the mask for N. First get the number of bits from the index.
3822 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3825 // Scale it for 16-bit indexes
3830 // Now using the number of bits, create a mask.
3832 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3835 // Now using the mask, extract N from the lower bits of the index.
3837 N
= (INT16
) (Index
&~Mask
);
3842 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3844 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3849 if (Index
& 0x8000) {
3851 // Do it the hard way to work around a bogus compiler warning
3853 // Offset = -1 * Offset;
3855 Offset
= (INT16
) ((INT32
) Offset
* -1);
3864 IN VM_CONTEXT
*VmPtr
,
3865 IN UINT32 CodeOffset
3869 Routine Description:
3870 Decode a 32-bit index to determine the offset.
3873 VmPtr - pointer to VM context
3874 CodeOffset - offset from IP of the location of the 32-bit index to decode
3877 Converted index per EBC VM specification
3888 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3891 // Get the mask for N. First get the number of bits from the index.
3893 NBits
= (Index
& 0x70000000) >> 28;
3896 // Scale it for 32-bit indexes
3901 // Now using the number of bits, create a mask.
3903 Mask
= (INT32
)~0 << NBits
;
3906 // Now using the mask, extract N from the lower bits of the index.
3913 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3915 Offset
= N
* sizeof (UINTN
) + C
;
3920 if (Index
& 0x80000000) {
3921 Offset
= Offset
* -1;
3930 IN VM_CONTEXT
*VmPtr
,
3931 IN UINT32 CodeOffset
3935 Routine Description:
3936 Decode a 64-bit index to determine the offset.
3939 VmPtr - pointer to VM context
3940 CodeOffset - offset from IP of the location of the 64-bit index to decode
3943 Converted index per EBC VM specification
3954 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3957 // Get the mask for N. First get the number of bits from the index.
3959 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3962 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3964 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3967 // Now using the number of bits, create a mask.
3969 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3972 // Now using the mask, extract N from the lower bits of the index.
3979 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3981 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3986 if (Index
& 0x8000000000000000ULL
) {
3987 Offset
= MultS64x64 (Offset
, -1);
3996 IN VM_CONTEXT
*VmPtr
,
4002 Routine Description:
4003 The following VmWriteMem? routines are called by the EBC data
4004 movement instructions that write to memory. Since these writes
4005 may be to the stack, which looks like (high address on top) this,
4007 [EBC entry point arguments]
4011 we need to detect all attempts to write to the EBC entry point argument
4012 stack area and adjust the address (which will initially point into the
4013 VM stack) to point into the EBC entry point arguments.
4016 VmPtr - pointer to a VM context
4017 Addr - adddress to write to
4018 Data - value to write to Addr
4026 // Convert the address if it's in the stack gap
4028 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4029 *(UINT8
*) Addr
= Data
;
4036 IN VM_CONTEXT
*VmPtr
,
4044 // Convert the address if it's in the stack gap
4046 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4049 // Do a simple write if aligned
4051 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4052 *(UINT16
*) Addr
= Data
;
4055 // Write as two bytes
4058 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4063 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4076 IN VM_CONTEXT
*VmPtr
,
4084 // Convert the address if it's in the stack gap
4086 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4089 // Do a simple write if aligned
4091 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4092 *(UINT32
*) Addr
= Data
;
4095 // Write as two words
4098 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4103 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4115 IN VM_CONTEXT
*VmPtr
,
4124 // Convert the address if it's in the stack gap
4126 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4129 // Do a simple write if aligned
4131 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4132 *(UINT64
*) Addr
= Data
;
4135 // Write as two 32-bit words
4138 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4143 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4144 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4156 IN VM_CONTEXT
*VmPtr
,
4164 Status
= EFI_SUCCESS
;
4167 // Convert the address if it's in the stack gap
4169 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4172 // Do a simple write if aligned
4174 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4175 *(UINTN
*) Addr
= Data
;
4177 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4179 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4181 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4191 IN VM_CONTEXT
*VmPtr
,
4196 Routine Description:
4198 The following VmReadImmed routines are called by the EBC execute
4199 functions to read EBC immediate values from the code stream.
4200 Since we can't assume alignment, each tries to read in the biggest
4201 chunks size available, but will revert to smaller reads if necessary.
4204 VmPtr - pointer to a VM context
4205 Offset - offset from IP of the code bytes to read.
4208 Signed data of the requested size from the specified address.
4213 // Simply return the data in flat memory space
4215 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4221 IN VM_CONTEXT
*VmPtr
,
4226 // Read direct if aligned
4228 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4229 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4232 // All code word reads should be aligned
4234 EbcDebugSignalException (
4235 EXCEPT_EBC_ALIGNMENT_CHECK
,
4236 EXCEPTION_FLAG_WARNING
,
4241 // Return unaligned data
4243 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4249 IN VM_CONTEXT
*VmPtr
,
4256 // Read direct if aligned
4258 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4259 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4262 // Return unaligned data
4264 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4265 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4272 IN VM_CONTEXT
*VmPtr
,
4281 // Read direct if aligned
4283 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4284 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4287 // Return unaligned data.
4289 Ptr
= (UINT8
*) &Data64
;
4290 Data32
= VmReadCode32 (VmPtr
, Offset
);
4291 *(UINT32
*) Ptr
= Data32
;
4292 Ptr
+= sizeof (Data32
);
4293 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4294 *(UINT32
*) Ptr
= Data32
;
4301 IN VM_CONTEXT
*VmPtr
,
4306 Routine Description:
4307 The following VmReadCode() routines provide the ability to read raw
4308 unsigned data from the code stream.
4311 VmPtr - pointer to VM context
4312 Offset - offset from current IP to the raw data to read.
4315 The raw unsigned 16-bit value from the code stream.
4320 // Read direct if aligned
4322 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4323 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4326 // All code word reads should be aligned
4328 EbcDebugSignalException (
4329 EXCEPT_EBC_ALIGNMENT_CHECK
,
4330 EXCEPTION_FLAG_WARNING
,
4335 // Return unaligned data
4337 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4343 IN VM_CONTEXT
*VmPtr
,
4349 // Read direct if aligned
4351 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4352 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4355 // Return unaligned data
4357 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4358 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4365 IN VM_CONTEXT
*VmPtr
,
4374 // Read direct if aligned
4376 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4377 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4380 // Return unaligned data.
4382 Ptr
= (UINT8
*) &Data64
;
4383 Data32
= VmReadCode32 (VmPtr
, Offset
);
4384 *(UINT32
*) Ptr
= Data32
;
4385 Ptr
+= sizeof (Data32
);
4386 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4387 *(UINT32
*) Ptr
= Data32
;
4394 IN VM_CONTEXT
*VmPtr
,
4399 // Convert the address if it's in the stack gap
4401 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4403 // Simply return the data in flat memory space
4405 return * (UINT8
*) Addr
;
4411 IN VM_CONTEXT
*VmPtr
,
4416 // Convert the address if it's in the stack gap
4418 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4420 // Read direct if aligned
4422 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4423 return * (UINT16
*) Addr
;
4426 // Return unaligned data
4428 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4434 IN VM_CONTEXT
*VmPtr
,
4441 // Convert the address if it's in the stack gap
4443 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4445 // Read direct if aligned
4447 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4448 return * (UINT32
*) Addr
;
4451 // Return unaligned data
4453 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4454 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4461 IN VM_CONTEXT
*VmPtr
,
4469 // Convert the address if it's in the stack gap
4471 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4474 // Read direct if aligned
4476 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4477 return * (UINT64
*) Addr
;
4480 // Return unaligned data. Assume little endian.
4482 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4483 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4484 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4491 IN VM_CONTEXT
*VmPtr
,
4496 Routine Description:
4498 Given an address that EBC is going to read from or write to, return
4499 an appropriate address that accounts for a gap in the stack.
4501 The stack for this application looks like this (high addr on top)
4502 [EBC entry point arguments]
4506 The EBC assumes that its arguments are at the top of its stack, which
4507 is where the VM stack is really. Therefore if the EBC does memory
4508 accesses into the VM stack area, then we need to convert the address
4509 to point to the EBC entry point arguments area. Do this here.
4513 VmPtr - pointer to VM context
4514 Addr - address of interest
4518 The unchanged address if it's not in the VM stack region. Otherwise,
4519 adjust for the stack gap and return the modified address.
4523 if ((Addr
>= VmPtr
->LowStackTop
) && (Addr
< VmPtr
->HighStackBottom
)) {
4525 // In the stack gap -- now make sure it's not in the VM itself, which
4526 // would be the case if it's accessing VM register contents.
4528 if ((Addr
< (UINTN
) VmPtr
) || (Addr
> (UINTN
) VmPtr
+ sizeof (VM_CONTEXT
))) {
4529 VmPtr
->LastAddrConverted
= Addr
;
4530 VmPtr
->LastAddrConvertedValue
= Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4531 return Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4541 IN VM_CONTEXT
*VmPtr
,
4546 Routine Description:
4547 Read a natural value from memory. May or may not be aligned.
4550 VmPtr - current VM context
4551 Addr - the address to read from
4554 The natural value at address Addr.
4563 // Convert the address if it's in the stack gap
4565 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4567 // Read direct if aligned
4569 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4570 return * (UINTN
*) Addr
;
4573 // Return unaligned data
4576 FromPtr
= (UINT8
*) Addr
;
4577 ToPtr
= (UINT8
*) &Data
;
4579 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4593 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));