ebcexecute.c
来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 2,978 行 · 第 1/5 页
C
2,978 行
/*++
Routine Description:
Given a pointer to a new VM context, execute one or more instructions. This
function is only used for test purposes via the EBC VM test protocol.
Arguments:
This - pointer to protocol interface
VmPtr - pointer to a VM context
InstructionCount - how many instructions to execute. 0 if don't count.
Returns:
EFI_UNSUPPORTED
EFI_SUCCESS
--*/
{
UINTN ExecFunc;
EFI_STATUS Status;
UINTN InstructionsLeft;
UINTN SavedInstructionCount;
Status = EFI_SUCCESS;
if (*InstructionCount == 0) {
InstructionsLeft = 1;
} else {
InstructionsLeft = *InstructionCount;
}
SavedInstructionCount = *InstructionCount;
*InstructionCount = 0;
//
// Index into the opcode table using the opcode byte for this instruction.
// This gives you the execute function, which we first test for null, then
// call it if it's not null.
//
while (InstructionsLeft != 0) {
ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & 0x3F)].ExecuteFunction;
if (ExecFunc == (UINTN) NULL) {
EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
return EFI_UNSUPPORTED;
} else {
mVmOpcodeTable[(*VmPtr->Ip & 0x3F)].ExecuteFunction (VmPtr);
*InstructionCount = *InstructionCount + 1;
}
//
// Decrement counter if applicable
//
if (SavedInstructionCount != 0) {
InstructionsLeft--;
}
}
return Status;
}
EFI_STATUS
EbcExecute (
IN VM_CONTEXT *VmPtr
)
/*++
Routine Description:
Execute an EBC image from an entry point or from a published protocol.
Arguments:
VmPtr - pointer to prepared VM context.
Returns:
Standard EBC status.
--*/
{
UINTN ExecFunc;
UINT8 StackCorrupted;
EFI_STATUS Status;
DEBUG_CODE (
EFI_GUID EbcSimpleDebuggerProtocolGuid = EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID;
EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL * EbcSimpleDebugger;
)
//
// end DEBUG_CODE
//
mVmPtr = VmPtr;
Status = EFI_SUCCESS;
StackCorrupted = 0;
//
// Make sure the magic value has been put on the stack before we got here.
//
if (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE) {
StackCorrupted = 1;
}
VmPtr->FramePtr = (VOID *) ((UINT8 *) (UINTN) VmPtr->R[0] + 8);
//
// Try to get the debug support for EBC
//
DEBUG_CODE (
Status = gBS->LocateProtocol (
&EbcSimpleDebuggerProtocolGuid,
NULL,
(VOID **) &EbcSimpleDebugger
);
if (EFI_ERROR (Status)) {
EbcSimpleDebugger = NULL;
}
)
//
// end DEBUG_CODE
//
// Save the start IP for debug. For example, if we take an exception we
// can print out the location of the exception relative to the entry point,
// which could then be used in a disassembly listing to find the problem.
//
VmPtr->EntryPoint = (VOID *) VmPtr->Ip;
//
// We'll wait for this flag to know when we're done. The RET
// instruction sets it if it runs out of stack.
//
VmPtr->StopFlags = 0;
while (!(VmPtr->StopFlags & STOPFLAG_APP_DONE)) {
//
// If we've found a simple debugger protocol, call it
//
DEBUG_CODE (
if (EbcSimpleDebugger != NULL) {
EbcSimpleDebugger->Debugger (EbcSimpleDebugger, VmPtr);
}
) // end DEBUG_CODE
//
// Verify the opcode is in range. Otherwise generate an exception.
//
if ((*VmPtr->Ip & OPCODE_M_OPCODE) >= (sizeof (mVmOpcodeTable) / sizeof (mVmOpcodeTable[0]))) {
EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
Status = EFI_UNSUPPORTED;
goto Done;
}
//
// Use the opcode bits to index into the opcode dispatch table. If the
// function pointer is null then generate an exception.
//
ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction;
if (ExecFunc == (UINTN) NULL) {
EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
Status = EFI_UNSUPPORTED;
goto Done;
}
//
// The EBC VM is a strongly ordered processor, so perform a fence operation before
// and after each instruction is executed.
//
MEMORY_FENCE ();
mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr);
MEMORY_FENCE ();
//
// If the step flag is set, signal an exception and continue. We don't
// clear it here. Assuming the debugger is responsible for clearing it.
//
if (VMFLAG_ISSET (VmPtr, VMFLAGS_STEP)) {
EbcDebugSignalException (EXCEPT_EBC_STEP, EXCEPTION_FLAG_NONE, VmPtr);
}
//
// Make sure stack has not been corrupted. Only report it once though.
//
if (!StackCorrupted && (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE)) {
EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr);
StackCorrupted = 1;
}
}
Done:
mVmPtr = NULL;
return Status;
}
STATIC
EFI_STATUS
ExecuteMOVxx (
IN VM_CONTEXT *VmPtr
)
/*++
Routine Description:
Execute the MOVxx instructions.
Arguments:
VmPtr - pointer to a VM context.
Returns:
EFI_UNSUPPORTED
EFI_SUCCESS
Instruction format:
MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
MOVqq {@}R1 {Index64}, {@}R2 {Index64}
Copies contents of [R2] -> [R1], zero extending where required.
First character indicates the size of the move.
Second character indicates the size of the index(s).
Invalid to have R1 direct with index.
--*/
{
UINT8 Opcode;
UINT8 OpcMasked;
UINT8 Operands;
UINT8 Size;
UINT8 MoveSize;
INT16 Index16;
INT32 Index32;
INT64 Index64Op1;
INT64 Index64Op2;
UINT64 Data64;
UINT64 DataMask;
UINTN Source;
Opcode = GETOPCODE (VmPtr);
OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE);
//
// Get the operands byte so we can get R1 and R2
//
Operands = GETOPERANDS (VmPtr);
//
// Assume no indexes
//
Index64Op1 = 0;
Index64Op2 = 0;
Data64 = 0;
//
// Determine if we have an index/immediate data. Base instruction size
// is 2 (opcode + operands). Add to this size each index specified.
//
Size = 2;
if (Opcode & (OPCODE_M_IMMED_OP1 | OPCODE_M_IMMED_OP2)) {
//
// Determine size of the index from the opcode. Then get it.
//
if ((OpcMasked <= OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVNW)) {
//
// MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
// Get one or both index values.
//
if (Opcode & OPCODE_M_IMMED_OP1) {
Index16 = VmReadIndex16 (VmPtr, 2);
Index64Op1 = (INT64) Index16;
Size += sizeof (UINT16);
}
if (Opcode & OPCODE_M_IMMED_OP2) {
Index16 = VmReadIndex16 (VmPtr, Size);
Index64Op2 = (INT64) Index16;
Size += sizeof (UINT16);
}
} else if ((OpcMasked <= OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVND)) {
//
// MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
//
if (Opcode & OPCODE_M_IMMED_OP1) {
Index32 = VmReadIndex32 (VmPtr, 2);
Index64Op1 = (INT64) Index32;
Size += sizeof (UINT32);
}
if (Opcode & OPCODE_M_IMMED_OP2) {
Index32 = VmReadIndex32 (VmPtr, Size);
Index64Op2 = (INT64) Index32;
Size += sizeof (UINT32);
}
} else if (OpcMasked == OPCODE_MOVQQ) {
//
// MOVqq -- only form with a 64-bit index
//
if (Opcode & OPCODE_M_IMMED_OP1) {
Index64Op1 = VmReadIndex64 (VmPtr, 2);
Size += sizeof (UINT64);
}
if (Opcode & OPCODE_M_IMMED_OP2) {
Index64Op2 = VmReadIndex64 (VmPtr, Size);
Size += sizeof (UINT64);
}
} else {
//
// Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
//
EbcDebugSignalException (
EXCEPT_EBC_INSTRUCTION_ENCODING,
EXCEPTION_FLAG_FATAL,
VmPtr
);
return EFI_UNSUPPORTED;
}
}
//
// Determine the size of the move, and create a mask for it so we can
// clear unused bits.
//
if ((OpcMasked == OPCODE_MOVBW) || (OpcMasked == OPCODE_MOVBD)) {
MoveSize = DATA_SIZE_8;
DataMask = 0xFF;
} else if ((OpcMasked == OPCODE_MOVWW) || (OpcMasked == OPCODE_MOVWD)) {
MoveSize = DATA_SIZE_16;
DataMask = 0xFFFF;
} else if ((OpcMasked == OPCODE_MOVDW) || (OpcMasked == OPCODE_MOVDD)) {
MoveSize = DATA_SIZE_32;
DataMask = 0xFFFFFFFF;
} else if ((OpcMasked == OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVQQ)) {
MoveSize = DATA_SIZE_64;
DataMask = (UINT64)~0;
} else if ((OpcMasked == OPCODE_MOVNW) || (OpcMasked == OPCODE_MOVND)) {
MoveSize = DATA_SIZE_N;
DataMask = (UINT64)~0 >> (64 - 8 * sizeof (UINTN));
} else {
//
// We were dispatched to this function and we don't recognize the opcode
//
EbcDebugSignalException (EXCEPT_EBC_UNDEFINED, EXCEPTION_FLAG_FATAL, VmPtr);
return EFI_UNSUPPORTED;
}
//
// Now get the source address
//
if (OPERAND2_INDIRECT (Operands)) {
//
// Indirect form @R2. Compute address of operand2
//
Source = (UINTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Index64Op2);
//
// Now get the data from the source. Always 0-extend and let the compiler
// sign-extend where required.
//
switch (MoveSize) {
case DATA_SIZE_8:
Data64 = (UINT64) (UINT8) VmReadMem8 (VmPtr, Source);
break;
case DATA_SIZE_16:
Data64 = (UINT64) (UINT16) VmReadMem16 (VmPtr, Source);
break;
case DATA_SIZE_32:
Data64 = (UINT64) (UINT32) VmReadMem32 (VmPtr, Source);
break;
case DATA_SIZE_64:
Data64 = (UINT64) VmReadMem64 (VmPtr, Source);
break;
case DATA_SIZE_N:
Data64 = (UINT64) (UINTN) VmReadMemN (VmPtr, Source);
break;
default:
//
// not reached
//
break;
}
} else {
//
// Not indirect source: MOVxx {@}Rx, Ry [Index]
//
Data64 = VmPtr->R[OPERAND2_REGNUM (Operands)] + Index64Op2;
//
// Did Operand2 have an index? If so, treat as two signed values since
// indexes are signed values.
//
if (Opcode & OPCODE_M_IMMED_OP2) {
//
// NOTE: need to find a way to fix this, most likely by changing the VM
// implementation to remove the stack gap. To do that, we'd need to
// allocate stack space for the VM and actually set the system
// stack pointer to the allocated buffer when the VM starts.
//
// Special case -- if someone took the address of a function parameter
// then we need to make sure it's not in the stack gap. We can identify
// this situation if (Operand2 register == 0) && (Operand2 is direct)
// && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
// Situations that to be aware of:
// * stack adjustments at beginning and end of functions R0 = R0 += stacksize
//
if ((OPERAND2_REGNUM (Operands) == 0) &&
(!OPERAND2_INDIRECT (Operands)) &&
(Index64Op2 > 0) &&
(OPERAND1_REGNUM (Operands) == 0) &&
(OPERAND1_INDIRECT (Operands))
) {
Data64 = (UINT64) ConvertStackAddr (VmPtr, (UINTN) (INT64) Data64);
}
}
}
//
// Now write it back
//
if (OPERAND1_INDIRECT (Operands)) {
//
// Reuse the Source variable to now be dest.
//
Source = (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index64Op1);
//
// Do the write based on the size
//
switch (MoveSize) {
case DATA_SIZE_8:
VmWriteMem8 (VmPtr, Source, (UINT8) Data64);
break;
case DATA_SIZE_16:
VmWriteMem16 (VmPtr, Source, (UINT16) Data64);
break;
case DATA_SIZE_32:
VmWriteMem32 (VmPtr, Source, (UINT32) Data64);
break;
case DATA_SIZE_64:
VmWriteMem64 (VmPtr, Source, Data64);
break;
case DATA_SIZE_N:
VmWriteMemN (VmPtr, Source, (UINTN) Data64);
break;
default:
//
// not reached
//
break;
}
} else {
//
// Operand1 direct.
// Make sure we didn't have an index on operand1.
//
if (Opcode & OPCODE_M_IMMED_OP1) {
EbcDebugSignalException (
EXCEPT_EBC_INSTRUCTION_ENCODING,
EXCEPTION_FLAG_FATAL,
VmPtr
);
return EFI_UNSUPPORTED;
}
//
// Direct storage in register. Clear unused bits and store back to
// register.
//
VmPtr->R[OPERAND1_REGNUM (Operands)] = Data64 & DataMask;
}
//
// Advance the instruction pointer
//
VmPtr->Ip += Size;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ExecuteBREAK (
IN VM_CONTEXT *VmPtr
)
/*++
Routine Description:
Execute the EBC BREAK instruction
Arguments:
VmPtr - pointer to current VM context
Returns:
EFI_UNSUPPORTED
EFI_SUCCESS
--*/
{
UINT8 Operands;
VOID *EbcEntryPoint;
VOID *Thunk;
EFI_STATUS Status;
UINT64 U64EbcEntryPoint;
INT32 Offset;
Operands = GETOPERANDS (VmPtr);
switch (Operands) {
//
// Runaway program break. Generate an exception and terminate
//
case 0:
EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr);
break;
//
// Get VM version -- return VM revision number in R7
//
case 1:
//
// Bits:
// 63-17 = 0
// 16-8 = Major version
// 7-0 = Minor version
//
VmPtr->R[7] = GetVmVersion ();
break;
//
// Debugger breakpoint
//
case 3:
VmPtr->StopFlags |= STOPFLAG_BREAKPOINT;
//
// See if someone has registered a handler
//
EbcDebugSignalException (
EXCEPT_EBC_BREAKPOINT,
EXCEPTION_FLAG_NONE,
VmPtr
);
//
// Don't advance the IP
//
return EFI_UNSUPPORTED;
break;
//
// System call, which there are none, so NOP it.
//
case 4:
break;
//
// Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
// "offset from self" pointer to the EBC entry point.
// After we're done, *(UINT64 *)R7 will be the address of the new thunk.
//
case 5:
Offset = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->R[7]);
U64EbcEntryPoint = (UINT64) (VmPtr->R[7] + Offset + 4);
EbcEntryPoint = (VOID *) (UINTN) U64EbcEntryPoint;
//
// Now create a new thunk
//
Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0);
//
// Finally replace the EBC entry point memory with the thunk address
//
VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[7], (UINT64) (UINTN) Thunk);
break;
//
// Compiler setting version per value in R7
//
case 6:
VmPtr->CompilerVersion = (UINT32) VmPtr->R[7];
//
// Check compiler version against VM version?
//
break;
//
// Unhandled break code. Signal exception.
//
default:
EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr);
break;
}
//
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?