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