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 + -
显示快捷键?