2 Contains code that implements the virtual machine.
4 Copyright (c) 2006, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "EbcExecute.h"
20 // Define some useful data size constants to allow switch statements based on
21 // size of operands or data.
23 #define DATA_SIZE_INVALID 0
25 #define DATA_SIZE_16 2
26 #define DATA_SIZE_32 4
27 #define DATA_SIZE_64 8
28 #define DATA_SIZE_N 48 // 4 or 8
30 // Structure we'll use to dispatch opcodes to execute functions.
33 EFI_STATUS (*ExecuteFunction
) (IN VM_CONTEXT
* VmPtr
);
39 (*DATA_MANIP_EXEC_FUNCTION
) (
40 IN VM_CONTEXT
* VmPtr
,
104 IN VM_CONTEXT
*VmPtr
,
112 IN VM_CONTEXT
*VmPtr
,
120 IN VM_CONTEXT
*VmPtr
,
128 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
,
185 IN BOOLEAN IsSignedOperation
189 // Functions that execute VM opcodes
283 ExecuteSignedDataManip (
289 ExecuteUnsignedDataManip (
318 // Data manipulation subfunctions
323 IN VM_CONTEXT
*VmPtr
,
331 IN VM_CONTEXT
*VmPtr
,
339 IN VM_CONTEXT
*VmPtr
,
347 IN VM_CONTEXT
*VmPtr
,
355 IN VM_CONTEXT
*VmPtr
,
363 IN VM_CONTEXT
*VmPtr
,
371 IN VM_CONTEXT
*VmPtr
,
379 IN VM_CONTEXT
*VmPtr
,
387 IN VM_CONTEXT
*VmPtr
,
395 IN VM_CONTEXT
*VmPtr
,
403 IN VM_CONTEXT
*VmPtr
,
411 IN VM_CONTEXT
*VmPtr
,
419 IN VM_CONTEXT
*VmPtr
,
427 IN VM_CONTEXT
*VmPtr
,
435 IN VM_CONTEXT
*VmPtr
,
443 IN VM_CONTEXT
*VmPtr
,
451 IN VM_CONTEXT
*VmPtr
,
459 IN VM_CONTEXT
*VmPtr
,
467 IN VM_CONTEXT
*VmPtr
,
473 // Once we retrieve the operands for the data manipulation instructions,
474 // call these functions to perform the operation.
476 static CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable
[] = {
498 static CONST VM_TABLE_ENTRY mVmOpcodeTable
[] = {
499 { ExecuteBREAK
}, // opcode 0x00
500 { ExecuteJMP
}, // opcode 0x01
501 { ExecuteJMP8
}, // opcode 0x02
502 { ExecuteCALL
}, // opcode 0x03
503 { ExecuteRET
}, // opcode 0x04
504 { ExecuteCMP
}, // opcode 0x05 CMPeq
505 { ExecuteCMP
}, // opcode 0x06 CMPlte
506 { ExecuteCMP
}, // opcode 0x07 CMPgte
507 { ExecuteCMP
}, // opcode 0x08 CMPulte
508 { ExecuteCMP
}, // opcode 0x09 CMPugte
509 { ExecuteUnsignedDataManip
}, // opcode 0x0A NOT
510 { ExecuteSignedDataManip
}, // opcode 0x0B NEG
511 { ExecuteSignedDataManip
}, // opcode 0x0C ADD
512 { ExecuteSignedDataManip
}, // opcode 0x0D SUB
513 { ExecuteSignedDataManip
}, // opcode 0x0E MUL
514 { ExecuteUnsignedDataManip
}, // opcode 0x0F MULU
515 { ExecuteSignedDataManip
}, // opcode 0x10 DIV
516 { ExecuteUnsignedDataManip
}, // opcode 0x11 DIVU
517 { ExecuteSignedDataManip
}, // opcode 0x12 MOD
518 { ExecuteUnsignedDataManip
}, // opcode 0x13 MODU
519 { ExecuteUnsignedDataManip
}, // opcode 0x14 AND
520 { ExecuteUnsignedDataManip
}, // opcode 0x15 OR
521 { ExecuteUnsignedDataManip
}, // opcode 0x16 XOR
522 { ExecuteUnsignedDataManip
}, // opcode 0x17 SHL
523 { ExecuteUnsignedDataManip
}, // opcode 0x18 SHR
524 { ExecuteSignedDataManip
}, // opcode 0x19 ASHR
525 { ExecuteUnsignedDataManip
}, // opcode 0x1A EXTNDB
526 { ExecuteUnsignedDataManip
}, // opcode 0x1B EXTNDW
527 { ExecuteUnsignedDataManip
}, // opcode 0x1C EXTNDD
528 { ExecuteMOVxx
}, // opcode 0x1D MOVBW
529 { ExecuteMOVxx
}, // opcode 0x1E MOVWW
530 { ExecuteMOVxx
}, // opcode 0x1F MOVDW
531 { ExecuteMOVxx
}, // opcode 0x20 MOVQW
532 { ExecuteMOVxx
}, // opcode 0x21 MOVBD
533 { ExecuteMOVxx
}, // opcode 0x22 MOVWD
534 { ExecuteMOVxx
}, // opcode 0x23 MOVDD
535 { ExecuteMOVxx
}, // opcode 0x24 MOVQD
536 { ExecuteMOVsnw
}, // opcode 0x25 MOVsnw
537 { ExecuteMOVsnd
}, // opcode 0x26 MOVsnd
538 { NULL
}, // opcode 0x27
539 { ExecuteMOVxx
}, // opcode 0x28 MOVqq
540 { ExecuteLOADSP
}, // opcode 0x29 LOADSP SP1, R2
541 { ExecuteSTORESP
}, // opcode 0x2A STORESP R1, SP2
542 { ExecutePUSH
}, // opcode 0x2B PUSH {@}R1 [imm16]
543 { ExecutePOP
}, // opcode 0x2C POP {@}R1 [imm16]
544 { ExecuteCMPI
}, // opcode 0x2D CMPIEQ
545 { ExecuteCMPI
}, // opcode 0x2E CMPILTE
546 { ExecuteCMPI
}, // opcode 0x2F CMPIGTE
547 { ExecuteCMPI
}, // opcode 0x30 CMPIULTE
548 { ExecuteCMPI
}, // opcode 0x31 CMPIUGTE
549 { ExecuteMOVxx
}, // opcode 0x32 MOVN
550 { ExecuteMOVxx
}, // opcode 0x33 MOVND
551 { NULL
}, // opcode 0x34
552 { ExecutePUSHn
}, // opcode 0x35
553 { ExecutePOPn
}, // opcode 0x36
554 { ExecuteMOVI
}, // opcode 0x37 - mov immediate data
555 { ExecuteMOVIn
}, // opcode 0x38 - mov immediate natural
556 { ExecuteMOVREL
} // opcode 0x39 - move data relative to PC
560 // Length of JMP instructions, depending on upper two bits of opcode.
562 static CONST UINT8 mJMPLen
[] = { 2, 2, 6, 10 };
565 // Simple Debugger Protocol GUID
567 EFI_GUID mEbcSimpleDebuggerProtocolGuid
= EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID
;
571 Given a pointer to a new VM context, execute one or more instructions. This
572 function is only used for test purposes via the EBC VM test protocol.
574 @param This pointer to protocol interface
575 @param VmPtr pointer to a VM context
576 @param InstructionCount how many instructions to execute. 0 if don't count.
578 @return EFI_UNSUPPORTED
583 EbcExecuteInstructions (
584 IN EFI_EBC_VM_TEST_PROTOCOL
*This
,
585 IN VM_CONTEXT
*VmPtr
,
586 IN OUT UINTN
*InstructionCount
591 UINTN InstructionsLeft
;
592 UINTN SavedInstructionCount
;
594 Status
= EFI_SUCCESS
;
596 if (*InstructionCount
== 0) {
597 InstructionsLeft
= 1;
599 InstructionsLeft
= *InstructionCount
;
602 SavedInstructionCount
= *InstructionCount
;
603 *InstructionCount
= 0;
606 // Index into the opcode table using the opcode byte for this instruction.
607 // This gives you the execute function, which we first test for null, then
608 // call it if it's not null.
610 while (InstructionsLeft
!= 0) {
611 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction
;
612 if (ExecFunc
== (UINTN
) NULL
) {
613 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
614 return EFI_UNSUPPORTED
;
616 mVmOpcodeTable
[(*VmPtr
->Ip
& 0x3F)].ExecuteFunction (VmPtr
);
617 *InstructionCount
= *InstructionCount
+ 1;
621 // Decrement counter if applicable
623 if (SavedInstructionCount
!= 0) {
633 Execute an EBC image from an entry point or from a published protocol.
635 @param VmPtr pointer to prepared VM context.
637 @return Standard EBC status.
646 UINT8 StackCorrupted
;
648 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL
*EbcSimpleDebugger
;
651 EbcSimpleDebugger
= NULL
;
652 Status
= EFI_SUCCESS
;
656 // Make sure the magic value has been put on the stack before we got here.
658 if (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
) {
662 VmPtr
->FramePtr
= (VOID
*) ((UINT8
*) (UINTN
) VmPtr
->R
[0] + 8);
665 // Try to get the debug support for EBC
668 Status
= gBS
->LocateProtocol (
669 &mEbcSimpleDebuggerProtocolGuid
,
671 (VOID
**) &EbcSimpleDebugger
673 if (EFI_ERROR (Status
)) {
674 EbcSimpleDebugger
= NULL
;
679 // Save the start IP for debug. For example, if we take an exception we
680 // can print out the location of the exception relative to the entry point,
681 // which could then be used in a disassembly listing to find the problem.
683 VmPtr
->EntryPoint
= (VOID
*) VmPtr
->Ip
;
686 // We'll wait for this flag to know when we're done. The RET
687 // instruction sets it if it runs out of stack.
689 VmPtr
->StopFlags
= 0;
690 while (!(VmPtr
->StopFlags
& STOPFLAG_APP_DONE
)) {
692 // If we've found a simple debugger protocol, call it
695 if (EbcSimpleDebugger
!= NULL
) {
696 EbcSimpleDebugger
->Debugger (EbcSimpleDebugger
, VmPtr
);
701 // Verify the opcode is in range. Otherwise generate an exception.
703 if ((*VmPtr
->Ip
& OPCODE_M_OPCODE
) >= (sizeof (mVmOpcodeTable
) / sizeof (mVmOpcodeTable
[0]))) {
704 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
705 Status
= EFI_UNSUPPORTED
;
709 // Use the opcode bits to index into the opcode dispatch table. If the
710 // function pointer is null then generate an exception.
712 ExecFunc
= (UINTN
) mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction
;
713 if (ExecFunc
== (UINTN
) NULL
) {
714 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE
, EXCEPTION_FLAG_FATAL
, VmPtr
);
715 Status
= EFI_UNSUPPORTED
;
719 // The EBC VM is a strongly ordered processor, so perform a fence operation before
720 // and after each instruction is executed.
724 mVmOpcodeTable
[(*VmPtr
->Ip
& OPCODE_M_OPCODE
)].ExecuteFunction (VmPtr
);
729 // If the step flag is set, signal an exception and continue. We don't
730 // clear it here. Assuming the debugger is responsible for clearing it.
732 if (VMFLAG_ISSET (VmPtr
, VMFLAGS_STEP
)) {
733 EbcDebugSignalException (EXCEPT_EBC_STEP
, EXCEPTION_FLAG_NONE
, VmPtr
);
736 // Make sure stack has not been corrupted. Only report it once though.
738 if (!StackCorrupted
&& (*VmPtr
->StackMagicPtr
!= (UINTN
) VM_STACK_KEY_VALUE
)) {
739 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
742 if (!StackCorrupted
&& ((UINT64
)VmPtr
->R
[0] <= (UINT64
)(UINTN
) VmPtr
->StackTop
)) {
743 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT
, EXCEPTION_FLAG_FATAL
, VmPtr
);
756 Execute the MOVxx instructions.
758 @param VmPtr pointer to a VM context.
760 @return EFI_UNSUPPORTED
762 @return Instruction format:
763 @return MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
764 @return MOVqq {@}R1 {Index64}, {@}R2 {Index64}
765 @return Copies contents of [R2] -> [R1], zero extending where required.
766 @return First character indicates the size of the move.
767 @return Second character indicates the size of the index(s).
768 @return Invalid to have R1 direct with index.
790 Opcode
= GETOPCODE (VmPtr
);
791 OpcMasked
= (UINT8
) (Opcode
& OPCODE_M_OPCODE
);
794 // Get the operands byte so we can get R1 and R2
796 Operands
= GETOPERANDS (VmPtr
);
806 // Determine if we have an index/immediate data. Base instruction size
807 // is 2 (opcode + operands). Add to this size each index specified.
810 if (Opcode
& (OPCODE_M_IMMED_OP1
| OPCODE_M_IMMED_OP2
)) {
812 // Determine size of the index from the opcode. Then get it.
814 if ((OpcMasked
<= OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVNW
)) {
816 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
817 // Get one or both index values.
819 if (Opcode
& OPCODE_M_IMMED_OP1
) {
820 Index16
= VmReadIndex16 (VmPtr
, 2);
821 Index64Op1
= (INT64
) Index16
;
822 Size
+= sizeof (UINT16
);
825 if (Opcode
& OPCODE_M_IMMED_OP2
) {
826 Index16
= VmReadIndex16 (VmPtr
, Size
);
827 Index64Op2
= (INT64
) Index16
;
828 Size
+= sizeof (UINT16
);
830 } else if ((OpcMasked
<= OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVND
)) {
832 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
834 if (Opcode
& OPCODE_M_IMMED_OP1
) {
835 Index32
= VmReadIndex32 (VmPtr
, 2);
836 Index64Op1
= (INT64
) Index32
;
837 Size
+= sizeof (UINT32
);
840 if (Opcode
& OPCODE_M_IMMED_OP2
) {
841 Index32
= VmReadIndex32 (VmPtr
, Size
);
842 Index64Op2
= (INT64
) Index32
;
843 Size
+= sizeof (UINT32
);
845 } else if (OpcMasked
== OPCODE_MOVQQ
) {
847 // MOVqq -- only form with a 64-bit index
849 if (Opcode
& OPCODE_M_IMMED_OP1
) {
850 Index64Op1
= VmReadIndex64 (VmPtr
, 2);
851 Size
+= sizeof (UINT64
);
854 if (Opcode
& OPCODE_M_IMMED_OP2
) {
855 Index64Op2
= VmReadIndex64 (VmPtr
, Size
);
856 Size
+= sizeof (UINT64
);
860 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
862 EbcDebugSignalException (
863 EXCEPT_EBC_INSTRUCTION_ENCODING
,
864 EXCEPTION_FLAG_FATAL
,
867 return EFI_UNSUPPORTED
;
871 // Determine the size of the move, and create a mask for it so we can
872 // clear unused bits.
874 if ((OpcMasked
== OPCODE_MOVBW
) || (OpcMasked
== OPCODE_MOVBD
)) {
875 MoveSize
= DATA_SIZE_8
;
877 } else if ((OpcMasked
== OPCODE_MOVWW
) || (OpcMasked
== OPCODE_MOVWD
)) {
878 MoveSize
= DATA_SIZE_16
;
880 } else if ((OpcMasked
== OPCODE_MOVDW
) || (OpcMasked
== OPCODE_MOVDD
)) {
881 MoveSize
= DATA_SIZE_32
;
882 DataMask
= 0xFFFFFFFF;
883 } else if ((OpcMasked
== OPCODE_MOVQW
) || (OpcMasked
== OPCODE_MOVQD
) || (OpcMasked
== OPCODE_MOVQQ
)) {
884 MoveSize
= DATA_SIZE_64
;
885 DataMask
= (UINT64
)~0;
886 } else if ((OpcMasked
== OPCODE_MOVNW
) || (OpcMasked
== OPCODE_MOVND
)) {
887 MoveSize
= DATA_SIZE_N
;
888 DataMask
= (UINT64
)~0 >> (64 - 8 * sizeof (UINTN
));
891 // We were dispatched to this function and we don't recognize the opcode
893 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED
, EXCEPTION_FLAG_FATAL
, VmPtr
);
894 return EFI_UNSUPPORTED
;
897 // Now get the source address
899 if (OPERAND2_INDIRECT (Operands
)) {
901 // Indirect form @R2. Compute address of operand2
903 Source
= (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
);
905 // Now get the data from the source. Always 0-extend and let the compiler
906 // sign-extend where required.
910 Data64
= (UINT64
) (UINT8
) VmReadMem8 (VmPtr
, Source
);
914 Data64
= (UINT64
) (UINT16
) VmReadMem16 (VmPtr
, Source
);
918 Data64
= (UINT64
) (UINT32
) VmReadMem32 (VmPtr
, Source
);
922 Data64
= (UINT64
) VmReadMem64 (VmPtr
, Source
);
926 Data64
= (UINT64
) (UINTN
) VmReadMemN (VmPtr
, Source
);
937 // Not indirect source: MOVxx {@}Rx, Ry [Index]
939 Data64
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index64Op2
;
941 // Did Operand2 have an index? If so, treat as two signed values since
942 // indexes are signed values.
944 if (Opcode
& OPCODE_M_IMMED_OP2
) {
946 // NOTE: need to find a way to fix this, most likely by changing the VM
947 // implementation to remove the stack gap. To do that, we'd need to
948 // allocate stack space for the VM and actually set the system
949 // stack pointer to the allocated buffer when the VM starts.
951 // Special case -- if someone took the address of a function parameter
952 // then we need to make sure it's not in the stack gap. We can identify
953 // this situation if (Operand2 register == 0) && (Operand2 is direct)
954 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
955 // Situations that to be aware of:
956 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
958 if ((OPERAND2_REGNUM (Operands
) == 0) &&
959 (!OPERAND2_INDIRECT (Operands
)) &&
961 (OPERAND1_REGNUM (Operands
) == 0) &&
962 (OPERAND1_INDIRECT (Operands
))
964 Data64
= (UINT64
) ConvertStackAddr (VmPtr
, (UINTN
) (INT64
) Data64
);
971 if (OPERAND1_INDIRECT (Operands
)) {
973 // Reuse the Source variable to now be dest.
975 Source
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index64Op1
);
977 // Do the write based on the size
981 VmWriteMem8 (VmPtr
, Source
, (UINT8
) Data64
);
985 VmWriteMem16 (VmPtr
, Source
, (UINT16
) Data64
);
989 VmWriteMem32 (VmPtr
, Source
, (UINT32
) Data64
);
993 VmWriteMem64 (VmPtr
, Source
, Data64
);
997 VmWriteMemN (VmPtr
, Source
, (UINTN
) Data64
);
1009 // Make sure we didn't have an index on operand1.
1011 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1012 EbcDebugSignalException (
1013 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1014 EXCEPTION_FLAG_FATAL
,
1017 return EFI_UNSUPPORTED
;
1020 // Direct storage in register. Clear unused bits and store back to
1023 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
& DataMask
;
1026 // Advance the instruction pointer
1034 Execute the EBC BREAK instruction
1036 @param VmPtr pointer to current VM context
1038 @return EFI_UNSUPPORTED
1045 IN VM_CONTEXT
*VmPtr
1049 VOID
*EbcEntryPoint
;
1051 UINT64 U64EbcEntryPoint
;
1054 Operands
= GETOPERANDS (VmPtr
);
1057 // Runaway program break. Generate an exception and terminate
1060 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1064 // Get VM version -- return VM revision number in R7
1070 // 16-8 = Major version
1071 // 7-0 = Minor version
1073 VmPtr
->R
[7] = GetVmVersion ();
1077 // Debugger breakpoint
1080 VmPtr
->StopFlags
|= STOPFLAG_BREAKPOINT
;
1082 // See if someone has registered a handler
1084 EbcDebugSignalException (
1085 EXCEPT_EBC_BREAKPOINT
,
1086 EXCEPTION_FLAG_NONE
,
1092 // System call, which there are none, so NOP it.
1098 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1099 // "offset from self" pointer to the EBC entry point.
1100 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1103 Offset
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[7]);
1104 U64EbcEntryPoint
= (UINT64
) (VmPtr
->R
[7] + Offset
+ 4);
1105 EbcEntryPoint
= (VOID
*) (UINTN
) U64EbcEntryPoint
;
1108 // Now create a new thunk
1110 EbcCreateThunks (VmPtr
->ImageHandle
, EbcEntryPoint
, &Thunk
, 0);
1113 // Finally replace the EBC entry point memory with the thunk address
1115 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[7], (UINT64
) (UINTN
) Thunk
);
1119 // Compiler setting version per value in R7
1122 VmPtr
->CompilerVersion
= (UINT32
) VmPtr
->R
[7];
1124 // Check compiler version against VM version?
1129 // Unhandled break code. Signal exception.
1132 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK
, EXCEPTION_FLAG_FATAL
, VmPtr
);
1144 Execute the JMP instruction
1146 @param VmPtr pointer to VM context
1148 @return Standard EFI_STATUS
1149 @return Instruction syntax:
1150 @return JMP64{cs|cc} Immed64
1151 @return JMP32{cs|cc} {@}R1 {Immed32|Index32}
1153 @retval b0.7 immediate data present
1154 @retval b0.6 1 = 64 bit immediate data 0 = 32 bit immediate data
1155 @retval b1.7 1 = conditional b1.6 1 = CS (condition set) 0 = CC
1156 (condition clear) b1.4 1 = relative address 0 =
1157 absolute address b1.3 1 = operand1 indirect b1.2-0
1164 IN VM_CONTEXT
*VmPtr
1169 UINT8 ConditionFlag
;
1176 Operand
= GETOPERANDS (VmPtr
);
1177 Opcode
= GETOPCODE (VmPtr
);
1180 // Get instruction length from the opcode. The upper two bits are used here
1181 // to index into the length array.
1183 Size
= mJMPLen
[(Opcode
>> 6) & 0x03];
1186 // Decode instruction conditions
1187 // If we haven't met the condition, then simply advance the IP and return.
1189 CompareSet
= (UINT8
) ((Operand
& JMP_M_CS
) ? 1 : 0);
1190 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1191 if (Operand
& CONDITION_M_CONDITIONAL
) {
1192 if (CompareSet
!= ConditionFlag
) {
1198 // Check for 64-bit form and do it right away since it's the most
1199 // straight-forward form.
1201 if (Opcode
& OPCODE_M_IMMDATA64
) {
1203 // Double check for immediate-data, which is required. If not there,
1204 // then signal an exception
1206 if (!(Opcode
& OPCODE_M_IMMDATA
)) {
1207 EbcDebugSignalException (
1208 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1209 EXCEPTION_FLAG_ERROR
,
1212 return EFI_UNSUPPORTED
;
1215 // 64-bit immediate data is full address. Read the immediate data,
1216 // check for alignment, and jump absolute.
1218 Data64
= VmReadImmed64 (VmPtr
, 2);
1219 if (!IS_ALIGNED ((UINTN
) Data64
, sizeof (UINT16
))) {
1220 EbcDebugSignalException (
1221 EXCEPT_EBC_ALIGNMENT_CHECK
,
1222 EXCEPTION_FLAG_FATAL
,
1226 return EFI_UNSUPPORTED
;
1230 // Take jump -- relative or absolute
1232 if (Operand
& JMP_M_RELATIVE
) {
1233 VmPtr
->Ip
+= (UINTN
) Data64
+ Size
;
1235 VmPtr
->Ip
= (VMIP
) (UINTN
) Data64
;
1242 // Get the index if there is one. May be either an index, or an immediate
1243 // offset depending on indirect operand.
1244 // JMP32 @R1 Index32 -- immediate data is an index
1245 // JMP32 R1 Immed32 -- immedate data is an offset
1247 if (Opcode
& OPCODE_M_IMMDATA
) {
1248 if (OPERAND1_INDIRECT (Operand
)) {
1249 Index32
= VmReadIndex32 (VmPtr
, 2);
1251 Index32
= VmReadImmed32 (VmPtr
, 2);
1257 // Get the register data. If R == 0, then special case where it's ignored.
1259 if (OPERAND1_REGNUM (Operand
) == 0) {
1262 Data64
= OPERAND1_REGDATA (VmPtr
, Operand
);
1267 if (OPERAND1_INDIRECT (Operand
)) {
1269 // Form: JMP32 @Rx {Index32}
1271 Addr
= VmReadMemN (VmPtr
, (UINTN
) Data64
+ Index32
);
1272 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1273 EbcDebugSignalException (
1274 EXCEPT_EBC_ALIGNMENT_CHECK
,
1275 EXCEPTION_FLAG_FATAL
,
1279 return EFI_UNSUPPORTED
;
1282 if (Operand
& JMP_M_RELATIVE
) {
1283 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1285 VmPtr
->Ip
= (VMIP
) Addr
;
1289 // Form: JMP32 Rx {Immed32}
1291 Addr
= (UINTN
) (Data64
+ Index32
);
1292 if (!IS_ALIGNED ((UINTN
) Addr
, sizeof (UINT16
))) {
1293 EbcDebugSignalException (
1294 EXCEPT_EBC_ALIGNMENT_CHECK
,
1295 EXCEPTION_FLAG_FATAL
,
1299 return EFI_UNSUPPORTED
;
1302 if (Operand
& JMP_M_RELATIVE
) {
1303 VmPtr
->Ip
+= (UINTN
) Addr
+ Size
;
1305 VmPtr
->Ip
= (VMIP
) Addr
;
1314 Execute the EBC JMP8 instruction
1316 @param VmPtr pointer to a VM context
1318 @return Standard EFI_STATUS
1319 @return Instruction syntax:
1320 @return JMP8{cs|cc} Offset/2
1326 IN VM_CONTEXT
*VmPtr
1330 UINT8 ConditionFlag
;
1335 // Decode instruction.
1337 Opcode
= GETOPCODE (VmPtr
);
1338 CompareSet
= (UINT8
) ((Opcode
& JMP_M_CS
) ? 1 : 0);
1339 ConditionFlag
= (UINT8
) VMFLAG_ISSET (VmPtr
, VMFLAGS_CC
);
1342 // If we haven't met the condition, then simply advance the IP and return
1344 if (Opcode
& CONDITION_M_CONDITIONAL
) {
1345 if (CompareSet
!= ConditionFlag
) {
1351 // Get the offset from the instruction stream. It's relative to the
1352 // following instruction, and divided by 2.
1354 Offset
= VmReadImmed8 (VmPtr
, 1);
1356 // Want to check for offset == -2 and then raise an exception?
1358 VmPtr
->Ip
+= (Offset
* 2) + 2;
1364 Execute the EBC MOVI
1366 @param VmPtr pointer to a VM context
1368 @return Standard EFI_STATUS
1369 @return Instruction syntax:
1370 @return MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1371 @return First variable character specifies the move size
1372 @return Second variable character specifies size of the immediate data
1373 @return Sign-extend the immediate data to the size of the operation, and zero-extend
1374 @return if storing to a register.
1375 @return Operand1 direct with index/immed is invalid.
1381 IN VM_CONTEXT
*VmPtr
1393 // Get the opcode and operands byte so we can get R1 and R2
1395 Opcode
= GETOPCODE (VmPtr
);
1396 Operands
= GETOPERANDS (VmPtr
);
1399 // Get the index (16-bit) if present
1401 if (Operands
& MOVI_M_IMMDATA
) {
1402 Index16
= VmReadIndex16 (VmPtr
, 2);
1409 // Extract the immediate data. Sign-extend always.
1411 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1412 ImmData64
= (INT64
) (INT16
) VmReadImmed16 (VmPtr
, Size
);
1414 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1415 ImmData64
= (INT64
) (INT32
) VmReadImmed32 (VmPtr
, Size
);
1417 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1418 ImmData64
= (INT64
) VmReadImmed64 (VmPtr
, Size
);
1424 EbcDebugSignalException (
1425 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1426 EXCEPTION_FLAG_FATAL
,
1429 return EFI_UNSUPPORTED
;
1432 // Now write back the result
1434 if (!OPERAND1_INDIRECT (Operands
)) {
1436 // Operand1 direct. Make sure it didn't have an index.
1438 if (Operands
& MOVI_M_IMMDATA
) {
1439 EbcDebugSignalException (
1440 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1441 EXCEPTION_FLAG_FATAL
,
1444 return EFI_UNSUPPORTED
;
1447 // Writing directly to a register. Clear unused bits.
1449 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1450 Mask64
= 0x000000FF;
1451 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1452 Mask64
= 0x0000FFFF;
1453 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1454 Mask64
= 0x00000000FFFFFFFF;
1456 Mask64
= (UINT64
)~0;
1459 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmData64
& Mask64
;
1462 // Get the address then write back based on size of the move
1464 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1465 if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH8
) {
1466 VmWriteMem8 (VmPtr
, (UINTN
) Op1
, (UINT8
) ImmData64
);
1467 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH16
) {
1468 VmWriteMem16 (VmPtr
, (UINTN
) Op1
, (UINT16
) ImmData64
);
1469 } else if ((Operands
& MOVI_M_MOVEWIDTH
) == MOVI_MOVEWIDTH32
) {
1470 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) ImmData64
);
1472 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, ImmData64
);
1476 // Advance the instruction pointer
1484 Execute the EBC MOV immediate natural. This instruction moves an immediate
1485 index value into a register or memory location.
1487 @param VmPtr pointer to a VM context
1489 @return Standard EFI_STATUS
1490 @return Instruction syntax:
1491 @return MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1497 IN VM_CONTEXT
*VmPtr
1510 // Get the opcode and operands byte so we can get R1 and R2
1512 Opcode
= GETOPCODE (VmPtr
);
1513 Operands
= GETOPERANDS (VmPtr
);
1516 // Get the operand1 index (16-bit) if present
1518 if (Operands
& MOVI_M_IMMDATA
) {
1519 Index16
= VmReadIndex16 (VmPtr
, 2);
1526 // Extract the immediate data and convert to a 64-bit index.
1528 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1529 ImmedIndex16
= VmReadIndex16 (VmPtr
, Size
);
1530 ImmedIndex64
= (INT64
) ImmedIndex16
;
1532 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1533 ImmedIndex32
= VmReadIndex32 (VmPtr
, Size
);
1534 ImmedIndex64
= (INT64
) ImmedIndex32
;
1536 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1537 ImmedIndex64
= VmReadIndex64 (VmPtr
, Size
);
1543 EbcDebugSignalException (
1544 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1545 EXCEPTION_FLAG_FATAL
,
1548 return EFI_UNSUPPORTED
;
1551 // Now write back the result
1553 if (!OPERAND1_INDIRECT (Operands
)) {
1555 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1558 if (Operands
& MOVI_M_IMMDATA
) {
1559 EbcDebugSignalException (
1560 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1561 EXCEPTION_FLAG_FATAL
,
1564 return EFI_UNSUPPORTED
;
1567 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = ImmedIndex64
;
1572 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1573 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (INTN
) ImmedIndex64
);
1576 // Advance the instruction pointer
1584 Execute the EBC MOVREL instruction.
1585 Dest <- Ip + ImmData
1587 @param VmPtr pointer to a VM context
1589 @return Standard EFI_STATUS
1590 @return Instruction syntax:
1591 @return MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1597 IN VM_CONTEXT
*VmPtr
1609 // Get the opcode and operands byte so we can get R1 and R2
1611 Opcode
= GETOPCODE (VmPtr
);
1612 Operands
= GETOPERANDS (VmPtr
);
1615 // Get the Operand 1 index (16-bit) if present
1617 if (Operands
& MOVI_M_IMMDATA
) {
1618 Index16
= VmReadIndex16 (VmPtr
, 2);
1625 // Get the immediate data.
1627 if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH16
) {
1628 ImmData64
= (INT64
) VmReadImmed16 (VmPtr
, Size
);
1630 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH32
) {
1631 ImmData64
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
1633 } else if ((Opcode
& MOVI_M_DATAWIDTH
) == MOVI_DATAWIDTH64
) {
1634 ImmData64
= VmReadImmed64 (VmPtr
, Size
);
1640 EbcDebugSignalException (
1641 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1642 EXCEPTION_FLAG_FATAL
,
1645 return EFI_UNSUPPORTED
;
1648 // Compute the value and write back the result
1650 Op2
= (UINT64
) ((INT64
) ((UINT64
) (UINTN
) VmPtr
->Ip
) + (INT64
) ImmData64
+ Size
);
1651 if (!OPERAND1_INDIRECT (Operands
)) {
1653 // Check for illegal combination of operand1 direct with immediate data
1655 if (Operands
& MOVI_M_IMMDATA
) {
1656 EbcDebugSignalException (
1657 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1658 EXCEPTION_FLAG_FATAL
,
1661 return EFI_UNSUPPORTED
;
1664 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (VM_REGISTER
) Op2
;
1667 // Get the address = [Rx] + Index16
1668 // Write back the result. Always a natural size write, since
1669 // we're talking addresses here.
1671 Op1
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1672 VmWriteMemN (VmPtr
, (UINTN
) Op1
, (UINTN
) Op2
);
1675 // Advance the instruction pointer
1683 Execute the EBC MOVsnw instruction. This instruction loads a signed
1684 natural value from memory or register to another memory or register. On
1685 32-bit machines, the value gets sign-extended to 64 bits if the destination
1688 @param VmPtr pointer to a VM context
1690 @return Standard EFI_STATUS
1691 @return Instruction syntax:
1692 @return MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1693 @return 0:7 1=>operand1 index present
1694 @return 0:6 1=>operand2 index present
1700 IN VM_CONTEXT
*VmPtr
1711 // Get the opcode and operand bytes
1713 Opcode
= GETOPCODE (VmPtr
);
1714 Operands
= GETOPERANDS (VmPtr
);
1716 Op1Index
= Op2Index
= 0;
1719 // Get the indexes if present.
1722 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1723 if (OPERAND1_INDIRECT (Operands
)) {
1724 Op1Index
= VmReadIndex16 (VmPtr
, 2);
1727 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1729 EbcDebugSignalException (
1730 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1731 EXCEPTION_FLAG_FATAL
,
1734 return EFI_UNSUPPORTED
;
1737 Size
+= sizeof (UINT16
);
1740 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1741 if (OPERAND2_INDIRECT (Operands
)) {
1742 Op2Index
= VmReadIndex16 (VmPtr
, Size
);
1744 Op2Index
= VmReadImmed16 (VmPtr
, Size
);
1747 Size
+= sizeof (UINT16
);
1750 // Get the data from the source.
1752 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1753 if (OPERAND2_INDIRECT (Operands
)) {
1754 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1757 // Now write back the result.
1759 if (!OPERAND1_INDIRECT (Operands
)) {
1760 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1762 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1765 // Advance the instruction pointer
1773 Execute the EBC MOVsnw instruction. This instruction loads a signed
1774 natural value from memory or register to another memory or register. On
1775 32-bit machines, the value gets sign-extended to 64 bits if the destination
1778 @param VmPtr pointer to a VM context
1780 @return Standard EFI_STATUS
1781 @return Instruction syntax:
1782 @return MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1783 @return 0:7 1=>operand1 index present
1784 @return 0:6 1=>operand2 index present
1790 IN VM_CONTEXT
*VmPtr
1801 // Get the opcode and operand bytes
1803 Opcode
= GETOPCODE (VmPtr
);
1804 Operands
= GETOPERANDS (VmPtr
);
1806 Op1Index
= Op2Index
= 0;
1809 // Get the indexes if present.
1812 if (Opcode
& OPCODE_M_IMMED_OP1
) {
1813 if (OPERAND1_INDIRECT (Operands
)) {
1814 Op1Index
= VmReadIndex32 (VmPtr
, 2);
1817 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1819 EbcDebugSignalException (
1820 EXCEPT_EBC_INSTRUCTION_ENCODING
,
1821 EXCEPTION_FLAG_FATAL
,
1824 return EFI_UNSUPPORTED
;
1827 Size
+= sizeof (UINT32
);
1830 if (Opcode
& OPCODE_M_IMMED_OP2
) {
1831 if (OPERAND2_INDIRECT (Operands
)) {
1832 Op2Index
= VmReadIndex32 (VmPtr
, Size
);
1834 Op2Index
= VmReadImmed32 (VmPtr
, Size
);
1837 Size
+= sizeof (UINT32
);
1840 // Get the data from the source.
1842 Op2
= (INT64
) ((INTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Op2Index
));
1843 if (OPERAND2_INDIRECT (Operands
)) {
1844 Op2
= (INT64
) (INTN
) VmReadMemN (VmPtr
, (UINTN
) Op2
);
1847 // Now write back the result.
1849 if (!OPERAND1_INDIRECT (Operands
)) {
1850 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
1852 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Op1Index
), (UINTN
) Op2
);
1855 // Advance the instruction pointer
1863 Execute the EBC PUSHn instruction
1865 @param VmPtr pointer to a VM context
1867 @return Standard EFI_STATUS
1868 @return Instruction syntax:
1869 @return PUSHn {@}R1 {Index16|Immed16}
1875 IN VM_CONTEXT
*VmPtr
1884 // Get opcode and operands
1886 Opcode
= GETOPCODE (VmPtr
);
1887 Operands
= GETOPERANDS (VmPtr
);
1890 // Get index if present
1892 if (Opcode
& PUSHPOP_M_IMMDATA
) {
1893 if (OPERAND1_INDIRECT (Operands
)) {
1894 Index16
= VmReadIndex16 (VmPtr
, 2);
1896 Index16
= VmReadImmed16 (VmPtr
, 2);
1905 // Get the data to push
1907 if (OPERAND1_INDIRECT (Operands
)) {
1908 DataN
= VmReadMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
1910 DataN
= (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
);
1913 // Adjust the stack down.
1915 VmPtr
->R
[0] -= sizeof (UINTN
);
1916 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], DataN
);
1922 Execute the EBC PUSH instruction
1924 @param VmPtr pointer to a VM context
1926 @return Standard EFI_STATUS
1927 @return Instruction syntax:
1928 @return PUSH[32|64] {@}R1 {Index16|Immed16}
1934 IN VM_CONTEXT
*VmPtr
1944 // Get opcode and operands
1946 Opcode
= GETOPCODE (VmPtr
);
1947 Operands
= GETOPERANDS (VmPtr
);
1949 // Get immediate index if present, then advance the IP.
1951 if (Opcode
& PUSHPOP_M_IMMDATA
) {
1952 if (OPERAND1_INDIRECT (Operands
)) {
1953 Index16
= VmReadIndex16 (VmPtr
, 2);
1955 Index16
= VmReadImmed16 (VmPtr
, 2);
1964 // Get the data to push
1966 if (Opcode
& PUSHPOP_M_64
) {
1967 if (OPERAND1_INDIRECT (Operands
)) {
1968 Data64
= VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
1970 Data64
= (UINT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1973 // Adjust the stack down, then write back the data
1975 VmPtr
->R
[0] -= sizeof (UINT64
);
1976 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data64
);
1981 if (OPERAND1_INDIRECT (Operands
)) {
1982 Data32
= VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
));
1984 Data32
= (UINT32
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
;
1987 // Adjust the stack down and write the data
1989 VmPtr
->R
[0] -= sizeof (UINT32
);
1990 VmWriteMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0], Data32
);
1998 Execute the EBC POPn instruction
2000 @param VmPtr pointer to a VM context
2002 @return Standard EFI_STATUS
2003 @return Instruction syntax:
2004 @return POPn {@}R1 {Index16|Immed16}
2010 IN VM_CONTEXT
*VmPtr
2019 // Get opcode and operands
2021 Opcode
= GETOPCODE (VmPtr
);
2022 Operands
= GETOPERANDS (VmPtr
);
2024 // Get immediate data if present, and advance the IP
2026 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2027 if (OPERAND1_INDIRECT (Operands
)) {
2028 Index16
= VmReadIndex16 (VmPtr
, 2);
2030 Index16
= VmReadImmed16 (VmPtr
, 2);
2039 // Read the data off the stack, then adjust the stack pointer
2041 DataN
= VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2042 VmPtr
->R
[0] += sizeof (UINTN
);
2044 // Do the write-back
2046 if (OPERAND1_INDIRECT (Operands
)) {
2047 VmWriteMemN (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), DataN
);
2049 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) (UINT64
) ((UINTN
) DataN
+ Index16
);
2057 Execute the EBC POP instruction
2059 @param VmPtr pointer to a VM context
2061 @return Standard EFI_STATUS
2062 @return Instruction syntax:
2063 @return POP {@}R1 {Index16|Immed16}
2069 IN VM_CONTEXT
*VmPtr
2079 // Get opcode and operands
2081 Opcode
= GETOPCODE (VmPtr
);
2082 Operands
= GETOPERANDS (VmPtr
);
2084 // Get immediate data if present, and advance the IP.
2086 if (Opcode
& PUSHPOP_M_IMMDATA
) {
2087 if (OPERAND1_INDIRECT (Operands
)) {
2088 Index16
= VmReadIndex16 (VmPtr
, 2);
2090 Index16
= VmReadImmed16 (VmPtr
, 2);
2099 // Get the data off the stack, then write it to the appropriate location
2101 if (Opcode
& PUSHPOP_M_64
) {
2103 // Read the data off the stack, then adjust the stack pointer
2105 Data64
= VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2106 VmPtr
->R
[0] += sizeof (UINT64
);
2108 // Do the write-back
2110 if (OPERAND1_INDIRECT (Operands
)) {
2111 VmWriteMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data64
);
2113 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Data64
+ Index16
;
2117 // 32-bit pop. Read it off the stack and adjust the stack pointer
2119 Data32
= (INT32
) VmReadMem32 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2120 VmPtr
->R
[0] += sizeof (UINT32
);
2122 // Do the write-back
2124 if (OPERAND1_INDIRECT (Operands
)) {
2125 VmWriteMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND1_REGNUM (Operands
)] + Index16
), Data32
);
2127 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (INT64
) Data32
+ Index16
;
2136 Implements the EBC CALL instruction.
2139 CALL32 {@}R1 {Immed32|Index32}
2141 CALLEX16 {@}R1 {Immed32}
2142 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2144 @param VmPtr pointer to a VM context.
2146 @return Standard EFI_STATUS
2152 IN VM_CONTEXT
*VmPtr
2163 // Get opcode and operands
2165 Opcode
= GETOPCODE (VmPtr
);
2166 Operands
= GETOPERANDS (VmPtr
);
2168 // Assign these as well to avoid compiler warnings
2173 FramePtr
= VmPtr
->FramePtr
;
2175 // Determine the instruction size, and get immediate data if present
2177 if (Opcode
& OPCODE_M_IMMDATA
) {
2178 if (Opcode
& OPCODE_M_IMMDATA64
) {
2179 Immed64
= VmReadImmed64 (VmPtr
, 2);
2183 // If register operand is indirect, then the immediate data is an index
2185 if (OPERAND1_INDIRECT (Operands
)) {
2186 Immed32
= VmReadIndex32 (VmPtr
, 2);
2188 Immed32
= VmReadImmed32 (VmPtr
, 2);
2197 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2198 // put our return address and frame pointer on the VM stack.
2200 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2202 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
2203 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
2205 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (UINTN
) (VmPtr
->Ip
+ Size
));
2208 // If 64-bit data, then absolute jump only
2210 if (Opcode
& OPCODE_M_IMMDATA64
) {
2212 // Native or EBC call?
2214 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2215 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2218 // Call external function, get the return value, and advance the IP
2220 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2224 // Get the register data. If operand1 == 0, then ignore register and
2225 // take immediate data as relative or absolute address.
2226 // Compiler should take care of upper bits if 32-bit machine.
2228 if (OPERAND1_REGNUM (Operands
) != 0) {
2229 Immed64
= (UINT64
) (UINTN
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2232 // Get final address
2234 if (OPERAND1_INDIRECT (Operands
)) {
2235 Immed64
= (INT64
) (UINT64
) (UINTN
) VmReadMemN (VmPtr
, (UINTN
) (Immed64
+ Immed32
));
2240 // Now determine if external call, and then if relative or absolute
2242 if ((Operands
& OPERAND_M_NATIVE_CALL
) == 0) {
2244 // EBC call. Relative or absolute? If relative, then it's relative to the
2245 // start of the next instruction.
2247 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2248 VmPtr
->Ip
+= Immed64
+ Size
;
2250 VmPtr
->Ip
= (VMIP
) (UINTN
) Immed64
;
2254 // Native call. Relative or absolute?
2256 if (Operands
& OPERAND_M_RELATIVE_ADDR
) {
2257 EbcLLCALLEX (VmPtr
, (UINTN
) (Immed64
+ VmPtr
->Ip
+ Size
), (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2259 if (VmPtr
->StopFlags
& STOPFLAG_BREAK_ON_CALLEX
) {
2263 EbcLLCALLEX (VmPtr
, (UINTN
) Immed64
, (UINTN
) VmPtr
->R
[0], FramePtr
, Size
);
2273 Execute the EBC RET instruction
2275 @param VmPtr pointer to a VM context
2277 @return Standard EFI_STATUS
2278 @return Instruction syntax:
2285 IN VM_CONTEXT
*VmPtr
2289 // If we're at the top of the stack, then simply set the done
2292 if (VmPtr
->StackRetAddr
== (UINT64
) VmPtr
->R
[0]) {
2293 VmPtr
->StopFlags
|= STOPFLAG_APP_DONE
;
2296 // Pull the return address off the VM app's stack and set the IP
2299 if (!IS_ALIGNED ((UINTN
) VmPtr
->R
[0], sizeof (UINT16
))) {
2300 EbcDebugSignalException (
2301 EXCEPT_EBC_ALIGNMENT_CHECK
,
2302 EXCEPTION_FLAG_FATAL
,
2307 // Restore the IP and frame pointer from the stack
2309 VmPtr
->Ip
= (VMIP
) (UINTN
) VmReadMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2311 VmPtr
->FramePtr
= (VOID
*) VmReadMemN (VmPtr
, (UINTN
) VmPtr
->R
[0]);
2320 Execute the EBC CMP instruction
2322 @param VmPtr pointer to a VM context
2324 @return Standard EFI_STATUS
2325 @return Instruction syntax:
2326 @return CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2332 IN VM_CONTEXT
*VmPtr
2344 // Get opcode and operands
2346 Opcode
= GETOPCODE (VmPtr
);
2347 Operands
= GETOPERANDS (VmPtr
);
2349 // Get the register data we're going to compare to
2351 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2353 // Get immediate data
2355 if (Opcode
& OPCODE_M_IMMDATA
) {
2356 if (OPERAND2_INDIRECT (Operands
)) {
2357 Index16
= VmReadIndex16 (VmPtr
, 2);
2359 Index16
= VmReadImmed16 (VmPtr
, 2);
2370 if (OPERAND2_INDIRECT (Operands
)) {
2371 if (Opcode
& OPCODE_M_64BIT
) {
2372 Op2
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
));
2375 // 32-bit operations. 0-extend the values for all cases.
2377 Op2
= (INT64
) (UINT64
) ((UINT32
) VmReadMem32 (VmPtr
, (UINTN
) (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
)));
2380 Op2
= VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
2383 // Now do the compare
2386 if (Opcode
& OPCODE_M_64BIT
) {
2390 switch (Opcode
& OPCODE_M_OPCODE
) {
2409 case OPCODE_CMPULTE
:
2410 if ((UINT64
) Op1
<= (UINT64
) Op2
) {
2415 case OPCODE_CMPUGTE
:
2416 if ((UINT64
) Op1
>= (UINT64
) Op2
) {
2428 switch (Opcode
& OPCODE_M_OPCODE
) {
2430 if ((INT32
) Op1
== (INT32
) Op2
) {
2436 if ((INT32
) Op1
<= (INT32
) Op2
) {
2442 if ((INT32
) Op1
>= (INT32
) Op2
) {
2447 case OPCODE_CMPULTE
:
2448 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2453 case OPCODE_CMPUGTE
:
2454 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2464 // Now set the flag accordingly for the comparison
2467 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2469 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2480 Execute the EBC CMPI instruction
2482 @param VmPtr pointer to a VM context
2484 @return Standard EFI_STATUS
2485 @return Instruction syntax:
2486 @return CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2492 IN VM_CONTEXT
*VmPtr
2504 // Get opcode and operands
2506 Opcode
= GETOPCODE (VmPtr
);
2507 Operands
= GETOPERANDS (VmPtr
);
2510 // Get operand1 index if present
2513 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2514 Index16
= VmReadIndex16 (VmPtr
, 2);
2520 // Get operand1 data we're going to compare to
2522 Op1
= (INT64
) VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
2523 if (OPERAND1_INDIRECT (Operands
)) {
2525 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2527 if (Opcode
& OPCODE_M_CMPI64
) {
2528 Op1
= (INT64
) VmReadMem64 (VmPtr
, (UINTN
) Op1
+ Index16
);
2530 Op1
= (INT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
+ Index16
);
2534 // Better not have been an index with direct. That is, CMPI R1 Index,...
2537 if (Operands
& OPERAND_M_CMPI_INDEX
) {
2538 EbcDebugSignalException (
2539 EXCEPT_EBC_INSTRUCTION_ENCODING
,
2540 EXCEPTION_FLAG_ERROR
,
2544 return EFI_UNSUPPORTED
;
2548 // Get immediate data -- 16- or 32-bit sign extended
2550 if (Opcode
& OPCODE_M_CMPI32_DATA
) {
2551 Op2
= (INT64
) VmReadImmed32 (VmPtr
, Size
);
2555 // 16-bit immediate data. Sign extend always.
2557 Op2
= (INT64
) ((INT16
) VmReadImmed16 (VmPtr
, Size
));
2561 // Now do the compare
2564 if (Opcode
& OPCODE_M_CMPI64
) {
2566 // 64 bit comparison
2568 switch (Opcode
& OPCODE_M_OPCODE
) {
2570 if (Op1
== (INT64
) Op2
) {
2575 case OPCODE_CMPILTE
:
2576 if (Op1
<= (INT64
) Op2
) {
2581 case OPCODE_CMPIGTE
:
2582 if (Op1
>= (INT64
) Op2
) {
2587 case OPCODE_CMPIULTE
:
2588 if ((UINT64
) Op1
<= (UINT64
) ((UINT32
) Op2
)) {
2593 case OPCODE_CMPIUGTE
:
2594 if ((UINT64
) Op1
>= (UINT64
) ((UINT32
) Op2
)) {
2604 // 32-bit comparisons
2606 switch (Opcode
& OPCODE_M_OPCODE
) {
2608 if ((INT32
) Op1
== Op2
) {
2613 case OPCODE_CMPILTE
:
2614 if ((INT32
) Op1
<= Op2
) {
2619 case OPCODE_CMPIGTE
:
2620 if ((INT32
) Op1
>= Op2
) {
2625 case OPCODE_CMPIULTE
:
2626 if ((UINT32
) Op1
<= (UINT32
) Op2
) {
2631 case OPCODE_CMPIUGTE
:
2632 if ((UINT32
) Op1
>= (UINT32
) Op2
) {
2642 // Now set the flag accordingly for the comparison
2645 VMFLAG_SET (VmPtr
, VMFLAGS_CC
);
2647 VMFLAG_CLEAR (VmPtr
, VMFLAGS_CC
);
2658 Execute the EBC NOT instruction
2660 @param VmPtr pointer to a VM context
2661 @param Op1 Operand 1 from the instruction
2662 @param Op2 Operand 2 from the instruction
2665 @return Instruction syntax:
2666 @return NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2672 IN VM_CONTEXT
*VmPtr
,
2682 Execute the EBC NEG instruction
2684 @param VmPtr pointer to a VM context
2685 @param Op1 Operand 1 from the instruction
2686 @param Op2 Operand 2 from the instruction
2689 @return Instruction syntax:
2690 @return NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2696 IN VM_CONTEXT
*VmPtr
,
2706 Execute the EBC ADD instruction
2708 @param VmPtr pointer to a VM context
2709 @param Op1 Operand 1 from the instruction
2710 @param Op2 Operand 2 from the instruction
2713 @return Instruction syntax:
2714 @return ADD[32|64] {@}R1, {@}R2 {Index16}
2720 IN VM_CONTEXT
*VmPtr
,
2730 Execute the EBC SUB instruction
2732 @param VmPtr pointer to a VM context
2733 @param Op1 Operand 1 from the instruction
2734 @param Op2 Operand 2 from the instruction
2736 @retval Op1 Op2 Standard EFI_STATUS
2737 @return Instruction syntax:
2738 @return SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2744 IN VM_CONTEXT
*VmPtr
,
2749 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2750 return (UINT64
) ((INT64
) ((INT64
) Op1
- (INT64
) Op2
));
2752 return (UINT64
) ((INT64
) ((INT32
) Op1
- (INT32
) Op2
));
2758 Execute the EBC MUL instruction
2760 @param VmPtr pointer to a VM context
2761 @param Op1 Operand 1 from the instruction
2762 @param Op2 Operand 2 from the instruction
2765 @return Instruction syntax:
2766 @return MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2772 IN VM_CONTEXT
*VmPtr
,
2777 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2778 return MultS64x64 ((INT64
)Op1
, (INT64
)Op2
);
2780 return (UINT64
) ((INT64
) ((INT32
) Op1
* (INT32
) Op2
));
2786 Execute the EBC MULU instruction
2788 @param VmPtr pointer to a VM context
2789 @param Op1 Operand 1 from the instruction
2790 @param Op2 Operand 2 from the instruction
2792 @return (unsigned)Op1 * (unsigned)Op2
2793 @return Instruction syntax:
2794 @return MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2800 IN VM_CONTEXT
*VmPtr
,
2805 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2806 return MultU64x64 (Op1
, Op2
);
2808 return (UINT64
) ((UINT32
) Op1
* (UINT32
) Op2
);
2814 Execute the EBC DIV instruction
2816 @param VmPtr pointer to a VM context
2817 @param Op1 Operand 1 from the instruction
2818 @param Op2 Operand 2 from the instruction
2821 @return Instruction syntax:
2822 @return DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
2828 IN VM_CONTEXT
*VmPtr
,
2836 // Check for divide-by-0
2839 EbcDebugSignalException (
2840 EXCEPT_EBC_DIVIDE_ERROR
,
2841 EXCEPTION_FLAG_FATAL
,
2847 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2848 return (UINT64
) (DivS64x64Remainder (Op1
, Op2
, &Remainder
));
2850 return (UINT64
) ((INT64
) ((INT32
) Op1
/ (INT32
) Op2
));
2857 Execute the EBC DIVU instruction
2859 @param VmPtr pointer to a VM context
2860 @param Op1 Operand 1 from the instruction
2861 @param Op2 Operand 2 from the instruction
2863 @return (unsigned)Op1 / (unsigned)Op2
2864 @return Instruction syntax:
2865 @return DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2871 IN VM_CONTEXT
*VmPtr
,
2879 // Check for divide-by-0
2882 EbcDebugSignalException (
2883 EXCEPT_EBC_DIVIDE_ERROR
,
2884 EXCEPTION_FLAG_FATAL
,
2890 // Get the destination register
2892 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
2893 return (UINT64
) (DivU64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
));
2895 return (UINT64
) ((UINT32
) Op1
/ (UINT32
) Op2
);
2902 Execute the EBC MOD instruction
2904 @param VmPtr pointer to a VM context
2905 @param Op1 Operand 1 from the instruction
2906 @param Op2 Operand 2 from the instruction
2908 @return Op1 MODULUS Op2
2909 @return Instruction syntax:
2910 @return MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
2916 IN VM_CONTEXT
*VmPtr
,
2924 // Check for divide-by-0
2927 EbcDebugSignalException (
2928 EXCEPT_EBC_DIVIDE_ERROR
,
2929 EXCEPTION_FLAG_FATAL
,
2934 DivS64x64Remainder ((INT64
)Op1
, (INT64
)Op2
, &Remainder
);
2941 Execute the EBC MODU instruction
2943 @param VmPtr pointer to a VM context
2944 @param Op1 Operand 1 from the instruction
2945 @param Op2 Operand 2 from the instruction
2947 @return Op1 UNSIGNED_MODULUS Op2
2948 @return Instruction syntax:
2949 @return MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2955 IN VM_CONTEXT
*VmPtr
,
2963 // Check for divide-by-0
2966 EbcDebugSignalException (
2967 EXCEPT_EBC_DIVIDE_ERROR
,
2968 EXCEPTION_FLAG_FATAL
,
2973 DivU64x64Remainder (Op1
, Op2
, &Remainder
);
2980 Execute the EBC AND instruction
2982 @param VmPtr pointer to a VM context
2983 @param Op1 Operand 1 from the instruction
2984 @param Op2 Operand 2 from the instruction
2987 @return Instruction syntax:
2988 @return AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
2994 IN VM_CONTEXT
*VmPtr
,
3004 Execute the EBC OR instruction
3006 @param VmPtr pointer to a VM context
3007 @param Op1 Operand 1 from the instruction
3008 @param Op2 Operand 2 from the instruction
3011 @return Instruction syntax:
3012 @return OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3018 IN VM_CONTEXT
*VmPtr
,
3028 Execute the EBC XOR instruction
3030 @param VmPtr pointer to a VM context
3031 @param Op1 Operand 1 from the instruction
3032 @param Op2 Operand 2 from the instruction
3035 @return Instruction syntax:
3036 @return XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3042 IN VM_CONTEXT
*VmPtr
,
3052 Execute the EBC SHL shift left instruction
3054 @param VmPtr pointer to a VM context
3055 @param Op1 Operand 1 from the instruction
3056 @param Op2 Operand 2 from the instruction
3059 @return Instruction syntax:
3060 @return SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3066 IN VM_CONTEXT
*VmPtr
,
3071 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3072 return LShiftU64 (Op1
, (UINTN
)Op2
);
3074 return (UINT64
) ((UINT32
) ((UINT32
) Op1
<< (UINT32
) Op2
));
3080 Execute the EBC SHR instruction
3082 @param VmPtr pointer to a VM context
3083 @param Op1 Operand 1 from the instruction
3084 @param Op2 Operand 2 from the instruction
3086 @return Op1 >> Op2 (unsigned operands)
3087 @return Instruction syntax:
3088 @return SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3094 IN VM_CONTEXT
*VmPtr
,
3099 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3100 return RShiftU64 (Op1
, (UINTN
)Op2
);
3102 return (UINT64
) ((UINT32
) Op1
>> (UINT32
) Op2
);
3108 Execute the EBC ASHR instruction
3110 @param VmPtr pointer to a VM context
3111 @param Op1 Operand 1 from the instruction
3112 @param Op2 Operand 2 from the instruction
3114 @return Op1 >> Op2 (signed)
3115 @return Instruction syntax:
3116 @return ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3122 IN VM_CONTEXT
*VmPtr
,
3127 if (*VmPtr
->Ip
& DATAMANIP_M_64
) {
3128 return ARShiftU64 (Op1
, (UINTN
)Op2
);
3130 return (UINT64
) ((INT64
) ((INT32
) Op1
>> (UINT32
) Op2
));
3136 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3138 @param VmPtr pointer to a VM context
3139 @param Op1 Operand 1 from the instruction
3140 @param Op2 Operand 2 from the instruction
3142 @return (INT64)(INT8)Op2
3143 @return Instruction syntax:
3144 @return EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3150 IN VM_CONTEXT
*VmPtr
,
3158 // Convert to byte, then return as 64-bit signed value to let compiler
3159 // sign-extend the value
3162 Data64
= (INT64
) Data8
;
3164 return (UINT64
) Data64
;
3169 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3171 @param VmPtr pointer to a VM context
3172 @param Op1 Operand 1 from the instruction
3173 @param Op2 Operand 2 from the instruction
3175 @return (INT64)(INT16)Op2
3176 @return Instruction syntax:
3177 @return EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3183 IN VM_CONTEXT
*VmPtr
,
3191 // Convert to word, then return as 64-bit signed value to let compiler
3192 // sign-extend the value
3194 Data16
= (INT16
) Op2
;
3195 Data64
= (INT64
) Data16
;
3197 return (UINT64
) Data64
;
3200 // Execute the EBC EXTNDD instruction.
3202 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3203 // EXTNDD Dest, Source
3205 // Operation: Dest <- SignExtended((DWORD)Source))
3209 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3211 @param VmPtr pointer to a VM context
3212 @param Op1 Operand 1 from the instruction
3213 @param Op2 Operand 2 from the instruction
3215 @return (INT64)(INT32)Op2
3216 @return Instruction syntax:
3217 @return EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3223 IN VM_CONTEXT
*VmPtr
,
3231 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3232 // sign-extend the value
3234 Data32
= (INT32
) Op2
;
3235 Data64
= (INT64
) Data32
;
3237 return (UINT64
) Data64
;
3242 ExecuteSignedDataManip (
3243 IN VM_CONTEXT
*VmPtr
3247 // Just call the data manipulation function with a flag indicating this
3248 // is a signed operation.
3250 return ExecuteDataManip (VmPtr
, TRUE
);
3255 ExecuteUnsignedDataManip (
3256 IN VM_CONTEXT
*VmPtr
3260 // Just call the data manipulation function with a flag indicating this
3261 // is not a signed operation.
3263 return ExecuteDataManip (VmPtr
, FALSE
);
3268 Execute all the EBC data manipulation instructions.
3269 Since the EBC data manipulation instructions all have the same basic form,
3270 they can share the code that does the fetch of operands and the write-back
3271 of the result. This function performs the fetch of the operands (even if
3272 both are not needed to be fetched, like NOT instruction), dispatches to the
3273 appropriate subfunction, then writes back the returned result.
3275 @param VmPtr pointer to VM context
3277 @return Standard EBC status
3279 @return INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3285 IN VM_CONTEXT
*VmPtr
,
3286 IN BOOLEAN IsSignedOp
3297 // Get opcode and operands
3299 Opcode
= GETOPCODE (VmPtr
);
3300 Operands
= GETOPERANDS (VmPtr
);
3303 // Determine if we have immediate data by the opcode
3305 if (Opcode
& DATAMANIP_M_IMMDATA
) {
3307 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3309 if (OPERAND2_INDIRECT (Operands
)) {
3310 Index16
= VmReadIndex16 (VmPtr
, 2);
3312 Index16
= VmReadImmed16 (VmPtr
, 2);
3321 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3323 Op2
= (UINT64
) VmPtr
->R
[OPERAND2_REGNUM (Operands
)] + Index16
;
3324 if (OPERAND2_INDIRECT (Operands
)) {
3326 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3328 if (Opcode
& DATAMANIP_M_64
) {
3329 Op2
= VmReadMem64 (VmPtr
, (UINTN
) Op2
);
3332 // Read as signed value where appropriate.
3335 Op2
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op2
));
3337 Op2
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op2
);
3341 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3343 Op2
= (UINT64
) (INT64
) ((INT32
) Op2
);
3345 Op2
= (UINT64
) ((UINT32
) Op2
);
3350 // Get operand1 (destination and sometimes also an actual operand)
3353 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3354 if (OPERAND1_INDIRECT (Operands
)) {
3355 if (Opcode
& DATAMANIP_M_64
) {
3356 Op1
= VmReadMem64 (VmPtr
, (UINTN
) Op1
);
3359 Op1
= (UINT64
) (INT64
) ((INT32
) VmReadMem32 (VmPtr
, (UINTN
) Op1
));
3361 Op1
= (UINT64
) VmReadMem32 (VmPtr
, (UINTN
) Op1
);
3365 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3367 Op1
= (UINT64
) (INT64
) ((INT32
) Op1
);
3369 Op1
= (UINT64
) ((UINT32
) Op1
);
3374 // Dispatch to the computation function
3376 if (((Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
) >=
3377 (sizeof (mDataManipDispatchTable
) / sizeof (mDataManipDispatchTable
[0]))
3379 EbcDebugSignalException (
3380 EXCEPT_EBC_INVALID_OPCODE
,
3381 EXCEPTION_FLAG_ERROR
,
3385 // Advance and return
3388 return EFI_UNSUPPORTED
;
3390 Op2
= mDataManipDispatchTable
[(Opcode
& OPCODE_M_OPCODE
) - OPCODE_NOT
](VmPtr
, Op1
, Op2
);
3393 // Write back the result.
3395 if (OPERAND1_INDIRECT (Operands
)) {
3396 Op1
= VmPtr
->R
[OPERAND1_REGNUM (Operands
)];
3397 if (Opcode
& DATAMANIP_M_64
) {
3398 VmWriteMem64 (VmPtr
, (UINTN
) Op1
, Op2
);
3400 VmWriteMem32 (VmPtr
, (UINTN
) Op1
, (UINT32
) Op2
);
3404 // Storage back to a register. Write back, clearing upper bits (as per
3405 // the specification) if 32-bit operation.
3407 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = Op2
;
3408 if ((Opcode
& DATAMANIP_M_64
) == 0) {
3409 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] &= 0xFFFFFFFF;
3413 // Advance the instruction pointer
3421 Execute the EBC LOADSP instruction
3423 @param VmPtr pointer to a VM context
3425 @return Standard EFI_STATUS
3426 @return Instruction syntax:
3427 @return LOADSP SP1, R2
3433 IN VM_CONTEXT
*VmPtr
3441 Operands
= GETOPERANDS (VmPtr
);
3446 switch (OPERAND1_REGNUM (Operands
)) {
3452 // Spec states that this instruction will not modify reserved bits in
3453 // the flags register.
3455 VmPtr
->Flags
= (VmPtr
->Flags
&~VMFLAGS_ALL_VALID
) | (VmPtr
->R
[OPERAND2_REGNUM (Operands
)] & VMFLAGS_ALL_VALID
);
3459 EbcDebugSignalException (
3460 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3461 EXCEPTION_FLAG_WARNING
,
3465 return EFI_UNSUPPORTED
;
3474 Execute the EBC STORESP instruction
3476 @param VmPtr pointer to a VM context
3478 @return Standard EFI_STATUS
3479 @return Instruction syntax:
3480 @return STORESP Rx, FLAGS|IP
3486 IN VM_CONTEXT
*VmPtr
3494 Operands
= GETOPERANDS (VmPtr
);
3499 switch (OPERAND2_REGNUM (Operands
)) {
3505 // Retrieve the value in the flags register, then clear reserved bits
3507 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (VmPtr
->Flags
& VMFLAGS_ALL_VALID
);
3511 // Get IP -- address of following instruction
3514 VmPtr
->R
[OPERAND1_REGNUM (Operands
)] = (UINT64
) (UINTN
) VmPtr
->Ip
+ 2;
3518 EbcDebugSignalException (
3519 EXCEPT_EBC_INSTRUCTION_ENCODING
,
3520 EXCEPTION_FLAG_WARNING
,
3524 return EFI_UNSUPPORTED
;
3534 Decode a 16-bit index to determine the offset. Given an index value:
3536 b14:12 - number of bits in this index assigned to natural units (=a)
3537 ba:11 - constant units = C
3538 b0:a - natural units = N
3539 Given this info, the offset can be computed by:
3540 offset = sign_bit * (C + N * sizeof(UINTN))
3541 Max offset is achieved with index = 0x7FFF giving an offset of
3542 0x27B (32-bit machine) or 0x477 (64-bit machine).
3543 Min offset is achieved with index =
3545 @param VmPtr pointer to VM context
3546 @param CodeOffset offset from IP of the location of the 16-bit index to
3549 @return The decoded offset.
3555 IN VM_CONTEXT
*VmPtr
,
3556 IN UINT32 CodeOffset
3567 // First read the index from the code stream
3569 Index
= VmReadCode16 (VmPtr
, CodeOffset
);
3572 // Get the mask for N. First get the number of bits from the index.
3574 NBits
= (INT16
) ((Index
& 0x7000) >> 12);
3577 // Scale it for 16-bit indexes
3582 // Now using the number of bits, create a mask.
3584 Mask
= (INT16
) ((INT16
)~0 << NBits
);
3587 // Now using the mask, extract N from the lower bits of the index.
3589 N
= (INT16
) (Index
&~Mask
);
3594 C
= (INT16
) (((Index
&~0xF000) & Mask
) >> NBits
);
3596 Offset
= (INT16
) (N
* sizeof (UINTN
) + C
);
3601 if (Index
& 0x8000) {
3603 // Do it the hard way to work around a bogus compiler warning
3605 // Offset = -1 * Offset;
3607 Offset
= (INT16
) ((INT32
) Offset
* -1);
3615 Decode a 32-bit index to determine the offset.
3617 @param VmPtr pointer to VM context
3618 @param CodeOffset offset from IP of the location of the 32-bit index to
3621 @return Converted index per EBC VM specification
3627 IN VM_CONTEXT
*VmPtr
,
3628 IN UINT32 CodeOffset
3638 Index
= VmReadImmed32 (VmPtr
, CodeOffset
);
3641 // Get the mask for N. First get the number of bits from the index.
3643 NBits
= (Index
& 0x70000000) >> 28;
3646 // Scale it for 32-bit indexes
3651 // Now using the number of bits, create a mask.
3653 Mask
= (INT32
)~0 << NBits
;
3656 // Now using the mask, extract N from the lower bits of the index.
3663 C
= ((Index
&~0xF0000000) & Mask
) >> NBits
;
3665 Offset
= N
* sizeof (UINTN
) + C
;
3670 if (Index
& 0x80000000) {
3671 Offset
= Offset
* -1;
3679 Decode a 64-bit index to determine the offset.
3681 @param VmPtr pointer to VM context
3682 @param CodeOffset offset from IP of the location of the 64-bit index to
3685 @return Converted index per EBC VM specification
3691 IN VM_CONTEXT
*VmPtr
,
3692 IN UINT32 CodeOffset
3702 Index
= VmReadCode64 (VmPtr
, CodeOffset
);
3705 // Get the mask for N. First get the number of bits from the index.
3707 NBits
= RShiftU64 ((Index
& 0x7000000000000000ULL
), 60);
3710 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3712 NBits
= LShiftU64 ((UINT64
)NBits
, 3);
3715 // Now using the number of bits, create a mask.
3717 Mask
= (LShiftU64 ((UINT64
)~0, (UINTN
)NBits
));
3720 // Now using the mask, extract N from the lower bits of the index.
3727 C
= ARShiftU64 (((Index
&~0xF000000000000000ULL
) & Mask
), (UINTN
)NBits
);
3729 Offset
= MultU64x64 (N
, sizeof (UINTN
)) + C
;
3734 if (Index
& 0x8000000000000000ULL
) {
3735 Offset
= MultS64x64 (Offset
, -1);
3743 The following VmWriteMem? routines are called by the EBC data
3744 movement instructions that write to memory. Since these writes
3745 may be to the stack, which looks like (high address on top) this,
3746 [EBC entry point arguments]
3749 we need to detect all attempts to write to the EBC entry point argument
3750 stack area and adjust the address (which will initially point into the
3751 VM stack) to point into the EBC entry point arguments.
3753 @param VmPtr pointer to a VM context
3754 @param Addr adddress to write to
3755 @param Data value to write to Addr
3757 @return Standard EFI_STATUS
3763 IN VM_CONTEXT
*VmPtr
,
3769 // Convert the address if it's in the stack gap
3771 Addr
= ConvertStackAddr (VmPtr
, Addr
);
3772 *(UINT8
*) Addr
= Data
;
3779 IN VM_CONTEXT
*VmPtr
,
3787 // Convert the address if it's in the stack gap
3789 Addr
= ConvertStackAddr (VmPtr
, Addr
);
3792 // Do a simple write if aligned
3794 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
3795 *(UINT16
*) Addr
= Data
;
3798 // Write as two bytes
3801 if ((Status
= VmWriteMem8 (VmPtr
, Addr
, (UINT8
) Data
)) != EFI_SUCCESS
) {
3806 if ((Status
= VmWriteMem8 (VmPtr
, Addr
+ 1, (UINT8
) (Data
>> 8))) != EFI_SUCCESS
) {
3819 IN VM_CONTEXT
*VmPtr
,
3827 // Convert the address if it's in the stack gap
3829 Addr
= ConvertStackAddr (VmPtr
, Addr
);
3832 // Do a simple write if aligned
3834 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
3835 *(UINT32
*) Addr
= Data
;
3838 // Write as two words
3841 if ((Status
= VmWriteMem16 (VmPtr
, Addr
, (UINT16
) Data
)) != EFI_SUCCESS
) {
3846 if ((Status
= VmWriteMem16 (VmPtr
, Addr
+ sizeof (UINT16
), (UINT16
) (Data
>> 16))) != EFI_SUCCESS
) {
3858 IN VM_CONTEXT
*VmPtr
,
3867 // Convert the address if it's in the stack gap
3869 Addr
= ConvertStackAddr (VmPtr
, Addr
);
3872 // Do a simple write if aligned
3874 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
3875 *(UINT64
*) Addr
= Data
;
3878 // Write as two 32-bit words
3881 if ((Status
= VmWriteMem32 (VmPtr
, Addr
, (UINT32
) Data
)) != EFI_SUCCESS
) {
3886 Data32
= (UINT32
) (((UINT32
*) &Data
)[1]);
3887 if ((Status
= VmWriteMem32 (VmPtr
, Addr
+ sizeof (UINT32
), Data32
)) != EFI_SUCCESS
) {
3899 IN VM_CONTEXT
*VmPtr
,
3907 Status
= EFI_SUCCESS
;
3910 // Convert the address if it's in the stack gap
3912 Addr
= ConvertStackAddr (VmPtr
, Addr
);
3915 // Do a simple write if aligned
3917 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
3918 *(UINTN
*) Addr
= Data
;
3920 for (Index
= 0; Index
< sizeof (UINTN
) / sizeof (UINT32
); Index
++) {
3922 Status
= VmWriteMem32 (VmPtr
, Addr
+ Index
* sizeof (UINT32
), (UINT32
) Data
);
3924 Data
= (UINTN
)RShiftU64 ((UINT64
)Data
, 32);
3933 The following VmReadImmed routines are called by the EBC execute
3934 functions to read EBC immediate values from the code stream.
3935 Since we can't assume alignment, each tries to read in the biggest
3936 chunks size available, but will revert to smaller reads if necessary.
3938 @param VmPtr pointer to a VM context
3939 @param Offset offset from IP of the code bytes to read.
3941 @return Signed data of the requested size from the specified address.
3947 IN VM_CONTEXT
*VmPtr
,
3952 // Simply return the data in flat memory space
3954 return * (INT8
*) (VmPtr
->Ip
+ Offset
);
3960 IN VM_CONTEXT
*VmPtr
,
3965 // Read direct if aligned
3967 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (INT16
))) {
3968 return * (INT16
*) (VmPtr
->Ip
+ Offset
);
3971 // All code word reads should be aligned
3973 EbcDebugSignalException (
3974 EXCEPT_EBC_ALIGNMENT_CHECK
,
3975 EXCEPTION_FLAG_WARNING
,
3980 // Return unaligned data
3982 return (INT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
3988 IN VM_CONTEXT
*VmPtr
,
3995 // Read direct if aligned
3997 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
3998 return * (INT32
*) (VmPtr
->Ip
+ Offset
);
4001 // Return unaligned data
4003 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4004 Data
|= (UINT32
) (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4011 IN VM_CONTEXT
*VmPtr
,
4020 // Read direct if aligned
4022 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4023 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4026 // Return unaligned data.
4028 Ptr
= (UINT8
*) &Data64
;
4029 Data32
= VmReadCode32 (VmPtr
, Offset
);
4030 *(UINT32
*) Ptr
= Data32
;
4031 Ptr
+= sizeof (Data32
);
4032 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4033 *(UINT32
*) Ptr
= Data32
;
4039 The following VmReadCode() routines provide the ability to read raw
4040 unsigned data from the code stream.
4042 @param VmPtr pointer to VM context
4043 @param Offset offset from current IP to the raw data to read.
4045 @return The raw unsigned 16-bit value from the code stream.
4051 IN VM_CONTEXT
*VmPtr
,
4056 // Read direct if aligned
4058 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT16
))) {
4059 return * (UINT16
*) (VmPtr
->Ip
+ Offset
);
4062 // All code word reads should be aligned
4064 EbcDebugSignalException (
4065 EXCEPT_EBC_ALIGNMENT_CHECK
,
4066 EXCEPTION_FLAG_WARNING
,
4071 // Return unaligned data
4073 return (UINT16
) (*(UINT8
*) (VmPtr
->Ip
+ Offset
) + (*(UINT8
*) (VmPtr
->Ip
+ Offset
+ 1) << 8));
4079 IN VM_CONTEXT
*VmPtr
,
4085 // Read direct if aligned
4087 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT32
))) {
4088 return * (UINT32
*) (VmPtr
->Ip
+ Offset
);
4091 // Return unaligned data
4093 Data
= (UINT32
) VmReadCode16 (VmPtr
, Offset
);
4094 Data
|= (VmReadCode16 (VmPtr
, Offset
+ 2) << 16);
4101 IN VM_CONTEXT
*VmPtr
,
4110 // Read direct if aligned
4112 if (IS_ALIGNED ((UINTN
) VmPtr
->Ip
+ Offset
, sizeof (UINT64
))) {
4113 return * (UINT64
*) (VmPtr
->Ip
+ Offset
);
4116 // Return unaligned data.
4118 Ptr
= (UINT8
*) &Data64
;
4119 Data32
= VmReadCode32 (VmPtr
, Offset
);
4120 *(UINT32
*) Ptr
= Data32
;
4121 Ptr
+= sizeof (Data32
);
4122 Data32
= VmReadCode32 (VmPtr
, Offset
+ sizeof (UINT32
));
4123 *(UINT32
*) Ptr
= Data32
;
4130 IN VM_CONTEXT
*VmPtr
,
4135 // Convert the address if it's in the stack gap
4137 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4139 // Simply return the data in flat memory space
4141 return * (UINT8
*) Addr
;
4147 IN VM_CONTEXT
*VmPtr
,
4152 // Convert the address if it's in the stack gap
4154 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4156 // Read direct if aligned
4158 if (IS_ALIGNED (Addr
, sizeof (UINT16
))) {
4159 return * (UINT16
*) Addr
;
4162 // Return unaligned data
4164 return (UINT16
) (*(UINT8
*) Addr
+ (*(UINT8
*) (Addr
+ 1) << 8));
4170 IN VM_CONTEXT
*VmPtr
,
4177 // Convert the address if it's in the stack gap
4179 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4181 // Read direct if aligned
4183 if (IS_ALIGNED (Addr
, sizeof (UINT32
))) {
4184 return * (UINT32
*) Addr
;
4187 // Return unaligned data
4189 Data
= (UINT32
) VmReadMem16 (VmPtr
, Addr
);
4190 Data
|= (VmReadMem16 (VmPtr
, Addr
+ 2) << 16);
4197 IN VM_CONTEXT
*VmPtr
,
4205 // Convert the address if it's in the stack gap
4207 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4210 // Read direct if aligned
4212 if (IS_ALIGNED (Addr
, sizeof (UINT64
))) {
4213 return * (UINT64
*) Addr
;
4216 // Return unaligned data. Assume little endian.
4218 Data
= (UINT64
) VmReadMem32 (VmPtr
, Addr
);
4219 Data32
= VmReadMem32 (VmPtr
, Addr
+ sizeof (UINT32
));
4220 *(UINT32
*) ((UINT32
*) &Data
+ 1) = Data32
;
4226 Given an address that EBC is going to read from or write to, return
4227 an appropriate address that accounts for a gap in the stack.
4228 The stack for this application looks like this (high addr on top)
4229 [EBC entry point arguments]
4232 The EBC assumes that its arguments are at the top of its stack, which
4233 is where the VM stack is really. Therefore if the EBC does memory
4234 accesses into the VM stack area, then we need to convert the address
4235 to point to the EBC entry point arguments area. Do this here.
4237 @param VmPtr pointer to VM context
4238 @param Addr address of interest
4240 @return The unchanged address if it's not in the VM stack region. Otherwise,
4241 @return adjust for the stack gap and return the modified address.
4247 IN VM_CONTEXT
*VmPtr
,
4251 ASSERT(((Addr
< VmPtr
->LowStackTop
) || (Addr
> VmPtr
->HighStackBottom
)));
4257 Read a natural value from memory. May or may not be aligned.
4259 @param VmPtr current VM context
4260 @param Addr the address to read from
4262 @return The natural value at address Addr.
4268 IN VM_CONTEXT
*VmPtr
,
4273 volatile UINT32 Size
;
4277 // Convert the address if it's in the stack gap
4279 Addr
= ConvertStackAddr (VmPtr
, Addr
);
4281 // Read direct if aligned
4283 if (IS_ALIGNED (Addr
, sizeof (UINTN
))) {
4284 return * (UINTN
*) Addr
;
4287 // Return unaligned data
4290 FromPtr
= (UINT8
*) Addr
;
4291 ToPtr
= (UINT8
*) &Data
;
4293 for (Size
= 0; Size
< sizeof (Data
); Size
++) {
4307 return (UINT64
) (((VM_MAJOR_VERSION
& 0xFFFF) << 16) | ((VM_MINOR_VERSION
& 0xFFFF)));