ebcsupport.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 916 行 · 第 1/2 页

C
916
字号
  I = RightShiftU64 (Addr, 63) & 0x01;
  //
  // ic = Addr[21:21]
  //
  Ic = RightShiftU64 (Addr, 21) & 0x01;
  //
  // imm5c = Addr[20:16] for 5 bits
  //
  Imm5c = RightShiftU64 (Addr, 16) & 0x1F;
  //
  // imm9d = Addr[15:7] for 9 bits
  //
  Imm9d = RightShiftU64 (Addr, 7) & 0x1FF;
  //
  // imm7b = Addr[6:0] for 7 bits
  //
  Imm7b = Addr & 0x7F;

  //
  // The EBC entry point will be put into r8, so r8 can be used here
  // temporary. R8 is general register and is auto-serialized.
  //
  RegNum = 8;

  //
  // Next is jumbled data, including opcode and rest of address
  //
  Code[2] = LeftShiftU64 (Imm7b, 13)
          | LeftShiftU64 (0x00, 20)   // vc
          | LeftShiftU64 (Ic, 21)
          | LeftShiftU64 (Imm5c, 22)
          | LeftShiftU64 (Imm9d, 27)
          | LeftShiftU64 (I, 36)
          | LeftShiftU64 ((UINT64)MOVL_OPCODE, 37)
          | LeftShiftU64 ((RegNum & 0x7F), 6);

  WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);

  //
  // *************************** FIRST BUNDLE ********************************
  //
  // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass
  // the ebc entry point in to the interpreter function via a processor
  // register.
  // Note -- we could easily change this to pass in a pointer to a structure
  // that contained, among other things, the EBC image's entry point. But
  // for now pass it directly.
  //
  Ptr += 16;
  Addr = (UINT64) EbcEntryPoint;

  //
  // Now generate the code bytes. First is nop.m 0x0
  //
  Code[0] = OPCODE_NOP;

  //
  // Next is simply Addr[62:22] (41 bits) of the address
  //
  Code[1] = RightShiftU64 (Addr, 22) & 0x1ffffffffff;

  //
  // Extract bits from the address for insertion into the instruction
  // i = Addr[63:63]
  //
  I = RightShiftU64 (Addr, 63) & 0x01;
  //
  // ic = Addr[21:21]
  //
  Ic = RightShiftU64 (Addr, 21) & 0x01;
  //
  // imm5c = Addr[20:16] for 5 bits
  //
  Imm5c = RightShiftU64 (Addr, 16) & 0x1F;
  //
  // imm9d = Addr[15:7] for 9 bits
  //
  Imm9d = RightShiftU64 (Addr, 7) & 0x1FF;
  //
  // imm7b = Addr[6:0] for 7 bits
  //
  Imm7b = Addr & 0x7F;

  //
  // Put the EBC entry point in r8, which is the location of the return value
  // for functions.
  //
  RegNum = 8;

  //
  // Next is jumbled data, including opcode and rest of address
  //
  Code[2] = LeftShiftU64 (Imm7b, 13)
          | LeftShiftU64 (0x00, 20)   // vc
          | LeftShiftU64 (Ic, 21)
          | LeftShiftU64 (Imm5c, 22)
          | LeftShiftU64 (Imm9d, 27)
          | LeftShiftU64 (I, 36)
          | LeftShiftU64 ((UINT64)MOVL_OPCODE, 37)
          | LeftShiftU64 ((RegNum & 0x7F), 6);

  WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);

  //
  // *************************** NEXT BUNDLE *********************************
  //
  // Write code bundle for:
  //   movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
  //
  // Advance pointer to next bundle, then compute the offset from this bundle
  // to the address of the entry point of the interpreter.
  //
  Ptr += 16;
  if (Flags & FLAG_THUNK_ENTRY_POINT) {
    Addr = (UINT64) ExecuteEbcImageEntryPoint;
  } else {
    Addr = (UINT64) EbcInterpret;
  }
  //
  // Indirection on Itanium-based systems
  //
  Addr = *(UINT64 *) Addr;

  //
  // Now write the code to load the offset into a register
  //
  Code[0] = OPCODE_NOP;

  //
  // Next is simply Addr[62:22] (41 bits) of the address
  //
  Code[1] = RightShiftU64 (Addr, 22) & 0x1ffffffffff;

  //
  // Extract bits from the address for insertion into the instruction
  // i = Addr[63:63]
  //
  I = RightShiftU64 (Addr, 63) & 0x01;
  //
  // ic = Addr[21:21]
  //
  Ic = RightShiftU64 (Addr, 21) & 0x01;
  //
  // imm5c = Addr[20:16] for 5 bits
  //
  Imm5c = RightShiftU64 (Addr, 16) & 0x1F;
  //
  // imm9d = Addr[15:7] for 9 bits
  //
  Imm9d = RightShiftU64 (Addr, 7) & 0x1FF;
  //
  // imm7b = Addr[6:0] for 7 bits
  //
  Imm7b = Addr & 0x7F;

  //
  // Put it in r31, a scratch register
  //
  RegNum = 31;

  //
  // Next is jumbled data, including opcode and rest of address
  //
  Code[2] =   LeftShiftU64(Imm7b, 13)
          | LeftShiftU64 (0x00, 20)   // vc
          | LeftShiftU64 (Ic, 21)
          | LeftShiftU64 (Imm5c, 22)
          | LeftShiftU64 (Imm9d, 27)
          | LeftShiftU64 (I, 36)
          | LeftShiftU64 ((UINT64)MOVL_OPCODE, 37)
          | LeftShiftU64 ((RegNum & 0x7F), 6);

  WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);

  //
  // *************************** NEXT BUNDLE *********************************
  //
  // Load branch register with EbcInterpret() function offset from the bundle
  // address: mov b6 = RegNum
  //
  // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
  //
  // Advance pointer to next bundle
  //
  Ptr += 16;
  Code[0] = OPCODE_NOP;
  Code[1] = OPCODE_NOP;
  Code[2] = OPCODE_MOV_BX_RX;

  //
  // Pick a branch register to use. Then fill in the bits for the branch
  // register and user register (same user register as previous bundle).
  //
  Br = 6;
  Code[2] |= LeftShiftU64 (Br, 6);
  Code[2] |= LeftShiftU64 (RegNum, 13);
  WriteBundle ((VOID *) Ptr, 0x0d, Code[0], Code[1], Code[2]);

  //
  // *************************** NEXT BUNDLE *********************************
  //
  // Now do the branch:  (p0) br.cond.sptk.few b6
  //
  // Advance pointer to next bundle.
  // Fill in the bits for the branch register (same reg as previous bundle)
  //
  Ptr += 16;
  Code[0] = OPCODE_NOP;
  Code[1] = OPCODE_NOP;
  Code[2] = OPCODE_BR_COND_SPTK_FEW;
  Code[2] |= LeftShiftU64 (Br, 13);
  WriteBundle ((VOID *) Ptr, 0x1d, Code[0], Code[1], Code[2]);

  //
  // Add the thunk to our list of allocated thunks so we can do some cleanup
  // when the image is unloaded. Do this last since the Add function flushes
  // the instruction cache for us.
  //
  EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);

  //
  // Done
  //
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
WriteBundle (
  IN    VOID    *MemPtr,
  IN    UINT8   Template,
  IN    UINT64  Slot0,
  IN    UINT64  Slot1,
  IN    UINT64  Slot2
  )
/*++

Routine Description:

  Given raw bytes of Itanium based code, format them into a bundle and
  write them out.
  
Arguments:

  MemPtr    - pointer to memory location to write the bundles to
  Template  - 5-bit template
  Slot0-2   - instruction slot data for the bundle

Returns:

  EFI_INVALID_PARAMETER - Pointer is not aligned
                        - No more than 5 bits in template
                        - More than 41 bits used in code
  EFI_SUCCESS           - All data is written.

--*/
{
  UINT8   *BPtr;
  UINT32  Index;
  UINT64  Low64;
  UINT64  High64;

  //
  // Verify pointer is aligned
  //
  if ((UINT64) MemPtr & 0xF) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Verify no more than 5 bits in template
  //
  if (Template &~0x1F) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Verify max of 41 bits used in code
  //
  if ((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) {
    return EFI_INVALID_PARAMETER;
  }

  Low64   = LeftShiftU64 (Slot1, 46) | LeftShiftU64 (Slot0, 5) | Template;
  High64  = RightShiftU64 (Slot1, 18) | LeftShiftU64 (Slot2, 23);

  //
  // Now write it all out
  //
  BPtr = (UINT8 *) MemPtr;
  for (Index = 0; Index < 8; Index++) {
    *BPtr = (UINT8) Low64;
    Low64 = RightShiftU64 (Low64, 8);
    BPtr++;
  }

  for (Index = 0; Index < 8; Index++) {
    *BPtr   = (UINT8) High64;
    High64  = RightShiftU64 (High64, 8);
    BPtr++;
  }

  return EFI_SUCCESS;
}

VOID
EbcLLCALLEX (
  IN VM_CONTEXT   *VmPtr,
  IN UINTN        FuncAddr,
  IN UINTN        NewStackPointer,
  IN VOID         *FramePtr,
  IN UINT8        Size
  )
/*++

Routine Description:

  This function is called to execute an EBC CALLEX instruction. 
  The function check the callee's content to see whether it is common native
  code or a thunk to another piece of EBC code.
  If the callee is common native code, use EbcLLCAllEXASM to manipulate,
  otherwise, set the VM->IP to target EBC code directly to avoid another VM
  be startup which cost time and stack space.
  
Arguments:

  VmPtr             - Pointer to a VM context.
  FuncAddr          - Callee's address
  NewStackPointer   - New stack pointer after the call
  FramePtr          - New frame pointer after the call
  Size              - The size of call instruction

Returns:

  None.
  
--*/
{
  UINTN    IsThunk;
  UINTN    TargetEbcAddr;
  UINTN    CodeOne18;
  UINTN    CodeOne23;
  UINTN    CodeTwoI;
  UINTN    CodeTwoIc;
  UINTN    CodeTwo7b;
  UINTN    CodeTwo5c;
  UINTN    CodeTwo9d;
  UINTN    CalleeAddr;

  IsThunk       = 1;
  TargetEbcAddr = 0;

  //
  // FuncAddr points to the descriptor of the target instructions.
  //
  CalleeAddr = *((UINT64 *)FuncAddr);

  //
  // Processor specific code to check whether the callee is a thunk to EBC.
  //
  if (*((UINT64 *)CalleeAddr) != 0xBCCA000100000005) {
    IsThunk = 0;
    goto Action;
  }
  if (*((UINT64 *)CalleeAddr + 1) != 0x697623C1004A112E)  {
    IsThunk = 0;
    goto Action;
  }

  CodeOne18 = RightShiftU64 (*((UINT64 *)CalleeAddr + 2), 46) & 0x3FFFF;
  CodeOne23 = (*((UINT64 *)CalleeAddr + 3)) & 0x7FFFFF;
  CodeTwoI  = RightShiftU64 (*((UINT64 *)CalleeAddr + 3), 59) & 0x1;
  CodeTwoIc = RightShiftU64 (*((UINT64 *)CalleeAddr + 3), 44) & 0x1;
  CodeTwo7b = RightShiftU64 (*((UINT64 *)CalleeAddr + 3), 36) & 0x7F;
  CodeTwo5c = RightShiftU64 (*((UINT64 *)CalleeAddr + 3), 45) & 0x1F;
  CodeTwo9d = RightShiftU64 (*((UINT64 *)CalleeAddr + 3), 50) & 0x1FF;

  TargetEbcAddr = CodeTwo7b
                  | LeftShiftU64 (CodeTwo9d, 7)
                  | LeftShiftU64 (CodeTwo5c, 16)
                  | LeftShiftU64 (CodeTwoIc, 21)
                  | LeftShiftU64 (CodeOne18, 22)
                  | LeftShiftU64 (CodeOne23, 40)
                  | LeftShiftU64 (CodeTwoI, 63)
                  ;

Action:
  if (IsThunk == 1){
    //
    // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
    // put our return address and frame pointer on the VM stack.
    // Then set the VM's IP to new EBC code.
    //
    VmPtr->R[0] -= 8;
    VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
    VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
    VmPtr->R[0] -= 8;
    VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));

    VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
  } else {
    //
    // The callee is not a thunk to EBC, call native code.
    //
    EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);

    //
    // Get return value and advance the IP.
    //
    VmPtr->R[7] = EbcLLGetReturnValue ();
    VmPtr->Ip += Size;
  }
}

VOID
EbcLLCALLEXNative (
  IN UINTN    CallAddr,
  IN UINTN    EbcSp,
  IN VOID     *FramePtr
  )
/*++

Routine Description:
  Implements the EBC CALLEX instruction to call an external function, which
  seems to be native code.

  We'll copy the entire EBC stack frame down below itself in memory and use
  that copy for passing parameters. 

Arguments:
  CallAddr    - address (function pointer) of function to call
  EbcSp       - current EBC stack pointer
  FramePtr    - current EBC frame pointer.

Returns:
  NA

--*/
{
  UINTN FrameSize;
  VOID  *Destination;
  VOID  *Source;
  //
  // The stack for an EBC function looks like this:
  //     FramePtr  (8)
  //     RetAddr   (8)
  //     Locals    (n)
  //     Stack for passing args (m)
  //
  // Pad the frame size with 64 bytes because the low-level code we call
  // will move the stack pointer up assuming worst-case 8 args in registers.
  //
  FrameSize   = (UINTN) FramePtr - (UINTN) EbcSp + 64;
  Source      = (VOID *) EbcSp;
  Destination = (VOID *) ((UINT8 *) EbcSp - FrameSize - IPF_STACK_ALIGNMENT);
  Destination = (VOID *) ((UINTN) ((UINTN) Destination + IPF_STACK_ALIGNMENT - 1) &~((UINTN) IPF_STACK_ALIGNMENT - 1));
  gBS->CopyMem (Destination, Source, FrameSize);
  EbcAsmLLCALLEX ((UINTN) CallAddr, (UINTN) Destination);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?