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
,
140 IN VM_CONTEXT
*VmPtr
,
147 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
,
197 IN BOOLEAN IsSignedOperation
201 // Functions that execute VM opcodes
295 ExecuteSignedDataManip (
301 ExecuteUnsignedDataManip (
330 // Data manipulation subfunctions
335 IN VM_CONTEXT
*VmPtr
,
343 IN VM_CONTEXT
*VmPtr
,
351 IN VM_CONTEXT
*VmPtr
,
359 IN VM_CONTEXT
*VmPtr
,
367 IN VM_CONTEXT
*VmPtr
,
375 IN VM_CONTEXT
*VmPtr
,
383 IN VM_CONTEXT
*VmPtr
,
391 IN VM_CONTEXT
*VmPtr
,
399 IN VM_CONTEXT
*VmPtr
,
407 IN VM_CONTEXT
*VmPtr
,
415 IN VM_CONTEXT
*VmPtr
,
423 IN VM_CONTEXT
*VmPtr
,
431 IN VM_CONTEXT
*VmPtr
,
439 IN VM_CONTEXT
*VmPtr
,
447 IN VM_CONTEXT
*VmPtr
,
455 IN VM_CONTEXT
*VmPtr
,
463 IN VM_CONTEXT
*VmPtr
,
471 IN VM_CONTEXT
*VmPtr
,
479 IN VM_CONTEXT
*VmPtr
,
485 // Once we retrieve the operands for the data manipulation instructions,
486 // call these functions to perform the operation.
488 static CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable
[] = {
510 static CONST VM_TABLE_ENTRY mVmOpcodeTable
[] = {
511 { ExecuteBREAK
}, // opcode 0x00
512 { ExecuteJMP
}, // opcode 0x01
513 { ExecuteJMP8
}, // opcode 0x02
514 { ExecuteCALL
}, // opcode 0x03
515 { ExecuteRET
}, // opcode 0x04
516 { ExecuteCMP
}, // opcode 0x05 CMPeq
517 { ExecuteCMP
}, // opcode 0x06 CMPlte
518 { ExecuteCMP
}, // opcode 0x07 CMPgte
519 { ExecuteCMP
}, // opcode 0x08 CMPulte
520 { ExecuteCMP
}, // opcode 0x09 CMPugte
521 { ExecuteUnsignedDataManip
}, // opcode 0x0A NOT
522 { ExecuteSignedDataManip
}, // opcode 0x0B NEG
523 { ExecuteSignedDataManip
}, // opcode 0x0C ADD
524 { ExecuteSignedDataManip
}, // opcode 0x0D SUB
525 { ExecuteSignedDataManip
}, // opcode 0x0E MUL
526 { ExecuteUnsignedDataManip
}, // opcode 0x0F MULU
527 { ExecuteSignedDataManip
}, // opcode 0x10 DIV
528 { ExecuteUnsignedDataManip
}, // opcode 0x11 DIVU
529 { ExecuteSignedDataManip
}, // opcode 0x12 MOD
530 { ExecuteUnsignedDataManip
}, // opcode 0x13 MODU
531 { ExecuteUnsignedDataManip
}, // opcode 0x14 AND
532 { ExecuteUnsignedDataManip
}, // opcode 0x15 OR
533 { ExecuteUnsignedDataManip
}, // opcode 0x16 XOR
534 { ExecuteUnsignedDataManip
}, // opcode 0x17 SHL
535 { ExecuteUnsignedDataManip
}, // opcode 0x18 SHR
536 { ExecuteSignedDataManip
}, // opcode 0x19 ASHR
537 { ExecuteUnsignedDataManip
}, // opcode 0x1A EXTNDB
538 { ExecuteUnsignedDataManip
}, // opcode 0x1B EXTNDW
539 { ExecuteUnsignedDataManip
}, // opcode 0x1C EXTNDD
540 { ExecuteMOVxx
}, // opcode 0x1D MOVBW
541 { ExecuteMOVxx
}, // opcode 0x1E MOVWW
542 { ExecuteMOVxx
}, // opcode 0x1F MOVDW
543 { ExecuteMOVxx
}, // opcode 0x20 MOVQW
544 { ExecuteMOVxx
}, // opcode 0x21 MOVBD
545 { ExecuteMOVxx
}, // opcode 0x22 MOVWD
546 { ExecuteMOVxx
}, // opcode 0x23 MOVDD
547 { ExecuteMOVxx
}, // opcode 0x24 MOVQD
548 { ExecuteMOVsnw
}, // opcode 0x25 MOVsnw
549 { ExecuteMOVsnd
}, // opcode 0x26 MOVsnd
550 { NULL
}, // opcode 0x27
551 { ExecuteMOVxx
}, // opcode 0x28 MOVqq
552 { ExecuteLOADSP
}, // opcode 0x29 LOADSP SP1, R2
553 { ExecuteSTORESP
}, // opcode 0x2A STORESP R1, SP2
554 { ExecutePUSH
}, // opcode 0x2B PUSH {@}R1 [imm16]
555 { ExecutePOP
}, // opcode 0x2C POP {@}R1 [imm16]
556 { ExecuteCMPI
}, // opcode 0x2D CMPIEQ
557 { ExecuteCMPI
}, // opcode 0x2E CMPILTE
558 { ExecuteCMPI
}, // opcode 0x2F CMPIGTE
559 { ExecuteCMPI
}, // opcode 0x30 CMPIULTE
560 { ExecuteCMPI
}, // opcode 0x31 CMPIUGTE
561 { ExecuteMOVxx
}, // opcode 0x32 MOVN
562 { ExecuteMOVxx
}, // opcode 0x33 MOVND
563 { NULL
}, // opcode 0x34
564 { ExecutePUSHn
}, // opcode 0x35
565 { ExecutePOPn
}, // opcode 0x36
566 { ExecuteMOVI
}, // opcode 0x37 - mov immediate data
567 { ExecuteMOVIn
}, // opcode 0x38 - mov immediate natural
568 { ExecuteMOVREL
} // opcode 0x39 - move data relative to PC
572 // Length of JMP instructions, depending on upper two bits of opcode.
574 static CONST UINT8 mJMPLen
[] = { 2, 2, 6, 10 };
577 // Simple Debugger Protocol GUID
579 EFI_GUID mEbcSimpleDebuggerProtocolGuid
= EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID
;
582 EbcExecuteInstructions (
583 IN EFI_EBC_VM_TEST_PROTOCOL
*This
,
584 IN VM_CONTEXT
*VmPtr
,
585 IN OUT UINTN
*InstructionCount
591 Given a pointer to a new VM context, execute one or more instructions. This
592 function is only used for test purposes via the EBC VM test protocol.
596 This - pointer to protocol interface
597 VmPtr - pointer to a VM context
598 InstructionCount - how many instructions to execute. 0 if don't count.
609 UINTN InstructionsLeft
;
610 UINTN SavedInstructionCount
;
612 Status
= EFI_SUCCESS
;
614 if (*InstructionCount
== 0) {
615 InstructionsLeft
= 1;
617 InstructionsLeft
= *InstructionCount
;
620 SavedInstructionCount
= *InstructionCount
;
621 *InstructionCount
= 0;
624 // Index into the opcode table using the opcode byte for this instruction.
625 // This gives you the execute function, which we first test for null, then
626 // call it if it's not null.
628 while (InstructionsLeft
!= 0) {
629 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction
;
630 if (ExecFunc
== (UINTN
) NULL
) {
631 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
632 return EFI_UNSUPPORTED
;
634 mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction (VmPtr
);
635 *InstructionCount
= *InstructionCount
+ 1;
639 // Decrement counter if applicable
641 if (SavedInstructionCount
!= 0) {
657 Execute an EBC image from an entry point or from a published protocol.
661 VmPtr - pointer to prepared VM context.
670 UINT8 StackCorrupted
;
672 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL
*EbcSimpleDebugger
;
675 EbcSimpleDebugger
= NULL
;
676 Status
= EFI_SUCCESS
;
680 // Make sure the magic value has been put on the stack before we got here.
682 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
686 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
689 // Try to get the debug support for EBC
692 Status
= gBS
->LocateProtocol (
693 &mEbcSimpleDebuggerProtocolGuid
,
695 (VOID
**) &EbcSimpleDebugger
697 if (EFI_ERROR (Status
)) {
698 EbcSimpleDebugger
= NULL
;
703 // Save the start IP for debug. For example, if we take an exception we
704 // can print out the location of the exception relative to the entry point,
705 // which could then be used in a disassembly listing to find the problem.
707 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
710 // We'll wait for this flag to know when we're done. The RET
711 // instruction sets it if it runs out of stack.
713 VmPtr
->StopFlags
= 0;
714 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
716 // If we've found a simple debugger protocol, call it
719 if (EbcSimpleDebugger
!= NULL
) {
720 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
725 // Verify the opcode is in range. Otherwise generate an exception.
727 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
728 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
729 Status
= EFI_UNSUPPORTED
;
733 // Use the opcode bits to index into the opcode dispatch table. If the
734 // function pointer is null then generate an exception.
736 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
737 if (ExecFunc
== (UINTN
) NULL
) {
738 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
739 Status
= EFI_UNSUPPORTED
;
743 // The EBC VM is a strongly ordered processor, so perform a fence operation before
744 // and after each instruction is executed.
748 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
753 // If the step flag is set, signal an exception and continue. We don't
754 // clear it here. Assuming the debugger is responsible for clearing it.
756 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
757 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
760 // Make sure stack has not been corrupted. Only report it once though.
762 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
763 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
782 Execute the MOVxx instructions.
786 VmPtr - pointer to a VM context.
795 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
796 MOVqq {@}R1 {Index64}, {@}R2 {Index64}
798 Copies contents of [R2] -> [R1], zero extending where required.
800 First character indicates the size of the move.
801 Second character indicates the size of the index(s).
803 Invalid to have R1 direct with index.
820 Opcode
= GETOPCODE (VmPtr
);
821 OpcMasked
= (UINT8
) (Opcode
& OPCODE_M_OPCODE
);
824 // Get the operands byte so we can get R1 and R2
826 Operands
= GETOPERANDS (VmPtr
);
836 // Determine if we have an index/immediate data. Base instruction size
837 // is 2 (opcode + operands). Add to this size each index specified.
840 if (Opcode
& (OPCODE_M_IMMED_OP1
| OPCODE_M_IMMED_OP2
)) {
842 // Determine size of the index from the opcode. Then get it.
844 if ((OpcMasked
<= OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVNW
)) {
846 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
847 // Get one or both index values.
849 if (Opcode
& OPCODE_M_IMMED_OP1
) {
850 Index16
= VmReadIndex16 (VmPtr
, 2);
851 Index64Op1
= (INT64
) Index16
;
852 Size
+= sizeof (UINT16
);
855 if (Opcode
& OPCODE_M_IMMED_OP2
) {
856 Index16
= VmReadIndex16 (VmPtr
, Size
);
857 Index64Op2
= (INT64
) Index16
;
858 Size
+= sizeof (UINT16
);
860 } else if ((OpcMasked
<= OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVND
)) {
862 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
864 if (Opcode
& OPCODE_M_IMMED_OP1
) {
865 Index32
= VmReadIndex32 (VmPtr
, 2);
866 Index64Op1
= (INT64
) Index32
;
867 Size
+= sizeof (UINT32
);
870 if (Opcode
& OPCODE_M_IMMED_OP2
) {
871 Index32
= VmReadIndex32 (VmPtr
, Size
);
872 Index64Op2
= (INT64
) Index32
;
873 Size
+= sizeof (UINT32
);
875 } else if (OpcMasked
== OPCODE_MOVQQ
) {
877 // MOVqq -- only form with a 64-bit index
879 if (Opcode
& OPCODE_M_IMMED_OP1
) {
880 Index64Op1
= VmReadIndex64 (VmPtr
, 2);
881 Size
+= sizeof (UINT64
);
884 if (Opcode
& OPCODE_M_IMMED_OP2
) {
885 Index64Op2
= VmReadIndex64 (VmPtr
, Size
);
886 Size
+= sizeof (UINT64
);
890 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
892 EbcDebugSignalException (
893 EXCEPT_EBC_INSTRUCTION_ENCODING
,
894 EXCEPTION_FLAG_FATAL
,
897 return EFI_UNSUPPORTED
;
901 // Determine the size of the move, and create a mask for it so we can
902 // clear unused bits.
904 if ((OpcMasked
== OPCODE_MOVBW
) || (OpcMasked
== OPCODE_MOVBD
)) {
905 MoveSize
= DATA_SIZE_8
;
907 } else if ((OpcMasked
== OPCODE_MOVWW
) || (OpcMasked
== OPCODE_MOVWD
)) {
908 MoveSize
= DATA_SIZE_16
;
910 } else if ((OpcMasked
== OPCODE_MOVDW
) || (OpcMasked
== OPCODE_MOVDD
)) {
911 MoveSize
= DATA_SIZE_32
;
912 DataMask
= 0xFFFFFFFF;
913 } else if ((OpcMasked
== OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVQQ
)) {
914 MoveSize
= DATA_SIZE_64
;
915 DataMask
= (UINT64
)~0;
916 } else if ((OpcMasked
== OPCODE_MOVNW
) || (OpcMasked
== OPCODE_MOVND
)) {
917 MoveSize
= DATA_SIZE_N
;
918 DataMask
= (UINT64
)~0 >> (64 - 8 * sizeof (UINTN
));
921 // We were dispatched to this function and we don't recognize the opcode
923 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED
, EXCEPTION_FLAG_FATAL
, VmPtr
);
924 return EFI_UNSUPPORTED
;
927 // Now get the source address
929 if (OPERAND2_INDIRECT (Operands
)) {
931 // Indirect form @R2. Compute address of operand2
933 Source
= (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
);
935 // Now get the data from the source. Always 0-extend and let the compiler
936 // sign-extend where required.
940 Data64
= (UINT64
) (UINT8
) VmReadMem8 (VmPtr
, Source
);
944 Data64
= (UINT64
) (UINT16
) VmReadMem16 (VmPtr
, Source
);
948 Data64
= (UINT64
) (UINT32
) VmReadMem32 (VmPtr
, Source
);
952 Data64
= (UINT64
) VmReadMem64 (VmPtr
, Source
);
956 Data64
= (UINT64
) (UINTN
) VmReadMemN (VmPtr
, Source
);
967 // Not indirect source: MOVxx {@}Rx, Ry [Index]
969 Data64
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
;
971 // Did Operand2 have an index? If so, treat as two signed values since
972 // indexes are signed values.
974 if (Opcode
& OPCODE_M_IMMED_OP2
) {
976 // NOTE: need to find a way to fix this, most likely by changing the VM
977 // implementation to remove the stack gap. To do that, we'd need to
978 // allocate stack space for the VM and actually set the system
979 // stack pointer to the allocated buffer when the VM starts.
981 // Special case -- if someone took the address of a function parameter
982 // then we need to make sure it's not in the stack gap. We can identify
983 // this situation if (Operand2 register == 0) && (Operand2 is direct)
984 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
985 // Situations that to be aware of:
986 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
988 if ((OPERAND2_REGNUM (Operands
) == 0) &&
989 (!OPERAND2_INDIRECT (Operands
)) &&
991 (OPERAND1_REGNUM (Operands
) == 0) &&
992 (OPERAND1_INDIRECT (Operands
))
994 Data64
= (UINT64
) ConvertStackAddr (VmPtr
, (UINTN
) (INT64
) Data64
);
1001 if (OPERAND1_INDIRECT (Operands
)) {
1003 // Reuse the Source variable to now be dest.
1005 Source
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index64Op1
);
1007 // Do the write based on the size
1011 VmWriteMem8 (VmPtr
, Source
, (UINT8
) Data64
);
1015 VmWriteMem16 (VmPtr
, Source
, (UINT16
) Data64
);
1019 VmWriteMem32 (VmPtr
, Source
, (UINT32
) Data64
);
1023 VmWriteMem64 (VmPtr
, Source
, Data64
);
1027 VmWriteMemN (VmPtr
, Source
, (UINTN
) Data64
);
1039 // Make sure we didn't have an index on operand1.
1041 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1042 EbcDebugSignalException (
1043 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1044 EXCEPTION_FLAG_FATAL
,
1047 return EFI_UNSUPPORTED
;
1050 // Direct storage in register. Clear unused bits and store back to
1053 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
& DataMask
;
1056 // Advance the instruction pointer
1065 IN VM_CONTEXT
*VmPtr
1069 Routine Description:
1071 Execute the EBC BREAK instruction
1075 VmPtr - pointer to current VM context
1085 VOID
*EbcEntryPoint
;
1087 UINT64 U64EbcEntryPoint
;
1090 Operands
= GETOPERANDS (VmPtr
);
1093 // Runaway program break. Generate an exception and terminate
1096 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1100 // Get VM version -- return VM revision number in R7
1106 // 16-8 = Major version
1107 // 7-0 = Minor version
1109 VmPtr
->R
[7] = GetVmVersion ();
1113 // Debugger breakpoint
1116 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1118 // See if someone has registered a handler
1120 EbcDebugSignalException (
1121 EXCEPT_EBC_BREAKPOINT
,
1122 EXCEPTION_FLAG_NONE
,
1126 // Don't advance the IP
1128 return EFI_UNSUPPORTED
;
1132 // System call, which there are none, so NOP it.
1138 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1139 // "offset from self" pointer to the EBC entry point.
1140 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1143 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1144 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1145 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1148 // Now create a new thunk
1150 EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1153 // Finally replace the EBC entry point memory with the thunk address
1155 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1159 // Compiler setting version per value in R7
1162 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1164 // Check compiler version against VM version?
1169 // Unhandled break code. Signal exception.
1172 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1185 IN VM_CONTEXT
*VmPtr
1189 Routine Description:
1190 Execute the JMP instruction
1193 VmPtr - pointer to VM context
1199 JMP64{cs|cc} Immed64
1200 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1203 b0.7 - immediate data present
1204 b0.6 - 1 = 64 bit immediate data
1205 0 = 32 bit immediate data
1206 b1.7 - 1 = conditional
1207 b1.6 1 = CS (condition set)
1208 0 = CC (condition clear)
1209 b1.4 1 = relative address
1210 0 = absolute address
1211 b1.3 1 = operand1 indirect
1218 UINT8 ConditionFlag
;
1225 Operand
= GETOPERANDS (VmPtr
);
1226 Opcode
= GETOPCODE (VmPtr
);
1229 // Get instruction length from the opcode. The upper two bits are used here
1230 // to index into the length array.
1232 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1235 // Decode instruction conditions
1236 // If we haven't met the condition, then simply advance the IP and return.
1238 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1239 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1240 if (Operand
& CONDITION_M_CONDITIONAL
) {
1241 if (CompareSet
!= ConditionFlag
) {
1247 // Check for 64-bit form and do it right away since it's the most
1248 // straight-forward form.
1250 if (Opcode
& OPCODE_M_IMMDATA64
) {
1252 // Double check for immediate-data, which is required. If not there,
1253 // then signal an exception
1255 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1256 EbcDebugSignalException (
1257 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1258 EXCEPTION_FLAG_ERROR
,
1261 return EFI_UNSUPPORTED
;
1264 // 64-bit immediate data is full address. Read the immediate data,
1265 // check for alignment, and jump absolute.
1267 Data64
= VmReadImmed64 (VmPtr
, 2);
1268 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1269 EbcDebugSignalException (
1270 EXCEPT_EBC_ALIGNMENT_CHECK
,
1271 EXCEPTION_FLAG_FATAL
,
1275 return EFI_UNSUPPORTED
;
1279 // Take jump -- relative or absolute
1281 if (Operand
& JMP_M_RELATIVE
) {
1282 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1284 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1291 // Get the index if there is one. May be either an index, or an immediate
1292 // offset depending on indirect operand.
1293 // JMP32 @R1 Index32 -- immediate data is an index
1294 // JMP32 R1 Immed32 -- immedate data is an offset
1296 if (Opcode
& OPCODE_M_IMMDATA
) {
1297 if (OPERAND1_INDIRECT (Operand
)) {
1298 Index32
= VmReadIndex32 (VmPtr
, 2);
1300 Index32
= VmReadImmed32 (VmPtr
, 2);
1306 // Get the register data. If R == 0, then special case where it's ignored.
1308 if (OPERAND1_REGNUM (Operand
) == 0) {
1311 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1316 if (OPERAND1_INDIRECT (Operand
)) {
1318 // Form: JMP32 @Rx {Index32}
1320 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1321 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1322 EbcDebugSignalException (
1323 EXCEPT_EBC_ALIGNMENT_CHECK
,
1324 EXCEPTION_FLAG_FATAL
,
1328 return EFI_UNSUPPORTED
;
1331 if (Operand
& JMP_M_RELATIVE
) {
1332 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1334 VmPtr
->Ip
= (VMIP
) Addr
;
1338 // Form: JMP32 Rx {Immed32}
1340 Addr
= (UINTN
) (Data64
+ Index32
);
1341 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1342 EbcDebugSignalException (
1343 EXCEPT_EBC_ALIGNMENT_CHECK
,
1344 EXCEPTION_FLAG_FATAL
,
1348 return EFI_UNSUPPORTED
;
1351 if (Operand
& JMP_M_RELATIVE
) {
1352 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1354 VmPtr
->Ip
= (VMIP
) Addr
;
1364 IN VM_CONTEXT
*VmPtr
1368 Routine Description:
1369 Execute the EBC JMP8 instruction
1372 VmPtr - pointer to a VM context
1378 JMP8{cs|cc} Offset/2
1383 UINT8 ConditionFlag
;
1388 // Decode instruction.
1390 Opcode
= GETOPCODE (VmPtr
);
1391 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1392 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1395 // If we haven't met the condition, then simply advance the IP and return
1397 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1398 if (CompareSet
!= ConditionFlag
) {
1404 // Get the offset from the instruction stream. It's relative to the
1405 // following instruction, and divided by 2.
1407 Offset
= VmReadImmed8 (VmPtr
, 1);
1409 // Want to check for offset == -2 and then raise an exception?
1411 VmPtr
->Ip
+= (Offset
* 2) + 2;
1418 IN VM_CONTEXT
*VmPtr
1422 Routine Description:
1424 Execute the EBC MOVI
1428 VmPtr - pointer to a VM context
1436 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1438 First variable character specifies the move size
1439 Second variable character specifies size of the immediate data
1441 Sign-extend the immediate data to the size of the operation, and zero-extend
1442 if storing to a register.
1444 Operand1 direct with index/immed is invalid.
1457 // Get the opcode and operands byte so we can get R1 and R2
1459 Opcode
= GETOPCODE (VmPtr
);
1460 Operands
= GETOPERANDS (VmPtr
);
1463 // Get the index (16-bit) if present
1465 if (Operands
& MOVI_M_IMMDATA
) {
1466 Index16
= VmReadIndex16 (VmPtr
, 2);
1473 // Extract the immediate data. Sign-extend always.
1475 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1476 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1478 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1479 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1481 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1482 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1488 EbcDebugSignalException (
1489 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1490 EXCEPTION_FLAG_FATAL
,
1493 return EFI_UNSUPPORTED
;
1496 // Now write back the result
1498 if (!OPERAND1_INDIRECT (Operands
)) {
1500 // Operand1 direct. Make sure it didn't have an index.
1502 if (Operands
& MOVI_M_IMMDATA
) {
1503 EbcDebugSignalException (
1504 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1505 EXCEPTION_FLAG_FATAL
,
1508 return EFI_UNSUPPORTED
;
1511 // Writing directly to a register. Clear unused bits.
1513 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1514 Mask64
= 0x000000FF;
1515 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1516 Mask64
= 0x0000FFFF;
1517 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1518 Mask64
= 0x00000000FFFFFFFF;
1520 Mask64
= (UINT64
)~0;
1523 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1526 // Get the address then write back based on size of the move
1528 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1529 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1530 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1531 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1532 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1533 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1534 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1536 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1540 // Advance the instruction pointer
1549 IN VM_CONTEXT
*VmPtr
1553 Routine Description:
1555 Execute the EBC MOV immediate natural. This instruction moves an immediate
1556 index value into a register or memory location.
1560 VmPtr - pointer to a VM context
1568 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1582 // Get the opcode and operands byte so we can get R1 and R2
1584 Opcode
= GETOPCODE (VmPtr
);
1585 Operands
= GETOPERANDS (VmPtr
);
1588 // Get the operand1 index (16-bit) if present
1590 if (Operands
& MOVI_M_IMMDATA
) {
1591 Index16
= VmReadIndex16 (VmPtr
, 2);
1598 // Extract the immediate data and convert to a 64-bit index.
1600 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1601 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1602 ImmedIndex64
= (INT64
) ImmedIndex16
;
1604 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1605 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1606 ImmedIndex64
= (INT64
) ImmedIndex32
;
1608 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1609 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1615 EbcDebugSignalException (
1616 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1617 EXCEPTION_FLAG_FATAL
,
1620 return EFI_UNSUPPORTED
;
1623 // Now write back the result
1625 if (!OPERAND1_INDIRECT (Operands
)) {
1627 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1630 if (Operands
& MOVI_M_IMMDATA
) {
1631 EbcDebugSignalException (
1632 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1633 EXCEPTION_FLAG_FATAL
,
1636 return EFI_UNSUPPORTED
;
1639 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1644 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1645 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1648 // Advance the instruction pointer
1657 IN VM_CONTEXT
*VmPtr
1661 Routine Description:
1663 Execute the EBC MOVREL instruction.
1664 Dest <- Ip + ImmData
1668 VmPtr - pointer to a VM context
1676 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1689 // Get the opcode and operands byte so we can get R1 and R2
1691 Opcode
= GETOPCODE (VmPtr
);
1692 Operands
= GETOPERANDS (VmPtr
);
1695 // Get the Operand 1 index (16-bit) if present
1697 if (Operands
& MOVI_M_IMMDATA
) {
1698 Index16
= VmReadIndex16 (VmPtr
, 2);
1705 // Get the immediate data.
1707 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1708 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1710 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1711 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1713 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1714 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1720 EbcDebugSignalException (
1721 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1722 EXCEPTION_FLAG_FATAL
,
1725 return EFI_UNSUPPORTED
;
1728 // Compute the value and write back the result
1730 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1731 if (!OPERAND1_INDIRECT (Operands
)) {
1733 // Check for illegal combination of operand1 direct with immediate data
1735 if (Operands
& MOVI_M_IMMDATA
) {
1736 EbcDebugSignalException (
1737 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1738 EXCEPTION_FLAG_FATAL
,
1741 return EFI_UNSUPPORTED
;
1744 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1747 // Get the address = [Rx] + Index16
1748 // Write back the result. Always a natural size write, since
1749 // we're talking addresses here.
1751 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1752 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1755 // Advance the instruction pointer
1764 IN VM_CONTEXT
*VmPtr
1768 Routine Description:
1770 Execute the EBC MOVsnw instruction. This instruction loads a signed
1771 natural value from memory or register to another memory or register. On
1772 32-bit machines, the value gets sign-extended to 64 bits if the destination
1777 VmPtr - pointer to a VM context
1785 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1787 0:7 1=>operand1 index present
1788 0:6 1=>operand2 index present
1800 // Get the opcode and operand bytes
1802 Opcode
= GETOPCODE (VmPtr
);
1803 Operands
= GETOPERANDS (VmPtr
);
1805 Op1Index
= Op2Index
= 0;
1808 // Get the indexes if present.
1811 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1812 if (OPERAND1_INDIRECT (Operands
)) {
1813 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1816 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1818 EbcDebugSignalException (
1819 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1820 EXCEPTION_FLAG_FATAL
,
1823 return EFI_UNSUPPORTED
;
1826 Size
+= sizeof (UINT16
);
1829 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1830 if (OPERAND2_INDIRECT (Operands
)) {
1831 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1833 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1836 Size
+= sizeof (UINT16
);
1839 // Get the data from the source.
1841 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1842 if (OPERAND2_INDIRECT (Operands
)) {
1843 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1846 // Now write back the result.
1848 if (!OPERAND1_INDIRECT (Operands
)) {
1849 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1851 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1854 // Advance the instruction pointer
1863 IN VM_CONTEXT
*VmPtr
1867 Routine Description:
1869 Execute the EBC MOVsnw instruction. This instruction loads a signed
1870 natural value from memory or register to another memory or register. On
1871 32-bit machines, the value gets sign-extended to 64 bits if the destination
1876 VmPtr - pointer to a VM context
1884 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1886 0:7 1=>operand1 index present
1887 0:6 1=>operand2 index present
1899 // Get the opcode and operand bytes
1901 Opcode
= GETOPCODE (VmPtr
);
1902 Operands
= GETOPERANDS (VmPtr
);
1904 Op1Index
= Op2Index
= 0;
1907 // Get the indexes if present.
1910 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1911 if (OPERAND1_INDIRECT (Operands
)) {
1912 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1915 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1917 EbcDebugSignalException (
1918 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1919 EXCEPTION_FLAG_FATAL
,
1922 return EFI_UNSUPPORTED
;
1925 Size
+= sizeof (UINT32
);
1928 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1929 if (OPERAND2_INDIRECT (Operands
)) {
1930 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1932 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1935 Size
+= sizeof (UINT32
);
1938 // Get the data from the source.
1940 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1941 if (OPERAND2_INDIRECT (Operands
)) {
1942 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1945 // Now write back the result.
1947 if (!OPERAND1_INDIRECT (Operands
)) {
1948 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1950 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1953 // Advance the instruction pointer
1962 IN VM_CONTEXT
*VmPtr
1966 Routine Description:
1967 Execute the EBC PUSHn instruction
1970 VmPtr - pointer to a VM context
1976 PUSHn {@}R1 {Index16|Immed16}
1986 // Get opcode and operands
1988 Opcode
= GETOPCODE (VmPtr
);
1989 Operands
= GETOPERANDS (VmPtr
);
1992 // Get index if present
1994 if (Opcode
& PUSHPOP_M_IMMDATA
) {
1995 if (OPERAND1_INDIRECT (Operands
)) {
1996 Index16
= VmReadIndex16 (VmPtr
, 2);
1998 Index16
= VmReadImmed16 (VmPtr
, 2);
2007 // Get the data to push
2009 if (OPERAND1_INDIRECT (Operands
)) {
2010 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2012 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2015 // Adjust the stack down.
2017 VmPtr
->R
[0] -= sizeof (UINTN
);
2018 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2025 IN VM_CONTEXT
*VmPtr
2029 Routine Description:
2030 Execute the EBC PUSH instruction
2033 VmPtr - pointer to a VM context
2039 PUSH[32|64] {@}R1 {Index16|Immed16}
2050 // Get opcode and operands
2052 Opcode
= GETOPCODE (VmPtr
);
2053 Operands
= GETOPERANDS (VmPtr
);
2055 // Get immediate index if present, then advance the IP.
2057 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2058 if (OPERAND1_INDIRECT (Operands
)) {
2059 Index16
= VmReadIndex16 (VmPtr
, 2);
2061 Index16
= VmReadImmed16 (VmPtr
, 2);
2070 // Get the data to push
2072 if (Opcode
& PUSHPOP_M_64
) {
2073 if (OPERAND1_INDIRECT (Operands
)) {
2074 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2076 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2079 // Adjust the stack down, then write back the data
2081 VmPtr
->R
[0] -= sizeof (UINT64
);
2082 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2087 if (OPERAND1_INDIRECT (Operands
)) {
2088 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2090 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2093 // Adjust the stack down and write the data
2095 VmPtr
->R
[0] -= sizeof (UINT32
);
2096 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2105 IN VM_CONTEXT
*VmPtr
2109 Routine Description:
2110 Execute the EBC POPn instruction
2113 VmPtr - pointer to a VM context
2119 POPn {@}R1 {Index16|Immed16}
2129 // Get opcode and operands
2131 Opcode
= GETOPCODE (VmPtr
);
2132 Operands
= GETOPERANDS (VmPtr
);
2134 // Get immediate data if present, and advance the IP
2136 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2137 if (OPERAND1_INDIRECT (Operands
)) {
2138 Index16
= VmReadIndex16 (VmPtr
, 2);
2140 Index16
= VmReadImmed16 (VmPtr
, 2);
2149 // Read the data off the stack, then adjust the stack pointer
2151 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2152 VmPtr
->R
[0] += sizeof (UINTN
);
2154 // Do the write-back
2156 if (OPERAND1_INDIRECT (Operands
)) {
2157 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2159 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2168 IN VM_CONTEXT
*VmPtr
2172 Routine Description:
2173 Execute the EBC POP instruction
2176 VmPtr - pointer to a VM context
2182 POP {@}R1 {Index16|Immed16}
2193 // Get opcode and operands
2195 Opcode
= GETOPCODE (VmPtr
);
2196 Operands
= GETOPERANDS (VmPtr
);
2198 // Get immediate data if present, and advance the IP.
2200 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2201 if (OPERAND1_INDIRECT (Operands
)) {
2202 Index16
= VmReadIndex16 (VmPtr
, 2);
2204 Index16
= VmReadImmed16 (VmPtr
, 2);
2213 // Get the data off the stack, then write it to the appropriate location
2215 if (Opcode
& PUSHPOP_M_64
) {
2217 // Read the data off the stack, then adjust the stack pointer
2219 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2220 VmPtr
->R
[0] += sizeof (UINT64
);
2222 // Do the write-back
2224 if (OPERAND1_INDIRECT (Operands
)) {
2225 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2227 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2231 // 32-bit pop. Read it off the stack and adjust the stack pointer
2233 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2234 VmPtr
->R
[0] += sizeof (UINT32
);
2236 // Do the write-back
2238 if (OPERAND1_INDIRECT (Operands
)) {
2239 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2241 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2251 IN VM_CONTEXT
*VmPtr
2255 Routine Description:
2256 Implements the EBC CALL instruction.
2261 CALL32 {@}R1 {Immed32|Index32}
2263 CALLEX16 {@}R1 {Immed32}
2265 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2268 VmPtr - pointer to a VM context.
2283 // Get opcode and operands
2285 Opcode
= GETOPCODE (VmPtr
);
2286 Operands
= GETOPERANDS (VmPtr
);
2288 // Assign these as well to avoid compiler warnings
2293 FramePtr
= VmPtr
->FramePtr
;
2295 // Determine the instruction size, and get immediate data if present
2297 if (Opcode
& OPCODE_M_IMMDATA
) {
2298 if (Opcode
& OPCODE_M_IMMDATA64
) {
2299 Immed64
= VmReadImmed64 (VmPtr
, 2);
2303 // If register operand is indirect, then the immediate data is an index
2305 if (OPERAND1_INDIRECT (Operands
)) {
2306 Immed32
= VmReadIndex32 (VmPtr
, 2);
2308 Immed32
= VmReadImmed32 (VmPtr
, 2);
2317 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2318 // put our return address and frame pointer on the VM stack.
2320 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2322 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2323 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2325 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2328 // If 64-bit data, then absolute jump only
2330 if (Opcode
& OPCODE_M_IMMDATA64
) {
2332 // Native or EBC call?
2334 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2335 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2338 // Call external function, get the return value, and advance the IP
2340 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2344 // Get the register data. If operand1 == 0, then ignore register and
2345 // take immediate data as relative or absolute address.
2346 // Compiler should take care of upper bits if 32-bit machine.
2348 if (OPERAND1_REGNUM (Operands
) != 0) {
2349 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2352 // Get final address
2354 if (OPERAND1_INDIRECT (Operands
)) {
2355 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2360 // Now determine if external call, and then if relative or absolute
2362 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2364 // EBC call. Relative or absolute? If relative, then it's relative to the
2365 // start of the next instruction.
2367 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2368 VmPtr
->Ip
+= Immed64
+ Size
;
2370 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2374 // Native call. Relative or absolute?
2376 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2377 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2379 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2383 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2394 IN VM_CONTEXT
*VmPtr
2398 Routine Description:
2399 Execute the EBC RET instruction
2402 VmPtr - pointer to a VM context
2413 // If we're at the top of the stack, then simply set the done
2416 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2417 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2420 // Pull the return address off the VM app's stack and set the IP
2423 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2424 EbcDebugSignalException (
2425 EXCEPT_EBC_ALIGNMENT_CHECK
,
2426 EXCEPTION_FLAG_FATAL
,
2431 // Restore the IP and frame pointer from the stack
2433 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2435 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2445 IN VM_CONTEXT
*VmPtr
2449 Routine Description:
2450 Execute the EBC CMP instruction
2453 VmPtr - pointer to a VM context
2459 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2472 // Get opcode and operands
2474 Opcode
= GETOPCODE (VmPtr
);
2475 Operands
= GETOPERANDS (VmPtr
);
2477 // Get the register data we're going to compare to
2479 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2481 // Get immediate data
2483 if (Opcode
& OPCODE_M_IMMDATA
) {
2484 if (OPERAND2_INDIRECT (Operands
)) {
2485 Index16
= VmReadIndex16 (VmPtr
, 2);
2487 Index16
= VmReadImmed16 (VmPtr
, 2);
2498 if (OPERAND2_INDIRECT (Operands
)) {
2499 if (Opcode
& OPCODE_M_64BIT
) {
2500 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2503 // 32-bit operations. 0-extend the values for all cases.
2505 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2508 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2511 // Now do the compare
2514 if (Opcode
& OPCODE_M_64BIT
) {
2518 switch (Opcode
& OPCODE_M_OPCODE
) {
2537 case OPCODE_CMPULTE
:
2538 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2543 case OPCODE_CMPUGTE
:
2544 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2556 switch (Opcode
& OPCODE_M_OPCODE
) {
2558 if ((INT32
) Op1
== (INT32
) Op2
) {
2564 if ((INT32
) Op1
<= (INT32
) Op2
) {
2570 if ((INT32
) Op1
>= (INT32
) Op2
) {
2575 case OPCODE_CMPULTE
:
2576 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2581 case OPCODE_CMPUGTE
:
2582 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2592 // Now set the flag accordingly for the comparison
2595 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2597 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2609 IN VM_CONTEXT
*VmPtr
2613 Routine Description:
2614 Execute the EBC CMPI instruction
2617 VmPtr - pointer to a VM context
2623 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2636 // Get opcode and operands
2638 Opcode
= GETOPCODE (VmPtr
);
2639 Operands
= GETOPERANDS (VmPtr
);
2642 // Get operand1 index if present
2645 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2646 Index16
= VmReadIndex16 (VmPtr
, 2);
2652 // Get operand1 data we're going to compare to
2654 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2655 if (OPERAND1_INDIRECT (Operands
)) {
2657 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2659 if (Opcode
& OPCODE_M_CMPI64
) {
2660 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2662 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2666 // Better not have been an index with direct. That is, CMPI R1 Index,...
2669 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2670 EbcDebugSignalException (
2671 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2672 EXCEPTION_FLAG_ERROR
,
2676 return EFI_UNSUPPORTED
;
2680 // Get immediate data -- 16- or 32-bit sign extended
2682 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2683 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2687 // 16-bit immediate data. Sign extend always.
2689 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2693 // Now do the compare
2696 if (Opcode
& OPCODE_M_CMPI64
) {
2698 // 64 bit comparison
2700 switch (Opcode
& OPCODE_M_OPCODE
) {
2702 if (Op1
== (INT64
) Op2
) {
2707 case OPCODE_CMPILTE
:
2708 if (Op1
<= (INT64
) Op2
) {
2713 case OPCODE_CMPIGTE
:
2714 if (Op1
>= (INT64
) Op2
) {
2719 case OPCODE_CMPIULTE
:
2720 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2725 case OPCODE_CMPIUGTE
:
2726 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2736 // 32-bit comparisons
2738 switch (Opcode
& OPCODE_M_OPCODE
) {
2740 if ((INT32
) Op1
== Op2
) {
2745 case OPCODE_CMPILTE
:
2746 if ((INT32
) Op1
<= Op2
) {
2751 case OPCODE_CMPIGTE
:
2752 if ((INT32
) Op1
>= Op2
) {
2757 case OPCODE_CMPIULTE
:
2758 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2763 case OPCODE_CMPIUGTE
:
2764 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2774 // Now set the flag accordingly for the comparison
2777 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2779 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2791 IN VM_CONTEXT
*VmPtr
,
2797 Routine Description:
2798 Execute the EBC NOT instruction
2801 VmPtr - pointer to a VM context
2802 Op1 - Operand 1 from the instruction
2803 Op2 - Operand 2 from the instruction
2809 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2819 IN VM_CONTEXT
*VmPtr
,
2825 Routine Description:
2826 Execute the EBC NEG instruction
2829 VmPtr - pointer to a VM context
2830 Op1 - Operand 1 from the instruction
2831 Op2 - Operand 2 from the instruction
2837 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2847 IN VM_CONTEXT
*VmPtr
,
2853 Routine Description:
2855 Execute the EBC ADD instruction
2858 VmPtr - pointer to a VM context
2859 Op1 - Operand 1 from the instruction
2860 Op2 - Operand 2 from the instruction
2866 ADD[32|64] {@}R1, {@}R2 {Index16}
2876 IN VM_CONTEXT
*VmPtr
,
2882 Routine Description:
2883 Execute the EBC SUB instruction
2886 VmPtr - pointer to a VM context
2887 Op1 - Operand 1 from the instruction
2888 Op2 - Operand 2 from the instruction
2895 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2899 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2900 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2902 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2909 IN VM_CONTEXT
*VmPtr
,
2915 Routine Description:
2917 Execute the EBC MUL instruction
2920 VmPtr - pointer to a VM context
2921 Op1 - Operand 1 from the instruction
2922 Op2 - Operand 2 from the instruction
2928 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2932 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2933 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2935 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2942 IN VM_CONTEXT
*VmPtr
,
2948 Routine Description:
2949 Execute the EBC MULU instruction
2952 VmPtr - pointer to a VM context
2953 Op1 - Operand 1 from the instruction
2954 Op2 - Operand 2 from the instruction
2957 (unsigned)Op1 * (unsigned)Op2
2960 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2964 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2965 return MultU64x64 (Op1
, Op2
);
2967 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2974 IN VM_CONTEXT
*VmPtr
,
2980 Routine Description:
2982 Execute the EBC DIV instruction
2985 VmPtr - pointer to a VM context
2986 Op1 - Operand 1 from the instruction
2987 Op2 - Operand 2 from the instruction
2993 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
3000 // Check for divide-by-0
3003 EbcDebugSignalException (
3004 EXCEPT_EBC_DIVIDE_ERROR
,
3005 EXCEPTION_FLAG_FATAL
,
3011 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3012 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
3014 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3022 IN VM_CONTEXT
*VmPtr
,
3028 Routine Description:
3029 Execute the EBC DIVU instruction
3032 VmPtr - pointer to a VM context
3033 Op1 - Operand 1 from the instruction
3034 Op2 - Operand 2 from the instruction
3037 (unsigned)Op1 / (unsigned)Op2
3040 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3047 // Check for divide-by-0
3050 EbcDebugSignalException (
3051 EXCEPT_EBC_DIVIDE_ERROR
,
3052 EXCEPTION_FLAG_FATAL
,
3058 // Get the destination register
3060 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3061 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
3063 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3071 IN VM_CONTEXT
*VmPtr
,
3077 Routine Description:
3078 Execute the EBC MOD instruction
3081 VmPtr - pointer to a VM context
3082 Op1 - Operand 1 from the instruction
3083 Op2 - Operand 2 from the instruction
3089 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3096 // Check for divide-by-0
3099 EbcDebugSignalException (
3100 EXCEPT_EBC_DIVIDE_ERROR
,
3101 EXCEPTION_FLAG_FATAL
,
3106 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
3114 IN VM_CONTEXT
*VmPtr
,
3120 Routine Description:
3121 Execute the EBC MODU instruction
3124 VmPtr - pointer to a VM context
3125 Op1 - Operand 1 from the instruction
3126 Op2 - Operand 2 from the instruction
3129 Op1 UNSIGNED_MODULUS Op2
3132 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3139 // Check for divide-by-0
3142 EbcDebugSignalException (
3143 EXCEPT_EBC_DIVIDE_ERROR
,
3144 EXCEPTION_FLAG_FATAL
,
3149 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
3157 IN VM_CONTEXT
*VmPtr
,
3163 Routine Description:
3164 Execute the EBC AND instruction
3167 VmPtr - pointer to a VM context
3168 Op1 - Operand 1 from the instruction
3169 Op2 - Operand 2 from the instruction
3175 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3185 IN VM_CONTEXT
*VmPtr
,
3191 Routine Description:
3192 Execute the EBC OR instruction
3195 VmPtr - pointer to a VM context
3196 Op1 - Operand 1 from the instruction
3197 Op2 - Operand 2 from the instruction
3203 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3213 IN VM_CONTEXT
*VmPtr
,
3219 Routine Description:
3220 Execute the EBC XOR instruction
3223 VmPtr - pointer to a VM context
3224 Op1 - Operand 1 from the instruction
3225 Op2 - Operand 2 from the instruction
3231 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3241 IN VM_CONTEXT
*VmPtr
,
3247 Routine Description:
3249 Execute the EBC SHL shift left instruction
3252 VmPtr - pointer to a VM context
3253 Op1 - Operand 1 from the instruction
3254 Op2 - Operand 2 from the instruction
3260 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3264 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3265 return LShiftU64 (Op1
, (UINTN
)Op2
);
3267 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3274 IN VM_CONTEXT
*VmPtr
,
3280 Routine Description:
3281 Execute the EBC SHR instruction
3284 VmPtr - pointer to a VM context
3285 Op1 - Operand 1 from the instruction
3286 Op2 - Operand 2 from the instruction
3289 Op1 >> Op2 (unsigned operands)
3292 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3296 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3297 return RShiftU64 (Op1
, (UINTN
)Op2
);
3299 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3306 IN VM_CONTEXT
*VmPtr
,
3312 Routine Description:
3313 Execute the EBC ASHR instruction
3316 VmPtr - pointer to a VM context
3317 Op1 - Operand 1 from the instruction
3318 Op2 - Operand 2 from the instruction
3324 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3328 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3329 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3331 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3338 IN VM_CONTEXT
*VmPtr
,
3344 Routine Description:
3345 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3348 VmPtr - pointer to a VM context
3349 Op1 - Operand 1 from the instruction
3350 Op2 - Operand 2 from the instruction
3356 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3364 // Convert to byte, then return as 64-bit signed value to let compiler
3365 // sign-extend the value
3368 Data64
= (INT64
) Data8
;
3370 return (UINT64
) Data64
;
3376 IN VM_CONTEXT
*VmPtr
,
3382 Routine Description:
3383 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3386 VmPtr - pointer to a VM context
3387 Op1 - Operand 1 from the instruction
3388 Op2 - Operand 2 from the instruction
3394 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3402 // Convert to word, then return as 64-bit signed value to let compiler
3403 // sign-extend the value
3405 Data16
= (INT16
) Op2
;
3406 Data64
= (INT64
) Data16
;
3408 return (UINT64
) Data64
;
3411 // Execute the EBC EXTNDD instruction.
3413 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3414 // EXTNDD Dest, Source
3416 // Operation: Dest <- SignExtended((DWORD)Source))
3421 IN VM_CONTEXT
*VmPtr
,
3427 Routine Description:
3428 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3431 VmPtr - pointer to a VM context
3432 Op1 - Operand 1 from the instruction
3433 Op2 - Operand 2 from the instruction
3439 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3447 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3448 // sign-extend the value
3450 Data32
= (INT32
) Op2
;
3451 Data64
= (INT64
) Data32
;
3453 return (UINT64
) Data64
;
3458 ExecuteSignedDataManip (
3459 IN VM_CONTEXT
*VmPtr
3463 // Just call the data manipulation function with a flag indicating this
3464 // is a signed operation.
3466 return ExecuteDataManip (VmPtr
, TRUE
);
3471 ExecuteUnsignedDataManip (
3472 IN VM_CONTEXT
*VmPtr
3476 // Just call the data manipulation function with a flag indicating this
3477 // is not a signed operation.
3479 return ExecuteDataManip (VmPtr
, FALSE
);
3485 IN VM_CONTEXT
*VmPtr
,
3486 IN BOOLEAN IsSignedOp
3490 Routine Description:
3491 Execute all the EBC data manipulation instructions.
3492 Since the EBC data manipulation instructions all have the same basic form,
3493 they can share the code that does the fetch of operands and the write-back
3494 of the result. This function performs the fetch of the operands (even if
3495 both are not needed to be fetched, like NOT instruction), dispatches to the
3496 appropriate subfunction, then writes back the returned result.
3499 VmPtr - pointer to VM context
3505 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3517 // Get opcode and operands
3519 Opcode
= GETOPCODE (VmPtr
);
3520 Operands
= GETOPERANDS (VmPtr
);
3523 // Determine if we have immediate data by the opcode
3525 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3527 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3529 if (OPERAND2_INDIRECT (Operands
)) {
3530 Index16
= VmReadIndex16 (VmPtr
, 2);
3532 Index16
= VmReadImmed16 (VmPtr
, 2);
3541 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3543 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3544 if (OPERAND2_INDIRECT (Operands
)) {
3546 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3548 if (Opcode
& DATAMANIP_M_64
) {
3549 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3552 // Read as signed value where appropriate.
3555 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3557 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3561 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3563 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3565 Op2
= (UINT64
) ((UINT32
) Op2
);
3570 // Get operand1 (destination and sometimes also an actual operand)
3573 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3574 if (OPERAND1_INDIRECT (Operands
)) {
3575 if (Opcode
& DATAMANIP_M_64
) {
3576 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3579 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3581 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3585 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3587 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3589 Op1
= (UINT64
) ((UINT32
) Op1
);
3594 // Dispatch to the computation function
3596 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3597 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3599 EbcDebugSignalException (
3600 EXCEPT_EBC_INVALID_OPCODE
,
3601 EXCEPTION_FLAG_ERROR
,
3605 // Advance and return
3608 return EFI_UNSUPPORTED
;
3610 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3613 // Write back the result.
3615 if (OPERAND1_INDIRECT (Operands
)) {
3616 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3617 if (Opcode
& DATAMANIP_M_64
) {
3618 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3620 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3624 // Storage back to a register. Write back, clearing upper bits (as per
3625 // the specification) if 32-bit operation.
3627 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3628 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3629 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3633 // Advance the instruction pointer
3642 IN VM_CONTEXT
*VmPtr
3646 Routine Description:
3647 Execute the EBC LOADSP instruction
3650 VmPtr - pointer to a VM context
3665 Operands
= GETOPERANDS (VmPtr
);
3670 switch (OPERAND1_REGNUM (Operands
)) {
3676 // Spec states that this instruction will not modify reserved bits in
3677 // the flags register.
3679 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3683 EbcDebugSignalException (
3684 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3685 EXCEPTION_FLAG_WARNING
,
3689 return EFI_UNSUPPORTED
;
3699 IN VM_CONTEXT
*VmPtr
3703 Routine Description:
3704 Execute the EBC STORESP instruction
3707 VmPtr - pointer to a VM context
3713 STORESP Rx, FLAGS|IP
3722 Operands
= GETOPERANDS (VmPtr
);
3727 switch (OPERAND2_REGNUM (Operands
)) {
3733 // Retrieve the value in the flags register, then clear reserved bits
3735 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3739 // Get IP -- address of following instruction
3742 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3746 EbcDebugSignalException (
3747 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3748 EXCEPTION_FLAG_WARNING
,
3752 return EFI_UNSUPPORTED
;
3763 IN VM_CONTEXT
*VmPtr
,
3764 IN UINT32 CodeOffset
3768 Routine Description:
3769 Decode a 16-bit index to determine the offset. Given an index value:
3772 b14:12 - number of bits in this index assigned to natural units (=a)
3773 ba:11 - constant units = C
3774 b0:a - natural units = N
3776 Given this info, the offset can be computed by:
3777 offset = sign_bit * (C + N * sizeof(UINTN))
3779 Max offset is achieved with index = 0x7FFF giving an offset of
3780 0x27B (32-bit machine) or 0x477 (64-bit machine).
3781 Min offset is achieved with index =
3784 VmPtr - pointer to VM context
3785 CodeOffset - offset from IP of the location of the 16-bit index to decode
3800 // First read the index from the code stream
3802 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3805 // Get the mask for N. First get the number of bits from the index.
3807 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3810 // Scale it for 16-bit indexes
3815 // Now using the number of bits, create a mask.
3817 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3820 // Now using the mask, extract N from the lower bits of the index.
3822 N
= (INT16
) (Index
&~Mask
);
3827 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3829 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3834 if (Index
& 0x8000) {
3836 // Do it the hard way to work around a bogus compiler warning
3838 // Offset = -1 * Offset;
3840 Offset
= (INT16
) ((INT32
) Offset
* -1);
3849 IN VM_CONTEXT
*VmPtr
,
3850 IN UINT32 CodeOffset
3854 Routine Description:
3855 Decode a 32-bit index to determine the offset.
3858 VmPtr - pointer to VM context
3859 CodeOffset - offset from IP of the location of the 32-bit index to decode
3862 Converted index per EBC VM specification
3873 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3876 // Get the mask for N. First get the number of bits from the index.
3878 NBits
= (Index
& 0x70000000) >> 28;
3881 // Scale it for 32-bit indexes
3886 // Now using the number of bits, create a mask.
3888 Mask
= (INT32
)~0 << NBits
;
3891 // Now using the mask, extract N from the lower bits of the index.
3898 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3900 Offset
= N
* sizeof (UINTN
) + C
;
3905 if (Index
& 0x80000000) {
3906 Offset
= Offset
* -1;
3915 IN VM_CONTEXT
*VmPtr
,
3916 IN UINT32 CodeOffset
3920 Routine Description:
3921 Decode a 64-bit index to determine the offset.
3924 VmPtr - pointer to VM context
3925 CodeOffset - offset from IP of the location of the 64-bit index to decode
3928 Converted index per EBC VM specification
3939 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3942 // Get the mask for N. First get the number of bits from the index.
3944 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3947 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3949 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3952 // Now using the number of bits, create a mask.
3954 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3957 // Now using the mask, extract N from the lower bits of the index.
3964 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3966 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3971 if (Index
& 0x8000000000000000ULL
) {
3972 Offset
= MultS64x64 (Offset
, -1);
3981 IN VM_CONTEXT
*VmPtr
,
3987 Routine Description:
3988 The following VmWriteMem? routines are called by the EBC data
3989 movement instructions that write to memory. Since these writes
3990 may be to the stack, which looks like (high address on top) this,
3992 [EBC entry point arguments]
3996 we need to detect all attempts to write to the EBC entry point argument
3997 stack area and adjust the address (which will initially point into the
3998 VM stack) to point into the EBC entry point arguments.
4001 VmPtr - pointer to a VM context
4002 Addr - adddress to write to
4003 Data - value to write to Addr
4011 // Convert the address if it's in the stack gap
4013 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4014 *(UINT8
*) Addr
= Data
;
4021 IN VM_CONTEXT
*VmPtr
,
4029 // Convert the address if it's in the stack gap
4031 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4034 // Do a simple write if aligned
4036 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4037 *(UINT16
*) Addr
= Data
;
4040 // Write as two bytes
4043 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4048 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4061 IN VM_CONTEXT
*VmPtr
,
4069 // Convert the address if it's in the stack gap
4071 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4074 // Do a simple write if aligned
4076 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4077 *(UINT32
*) Addr
= Data
;
4080 // Write as two words
4083 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4088 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4100 IN VM_CONTEXT
*VmPtr
,
4109 // Convert the address if it's in the stack gap
4111 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4114 // Do a simple write if aligned
4116 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4117 *(UINT64
*) Addr
= Data
;
4120 // Write as two 32-bit words
4123 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4128 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4129 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4141 IN VM_CONTEXT
*VmPtr
,
4149 Status
= EFI_SUCCESS
;
4152 // Convert the address if it's in the stack gap
4154 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4157 // Do a simple write if aligned
4159 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4160 *(UINTN
*) Addr
= Data
;
4162 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4164 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4166 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4176 IN VM_CONTEXT
*VmPtr
,
4181 Routine Description:
4183 The following VmReadImmed routines are called by the EBC execute
4184 functions to read EBC immediate values from the code stream.
4185 Since we can't assume alignment, each tries to read in the biggest
4186 chunks size available, but will revert to smaller reads if necessary.
4189 VmPtr - pointer to a VM context
4190 Offset - offset from IP of the code bytes to read.
4193 Signed data of the requested size from the specified address.
4198 // Simply return the data in flat memory space
4200 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4206 IN VM_CONTEXT
*VmPtr
,
4211 // Read direct if aligned
4213 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4214 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4217 // All code word reads should be aligned
4219 EbcDebugSignalException (
4220 EXCEPT_EBC_ALIGNMENT_CHECK
,
4221 EXCEPTION_FLAG_WARNING
,
4226 // Return unaligned data
4228 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4234 IN VM_CONTEXT
*VmPtr
,
4241 // Read direct if aligned
4243 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4244 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4247 // Return unaligned data
4249 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4250 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4257 IN VM_CONTEXT
*VmPtr
,
4266 // Read direct if aligned
4268 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4269 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4272 // Return unaligned data.
4274 Ptr
= (UINT8
*) &Data64
;
4275 Data32
= VmReadCode32 (VmPtr
, Offset
);
4276 *(UINT32
*) Ptr
= Data32
;
4277 Ptr
+= sizeof (Data32
);
4278 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4279 *(UINT32
*) Ptr
= Data32
;
4286 IN VM_CONTEXT
*VmPtr
,
4291 Routine Description:
4292 The following VmReadCode() routines provide the ability to read raw
4293 unsigned data from the code stream.
4296 VmPtr - pointer to VM context
4297 Offset - offset from current IP to the raw data to read.
4300 The raw unsigned 16-bit value from the code stream.
4305 // Read direct if aligned
4307 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4308 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4311 // All code word reads should be aligned
4313 EbcDebugSignalException (
4314 EXCEPT_EBC_ALIGNMENT_CHECK
,
4315 EXCEPTION_FLAG_WARNING
,
4320 // Return unaligned data
4322 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4328 IN VM_CONTEXT
*VmPtr
,
4334 // Read direct if aligned
4336 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4337 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4340 // Return unaligned data
4342 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4343 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4350 IN VM_CONTEXT
*VmPtr
,
4359 // Read direct if aligned
4361 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4362 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4365 // Return unaligned data.
4367 Ptr
= (UINT8
*) &Data64
;
4368 Data32
= VmReadCode32 (VmPtr
, Offset
);
4369 *(UINT32
*) Ptr
= Data32
;
4370 Ptr
+= sizeof (Data32
);
4371 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4372 *(UINT32
*) Ptr
= Data32
;
4379 IN VM_CONTEXT
*VmPtr
,
4384 // Convert the address if it's in the stack gap
4386 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4388 // Simply return the data in flat memory space
4390 return * (UINT8
*) Addr
;
4396 IN VM_CONTEXT
*VmPtr
,
4401 // Convert the address if it's in the stack gap
4403 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4405 // Read direct if aligned
4407 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4408 return * (UINT16
*) Addr
;
4411 // Return unaligned data
4413 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4419 IN VM_CONTEXT
*VmPtr
,
4426 // Convert the address if it's in the stack gap
4428 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4430 // Read direct if aligned
4432 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4433 return * (UINT32
*) Addr
;
4436 // Return unaligned data
4438 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4439 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4446 IN VM_CONTEXT
*VmPtr
,
4454 // Convert the address if it's in the stack gap
4456 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4459 // Read direct if aligned
4461 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4462 return * (UINT64
*) Addr
;
4465 // Return unaligned data. Assume little endian.
4467 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4468 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4469 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4476 IN VM_CONTEXT
*VmPtr
,
4481 Routine Description:
4483 Given an address that EBC is going to read from or write to, return
4484 an appropriate address that accounts for a gap in the stack.
4486 The stack for this application looks like this (high addr on top)
4487 [EBC entry point arguments]
4491 The EBC assumes that its arguments are at the top of its stack, which
4492 is where the VM stack is really. Therefore if the EBC does memory
4493 accesses into the VM stack area, then we need to convert the address
4494 to point to the EBC entry point arguments area. Do this here.
4498 VmPtr - pointer to VM context
4499 Addr - address of interest
4503 The unchanged address if it's not in the VM stack region. Otherwise,
4504 adjust for the stack gap and return the modified address.
4508 if ((Addr
>= VmPtr
->LowStackTop
) && (Addr
< VmPtr
->HighStackBottom
)) {
4510 // In the stack gap -- now make sure it's not in the VM itself, which
4511 // would be the case if it's accessing VM register contents.
4513 if ((Addr
< (UINTN
) VmPtr
) || (Addr
> (UINTN
) VmPtr
+ sizeof (VM_CONTEXT
))) {
4514 VmPtr
->LastAddrConverted
= Addr
;
4515 VmPtr
->LastAddrConvertedValue
= Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4516 return Addr
- VmPtr
->LowStackTop
+ VmPtr
->HighStackBottom
;
4526 IN VM_CONTEXT
*VmPtr
,
4531 Routine Description:
4532 Read a natural value from memory. May or may not be aligned.
4535 VmPtr - current VM context
4536 Addr - the address to read from
4539 The natural value at address Addr.
4548 // Convert the address if it's in the stack gap
4550 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4552 // Read direct if aligned
4554 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4555 return * (UINTN
*) Addr
;
4558 // Return unaligned data
4561 FromPtr
= (UINT8
*) Addr
;
4562 ToPtr
= (UINT8
*) &Data
;
4564 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4578 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));