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
;
691 EbcSimpleDebugger
= NULL
;
692 Status
= EFI_SUCCESS
;
696 // Make sure the magic value has been put on the stack before we got here.
698 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
702 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
705 // Try to get the debug support for EBC
708 Status
= gBS
->LocateProtocol (
709 &mEbcSimpleDebuggerProtocolGuid
,
711 (VOID
**) &EbcSimpleDebugger
713 if (EFI_ERROR (Status
)) {
714 EbcSimpleDebugger
= NULL
;
719 // Save the start IP for debug. For example, if we take an exception we
720 // can print out the location of the exception relative to the entry point,
721 // which could then be used in a disassembly listing to find the problem.
723 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
726 // We'll wait for this flag to know when we're done. The RET
727 // instruction sets it if it runs out of stack.
729 VmPtr
->StopFlags
= 0;
730 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
732 // If we've found a simple debugger protocol, call it
735 if (EbcSimpleDebugger
!= NULL
) {
736 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
741 // Verify the opcode is in range. Otherwise generate an exception.
743 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
744 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
745 Status
= EFI_UNSUPPORTED
;
749 // Use the opcode bits to index into the opcode dispatch table. If the
750 // function pointer is null then generate an exception.
752 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
753 if (ExecFunc
== (UINTN
) NULL
) {
754 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
755 Status
= EFI_UNSUPPORTED
;
759 // The EBC VM is a strongly ordered processor, so perform a fence operation before
760 // and after each instruction is executed.
764 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
769 // If the step flag is set, signal an exception and continue. We don't
770 // clear it here. Assuming the debugger is responsible for clearing it.
772 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
773 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
776 // Make sure stack has not been corrupted. Only report it once though.
778 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
779 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
797 Execute the MOVxx instructions.
801 VmPtr - pointer to a VM context.
810 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
811 MOVqq {@}R1 {Index64}, {@}R2 {Index64}
813 Copies contents of [R2] -> [R1], zero extending where required.
815 First character indicates the size of the move.
816 Second character indicates the size of the index(s).
818 Invalid to have R1 direct with index.
835 Opcode
= GETOPCODE (VmPtr
);
836 OpcMasked
= (UINT8
) (Opcode
& OPCODE_M_OPCODE
);
839 // Get the operands byte so we can get R1 and R2
841 Operands
= GETOPERANDS (VmPtr
);
851 // Determine if we have an index/immediate data. Base instruction size
852 // is 2 (opcode + operands). Add to this size each index specified.
855 if (Opcode
& (OPCODE_M_IMMED_OP1
| OPCODE_M_IMMED_OP2
)) {
857 // Determine size of the index from the opcode. Then get it.
859 if ((OpcMasked
<= OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVNW
)) {
861 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
862 // Get one or both index values.
864 if (Opcode
& OPCODE_M_IMMED_OP1
) {
865 Index16
= VmReadIndex16 (VmPtr
, 2);
866 Index64Op1
= (INT64
) Index16
;
867 Size
+= sizeof (UINT16
);
870 if (Opcode
& OPCODE_M_IMMED_OP2
) {
871 Index16
= VmReadIndex16 (VmPtr
, Size
);
872 Index64Op2
= (INT64
) Index16
;
873 Size
+= sizeof (UINT16
);
875 } else if ((OpcMasked
<= OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVND
)) {
877 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
879 if (Opcode
& OPCODE_M_IMMED_OP1
) {
880 Index32
= VmReadIndex32 (VmPtr
, 2);
881 Index64Op1
= (INT64
) Index32
;
882 Size
+= sizeof (UINT32
);
885 if (Opcode
& OPCODE_M_IMMED_OP2
) {
886 Index32
= VmReadIndex32 (VmPtr
, Size
);
887 Index64Op2
= (INT64
) Index32
;
888 Size
+= sizeof (UINT32
);
890 } else if (OpcMasked
== OPCODE_MOVQQ
) {
892 // MOVqq -- only form with a 64-bit index
894 if (Opcode
& OPCODE_M_IMMED_OP1
) {
895 Index64Op1
= VmReadIndex64 (VmPtr
, 2);
896 Size
+= sizeof (UINT64
);
899 if (Opcode
& OPCODE_M_IMMED_OP2
) {
900 Index64Op2
= VmReadIndex64 (VmPtr
, Size
);
901 Size
+= sizeof (UINT64
);
905 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
907 EbcDebugSignalException (
908 EXCEPT_EBC_INSTRUCTION_ENCODING
,
909 EXCEPTION_FLAG_FATAL
,
912 return EFI_UNSUPPORTED
;
916 // Determine the size of the move, and create a mask for it so we can
917 // clear unused bits.
919 if ((OpcMasked
== OPCODE_MOVBW
) || (OpcMasked
== OPCODE_MOVBD
)) {
920 MoveSize
= DATA_SIZE_8
;
922 } else if ((OpcMasked
== OPCODE_MOVWW
) || (OpcMasked
== OPCODE_MOVWD
)) {
923 MoveSize
= DATA_SIZE_16
;
925 } else if ((OpcMasked
== OPCODE_MOVDW
) || (OpcMasked
== OPCODE_MOVDD
)) {
926 MoveSize
= DATA_SIZE_32
;
927 DataMask
= 0xFFFFFFFF;
928 } else if ((OpcMasked
== OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVQQ
)) {
929 MoveSize
= DATA_SIZE_64
;
930 DataMask
= (UINT64
)~0;
931 } else if ((OpcMasked
== OPCODE_MOVNW
) || (OpcMasked
== OPCODE_MOVND
)) {
932 MoveSize
= DATA_SIZE_N
;
933 DataMask
= (UINT64
)~0 >> (64 - 8 * sizeof (UINTN
));
936 // We were dispatched to this function and we don't recognize the opcode
938 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED
, EXCEPTION_FLAG_FATAL
, VmPtr
);
939 return EFI_UNSUPPORTED
;
942 // Now get the source address
944 if (OPERAND2_INDIRECT (Operands
)) {
946 // Indirect form @R2. Compute address of operand2
948 Source
= (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
);
950 // Now get the data from the source. Always 0-extend and let the compiler
951 // sign-extend where required.
955 Data64
= (UINT64
) (UINT8
) VmReadMem8 (VmPtr
, Source
);
959 Data64
= (UINT64
) (UINT16
) VmReadMem16 (VmPtr
, Source
);
963 Data64
= (UINT64
) (UINT32
) VmReadMem32 (VmPtr
, Source
);
967 Data64
= (UINT64
) VmReadMem64 (VmPtr
, Source
);
971 Data64
= (UINT64
) (UINTN
) VmReadMemN (VmPtr
, Source
);
982 // Not indirect source: MOVxx {@}Rx, Ry [Index]
984 Data64
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
;
986 // Did Operand2 have an index? If so, treat as two signed values since
987 // indexes are signed values.
989 if (Opcode
& OPCODE_M_IMMED_OP2
) {
991 // NOTE: need to find a way to fix this, most likely by changing the VM
992 // implementation to remove the stack gap. To do that, we'd need to
993 // allocate stack space for the VM and actually set the system
994 // stack pointer to the allocated buffer when the VM starts.
996 // Special case -- if someone took the address of a function parameter
997 // then we need to make sure it's not in the stack gap. We can identify
998 // this situation if (Operand2 register == 0) && (Operand2 is direct)
999 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
1000 // Situations that to be aware of:
1001 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
1003 if ((OPERAND2_REGNUM (Operands
) == 0) &&
1004 (!OPERAND2_INDIRECT (Operands
)) &&
1006 (OPERAND1_REGNUM (Operands
) == 0) &&
1007 (OPERAND1_INDIRECT (Operands
))
1009 Data64
= (UINT64
) ConvertStackAddr (VmPtr
, (UINTN
) (INT64
) Data64
);
1014 // Now write it back
1016 if (OPERAND1_INDIRECT (Operands
)) {
1018 // Reuse the Source variable to now be dest.
1020 Source
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index64Op1
);
1022 // Do the write based on the size
1026 VmWriteMem8 (VmPtr
, Source
, (UINT8
) Data64
);
1030 VmWriteMem16 (VmPtr
, Source
, (UINT16
) Data64
);
1034 VmWriteMem32 (VmPtr
, Source
, (UINT32
) Data64
);
1038 VmWriteMem64 (VmPtr
, Source
, Data64
);
1042 VmWriteMemN (VmPtr
, Source
, (UINTN
) Data64
);
1054 // Make sure we didn't have an index on operand1.
1056 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1057 EbcDebugSignalException (
1058 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1059 EXCEPTION_FLAG_FATAL
,
1062 return EFI_UNSUPPORTED
;
1065 // Direct storage in register. Clear unused bits and store back to
1068 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
& DataMask
;
1071 // Advance the instruction pointer
1080 IN VM_CONTEXT
*VmPtr
1084 Routine Description:
1086 Execute the EBC BREAK instruction
1090 VmPtr - pointer to current VM context
1100 VOID
*EbcEntryPoint
;
1103 UINT64 U64EbcEntryPoint
;
1106 Operands
= GETOPERANDS (VmPtr
);
1109 // Runaway program break. Generate an exception and terminate
1112 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1116 // Get VM version -- return VM revision number in R7
1122 // 16-8 = Major version
1123 // 7-0 = Minor version
1125 VmPtr
->R
[7] = GetVmVersion ();
1129 // Debugger breakpoint
1132 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1134 // See if someone has registered a handler
1136 EbcDebugSignalException (
1137 EXCEPT_EBC_BREAKPOINT
,
1138 EXCEPTION_FLAG_NONE
,
1142 // Don't advance the IP
1144 return EFI_UNSUPPORTED
;
1148 // System call, which there are none, so NOP it.
1154 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1155 // "offset from self" pointer to the EBC entry point.
1156 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1159 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1160 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1161 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1164 // Now create a new thunk
1166 Status
= EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1169 // Finally replace the EBC entry point memory with the thunk address
1171 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1175 // Compiler setting version per value in R7
1178 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1180 // Check compiler version against VM version?
1185 // Unhandled break code. Signal exception.
1188 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1201 IN VM_CONTEXT
*VmPtr
1205 Routine Description:
1206 Execute the JMP instruction
1209 VmPtr - pointer to VM context
1215 JMP64{cs|cc} Immed64
1216 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1219 b0.7 - immediate data present
1220 b0.6 - 1 = 64 bit immediate data
1221 0 = 32 bit immediate data
1222 b1.7 - 1 = conditional
1223 b1.6 1 = CS (condition set)
1224 0 = CC (condition clear)
1225 b1.4 1 = relative address
1226 0 = absolute address
1227 b1.3 1 = operand1 indirect
1234 UINT8 ConditionFlag
;
1241 Operand
= GETOPERANDS (VmPtr
);
1242 Opcode
= GETOPCODE (VmPtr
);
1245 // Get instruction length from the opcode. The upper two bits are used here
1246 // to index into the length array.
1248 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1251 // Decode instruction conditions
1252 // If we haven't met the condition, then simply advance the IP and return.
1254 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1255 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1256 if (Operand
& CONDITION_M_CONDITIONAL
) {
1257 if (CompareSet
!= ConditionFlag
) {
1263 // Check for 64-bit form and do it right away since it's the most
1264 // straight-forward form.
1266 if (Opcode
& OPCODE_M_IMMDATA64
) {
1268 // Double check for immediate-data, which is required. If not there,
1269 // then signal an exception
1271 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1272 EbcDebugSignalException (
1273 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1274 EXCEPTION_FLAG_ERROR
,
1277 return EFI_UNSUPPORTED
;
1280 // 64-bit immediate data is full address. Read the immediate data,
1281 // check for alignment, and jump absolute.
1283 Data64
= VmReadImmed64 (VmPtr
, 2);
1284 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1285 EbcDebugSignalException (
1286 EXCEPT_EBC_ALIGNMENT_CHECK
,
1287 EXCEPTION_FLAG_FATAL
,
1291 return EFI_UNSUPPORTED
;
1295 // Take jump -- relative or absolute
1297 if (Operand
& JMP_M_RELATIVE
) {
1298 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1300 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1307 // Get the index if there is one. May be either an index, or an immediate
1308 // offset depending on indirect operand.
1309 // JMP32 @R1 Index32 -- immediate data is an index
1310 // JMP32 R1 Immed32 -- immedate data is an offset
1312 if (Opcode
& OPCODE_M_IMMDATA
) {
1313 if (OPERAND1_INDIRECT (Operand
)) {
1314 Index32
= VmReadIndex32 (VmPtr
, 2);
1316 Index32
= VmReadImmed32 (VmPtr
, 2);
1322 // Get the register data. If R == 0, then special case where it's ignored.
1324 if (OPERAND1_REGNUM (Operand
) == 0) {
1327 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1332 if (OPERAND1_INDIRECT (Operand
)) {
1334 // Form: JMP32 @Rx {Index32}
1336 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1337 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1338 EbcDebugSignalException (
1339 EXCEPT_EBC_ALIGNMENT_CHECK
,
1340 EXCEPTION_FLAG_FATAL
,
1344 return EFI_UNSUPPORTED
;
1347 if (Operand
& JMP_M_RELATIVE
) {
1348 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1350 VmPtr
->Ip
= (VMIP
) Addr
;
1354 // Form: JMP32 Rx {Immed32}
1356 Addr
= (UINTN
) (Data64
+ Index32
);
1357 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1358 EbcDebugSignalException (
1359 EXCEPT_EBC_ALIGNMENT_CHECK
,
1360 EXCEPTION_FLAG_FATAL
,
1364 return EFI_UNSUPPORTED
;
1367 if (Operand
& JMP_M_RELATIVE
) {
1368 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1370 VmPtr
->Ip
= (VMIP
) Addr
;
1380 IN VM_CONTEXT
*VmPtr
1384 Routine Description:
1385 Execute the EBC JMP8 instruction
1388 VmPtr - pointer to a VM context
1394 JMP8{cs|cc} Offset/2
1399 UINT8 ConditionFlag
;
1404 // Decode instruction.
1406 Opcode
= GETOPCODE (VmPtr
);
1407 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1408 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1411 // If we haven't met the condition, then simply advance the IP and return
1413 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1414 if (CompareSet
!= ConditionFlag
) {
1420 // Get the offset from the instruction stream. It's relative to the
1421 // following instruction, and divided by 2.
1423 Offset
= VmReadImmed8 (VmPtr
, 1);
1425 // Want to check for offset == -2 and then raise an exception?
1427 VmPtr
->Ip
+= (Offset
* 2) + 2;
1434 IN VM_CONTEXT
*VmPtr
1438 Routine Description:
1440 Execute the EBC MOVI
1444 VmPtr - pointer to a VM context
1452 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1454 First variable character specifies the move size
1455 Second variable character specifies size of the immediate data
1457 Sign-extend the immediate data to the size of the operation, and zero-extend
1458 if storing to a register.
1460 Operand1 direct with index/immed is invalid.
1473 // Get the opcode and operands byte so we can get R1 and R2
1475 Opcode
= GETOPCODE (VmPtr
);
1476 Operands
= GETOPERANDS (VmPtr
);
1479 // Get the index (16-bit) if present
1481 if (Operands
& MOVI_M_IMMDATA
) {
1482 Index16
= VmReadIndex16 (VmPtr
, 2);
1489 // Extract the immediate data. Sign-extend always.
1491 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1492 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1494 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1495 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1497 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1498 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1504 EbcDebugSignalException (
1505 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1506 EXCEPTION_FLAG_FATAL
,
1509 return EFI_UNSUPPORTED
;
1512 // Now write back the result
1514 if (!OPERAND1_INDIRECT (Operands
)) {
1516 // Operand1 direct. Make sure it didn't have an index.
1518 if (Operands
& MOVI_M_IMMDATA
) {
1519 EbcDebugSignalException (
1520 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1521 EXCEPTION_FLAG_FATAL
,
1524 return EFI_UNSUPPORTED
;
1527 // Writing directly to a register. Clear unused bits.
1529 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1530 Mask64
= 0x000000FF;
1531 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1532 Mask64
= 0x0000FFFF;
1533 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1534 Mask64
= 0x00000000FFFFFFFF;
1536 Mask64
= (UINT64
)~0;
1539 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1542 // Get the address then write back based on size of the move
1544 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1545 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1546 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1547 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1548 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1549 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1550 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1552 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1556 // Advance the instruction pointer
1565 IN VM_CONTEXT
*VmPtr
1569 Routine Description:
1571 Execute the EBC MOV immediate natural. This instruction moves an immediate
1572 index value into a register or memory location.
1576 VmPtr - pointer to a VM context
1584 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1598 // Get the opcode and operands byte so we can get R1 and R2
1600 Opcode
= GETOPCODE (VmPtr
);
1601 Operands
= GETOPERANDS (VmPtr
);
1604 // Get the operand1 index (16-bit) if present
1606 if (Operands
& MOVI_M_IMMDATA
) {
1607 Index16
= VmReadIndex16 (VmPtr
, 2);
1614 // Extract the immediate data and convert to a 64-bit index.
1616 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1617 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1618 ImmedIndex64
= (INT64
) ImmedIndex16
;
1620 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1621 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1622 ImmedIndex64
= (INT64
) ImmedIndex32
;
1624 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1625 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1631 EbcDebugSignalException (
1632 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1633 EXCEPTION_FLAG_FATAL
,
1636 return EFI_UNSUPPORTED
;
1639 // Now write back the result
1641 if (!OPERAND1_INDIRECT (Operands
)) {
1643 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1646 if (Operands
& MOVI_M_IMMDATA
) {
1647 EbcDebugSignalException (
1648 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1649 EXCEPTION_FLAG_FATAL
,
1652 return EFI_UNSUPPORTED
;
1655 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1660 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1661 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1664 // Advance the instruction pointer
1673 IN VM_CONTEXT
*VmPtr
1677 Routine Description:
1679 Execute the EBC MOVREL instruction.
1680 Dest <- Ip + ImmData
1684 VmPtr - pointer to a VM context
1692 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1705 // Get the opcode and operands byte so we can get R1 and R2
1707 Opcode
= GETOPCODE (VmPtr
);
1708 Operands
= GETOPERANDS (VmPtr
);
1711 // Get the Operand 1 index (16-bit) if present
1713 if (Operands
& MOVI_M_IMMDATA
) {
1714 Index16
= VmReadIndex16 (VmPtr
, 2);
1721 // Get the immediate data.
1723 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1724 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1726 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1727 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1729 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1730 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1736 EbcDebugSignalException (
1737 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1738 EXCEPTION_FLAG_FATAL
,
1741 return EFI_UNSUPPORTED
;
1744 // Compute the value and write back the result
1746 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1747 if (!OPERAND1_INDIRECT (Operands
)) {
1749 // Check for illegal combination of operand1 direct with immediate data
1751 if (Operands
& MOVI_M_IMMDATA
) {
1752 EbcDebugSignalException (
1753 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1754 EXCEPTION_FLAG_FATAL
,
1757 return EFI_UNSUPPORTED
;
1760 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1763 // Get the address = [Rx] + Index16
1764 // Write back the result. Always a natural size write, since
1765 // we're talking addresses here.
1767 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1768 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1771 // Advance the instruction pointer
1780 IN VM_CONTEXT
*VmPtr
1784 Routine Description:
1786 Execute the EBC MOVsnw instruction. This instruction loads a signed
1787 natural value from memory or register to another memory or register. On
1788 32-bit machines, the value gets sign-extended to 64 bits if the destination
1793 VmPtr - pointer to a VM context
1801 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1803 0:7 1=>operand1 index present
1804 0:6 1=>operand2 index present
1816 // Get the opcode and operand bytes
1818 Opcode
= GETOPCODE (VmPtr
);
1819 Operands
= GETOPERANDS (VmPtr
);
1821 Op1Index
= Op2Index
= 0;
1824 // Get the indexes if present.
1827 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1828 if (OPERAND1_INDIRECT (Operands
)) {
1829 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1832 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1834 EbcDebugSignalException (
1835 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1836 EXCEPTION_FLAG_FATAL
,
1839 return EFI_UNSUPPORTED
;
1842 Size
+= sizeof (UINT16
);
1845 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1846 if (OPERAND2_INDIRECT (Operands
)) {
1847 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1849 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1852 Size
+= sizeof (UINT16
);
1855 // Get the data from the source.
1857 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1858 if (OPERAND2_INDIRECT (Operands
)) {
1859 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1862 // Now write back the result.
1864 if (!OPERAND1_INDIRECT (Operands
)) {
1865 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1867 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1870 // Advance the instruction pointer
1879 IN VM_CONTEXT
*VmPtr
1883 Routine Description:
1885 Execute the EBC MOVsnw instruction. This instruction loads a signed
1886 natural value from memory or register to another memory or register. On
1887 32-bit machines, the value gets sign-extended to 64 bits if the destination
1892 VmPtr - pointer to a VM context
1900 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1902 0:7 1=>operand1 index present
1903 0:6 1=>operand2 index present
1915 // Get the opcode and operand bytes
1917 Opcode
= GETOPCODE (VmPtr
);
1918 Operands
= GETOPERANDS (VmPtr
);
1920 Op1Index
= Op2Index
= 0;
1923 // Get the indexes if present.
1926 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1927 if (OPERAND1_INDIRECT (Operands
)) {
1928 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1931 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1933 EbcDebugSignalException (
1934 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1935 EXCEPTION_FLAG_FATAL
,
1938 return EFI_UNSUPPORTED
;
1941 Size
+= sizeof (UINT32
);
1944 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1945 if (OPERAND2_INDIRECT (Operands
)) {
1946 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1948 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1951 Size
+= sizeof (UINT32
);
1954 // Get the data from the source.
1956 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1957 if (OPERAND2_INDIRECT (Operands
)) {
1958 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1961 // Now write back the result.
1963 if (!OPERAND1_INDIRECT (Operands
)) {
1964 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1966 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1969 // Advance the instruction pointer
1978 IN VM_CONTEXT
*VmPtr
1982 Routine Description:
1983 Execute the EBC PUSHn instruction
1986 VmPtr - pointer to a VM context
1992 PUSHn {@}R1 {Index16|Immed16}
2002 // Get opcode and operands
2004 Opcode
= GETOPCODE (VmPtr
);
2005 Operands
= GETOPERANDS (VmPtr
);
2008 // Get index if present
2010 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2011 if (OPERAND1_INDIRECT (Operands
)) {
2012 Index16
= VmReadIndex16 (VmPtr
, 2);
2014 Index16
= VmReadImmed16 (VmPtr
, 2);
2023 // Get the data to push
2025 if (OPERAND1_INDIRECT (Operands
)) {
2026 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2028 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2031 // Adjust the stack down.
2033 VmPtr
->R
[0] -= sizeof (UINTN
);
2034 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2041 IN VM_CONTEXT
*VmPtr
2045 Routine Description:
2046 Execute the EBC PUSH instruction
2049 VmPtr - pointer to a VM context
2055 PUSH[32|64] {@}R1 {Index16|Immed16}
2066 // Get opcode and operands
2068 Opcode
= GETOPCODE (VmPtr
);
2069 Operands
= GETOPERANDS (VmPtr
);
2071 // Get immediate index if present, then advance the IP.
2073 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2074 if (OPERAND1_INDIRECT (Operands
)) {
2075 Index16
= VmReadIndex16 (VmPtr
, 2);
2077 Index16
= VmReadImmed16 (VmPtr
, 2);
2086 // Get the data to push
2088 if (Opcode
& PUSHPOP_M_64
) {
2089 if (OPERAND1_INDIRECT (Operands
)) {
2090 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2092 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2095 // Adjust the stack down, then write back the data
2097 VmPtr
->R
[0] -= sizeof (UINT64
);
2098 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2103 if (OPERAND1_INDIRECT (Operands
)) {
2104 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2106 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2109 // Adjust the stack down and write the data
2111 VmPtr
->R
[0] -= sizeof (UINT32
);
2112 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2121 IN VM_CONTEXT
*VmPtr
2125 Routine Description:
2126 Execute the EBC POPn instruction
2129 VmPtr - pointer to a VM context
2135 POPn {@}R1 {Index16|Immed16}
2145 // Get opcode and operands
2147 Opcode
= GETOPCODE (VmPtr
);
2148 Operands
= GETOPERANDS (VmPtr
);
2150 // Get immediate data if present, and advance the IP
2152 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2153 if (OPERAND1_INDIRECT (Operands
)) {
2154 Index16
= VmReadIndex16 (VmPtr
, 2);
2156 Index16
= VmReadImmed16 (VmPtr
, 2);
2165 // Read the data off the stack, then adjust the stack pointer
2167 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2168 VmPtr
->R
[0] += sizeof (UINTN
);
2170 // Do the write-back
2172 if (OPERAND1_INDIRECT (Operands
)) {
2173 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2175 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2184 IN VM_CONTEXT
*VmPtr
2188 Routine Description:
2189 Execute the EBC POP instruction
2192 VmPtr - pointer to a VM context
2198 POP {@}R1 {Index16|Immed16}
2209 // Get opcode and operands
2211 Opcode
= GETOPCODE (VmPtr
);
2212 Operands
= GETOPERANDS (VmPtr
);
2214 // Get immediate data if present, and advance the IP.
2216 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2217 if (OPERAND1_INDIRECT (Operands
)) {
2218 Index16
= VmReadIndex16 (VmPtr
, 2);
2220 Index16
= VmReadImmed16 (VmPtr
, 2);
2229 // Get the data off the stack, then write it to the appropriate location
2231 if (Opcode
& PUSHPOP_M_64
) {
2233 // Read the data off the stack, then adjust the stack pointer
2235 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2236 VmPtr
->R
[0] += sizeof (UINT64
);
2238 // Do the write-back
2240 if (OPERAND1_INDIRECT (Operands
)) {
2241 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2243 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2247 // 32-bit pop. Read it off the stack and adjust the stack pointer
2249 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2250 VmPtr
->R
[0] += sizeof (UINT32
);
2252 // Do the write-back
2254 if (OPERAND1_INDIRECT (Operands
)) {
2255 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2257 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2267 IN VM_CONTEXT
*VmPtr
2271 Routine Description:
2272 Implements the EBC CALL instruction.
2277 CALL32 {@}R1 {Immed32|Index32}
2279 CALLEX16 {@}R1 {Immed32}
2281 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2284 VmPtr - pointer to a VM context.
2299 // Get opcode and operands
2301 Opcode
= GETOPCODE (VmPtr
);
2302 Operands
= GETOPERANDS (VmPtr
);
2304 // Assign these as well to avoid compiler warnings
2309 FramePtr
= VmPtr
->FramePtr
;
2311 // Determine the instruction size, and get immediate data if present
2313 if (Opcode
& OPCODE_M_IMMDATA
) {
2314 if (Opcode
& OPCODE_M_IMMDATA64
) {
2315 Immed64
= VmReadImmed64 (VmPtr
, 2);
2319 // If register operand is indirect, then the immediate data is an index
2321 if (OPERAND1_INDIRECT (Operands
)) {
2322 Immed32
= VmReadIndex32 (VmPtr
, 2);
2324 Immed32
= VmReadImmed32 (VmPtr
, 2);
2333 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2334 // put our return address and frame pointer on the VM stack.
2336 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2338 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2339 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2341 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2344 // If 64-bit data, then absolute jump only
2346 if (Opcode
& OPCODE_M_IMMDATA64
) {
2348 // Native or EBC call?
2350 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2351 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2354 // Call external function, get the return value, and advance the IP
2356 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2360 // Get the register data. If operand1 == 0, then ignore register and
2361 // take immediate data as relative or absolute address.
2362 // Compiler should take care of upper bits if 32-bit machine.
2364 if (OPERAND1_REGNUM (Operands
) != 0) {
2365 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2368 // Get final address
2370 if (OPERAND1_INDIRECT (Operands
)) {
2371 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2376 // Now determine if external call, and then if relative or absolute
2378 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2380 // EBC call. Relative or absolute? If relative, then it's relative to the
2381 // start of the next instruction.
2383 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2384 VmPtr
->Ip
+= Immed64
+ Size
;
2386 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2390 // Native call. Relative or absolute?
2392 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2393 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2395 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2399 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2410 IN VM_CONTEXT
*VmPtr
2414 Routine Description:
2415 Execute the EBC RET instruction
2418 VmPtr - pointer to a VM context
2429 // If we're at the top of the stack, then simply set the done
2432 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2433 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2436 // Pull the return address off the VM app's stack and set the IP
2439 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2440 EbcDebugSignalException (
2441 EXCEPT_EBC_ALIGNMENT_CHECK
,
2442 EXCEPTION_FLAG_FATAL
,
2447 // Restore the IP and frame pointer from the stack
2449 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2451 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2461 IN VM_CONTEXT
*VmPtr
2465 Routine Description:
2466 Execute the EBC CMP instruction
2469 VmPtr - pointer to a VM context
2475 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2488 // Get opcode and operands
2490 Opcode
= GETOPCODE (VmPtr
);
2491 Operands
= GETOPERANDS (VmPtr
);
2493 // Get the register data we're going to compare to
2495 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2497 // Get immediate data
2499 if (Opcode
& OPCODE_M_IMMDATA
) {
2500 if (OPERAND2_INDIRECT (Operands
)) {
2501 Index16
= VmReadIndex16 (VmPtr
, 2);
2503 Index16
= VmReadImmed16 (VmPtr
, 2);
2514 if (OPERAND2_INDIRECT (Operands
)) {
2515 if (Opcode
& OPCODE_M_64BIT
) {
2516 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2519 // 32-bit operations. 0-extend the values for all cases.
2521 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2524 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2527 // Now do the compare
2530 if (Opcode
& OPCODE_M_64BIT
) {
2534 switch (Opcode
& OPCODE_M_OPCODE
) {
2553 case OPCODE_CMPULTE
:
2554 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2559 case OPCODE_CMPUGTE
:
2560 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2572 switch (Opcode
& OPCODE_M_OPCODE
) {
2574 if ((INT32
) Op1
== (INT32
) Op2
) {
2580 if ((INT32
) Op1
<= (INT32
) Op2
) {
2586 if ((INT32
) Op1
>= (INT32
) Op2
) {
2591 case OPCODE_CMPULTE
:
2592 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2597 case OPCODE_CMPUGTE
:
2598 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2608 // Now set the flag accordingly for the comparison
2611 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2613 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2625 IN VM_CONTEXT
*VmPtr
2629 Routine Description:
2630 Execute the EBC CMPI instruction
2633 VmPtr - pointer to a VM context
2639 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2652 // Get opcode and operands
2654 Opcode
= GETOPCODE (VmPtr
);
2655 Operands
= GETOPERANDS (VmPtr
);
2658 // Get operand1 index if present
2661 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2662 Index16
= VmReadIndex16 (VmPtr
, 2);
2668 // Get operand1 data we're going to compare to
2670 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2671 if (OPERAND1_INDIRECT (Operands
)) {
2673 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2675 if (Opcode
& OPCODE_M_CMPI64
) {
2676 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2678 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2682 // Better not have been an index with direct. That is, CMPI R1 Index,...
2685 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2686 EbcDebugSignalException (
2687 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2688 EXCEPTION_FLAG_ERROR
,
2692 return EFI_UNSUPPORTED
;
2696 // Get immediate data -- 16- or 32-bit sign extended
2698 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2699 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2703 // 16-bit immediate data. Sign extend always.
2705 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2709 // Now do the compare
2712 if (Opcode
& OPCODE_M_CMPI64
) {
2714 // 64 bit comparison
2716 switch (Opcode
& OPCODE_M_OPCODE
) {
2718 if (Op1
== (INT64
) Op2
) {
2723 case OPCODE_CMPILTE
:
2724 if (Op1
<= (INT64
) Op2
) {
2729 case OPCODE_CMPIGTE
:
2730 if (Op1
>= (INT64
) Op2
) {
2735 case OPCODE_CMPIULTE
:
2736 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2741 case OPCODE_CMPIUGTE
:
2742 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2752 // 32-bit comparisons
2754 switch (Opcode
& OPCODE_M_OPCODE
) {
2756 if ((INT32
) Op1
== Op2
) {
2761 case OPCODE_CMPILTE
:
2762 if ((INT32
) Op1
<= Op2
) {
2767 case OPCODE_CMPIGTE
:
2768 if ((INT32
) Op1
>= Op2
) {
2773 case OPCODE_CMPIULTE
:
2774 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2779 case OPCODE_CMPIUGTE
:
2780 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2790 // Now set the flag accordingly for the comparison
2793 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2795 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2807 IN VM_CONTEXT
*VmPtr
,
2813 Routine Description:
2814 Execute the EBC NOT instruction
2817 VmPtr - pointer to a VM context
2818 Op1 - Operand 1 from the instruction
2819 Op2 - Operand 2 from the instruction
2825 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2835 IN VM_CONTEXT
*VmPtr
,
2841 Routine Description:
2842 Execute the EBC NEG instruction
2845 VmPtr - pointer to a VM context
2846 Op1 - Operand 1 from the instruction
2847 Op2 - Operand 2 from the instruction
2853 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2863 IN VM_CONTEXT
*VmPtr
,
2869 Routine Description:
2871 Execute the EBC ADD instruction
2874 VmPtr - pointer to a VM context
2875 Op1 - Operand 1 from the instruction
2876 Op2 - Operand 2 from the instruction
2882 ADD[32|64] {@}R1, {@}R2 {Index16}
2892 IN VM_CONTEXT
*VmPtr
,
2898 Routine Description:
2899 Execute the EBC SUB instruction
2902 VmPtr - pointer to a VM context
2903 Op1 - Operand 1 from the instruction
2904 Op2 - Operand 2 from the instruction
2911 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2915 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2916 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2918 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2925 IN VM_CONTEXT
*VmPtr
,
2931 Routine Description:
2933 Execute the EBC MUL instruction
2936 VmPtr - pointer to a VM context
2937 Op1 - Operand 1 from the instruction
2938 Op2 - Operand 2 from the instruction
2944 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2950 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2951 return MulS64x64 (Op1
, Op2
, &ResultHigh
);
2953 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2960 IN VM_CONTEXT
*VmPtr
,
2966 Routine Description:
2967 Execute the EBC MULU instruction
2970 VmPtr - pointer to a VM context
2971 Op1 - Operand 1 from the instruction
2972 Op2 - Operand 2 from the instruction
2975 (unsigned)Op1 * (unsigned)Op2
2978 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2983 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2984 return MulU64x64 (Op1
, Op2
, (UINT64
*)&ResultHigh
);
2986 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2993 IN VM_CONTEXT
*VmPtr
,
2999 Routine Description:
3001 Execute the EBC DIV instruction
3004 VmPtr - pointer to a VM context
3005 Op1 - Operand 1 from the instruction
3006 Op2 - Operand 2 from the instruction
3012 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
3020 // Check for divide-by-0
3023 EbcDebugSignalException (
3024 EXCEPT_EBC_DIVIDE_ERROR
,
3025 EXCEPTION_FLAG_FATAL
,
3031 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3032 return (UINT64
) (DivS64x64 (Op1
, Op2
, &Remainder
, &Error
));
3034 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3042 IN VM_CONTEXT
*VmPtr
,
3048 Routine Description:
3049 Execute the EBC DIVU instruction
3052 VmPtr - pointer to a VM context
3053 Op1 - Operand 1 from the instruction
3054 Op2 - Operand 2 from the instruction
3057 (unsigned)Op1 / (unsigned)Op2
3060 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3068 // Check for divide-by-0
3071 EbcDebugSignalException (
3072 EXCEPT_EBC_DIVIDE_ERROR
,
3073 EXCEPTION_FLAG_FATAL
,
3079 // Get the destination register
3081 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3082 return (UINT64
) (DivU64x64 (Op1
, Op2
, &Remainder
, &Error
));
3084 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3092 IN VM_CONTEXT
*VmPtr
,
3098 Routine Description:
3099 Execute the EBC MOD instruction
3102 VmPtr - pointer to a VM context
3103 Op1 - Operand 1 from the instruction
3104 Op2 - Operand 2 from the instruction
3110 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3118 // Check for divide-by-0
3121 EbcDebugSignalException (
3122 EXCEPT_EBC_DIVIDE_ERROR
,
3123 EXCEPTION_FLAG_FATAL
,
3128 DivS64x64 ((INT64
) Op1
, (INT64
) Op2
, &Remainder
, &Error
);
3136 IN VM_CONTEXT
*VmPtr
,
3142 Routine Description:
3143 Execute the EBC MODU instruction
3146 VmPtr - pointer to a VM context
3147 Op1 - Operand 1 from the instruction
3148 Op2 - Operand 2 from the instruction
3151 Op1 UNSIGNED_MODULUS Op2
3154 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3162 // Check for divide-by-0
3165 EbcDebugSignalException (
3166 EXCEPT_EBC_DIVIDE_ERROR
,
3167 EXCEPTION_FLAG_FATAL
,
3172 DivU64x64 (Op1
, Op2
, &Remainder
, &Error
);
3180 IN VM_CONTEXT
*VmPtr
,
3186 Routine Description:
3187 Execute the EBC AND instruction
3190 VmPtr - pointer to a VM context
3191 Op1 - Operand 1 from the instruction
3192 Op2 - Operand 2 from the instruction
3198 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3208 IN VM_CONTEXT
*VmPtr
,
3214 Routine Description:
3215 Execute the EBC OR instruction
3218 VmPtr - pointer to a VM context
3219 Op1 - Operand 1 from the instruction
3220 Op2 - Operand 2 from the instruction
3226 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3236 IN VM_CONTEXT
*VmPtr
,
3242 Routine Description:
3243 Execute the EBC XOR instruction
3246 VmPtr - pointer to a VM context
3247 Op1 - Operand 1 from the instruction
3248 Op2 - Operand 2 from the instruction
3254 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3264 IN VM_CONTEXT
*VmPtr
,
3270 Routine Description:
3272 Execute the EBC SHL shift left instruction
3275 VmPtr - pointer to a VM context
3276 Op1 - Operand 1 from the instruction
3277 Op2 - Operand 2 from the instruction
3283 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3287 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3288 return LeftShiftU64 (Op1
, Op2
);
3290 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3297 IN VM_CONTEXT
*VmPtr
,
3303 Routine Description:
3304 Execute the EBC SHR instruction
3307 VmPtr - pointer to a VM context
3308 Op1 - Operand 1 from the instruction
3309 Op2 - Operand 2 from the instruction
3312 Op1 >> Op2 (unsigned operands)
3315 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3319 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3320 return RightShiftU64 (Op1
, Op2
);
3322 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3329 IN VM_CONTEXT
*VmPtr
,
3335 Routine Description:
3336 Execute the EBC ASHR instruction
3339 VmPtr - pointer to a VM context
3340 Op1 - Operand 1 from the instruction
3341 Op2 - Operand 2 from the instruction
3347 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3351 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3352 return ARightShift64 (Op1
, Op2
);
3354 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3361 IN VM_CONTEXT
*VmPtr
,
3367 Routine Description:
3368 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3371 VmPtr - pointer to a VM context
3372 Op1 - Operand 1 from the instruction
3373 Op2 - Operand 2 from the instruction
3379 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3387 // Convert to byte, then return as 64-bit signed value to let compiler
3388 // sign-extend the value
3391 Data64
= (INT64
) Data8
;
3393 return (UINT64
) Data64
;
3399 IN VM_CONTEXT
*VmPtr
,
3405 Routine Description:
3406 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3409 VmPtr - pointer to a VM context
3410 Op1 - Operand 1 from the instruction
3411 Op2 - Operand 2 from the instruction
3417 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3425 // Convert to word, then return as 64-bit signed value to let compiler
3426 // sign-extend the value
3428 Data16
= (INT16
) Op2
;
3429 Data64
= (INT64
) Data16
;
3431 return (UINT64
) Data64
;
3434 // Execute the EBC EXTNDD instruction.
3436 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3437 // EXTNDD Dest, Source
3439 // Operation: Dest <- SignExtended((DWORD)Source))
3444 IN VM_CONTEXT
*VmPtr
,
3450 Routine Description:
3451 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3454 VmPtr - pointer to a VM context
3455 Op1 - Operand 1 from the instruction
3456 Op2 - Operand 2 from the instruction
3462 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3470 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3471 // sign-extend the value
3473 Data32
= (INT32
) Op2
;
3474 Data64
= (INT64
) Data32
;
3476 return (UINT64
) Data64
;
3481 ExecuteSignedDataManip (
3482 IN VM_CONTEXT
*VmPtr
3486 // Just call the data manipulation function with a flag indicating this
3487 // is a signed operation.
3489 return ExecuteDataManip (VmPtr
, TRUE
);
3494 ExecuteUnsignedDataManip (
3495 IN VM_CONTEXT
*VmPtr
3499 // Just call the data manipulation function with a flag indicating this
3500 // is not a signed operation.
3502 return ExecuteDataManip (VmPtr
, FALSE
);
3508 IN VM_CONTEXT
*VmPtr
,
3509 IN BOOLEAN IsSignedOp
3513 Routine Description:
3514 Execute all the EBC data manipulation instructions.
3515 Since the EBC data manipulation instructions all have the same basic form,
3516 they can share the code that does the fetch of operands and the write-back
3517 of the result. This function performs the fetch of the operands (even if
3518 both are not needed to be fetched, like NOT instruction), dispatches to the
3519 appropriate subfunction, then writes back the returned result.
3522 VmPtr - pointer to VM context
3528 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3540 // Get opcode and operands
3542 Opcode
= GETOPCODE (VmPtr
);
3543 Operands
= GETOPERANDS (VmPtr
);
3546 // Determine if we have immediate data by the opcode
3548 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3550 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3552 if (OPERAND2_INDIRECT (Operands
)) {
3553 Index16
= VmReadIndex16 (VmPtr
, 2);
3555 Index16
= VmReadImmed16 (VmPtr
, 2);
3564 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3566 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3567 if (OPERAND2_INDIRECT (Operands
)) {
3569 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3571 if (Opcode
& DATAMANIP_M_64
) {
3572 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3575 // Read as signed value where appropriate.
3578 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3580 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3584 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3586 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3588 Op2
= (UINT64
) ((UINT32
) Op2
);
3593 // Get operand1 (destination and sometimes also an actual operand)
3596 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3597 if (OPERAND1_INDIRECT (Operands
)) {
3598 if (Opcode
& DATAMANIP_M_64
) {
3599 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3602 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3604 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3608 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3610 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3612 Op1
= (UINT64
) ((UINT32
) Op1
);
3617 // Dispatch to the computation function
3619 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3620 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3622 EbcDebugSignalException (
3623 EXCEPT_EBC_INVALID_OPCODE
,
3624 EXCEPTION_FLAG_ERROR
,
3628 // Advance and return
3631 return EFI_UNSUPPORTED
;
3633 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3636 // Write back the result.
3638 if (OPERAND1_INDIRECT (Operands
)) {
3639 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3640 if (Opcode
& DATAMANIP_M_64
) {
3641 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3643 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3647 // Storage back to a register. Write back, clearing upper bits (as per
3648 // the specification) if 32-bit operation.
3650 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3651 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3652 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3656 // Advance the instruction pointer
3665 IN VM_CONTEXT
*VmPtr
3669 Routine Description:
3670 Execute the EBC LOADSP instruction
3673 VmPtr - pointer to a VM context
3688 Operands
= GETOPERANDS (VmPtr
);
3693 switch (OPERAND1_REGNUM (Operands
)) {
3699 // Spec states that this instruction will not modify reserved bits in
3700 // the flags register.
3702 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3706 EbcDebugSignalException (
3707 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3708 EXCEPTION_FLAG_WARNING
,
3712 return EFI_UNSUPPORTED
;
3722 IN VM_CONTEXT
*VmPtr
3726 Routine Description:
3727 Execute the EBC STORESP instruction
3730 VmPtr - pointer to a VM context
3736 STORESP Rx, FLAGS|IP
3745 Operands
= GETOPERANDS (VmPtr
);
3750 switch (OPERAND2_REGNUM (Operands
)) {
3756 // Retrieve the value in the flags register, then clear reserved bits
3758 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3762 // Get IP -- address of following instruction
3765 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3769 EbcDebugSignalException (
3770 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3771 EXCEPTION_FLAG_WARNING
,
3775 return EFI_UNSUPPORTED
;
3786 IN VM_CONTEXT
*VmPtr
,
3787 IN UINT32 CodeOffset
3791 Routine Description:
3792 Decode a 16-bit index to determine the offset. Given an index value:
3795 b14:12 - number of bits in this index assigned to natural units (=a)
3796 ba:11 - constant units = C
3797 b0:a - natural units = N
3799 Given this info, the offset can be computed by:
3800 offset = sign_bit * (C + N * sizeof(UINTN))
3802 Max offset is achieved with index = 0x7FFF giving an offset of
3803 0x27B (32-bit machine) or 0x477 (64-bit machine).
3804 Min offset is achieved with index =
3807 VmPtr - pointer to VM context
3808 CodeOffset - offset from IP of the location of the 16-bit index to decode
3823 // First read the index from the code stream
3825 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3828 // Get the mask for N. First get the number of bits from the index.
3830 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3833 // Scale it for 16-bit indexes
3838 // Now using the number of bits, create a mask.
3840 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3843 // Now using the mask, extract N from the lower bits of the index.
3845 N
= (INT16
) (Index
&~Mask
);
3850 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3852 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3857 if (Index
& 0x8000) {
3859 // Do it the hard way to work around a bogus compiler warning
3861 // Offset = -1 * Offset;
3863 Offset
= (INT16
) ((INT32
) Offset
* -1);
3872 IN VM_CONTEXT
*VmPtr
,
3873 IN UINT32 CodeOffset
3877 Routine Description:
3878 Decode a 32-bit index to determine the offset.
3881 VmPtr - pointer to VM context
3882 CodeOffset - offset from IP of the location of the 32-bit index to decode
3885 Converted index per EBC VM specification
3896 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3899 // Get the mask for N. First get the number of bits from the index.
3901 NBits
= (Index
& 0x70000000) >> 28;
3904 // Scale it for 32-bit indexes
3909 // Now using the number of bits, create a mask.
3911 Mask
= (INT32
)~0 << NBits
;
3914 // Now using the mask, extract N from the lower bits of the index.
3921 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3923 Offset
= N
* sizeof (UINTN
) + C
;
3928 if (Index
& 0x80000000) {
3929 Offset
= Offset
* -1;
3938 IN VM_CONTEXT
*VmPtr
,
3939 IN UINT32 CodeOffset
3943 Routine Description:
3944 Decode a 64-bit index to determine the offset.
3947 VmPtr - pointer to VM context
3948 CodeOffset - offset from IP of the location of the 64-bit index to decode
3951 Converted index per EBC VM specification
3963 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3966 // Get the mask for N. First get the number of bits from the index.
3968 NBits
= RightShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3971 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3973 NBits
= LeftShiftU64 (NBits
, 3);
3976 // Now using the number of bits, create a mask.
3978 Mask
= (LeftShiftU64 ((UINT64
)~0, (UINT64
) NBits
));
3981 // Now using the mask, extract N from the lower bits of the index.
3988 C
= ARightShift64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
) NBits
);
3990 Offset
= MulU64x64 (N
, sizeof (UINTN
), &Remainder
) + C
;
3995 if (Index
& 0x8000000000000000ULL
) {
3996 Offset
= MulS64x64 (Offset
, -1, (INT64
*)&Index
);
4005 IN VM_CONTEXT
*VmPtr
,
4011 Routine Description:
4012 The following VmWriteMem? routines are called by the EBC data
4013 movement instructions that write to memory. Since these writes
4014 may be to the stack, which looks like (high address on top) this,
4016 [EBC entry point arguments]
4020 we need to detect all attempts to write to the EBC entry point argument
4021 stack area and adjust the address (which will initially point into the
4022 VM stack) to point into the EBC entry point arguments.
4025 VmPtr - pointer to a VM context
4026 Addr - adddress to write to
4027 Data - value to write to Addr
4035 // Convert the address if it's in the stack gap
4037 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4038 *(UINT8
*) Addr
= Data
;
4045 IN VM_CONTEXT
*VmPtr
,
4053 // Convert the address if it's in the stack gap
4055 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4058 // Do a simple write if aligned
4060 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4061 *(UINT16
*) Addr
= Data
;
4064 // Write as two bytes
4067 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4072 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4085 IN VM_CONTEXT
*VmPtr
,
4093 // Convert the address if it's in the stack gap
4095 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4098 // Do a simple write if aligned
4100 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4101 *(UINT32
*) Addr
= Data
;
4104 // Write as two words
4107 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4112 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4124 IN VM_CONTEXT
*VmPtr
,
4133 // Convert the address if it's in the stack gap
4135 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4138 // Do a simple write if aligned
4140 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4141 *(UINT64
*) Addr
= Data
;
4144 // Write as two 32-bit words
4147 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4152 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4153 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4165 IN VM_CONTEXT
*VmPtr
,
4173 Status
= EFI_SUCCESS
;
4176 // Convert the address if it's in the stack gap
4178 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4181 // Do a simple write if aligned
4183 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4184 *(UINTN
*) Addr
= Data
;
4186 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4188 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4190 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4200 IN VM_CONTEXT
*VmPtr
,
4205 Routine Description:
4207 The following VmReadImmed routines are called by the EBC execute
4208 functions to read EBC immediate values from the code stream.
4209 Since we can't assume alignment, each tries to read in the biggest
4210 chunks size available, but will revert to smaller reads if necessary.
4213 VmPtr - pointer to a VM context
4214 Offset - offset from IP of the code bytes to read.
4217 Signed data of the requested size from the specified address.
4222 // Simply return the data in flat memory space
4224 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4230 IN VM_CONTEXT
*VmPtr
,
4235 // Read direct if aligned
4237 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4238 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4241 // All code word reads should be aligned
4243 EbcDebugSignalException (
4244 EXCEPT_EBC_ALIGNMENT_CHECK
,
4245 EXCEPTION_FLAG_WARNING
,
4250 // Return unaligned data
4252 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4258 IN VM_CONTEXT
*VmPtr
,
4265 // Read direct if aligned
4267 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4268 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4271 // Return unaligned data
4273 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4274 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4281 IN VM_CONTEXT
*VmPtr
,
4290 // Read direct if aligned
4292 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4293 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4296 // Return unaligned data.
4298 Ptr
= (UINT8
*) &Data64
;
4299 Data32
= VmReadCode32 (VmPtr
, Offset
);
4300 *(UINT32
*) Ptr
= Data32
;
4301 Ptr
+= sizeof (Data32
);
4302 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4303 *(UINT32
*) Ptr
= Data32
;
4310 IN VM_CONTEXT
*VmPtr
,
4315 Routine Description:
4316 The following VmReadCode() routines provide the ability to read raw
4317 unsigned data from the code stream.
4320 VmPtr - pointer to VM context
4321 Offset - offset from current IP to the raw data to read.
4324 The raw unsigned 16-bit value from the code stream.
4329 // Read direct if aligned
4331 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4332 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4335 // All code word reads should be aligned
4337 EbcDebugSignalException (
4338 EXCEPT_EBC_ALIGNMENT_CHECK
,
4339 EXCEPTION_FLAG_WARNING
,
4344 // Return unaligned data
4346 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4352 IN VM_CONTEXT
*VmPtr
,
4358 // Read direct if aligned
4360 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4361 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4364 // Return unaligned data
4366 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4367 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4374 IN VM_CONTEXT
*VmPtr
,
4383 // Read direct if aligned
4385 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4386 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4389 // Return unaligned data.
4391 Ptr
= (UINT8
*) &Data64
;
4392 Data32
= VmReadCode32 (VmPtr
, Offset
);
4393 *(UINT32
*) Ptr
= Data32
;
4394 Ptr
+= sizeof (Data32
);
4395 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4396 *(UINT32
*) Ptr
= Data32
;
4403 IN VM_CONTEXT
*VmPtr
,
4408 // Convert the address if it's in the stack gap
4410 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4412 // Simply return the data in flat memory space
4414 return * (UINT8
*) Addr
;
4420 IN VM_CONTEXT
*VmPtr
,
4425 // Convert the address if it's in the stack gap
4427 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4429 // Read direct if aligned
4431 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4432 return * (UINT16
*) Addr
;
4435 // Return unaligned data
4437 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4443 IN VM_CONTEXT
*VmPtr
,
4450 // Convert the address if it's in the stack gap
4452 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4454 // Read direct if aligned
4456 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4457 return * (UINT32
*) Addr
;
4460 // Return unaligned data
4462 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4463 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4470 IN VM_CONTEXT
*VmPtr
,
4478 // Convert the address if it's in the stack gap
4480 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4483 // Read direct if aligned
4485 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4486 return * (UINT64
*) Addr
;
4489 // Return unaligned data. Assume little endian.
4491 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4492 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4493 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4500 IN VM_CONTEXT
*VmPtr
,
4505 Routine Description:
4507 Given an address that EBC is going to read from or write to, return
4508 an appropriate address that accounts for a gap in the stack.
4510 The stack for this application looks like this (high addr on top)
4511 [EBC entry point arguments]
4515 The EBC assumes that its arguments are at the top of its stack, which
4516 is where the VM stack is really. Therefore if the EBC does memory
4517 accesses into the VM stack area, then we need to convert the address
4518 to point to the EBC entry point arguments area. Do this here.
4522 VmPtr - pointer to VM context
4523 Addr - address of interest
4527 The unchanged address if it's not in the VM stack region. Otherwise,
4528 adjust for the stack gap and return the modified address.
4532 if ((Addr
>= VmPtr
->LowStackTop
) && (Addr
< VmPtr
->HighStackBottom
)) {
4534 // In the stack gap -- now make sure it's not in the VM itself, which
4535 // would be the case if it's accessing VM register contents.
4537 if ((Addr
< (UINTN
) VmPtr
) || (Addr
> (UINTN
) VmPtr
+ sizeof (VM_CONTEXT
))) {
4538 VmPtr
->LastAddrConverted
= Addr
;
4539 VmPtr
->LastAddrConvertedValue
= Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4540 return Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4550 IN VM_CONTEXT
*VmPtr
,
4555 Routine Description:
4556 Read a natural value from memory. May or may not be aligned.
4559 VmPtr - current VM context
4560 Addr - the address to read from
4563 The natural value at address Addr.
4572 // Convert the address if it's in the stack gap
4574 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4576 // Read direct if aligned
4578 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4579 return * (UINTN
*) Addr
;
4582 // Return unaligned data
4585 FromPtr
= (UINT8
*) Addr
;
4586 ToPtr
= (UINT8
*) &Data
;
4588 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4602 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));