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"
27 // Define some useful data size constants to allow switch statements based on
28 // size of operands or data.
30 #define DATA_SIZE_INVALID 0
32 #define DATA_SIZE_16 2
33 #define DATA_SIZE_32 4
34 #define DATA_SIZE_64 8
35 #define DATA_SIZE_N 48 // 4 or 8
37 // Structure we'll use to dispatch opcodes to execute functions.
40 EFI_STATUS (*ExecuteFunction
) (IN VM_CONTEXT
* VmPtr
);
46 (*DATA_MANIP_EXEC_FUNCTION
) (
47 IN VM_CONTEXT
* VmPtr
,
104 IN VM_CONTEXT
*VmPtr
,
111 IN VM_CONTEXT
*VmPtr
,
119 IN VM_CONTEXT
*VmPtr
,
127 IN VM_CONTEXT
*VmPtr
,
135 IN VM_CONTEXT
*VmPtr
,
142 IN VM_CONTEXT
*VmPtr
,
149 IN VM_CONTEXT
*VmPtr
,
156 IN VM_CONTEXT
*VmPtr
,
163 IN VM_CONTEXT
*VmPtr
,
170 IN VM_CONTEXT
*VmPtr
,
177 IN VM_CONTEXT
*VmPtr
,
184 IN VM_CONTEXT
*VmPtr
,
191 IN VM_CONTEXT
*VmPtr
,
192 IN BOOLEAN IsSignedOperation
196 // Functions that execute VM opcodes
290 ExecuteSignedDataManip (
296 ExecuteUnsignedDataManip (
325 // Data manipulation subfunctions
330 IN VM_CONTEXT
*VmPtr
,
338 IN VM_CONTEXT
*VmPtr
,
346 IN VM_CONTEXT
*VmPtr
,
354 IN VM_CONTEXT
*VmPtr
,
362 IN VM_CONTEXT
*VmPtr
,
370 IN VM_CONTEXT
*VmPtr
,
378 IN VM_CONTEXT
*VmPtr
,
386 IN VM_CONTEXT
*VmPtr
,
394 IN VM_CONTEXT
*VmPtr
,
402 IN VM_CONTEXT
*VmPtr
,
410 IN VM_CONTEXT
*VmPtr
,
418 IN VM_CONTEXT
*VmPtr
,
426 IN VM_CONTEXT
*VmPtr
,
434 IN VM_CONTEXT
*VmPtr
,
442 IN VM_CONTEXT
*VmPtr
,
450 IN VM_CONTEXT
*VmPtr
,
458 IN VM_CONTEXT
*VmPtr
,
466 IN VM_CONTEXT
*VmPtr
,
474 IN VM_CONTEXT
*VmPtr
,
480 // Once we retrieve the operands for the data manipulation instructions,
481 // call these functions to perform the operation.
483 static CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable
[] = {
505 static CONST VM_TABLE_ENTRY mVmOpcodeTable
[] = {
506 { ExecuteBREAK
}, // opcode 0x00
507 { ExecuteJMP
}, // opcode 0x01
508 { ExecuteJMP8
}, // opcode 0x02
509 { ExecuteCALL
}, // opcode 0x03
510 { ExecuteRET
}, // opcode 0x04
511 { ExecuteCMP
}, // opcode 0x05 CMPeq
512 { ExecuteCMP
}, // opcode 0x06 CMPlte
513 { ExecuteCMP
}, // opcode 0x07 CMPgte
514 { ExecuteCMP
}, // opcode 0x08 CMPulte
515 { ExecuteCMP
}, // opcode 0x09 CMPugte
516 { ExecuteUnsignedDataManip
}, // opcode 0x0A NOT
517 { ExecuteSignedDataManip
}, // opcode 0x0B NEG
518 { ExecuteSignedDataManip
}, // opcode 0x0C ADD
519 { ExecuteSignedDataManip
}, // opcode 0x0D SUB
520 { ExecuteSignedDataManip
}, // opcode 0x0E MUL
521 { ExecuteUnsignedDataManip
}, // opcode 0x0F MULU
522 { ExecuteSignedDataManip
}, // opcode 0x10 DIV
523 { ExecuteUnsignedDataManip
}, // opcode 0x11 DIVU
524 { ExecuteSignedDataManip
}, // opcode 0x12 MOD
525 { ExecuteUnsignedDataManip
}, // opcode 0x13 MODU
526 { ExecuteUnsignedDataManip
}, // opcode 0x14 AND
527 { ExecuteUnsignedDataManip
}, // opcode 0x15 OR
528 { ExecuteUnsignedDataManip
}, // opcode 0x16 XOR
529 { ExecuteUnsignedDataManip
}, // opcode 0x17 SHL
530 { ExecuteUnsignedDataManip
}, // opcode 0x18 SHR
531 { ExecuteSignedDataManip
}, // opcode 0x19 ASHR
532 { ExecuteUnsignedDataManip
}, // opcode 0x1A EXTNDB
533 { ExecuteUnsignedDataManip
}, // opcode 0x1B EXTNDW
534 { ExecuteUnsignedDataManip
}, // opcode 0x1C EXTNDD
535 { ExecuteMOVxx
}, // opcode 0x1D MOVBW
536 { ExecuteMOVxx
}, // opcode 0x1E MOVWW
537 { ExecuteMOVxx
}, // opcode 0x1F MOVDW
538 { ExecuteMOVxx
}, // opcode 0x20 MOVQW
539 { ExecuteMOVxx
}, // opcode 0x21 MOVBD
540 { ExecuteMOVxx
}, // opcode 0x22 MOVWD
541 { ExecuteMOVxx
}, // opcode 0x23 MOVDD
542 { ExecuteMOVxx
}, // opcode 0x24 MOVQD
543 { ExecuteMOVsnw
}, // opcode 0x25 MOVsnw
544 { ExecuteMOVsnd
}, // opcode 0x26 MOVsnd
545 { NULL
}, // opcode 0x27
546 { ExecuteMOVxx
}, // opcode 0x28 MOVqq
547 { ExecuteLOADSP
}, // opcode 0x29 LOADSP SP1, R2
548 { ExecuteSTORESP
}, // opcode 0x2A STORESP R1, SP2
549 { ExecutePUSH
}, // opcode 0x2B PUSH {@}R1 [imm16]
550 { ExecutePOP
}, // opcode 0x2C POP {@}R1 [imm16]
551 { ExecuteCMPI
}, // opcode 0x2D CMPIEQ
552 { ExecuteCMPI
}, // opcode 0x2E CMPILTE
553 { ExecuteCMPI
}, // opcode 0x2F CMPIGTE
554 { ExecuteCMPI
}, // opcode 0x30 CMPIULTE
555 { ExecuteCMPI
}, // opcode 0x31 CMPIUGTE
556 { ExecuteMOVxx
}, // opcode 0x32 MOVN
557 { ExecuteMOVxx
}, // opcode 0x33 MOVND
558 { NULL
}, // opcode 0x34
559 { ExecutePUSHn
}, // opcode 0x35
560 { ExecutePOPn
}, // opcode 0x36
561 { ExecuteMOVI
}, // opcode 0x37 - mov immediate data
562 { ExecuteMOVIn
}, // opcode 0x38 - mov immediate natural
563 { ExecuteMOVREL
} // opcode 0x39 - move data relative to PC
567 // Length of JMP instructions, depending on upper two bits of opcode.
569 static CONST UINT8 mJMPLen
[] = { 2, 2, 6, 10 };
572 // Simple Debugger Protocol GUID
574 EFI_GUID mEbcSimpleDebuggerProtocolGuid
= EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID
;
577 EbcExecuteInstructions (
578 IN EFI_EBC_VM_TEST_PROTOCOL
*This
,
579 IN VM_CONTEXT
*VmPtr
,
580 IN OUT UINTN
*InstructionCount
586 Given a pointer to a new VM context, execute one or more instructions. This
587 function is only used for test purposes via the EBC VM test protocol.
591 This - pointer to protocol interface
592 VmPtr - pointer to a VM context
593 InstructionCount - how many instructions to execute. 0 if don't count.
604 UINTN InstructionsLeft
;
605 UINTN SavedInstructionCount
;
607 Status
= EFI_SUCCESS
;
609 if (*InstructionCount
== 0) {
610 InstructionsLeft
= 1;
612 InstructionsLeft
= *InstructionCount
;
615 SavedInstructionCount
= *InstructionCount
;
616 *InstructionCount
= 0;
619 // Index into the opcode table using the opcode byte for this instruction.
620 // This gives you the execute function, which we first test for null, then
621 // call it if it's not null.
623 while (InstructionsLeft
!= 0) {
624 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction
;
625 if (ExecFunc
== (UINTN
) NULL
) {
626 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
627 return EFI_UNSUPPORTED
;
629 mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction (VmPtr
);
630 *InstructionCount
= *InstructionCount
+ 1;
634 // Decrement counter if applicable
636 if (SavedInstructionCount
!= 0) {
652 Execute an EBC image from an entry point or from a published protocol.
656 VmPtr - pointer to prepared VM context.
665 UINT8 StackCorrupted
;
667 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL
*EbcSimpleDebugger
;
670 EbcSimpleDebugger
= NULL
;
671 Status
= EFI_SUCCESS
;
675 // Make sure the magic value has been put on the stack before we got here.
677 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
681 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
684 // Try to get the debug support for EBC
687 Status
= gBS
->LocateProtocol (
688 &mEbcSimpleDebuggerProtocolGuid
,
690 (VOID
**) &EbcSimpleDebugger
692 if (EFI_ERROR (Status
)) {
693 EbcSimpleDebugger
= NULL
;
698 // Save the start IP for debug. For example, if we take an exception we
699 // can print out the location of the exception relative to the entry point,
700 // which could then be used in a disassembly listing to find the problem.
702 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
705 // We'll wait for this flag to know when we're done. The RET
706 // instruction sets it if it runs out of stack.
708 VmPtr
->StopFlags
= 0;
709 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
711 // If we've found a simple debugger protocol, call it
714 if (EbcSimpleDebugger
!= NULL
) {
715 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
720 // Verify the opcode is in range. Otherwise generate an exception.
722 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
723 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
724 Status
= EFI_UNSUPPORTED
;
728 // Use the opcode bits to index into the opcode dispatch table. If the
729 // function pointer is null then generate an exception.
731 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
732 if (ExecFunc
== (UINTN
) NULL
) {
733 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
734 Status
= EFI_UNSUPPORTED
;
738 // The EBC VM is a strongly ordered processor, so perform a fence operation before
739 // and after each instruction is executed.
743 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
748 // If the step flag is set, signal an exception and continue. We don't
749 // clear it here. Assuming the debugger is responsible for clearing it.
751 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
752 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
755 // Make sure stack has not been corrupted. Only report it once though.
757 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
758 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
761 if (!StackCorrupted
&& ((UINT64
)VmPtr
->R
[0] <= (UINT64
)(UINTN
) VmPtr
->StackTop
)) {
762 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
,
1128 // System call, which there are none, so NOP it.
1134 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1135 // "offset from self" pointer to the EBC entry point.
1136 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1139 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1140 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1141 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1144 // Now create a new thunk
1146 EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1149 // Finally replace the EBC entry point memory with the thunk address
1151 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1155 // Compiler setting version per value in R7
1158 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1160 // Check compiler version against VM version?
1165 // Unhandled break code. Signal exception.
1168 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1181 IN VM_CONTEXT
*VmPtr
1185 Routine Description:
1186 Execute the JMP instruction
1189 VmPtr - pointer to VM context
1195 JMP64{cs|cc} Immed64
1196 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1199 b0.7 - immediate data present
1200 b0.6 - 1 = 64 bit immediate data
1201 0 = 32 bit immediate data
1202 b1.7 - 1 = conditional
1203 b1.6 1 = CS (condition set)
1204 0 = CC (condition clear)
1205 b1.4 1 = relative address
1206 0 = absolute address
1207 b1.3 1 = operand1 indirect
1214 UINT8 ConditionFlag
;
1221 Operand
= GETOPERANDS (VmPtr
);
1222 Opcode
= GETOPCODE (VmPtr
);
1225 // Get instruction length from the opcode. The upper two bits are used here
1226 // to index into the length array.
1228 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1231 // Decode instruction conditions
1232 // If we haven't met the condition, then simply advance the IP and return.
1234 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1235 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1236 if (Operand
& CONDITION_M_CONDITIONAL
) {
1237 if (CompareSet
!= ConditionFlag
) {
1243 // Check for 64-bit form and do it right away since it's the most
1244 // straight-forward form.
1246 if (Opcode
& OPCODE_M_IMMDATA64
) {
1248 // Double check for immediate-data, which is required. If not there,
1249 // then signal an exception
1251 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1252 EbcDebugSignalException (
1253 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1254 EXCEPTION_FLAG_ERROR
,
1257 return EFI_UNSUPPORTED
;
1260 // 64-bit immediate data is full address. Read the immediate data,
1261 // check for alignment, and jump absolute.
1263 Data64
= VmReadImmed64 (VmPtr
, 2);
1264 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1265 EbcDebugSignalException (
1266 EXCEPT_EBC_ALIGNMENT_CHECK
,
1267 EXCEPTION_FLAG_FATAL
,
1271 return EFI_UNSUPPORTED
;
1275 // Take jump -- relative or absolute
1277 if (Operand
& JMP_M_RELATIVE
) {
1278 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1280 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1287 // Get the index if there is one. May be either an index, or an immediate
1288 // offset depending on indirect operand.
1289 // JMP32 @R1 Index32 -- immediate data is an index
1290 // JMP32 R1 Immed32 -- immedate data is an offset
1292 if (Opcode
& OPCODE_M_IMMDATA
) {
1293 if (OPERAND1_INDIRECT (Operand
)) {
1294 Index32
= VmReadIndex32 (VmPtr
, 2);
1296 Index32
= VmReadImmed32 (VmPtr
, 2);
1302 // Get the register data. If R == 0, then special case where it's ignored.
1304 if (OPERAND1_REGNUM (Operand
) == 0) {
1307 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1312 if (OPERAND1_INDIRECT (Operand
)) {
1314 // Form: JMP32 @Rx {Index32}
1316 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1317 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1318 EbcDebugSignalException (
1319 EXCEPT_EBC_ALIGNMENT_CHECK
,
1320 EXCEPTION_FLAG_FATAL
,
1324 return EFI_UNSUPPORTED
;
1327 if (Operand
& JMP_M_RELATIVE
) {
1328 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1330 VmPtr
->Ip
= (VMIP
) Addr
;
1334 // Form: JMP32 Rx {Immed32}
1336 Addr
= (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
;
1360 IN VM_CONTEXT
*VmPtr
1364 Routine Description:
1365 Execute the EBC JMP8 instruction
1368 VmPtr - pointer to a VM context
1374 JMP8{cs|cc} Offset/2
1379 UINT8 ConditionFlag
;
1384 // Decode instruction.
1386 Opcode
= GETOPCODE (VmPtr
);
1387 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1388 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1391 // If we haven't met the condition, then simply advance the IP and return
1393 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1394 if (CompareSet
!= ConditionFlag
) {
1400 // Get the offset from the instruction stream. It's relative to the
1401 // following instruction, and divided by 2.
1403 Offset
= VmReadImmed8 (VmPtr
, 1);
1405 // Want to check for offset == -2 and then raise an exception?
1407 VmPtr
->Ip
+= (Offset
* 2) + 2;
1414 IN VM_CONTEXT
*VmPtr
1418 Routine Description:
1420 Execute the EBC MOVI
1424 VmPtr - pointer to a VM context
1432 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1434 First variable character specifies the move size
1435 Second variable character specifies size of the immediate data
1437 Sign-extend the immediate data to the size of the operation, and zero-extend
1438 if storing to a register.
1440 Operand1 direct with index/immed is invalid.
1453 // Get the opcode and operands byte so we can get R1 and R2
1455 Opcode
= GETOPCODE (VmPtr
);
1456 Operands
= GETOPERANDS (VmPtr
);
1459 // Get the index (16-bit) if present
1461 if (Operands
& MOVI_M_IMMDATA
) {
1462 Index16
= VmReadIndex16 (VmPtr
, 2);
1469 // Extract the immediate data. Sign-extend always.
1471 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1472 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1474 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1475 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1477 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1478 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1484 EbcDebugSignalException (
1485 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1486 EXCEPTION_FLAG_FATAL
,
1489 return EFI_UNSUPPORTED
;
1492 // Now write back the result
1494 if (!OPERAND1_INDIRECT (Operands
)) {
1496 // Operand1 direct. Make sure it didn't have an index.
1498 if (Operands
& MOVI_M_IMMDATA
) {
1499 EbcDebugSignalException (
1500 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1501 EXCEPTION_FLAG_FATAL
,
1504 return EFI_UNSUPPORTED
;
1507 // Writing directly to a register. Clear unused bits.
1509 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1510 Mask64
= 0x000000FF;
1511 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1512 Mask64
= 0x0000FFFF;
1513 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1514 Mask64
= 0x00000000FFFFFFFF;
1516 Mask64
= (UINT64
)~0;
1519 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1522 // Get the address then write back based on size of the move
1524 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1525 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1526 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1527 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1528 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1529 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1530 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1532 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1536 // Advance the instruction pointer
1545 IN VM_CONTEXT
*VmPtr
1549 Routine Description:
1551 Execute the EBC MOV immediate natural. This instruction moves an immediate
1552 index value into a register or memory location.
1556 VmPtr - pointer to a VM context
1564 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1578 // Get the opcode and operands byte so we can get R1 and R2
1580 Opcode
= GETOPCODE (VmPtr
);
1581 Operands
= GETOPERANDS (VmPtr
);
1584 // Get the operand1 index (16-bit) if present
1586 if (Operands
& MOVI_M_IMMDATA
) {
1587 Index16
= VmReadIndex16 (VmPtr
, 2);
1594 // Extract the immediate data and convert to a 64-bit index.
1596 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1597 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1598 ImmedIndex64
= (INT64
) ImmedIndex16
;
1600 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1601 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1602 ImmedIndex64
= (INT64
) ImmedIndex32
;
1604 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1605 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1611 EbcDebugSignalException (
1612 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1613 EXCEPTION_FLAG_FATAL
,
1616 return EFI_UNSUPPORTED
;
1619 // Now write back the result
1621 if (!OPERAND1_INDIRECT (Operands
)) {
1623 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1626 if (Operands
& MOVI_M_IMMDATA
) {
1627 EbcDebugSignalException (
1628 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1629 EXCEPTION_FLAG_FATAL
,
1632 return EFI_UNSUPPORTED
;
1635 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1640 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1641 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1644 // Advance the instruction pointer
1653 IN VM_CONTEXT
*VmPtr
1657 Routine Description:
1659 Execute the EBC MOVREL instruction.
1660 Dest <- Ip + ImmData
1664 VmPtr - pointer to a VM context
1672 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1685 // Get the opcode and operands byte so we can get R1 and R2
1687 Opcode
= GETOPCODE (VmPtr
);
1688 Operands
= GETOPERANDS (VmPtr
);
1691 // Get the Operand 1 index (16-bit) if present
1693 if (Operands
& MOVI_M_IMMDATA
) {
1694 Index16
= VmReadIndex16 (VmPtr
, 2);
1701 // Get the immediate data.
1703 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1704 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1706 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1707 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1709 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1710 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1716 EbcDebugSignalException (
1717 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1718 EXCEPTION_FLAG_FATAL
,
1721 return EFI_UNSUPPORTED
;
1724 // Compute the value and write back the result
1726 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1727 if (!OPERAND1_INDIRECT (Operands
)) {
1729 // Check for illegal combination of operand1 direct with immediate data
1731 if (Operands
& MOVI_M_IMMDATA
) {
1732 EbcDebugSignalException (
1733 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1734 EXCEPTION_FLAG_FATAL
,
1737 return EFI_UNSUPPORTED
;
1740 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1743 // Get the address = [Rx] + Index16
1744 // Write back the result. Always a natural size write, since
1745 // we're talking addresses here.
1747 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1748 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1751 // Advance the instruction pointer
1760 IN VM_CONTEXT
*VmPtr
1764 Routine Description:
1766 Execute the EBC MOVsnw instruction. This instruction loads a signed
1767 natural value from memory or register to another memory or register. On
1768 32-bit machines, the value gets sign-extended to 64 bits if the destination
1773 VmPtr - pointer to a VM context
1781 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1783 0:7 1=>operand1 index present
1784 0:6 1=>operand2 index present
1796 // Get the opcode and operand bytes
1798 Opcode
= GETOPCODE (VmPtr
);
1799 Operands
= GETOPERANDS (VmPtr
);
1801 Op1Index
= Op2Index
= 0;
1804 // Get the indexes if present.
1807 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1808 if (OPERAND1_INDIRECT (Operands
)) {
1809 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1812 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1814 EbcDebugSignalException (
1815 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1816 EXCEPTION_FLAG_FATAL
,
1819 return EFI_UNSUPPORTED
;
1822 Size
+= sizeof (UINT16
);
1825 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1826 if (OPERAND2_INDIRECT (Operands
)) {
1827 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1829 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1832 Size
+= sizeof (UINT16
);
1835 // Get the data from the source.
1837 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1838 if (OPERAND2_INDIRECT (Operands
)) {
1839 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1842 // Now write back the result.
1844 if (!OPERAND1_INDIRECT (Operands
)) {
1845 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1847 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1850 // Advance the instruction pointer
1859 IN VM_CONTEXT
*VmPtr
1863 Routine Description:
1865 Execute the EBC MOVsnw instruction. This instruction loads a signed
1866 natural value from memory or register to another memory or register. On
1867 32-bit machines, the value gets sign-extended to 64 bits if the destination
1872 VmPtr - pointer to a VM context
1880 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1882 0:7 1=>operand1 index present
1883 0:6 1=>operand2 index present
1895 // Get the opcode and operand bytes
1897 Opcode
= GETOPCODE (VmPtr
);
1898 Operands
= GETOPERANDS (VmPtr
);
1900 Op1Index
= Op2Index
= 0;
1903 // Get the indexes if present.
1906 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1907 if (OPERAND1_INDIRECT (Operands
)) {
1908 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1911 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1913 EbcDebugSignalException (
1914 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1915 EXCEPTION_FLAG_FATAL
,
1918 return EFI_UNSUPPORTED
;
1921 Size
+= sizeof (UINT32
);
1924 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1925 if (OPERAND2_INDIRECT (Operands
)) {
1926 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1928 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1931 Size
+= sizeof (UINT32
);
1934 // Get the data from the source.
1936 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1937 if (OPERAND2_INDIRECT (Operands
)) {
1938 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1941 // Now write back the result.
1943 if (!OPERAND1_INDIRECT (Operands
)) {
1944 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1946 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1949 // Advance the instruction pointer
1958 IN VM_CONTEXT
*VmPtr
1962 Routine Description:
1963 Execute the EBC PUSHn instruction
1966 VmPtr - pointer to a VM context
1972 PUSHn {@}R1 {Index16|Immed16}
1982 // Get opcode and operands
1984 Opcode
= GETOPCODE (VmPtr
);
1985 Operands
= GETOPERANDS (VmPtr
);
1988 // Get index if present
1990 if (Opcode
& PUSHPOP_M_IMMDATA
) {
1991 if (OPERAND1_INDIRECT (Operands
)) {
1992 Index16
= VmReadIndex16 (VmPtr
, 2);
1994 Index16
= VmReadImmed16 (VmPtr
, 2);
2003 // Get the data to push
2005 if (OPERAND1_INDIRECT (Operands
)) {
2006 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2008 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
2011 // Adjust the stack down.
2013 VmPtr
->R
[0] -= sizeof (UINTN
);
2014 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
2021 IN VM_CONTEXT
*VmPtr
2025 Routine Description:
2026 Execute the EBC PUSH instruction
2029 VmPtr - pointer to a VM context
2035 PUSH[32|64] {@}R1 {Index16|Immed16}
2046 // Get opcode and operands
2048 Opcode
= GETOPCODE (VmPtr
);
2049 Operands
= GETOPERANDS (VmPtr
);
2051 // Get immediate index if present, then advance the IP.
2053 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2054 if (OPERAND1_INDIRECT (Operands
)) {
2055 Index16
= VmReadIndex16 (VmPtr
, 2);
2057 Index16
= VmReadImmed16 (VmPtr
, 2);
2066 // Get the data to push
2068 if (Opcode
& PUSHPOP_M_64
) {
2069 if (OPERAND1_INDIRECT (Operands
)) {
2070 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2072 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2075 // Adjust the stack down, then write back the data
2077 VmPtr
->R
[0] -= sizeof (UINT64
);
2078 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
2083 if (OPERAND1_INDIRECT (Operands
)) {
2084 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
2086 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
2089 // Adjust the stack down and write the data
2091 VmPtr
->R
[0] -= sizeof (UINT32
);
2092 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
2101 IN VM_CONTEXT
*VmPtr
2105 Routine Description:
2106 Execute the EBC POPn instruction
2109 VmPtr - pointer to a VM context
2115 POPn {@}R1 {Index16|Immed16}
2125 // Get opcode and operands
2127 Opcode
= GETOPCODE (VmPtr
);
2128 Operands
= GETOPERANDS (VmPtr
);
2130 // Get immediate data if present, and advance the IP
2132 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2133 if (OPERAND1_INDIRECT (Operands
)) {
2134 Index16
= VmReadIndex16 (VmPtr
, 2);
2136 Index16
= VmReadImmed16 (VmPtr
, 2);
2145 // Read the data off the stack, then adjust the stack pointer
2147 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2148 VmPtr
->R
[0] += sizeof (UINTN
);
2150 // Do the write-back
2152 if (OPERAND1_INDIRECT (Operands
)) {
2153 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2155 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2164 IN VM_CONTEXT
*VmPtr
2168 Routine Description:
2169 Execute the EBC POP instruction
2172 VmPtr - pointer to a VM context
2178 POP {@}R1 {Index16|Immed16}
2189 // Get opcode and operands
2191 Opcode
= GETOPCODE (VmPtr
);
2192 Operands
= GETOPERANDS (VmPtr
);
2194 // Get immediate data if present, and advance the IP.
2196 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2197 if (OPERAND1_INDIRECT (Operands
)) {
2198 Index16
= VmReadIndex16 (VmPtr
, 2);
2200 Index16
= VmReadImmed16 (VmPtr
, 2);
2209 // Get the data off the stack, then write it to the appropriate location
2211 if (Opcode
& PUSHPOP_M_64
) {
2213 // Read the data off the stack, then adjust the stack pointer
2215 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2216 VmPtr
->R
[0] += sizeof (UINT64
);
2218 // Do the write-back
2220 if (OPERAND1_INDIRECT (Operands
)) {
2221 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2223 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2227 // 32-bit pop. Read it off the stack and adjust the stack pointer
2229 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2230 VmPtr
->R
[0] += sizeof (UINT32
);
2232 // Do the write-back
2234 if (OPERAND1_INDIRECT (Operands
)) {
2235 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2237 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2247 IN VM_CONTEXT
*VmPtr
2251 Routine Description:
2252 Implements the EBC CALL instruction.
2257 CALL32 {@}R1 {Immed32|Index32}
2259 CALLEX16 {@}R1 {Immed32}
2261 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2264 VmPtr - pointer to a VM context.
2279 // Get opcode and operands
2281 Opcode
= GETOPCODE (VmPtr
);
2282 Operands
= GETOPERANDS (VmPtr
);
2284 // Assign these as well to avoid compiler warnings
2289 FramePtr
= VmPtr
->FramePtr
;
2291 // Determine the instruction size, and get immediate data if present
2293 if (Opcode
& OPCODE_M_IMMDATA
) {
2294 if (Opcode
& OPCODE_M_IMMDATA64
) {
2295 Immed64
= VmReadImmed64 (VmPtr
, 2);
2299 // If register operand is indirect, then the immediate data is an index
2301 if (OPERAND1_INDIRECT (Operands
)) {
2302 Immed32
= VmReadIndex32 (VmPtr
, 2);
2304 Immed32
= VmReadImmed32 (VmPtr
, 2);
2313 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2314 // put our return address and frame pointer on the VM stack.
2316 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2318 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2319 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2321 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2324 // If 64-bit data, then absolute jump only
2326 if (Opcode
& OPCODE_M_IMMDATA64
) {
2328 // Native or EBC call?
2330 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2331 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2334 // Call external function, get the return value, and advance the IP
2336 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2340 // Get the register data. If operand1 == 0, then ignore register and
2341 // take immediate data as relative or absolute address.
2342 // Compiler should take care of upper bits if 32-bit machine.
2344 if (OPERAND1_REGNUM (Operands
) != 0) {
2345 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2348 // Get final address
2350 if (OPERAND1_INDIRECT (Operands
)) {
2351 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2356 // Now determine if external call, and then if relative or absolute
2358 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2360 // EBC call. Relative or absolute? If relative, then it's relative to the
2361 // start of the next instruction.
2363 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2364 VmPtr
->Ip
+= Immed64
+ Size
;
2366 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2370 // Native call. Relative or absolute?
2372 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2373 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2375 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2379 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2390 IN VM_CONTEXT
*VmPtr
2394 Routine Description:
2395 Execute the EBC RET instruction
2398 VmPtr - pointer to a VM context
2409 // If we're at the top of the stack, then simply set the done
2412 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2413 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2416 // Pull the return address off the VM app's stack and set the IP
2419 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2420 EbcDebugSignalException (
2421 EXCEPT_EBC_ALIGNMENT_CHECK
,
2422 EXCEPTION_FLAG_FATAL
,
2427 // Restore the IP and frame pointer from the stack
2429 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2431 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2441 IN VM_CONTEXT
*VmPtr
2445 Routine Description:
2446 Execute the EBC CMP instruction
2449 VmPtr - pointer to a VM context
2455 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2468 // Get opcode and operands
2470 Opcode
= GETOPCODE (VmPtr
);
2471 Operands
= GETOPERANDS (VmPtr
);
2473 // Get the register data we're going to compare to
2475 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2477 // Get immediate data
2479 if (Opcode
& OPCODE_M_IMMDATA
) {
2480 if (OPERAND2_INDIRECT (Operands
)) {
2481 Index16
= VmReadIndex16 (VmPtr
, 2);
2483 Index16
= VmReadImmed16 (VmPtr
, 2);
2494 if (OPERAND2_INDIRECT (Operands
)) {
2495 if (Opcode
& OPCODE_M_64BIT
) {
2496 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2499 // 32-bit operations. 0-extend the values for all cases.
2501 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2504 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2507 // Now do the compare
2510 if (Opcode
& OPCODE_M_64BIT
) {
2514 switch (Opcode
& OPCODE_M_OPCODE
) {
2533 case OPCODE_CMPULTE
:
2534 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2539 case OPCODE_CMPUGTE
:
2540 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2552 switch (Opcode
& OPCODE_M_OPCODE
) {
2554 if ((INT32
) Op1
== (INT32
) Op2
) {
2560 if ((INT32
) Op1
<= (INT32
) Op2
) {
2566 if ((INT32
) Op1
>= (INT32
) Op2
) {
2571 case OPCODE_CMPULTE
:
2572 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2577 case OPCODE_CMPUGTE
:
2578 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2588 // Now set the flag accordingly for the comparison
2591 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2593 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2605 IN VM_CONTEXT
*VmPtr
2609 Routine Description:
2610 Execute the EBC CMPI instruction
2613 VmPtr - pointer to a VM context
2619 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2632 // Get opcode and operands
2634 Opcode
= GETOPCODE (VmPtr
);
2635 Operands
= GETOPERANDS (VmPtr
);
2638 // Get operand1 index if present
2641 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2642 Index16
= VmReadIndex16 (VmPtr
, 2);
2648 // Get operand1 data we're going to compare to
2650 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2651 if (OPERAND1_INDIRECT (Operands
)) {
2653 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2655 if (Opcode
& OPCODE_M_CMPI64
) {
2656 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2658 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2662 // Better not have been an index with direct. That is, CMPI R1 Index,...
2665 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2666 EbcDebugSignalException (
2667 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2668 EXCEPTION_FLAG_ERROR
,
2672 return EFI_UNSUPPORTED
;
2676 // Get immediate data -- 16- or 32-bit sign extended
2678 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2679 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2683 // 16-bit immediate data. Sign extend always.
2685 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2689 // Now do the compare
2692 if (Opcode
& OPCODE_M_CMPI64
) {
2694 // 64 bit comparison
2696 switch (Opcode
& OPCODE_M_OPCODE
) {
2698 if (Op1
== (INT64
) Op2
) {
2703 case OPCODE_CMPILTE
:
2704 if (Op1
<= (INT64
) Op2
) {
2709 case OPCODE_CMPIGTE
:
2710 if (Op1
>= (INT64
) Op2
) {
2715 case OPCODE_CMPIULTE
:
2716 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2721 case OPCODE_CMPIUGTE
:
2722 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2732 // 32-bit comparisons
2734 switch (Opcode
& OPCODE_M_OPCODE
) {
2736 if ((INT32
) Op1
== Op2
) {
2741 case OPCODE_CMPILTE
:
2742 if ((INT32
) Op1
<= Op2
) {
2747 case OPCODE_CMPIGTE
:
2748 if ((INT32
) Op1
>= Op2
) {
2753 case OPCODE_CMPIULTE
:
2754 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2759 case OPCODE_CMPIUGTE
:
2760 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2770 // Now set the flag accordingly for the comparison
2773 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2775 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2787 IN VM_CONTEXT
*VmPtr
,
2793 Routine Description:
2794 Execute the EBC NOT instruction
2797 VmPtr - pointer to a VM context
2798 Op1 - Operand 1 from the instruction
2799 Op2 - Operand 2 from the instruction
2805 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2815 IN VM_CONTEXT
*VmPtr
,
2821 Routine Description:
2822 Execute the EBC NEG instruction
2825 VmPtr - pointer to a VM context
2826 Op1 - Operand 1 from the instruction
2827 Op2 - Operand 2 from the instruction
2833 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2843 IN VM_CONTEXT
*VmPtr
,
2849 Routine Description:
2851 Execute the EBC ADD instruction
2854 VmPtr - pointer to a VM context
2855 Op1 - Operand 1 from the instruction
2856 Op2 - Operand 2 from the instruction
2862 ADD[32|64] {@}R1, {@}R2 {Index16}
2872 IN VM_CONTEXT
*VmPtr
,
2878 Routine Description:
2879 Execute the EBC SUB instruction
2882 VmPtr - pointer to a VM context
2883 Op1 - Operand 1 from the instruction
2884 Op2 - Operand 2 from the instruction
2891 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2895 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2896 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2898 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2905 IN VM_CONTEXT
*VmPtr
,
2911 Routine Description:
2913 Execute the EBC MUL instruction
2916 VmPtr - pointer to a VM context
2917 Op1 - Operand 1 from the instruction
2918 Op2 - Operand 2 from the instruction
2924 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2928 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2929 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2931 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2938 IN VM_CONTEXT
*VmPtr
,
2944 Routine Description:
2945 Execute the EBC MULU instruction
2948 VmPtr - pointer to a VM context
2949 Op1 - Operand 1 from the instruction
2950 Op2 - Operand 2 from the instruction
2953 (unsigned)Op1 * (unsigned)Op2
2956 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2960 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2961 return MultU64x64 (Op1
, Op2
);
2963 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2970 IN VM_CONTEXT
*VmPtr
,
2976 Routine Description:
2978 Execute the EBC DIV instruction
2981 VmPtr - pointer to a VM context
2982 Op1 - Operand 1 from the instruction
2983 Op2 - Operand 2 from the instruction
2989 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
2996 // Check for divide-by-0
2999 EbcDebugSignalException (
3000 EXCEPT_EBC_DIVIDE_ERROR
,
3001 EXCEPTION_FLAG_FATAL
,
3007 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3008 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
3010 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
3018 IN VM_CONTEXT
*VmPtr
,
3024 Routine Description:
3025 Execute the EBC DIVU instruction
3028 VmPtr - pointer to a VM context
3029 Op1 - Operand 1 from the instruction
3030 Op2 - Operand 2 from the instruction
3033 (unsigned)Op1 / (unsigned)Op2
3036 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3043 // Check for divide-by-0
3046 EbcDebugSignalException (
3047 EXCEPT_EBC_DIVIDE_ERROR
,
3048 EXCEPTION_FLAG_FATAL
,
3054 // Get the destination register
3056 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3057 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
3059 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
3067 IN VM_CONTEXT
*VmPtr
,
3073 Routine Description:
3074 Execute the EBC MOD instruction
3077 VmPtr - pointer to a VM context
3078 Op1 - Operand 1 from the instruction
3079 Op2 - Operand 2 from the instruction
3085 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3092 // Check for divide-by-0
3095 EbcDebugSignalException (
3096 EXCEPT_EBC_DIVIDE_ERROR
,
3097 EXCEPTION_FLAG_FATAL
,
3102 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
3110 IN VM_CONTEXT
*VmPtr
,
3116 Routine Description:
3117 Execute the EBC MODU instruction
3120 VmPtr - pointer to a VM context
3121 Op1 - Operand 1 from the instruction
3122 Op2 - Operand 2 from the instruction
3125 Op1 UNSIGNED_MODULUS Op2
3128 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3135 // Check for divide-by-0
3138 EbcDebugSignalException (
3139 EXCEPT_EBC_DIVIDE_ERROR
,
3140 EXCEPTION_FLAG_FATAL
,
3145 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
3153 IN VM_CONTEXT
*VmPtr
,
3159 Routine Description:
3160 Execute the EBC AND instruction
3163 VmPtr - pointer to a VM context
3164 Op1 - Operand 1 from the instruction
3165 Op2 - Operand 2 from the instruction
3171 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3181 IN VM_CONTEXT
*VmPtr
,
3187 Routine Description:
3188 Execute the EBC OR instruction
3191 VmPtr - pointer to a VM context
3192 Op1 - Operand 1 from the instruction
3193 Op2 - Operand 2 from the instruction
3199 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3209 IN VM_CONTEXT
*VmPtr
,
3215 Routine Description:
3216 Execute the EBC XOR instruction
3219 VmPtr - pointer to a VM context
3220 Op1 - Operand 1 from the instruction
3221 Op2 - Operand 2 from the instruction
3227 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3237 IN VM_CONTEXT
*VmPtr
,
3243 Routine Description:
3245 Execute the EBC SHL shift left instruction
3248 VmPtr - pointer to a VM context
3249 Op1 - Operand 1 from the instruction
3250 Op2 - Operand 2 from the instruction
3256 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3260 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3261 return LShiftU64 (Op1
, (UINTN
)Op2
);
3263 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3270 IN VM_CONTEXT
*VmPtr
,
3276 Routine Description:
3277 Execute the EBC SHR instruction
3280 VmPtr - pointer to a VM context
3281 Op1 - Operand 1 from the instruction
3282 Op2 - Operand 2 from the instruction
3285 Op1 >> Op2 (unsigned operands)
3288 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3292 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3293 return RShiftU64 (Op1
, (UINTN
)Op2
);
3295 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3302 IN VM_CONTEXT
*VmPtr
,
3308 Routine Description:
3309 Execute the EBC ASHR instruction
3312 VmPtr - pointer to a VM context
3313 Op1 - Operand 1 from the instruction
3314 Op2 - Operand 2 from the instruction
3320 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3324 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3325 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3327 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3334 IN VM_CONTEXT
*VmPtr
,
3340 Routine Description:
3341 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3344 VmPtr - pointer to a VM context
3345 Op1 - Operand 1 from the instruction
3346 Op2 - Operand 2 from the instruction
3352 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3360 // Convert to byte, then return as 64-bit signed value to let compiler
3361 // sign-extend the value
3364 Data64
= (INT64
) Data8
;
3366 return (UINT64
) Data64
;
3372 IN VM_CONTEXT
*VmPtr
,
3378 Routine Description:
3379 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3382 VmPtr - pointer to a VM context
3383 Op1 - Operand 1 from the instruction
3384 Op2 - Operand 2 from the instruction
3390 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3398 // Convert to word, then return as 64-bit signed value to let compiler
3399 // sign-extend the value
3401 Data16
= (INT16
) Op2
;
3402 Data64
= (INT64
) Data16
;
3404 return (UINT64
) Data64
;
3407 // Execute the EBC EXTNDD instruction.
3409 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3410 // EXTNDD Dest, Source
3412 // Operation: Dest <- SignExtended((DWORD)Source))
3417 IN VM_CONTEXT
*VmPtr
,
3423 Routine Description:
3424 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3427 VmPtr - pointer to a VM context
3428 Op1 - Operand 1 from the instruction
3429 Op2 - Operand 2 from the instruction
3435 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3443 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3444 // sign-extend the value
3446 Data32
= (INT32
) Op2
;
3447 Data64
= (INT64
) Data32
;
3449 return (UINT64
) Data64
;
3454 ExecuteSignedDataManip (
3455 IN VM_CONTEXT
*VmPtr
3459 // Just call the data manipulation function with a flag indicating this
3460 // is a signed operation.
3462 return ExecuteDataManip (VmPtr
, TRUE
);
3467 ExecuteUnsignedDataManip (
3468 IN VM_CONTEXT
*VmPtr
3472 // Just call the data manipulation function with a flag indicating this
3473 // is not a signed operation.
3475 return ExecuteDataManip (VmPtr
, FALSE
);
3481 IN VM_CONTEXT
*VmPtr
,
3482 IN BOOLEAN IsSignedOp
3486 Routine Description:
3487 Execute all the EBC data manipulation instructions.
3488 Since the EBC data manipulation instructions all have the same basic form,
3489 they can share the code that does the fetch of operands and the write-back
3490 of the result. This function performs the fetch of the operands (even if
3491 both are not needed to be fetched, like NOT instruction), dispatches to the
3492 appropriate subfunction, then writes back the returned result.
3495 VmPtr - pointer to VM context
3501 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3513 // Get opcode and operands
3515 Opcode
= GETOPCODE (VmPtr
);
3516 Operands
= GETOPERANDS (VmPtr
);
3519 // Determine if we have immediate data by the opcode
3521 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3523 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3525 if (OPERAND2_INDIRECT (Operands
)) {
3526 Index16
= VmReadIndex16 (VmPtr
, 2);
3528 Index16
= VmReadImmed16 (VmPtr
, 2);
3537 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3539 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3540 if (OPERAND2_INDIRECT (Operands
)) {
3542 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3544 if (Opcode
& DATAMANIP_M_64
) {
3545 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3548 // Read as signed value where appropriate.
3551 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3553 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3557 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3559 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3561 Op2
= (UINT64
) ((UINT32
) Op2
);
3566 // Get operand1 (destination and sometimes also an actual operand)
3569 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3570 if (OPERAND1_INDIRECT (Operands
)) {
3571 if (Opcode
& DATAMANIP_M_64
) {
3572 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3575 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3577 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3581 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3583 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3585 Op1
= (UINT64
) ((UINT32
) Op1
);
3590 // Dispatch to the computation function
3592 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3593 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3595 EbcDebugSignalException (
3596 EXCEPT_EBC_INVALID_OPCODE
,
3597 EXCEPTION_FLAG_ERROR
,
3601 // Advance and return
3604 return EFI_UNSUPPORTED
;
3606 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3609 // Write back the result.
3611 if (OPERAND1_INDIRECT (Operands
)) {
3612 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3613 if (Opcode
& DATAMANIP_M_64
) {
3614 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3616 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3620 // Storage back to a register. Write back, clearing upper bits (as per
3621 // the specification) if 32-bit operation.
3623 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3624 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3625 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3629 // Advance the instruction pointer
3638 IN VM_CONTEXT
*VmPtr
3642 Routine Description:
3643 Execute the EBC LOADSP instruction
3646 VmPtr - pointer to a VM context
3661 Operands
= GETOPERANDS (VmPtr
);
3666 switch (OPERAND1_REGNUM (Operands
)) {
3672 // Spec states that this instruction will not modify reserved bits in
3673 // the flags register.
3675 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3679 EbcDebugSignalException (
3680 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3681 EXCEPTION_FLAG_WARNING
,
3685 return EFI_UNSUPPORTED
;
3695 IN VM_CONTEXT
*VmPtr
3699 Routine Description:
3700 Execute the EBC STORESP instruction
3703 VmPtr - pointer to a VM context
3709 STORESP Rx, FLAGS|IP
3718 Operands
= GETOPERANDS (VmPtr
);
3723 switch (OPERAND2_REGNUM (Operands
)) {
3729 // Retrieve the value in the flags register, then clear reserved bits
3731 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3735 // Get IP -- address of following instruction
3738 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3742 EbcDebugSignalException (
3743 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3744 EXCEPTION_FLAG_WARNING
,
3748 return EFI_UNSUPPORTED
;
3759 IN VM_CONTEXT
*VmPtr
,
3760 IN UINT32 CodeOffset
3764 Routine Description:
3765 Decode a 16-bit index to determine the offset. Given an index value:
3768 b14:12 - number of bits in this index assigned to natural units (=a)
3769 ba:11 - constant units = C
3770 b0:a - natural units = N
3772 Given this info, the offset can be computed by:
3773 offset = sign_bit * (C + N * sizeof(UINTN))
3775 Max offset is achieved with index = 0x7FFF giving an offset of
3776 0x27B (32-bit machine) or 0x477 (64-bit machine).
3777 Min offset is achieved with index =
3780 VmPtr - pointer to VM context
3781 CodeOffset - offset from IP of the location of the 16-bit index to decode
3796 // First read the index from the code stream
3798 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3801 // Get the mask for N. First get the number of bits from the index.
3803 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3806 // Scale it for 16-bit indexes
3811 // Now using the number of bits, create a mask.
3813 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3816 // Now using the mask, extract N from the lower bits of the index.
3818 N
= (INT16
) (Index
&~Mask
);
3823 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3825 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3830 if (Index
& 0x8000) {
3832 // Do it the hard way to work around a bogus compiler warning
3834 // Offset = -1 * Offset;
3836 Offset
= (INT16
) ((INT32
) Offset
* -1);
3845 IN VM_CONTEXT
*VmPtr
,
3846 IN UINT32 CodeOffset
3850 Routine Description:
3851 Decode a 32-bit index to determine the offset.
3854 VmPtr - pointer to VM context
3855 CodeOffset - offset from IP of the location of the 32-bit index to decode
3858 Converted index per EBC VM specification
3869 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3872 // Get the mask for N. First get the number of bits from the index.
3874 NBits
= (Index
& 0x70000000) >> 28;
3877 // Scale it for 32-bit indexes
3882 // Now using the number of bits, create a mask.
3884 Mask
= (INT32
)~0 << NBits
;
3887 // Now using the mask, extract N from the lower bits of the index.
3894 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3896 Offset
= N
* sizeof (UINTN
) + C
;
3901 if (Index
& 0x80000000) {
3902 Offset
= Offset
* -1;
3911 IN VM_CONTEXT
*VmPtr
,
3912 IN UINT32 CodeOffset
3916 Routine Description:
3917 Decode a 64-bit index to determine the offset.
3920 VmPtr - pointer to VM context
3921 CodeOffset - offset from IP of the location of the 64-bit index to decode
3924 Converted index per EBC VM specification
3935 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3938 // Get the mask for N. First get the number of bits from the index.
3940 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3943 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3945 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3948 // Now using the number of bits, create a mask.
3950 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3953 // Now using the mask, extract N from the lower bits of the index.
3960 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3962 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3967 if (Index
& 0x8000000000000000ULL
) {
3968 Offset
= MultS64x64 (Offset
, -1);
3977 IN VM_CONTEXT
*VmPtr
,
3983 Routine Description:
3984 The following VmWriteMem? routines are called by the EBC data
3985 movement instructions that write to memory. Since these writes
3986 may be to the stack, which looks like (high address on top) this,
3988 [EBC entry point arguments]
3992 we need to detect all attempts to write to the EBC entry point argument
3993 stack area and adjust the address (which will initially point into the
3994 VM stack) to point into the EBC entry point arguments.
3997 VmPtr - pointer to a VM context
3998 Addr - adddress to write to
3999 Data - value to write to Addr
4007 // Convert the address if it's in the stack gap
4009 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4010 *(UINT8
*) Addr
= Data
;
4017 IN VM_CONTEXT
*VmPtr
,
4025 // Convert the address if it's in the stack gap
4027 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4030 // Do a simple write if aligned
4032 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4033 *(UINT16
*) Addr
= Data
;
4036 // Write as two bytes
4039 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
4044 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
4057 IN VM_CONTEXT
*VmPtr
,
4065 // Convert the address if it's in the stack gap
4067 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4070 // Do a simple write if aligned
4072 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4073 *(UINT32
*) Addr
= Data
;
4076 // Write as two words
4079 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
4084 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
4096 IN VM_CONTEXT
*VmPtr
,
4105 // Convert the address if it's in the stack gap
4107 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4110 // Do a simple write if aligned
4112 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4113 *(UINT64
*) Addr
= Data
;
4116 // Write as two 32-bit words
4119 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
4124 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
4125 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
4137 IN VM_CONTEXT
*VmPtr
,
4145 Status
= EFI_SUCCESS
;
4148 // Convert the address if it's in the stack gap
4150 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4153 // Do a simple write if aligned
4155 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4156 *(UINTN
*) Addr
= Data
;
4158 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
4160 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
4162 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
4172 IN VM_CONTEXT
*VmPtr
,
4177 Routine Description:
4179 The following VmReadImmed routines are called by the EBC execute
4180 functions to read EBC immediate values from the code stream.
4181 Since we can't assume alignment, each tries to read in the biggest
4182 chunks size available, but will revert to smaller reads if necessary.
4185 VmPtr - pointer to a VM context
4186 Offset - offset from IP of the code bytes to read.
4189 Signed data of the requested size from the specified address.
4194 // Simply return the data in flat memory space
4196 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
4202 IN VM_CONTEXT
*VmPtr
,
4207 // Read direct if aligned
4209 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
4210 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
4213 // All code word reads should be aligned
4215 EbcDebugSignalException (
4216 EXCEPT_EBC_ALIGNMENT_CHECK
,
4217 EXCEPTION_FLAG_WARNING
,
4222 // Return unaligned data
4224 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4230 IN VM_CONTEXT
*VmPtr
,
4237 // Read direct if aligned
4239 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4240 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4243 // Return unaligned data
4245 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4246 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4253 IN VM_CONTEXT
*VmPtr
,
4262 // Read direct if aligned
4264 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4265 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4268 // Return unaligned data.
4270 Ptr
= (UINT8
*) &Data64
;
4271 Data32
= VmReadCode32 (VmPtr
, Offset
);
4272 *(UINT32
*) Ptr
= Data32
;
4273 Ptr
+= sizeof (Data32
);
4274 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4275 *(UINT32
*) Ptr
= Data32
;
4282 IN VM_CONTEXT
*VmPtr
,
4287 Routine Description:
4288 The following VmReadCode() routines provide the ability to read raw
4289 unsigned data from the code stream.
4292 VmPtr - pointer to VM context
4293 Offset - offset from current IP to the raw data to read.
4296 The raw unsigned 16-bit value from the code stream.
4301 // Read direct if aligned
4303 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4304 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4307 // All code word reads should be aligned
4309 EbcDebugSignalException (
4310 EXCEPT_EBC_ALIGNMENT_CHECK
,
4311 EXCEPTION_FLAG_WARNING
,
4316 // Return unaligned data
4318 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4324 IN VM_CONTEXT
*VmPtr
,
4330 // Read direct if aligned
4332 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4333 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4336 // Return unaligned data
4338 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4339 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4346 IN VM_CONTEXT
*VmPtr
,
4355 // Read direct if aligned
4357 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4358 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4361 // Return unaligned data.
4363 Ptr
= (UINT8
*) &Data64
;
4364 Data32
= VmReadCode32 (VmPtr
, Offset
);
4365 *(UINT32
*) Ptr
= Data32
;
4366 Ptr
+= sizeof (Data32
);
4367 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4368 *(UINT32
*) Ptr
= Data32
;
4375 IN VM_CONTEXT
*VmPtr
,
4380 // Convert the address if it's in the stack gap
4382 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4384 // Simply return the data in flat memory space
4386 return * (UINT8
*) Addr
;
4392 IN VM_CONTEXT
*VmPtr
,
4397 // Convert the address if it's in the stack gap
4399 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4401 // Read direct if aligned
4403 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4404 return * (UINT16
*) Addr
;
4407 // Return unaligned data
4409 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4415 IN VM_CONTEXT
*VmPtr
,
4422 // Convert the address if it's in the stack gap
4424 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4426 // Read direct if aligned
4428 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4429 return * (UINT32
*) Addr
;
4432 // Return unaligned data
4434 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4435 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4442 IN VM_CONTEXT
*VmPtr
,
4450 // Convert the address if it's in the stack gap
4452 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4455 // Read direct if aligned
4457 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4458 return * (UINT64
*) Addr
;
4461 // Return unaligned data. Assume little endian.
4463 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4464 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4465 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4472 IN VM_CONTEXT
*VmPtr
,
4477 Routine Description:
4479 Given an address that EBC is going to read from or write to, return
4480 an appropriate address that accounts for a gap in the stack.
4482 The stack for this application looks like this (high addr on top)
4483 [EBC entry point arguments]
4487 The EBC assumes that its arguments are at the top of its stack, which
4488 is where the VM stack is really. Therefore if the EBC does memory
4489 accesses into the VM stack area, then we need to convert the address
4490 to point to the EBC entry point arguments area. Do this here.
4494 VmPtr - pointer to VM context
4495 Addr - address of interest
4499 The unchanged address if it's not in the VM stack region. Otherwise,
4500 adjust for the stack gap and return the modified address.
4504 ASSERT(((Addr
< VmPtr
->LowStackTop
) || (Addr
> VmPtr
->HighStackBottom
)));
4511 IN VM_CONTEXT
*VmPtr
,
4516 Routine Description:
4517 Read a natural value from memory. May or may not be aligned.
4520 VmPtr - current VM context
4521 Addr - the address to read from
4524 The natural value at address Addr.
4529 volatile UINT32 Size
;
4533 // Convert the address if it's in the stack gap
4535 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4537 // Read direct if aligned
4539 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4540 return * (UINTN
*) Addr
;
4543 // Return unaligned data
4546 FromPtr
= (UINT8
*) Addr
;
4547 ToPtr
= (UINT8
*) &Data
;
4549 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4563 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));