ebcsupport.c
来自「Next BIOS Source code : Extensible Firmw」· C语言 代码 · 共 725 行 · 第 1/2 页
C
725 行
// that the returned buffer is not aligned, so round up to the next
// alignment size.
//
Size = EBC_THUNK_SIZE + EBC_THUNK_ALIGNMENT - 1;
ThunkSize = Size;
Status = gBS->AllocatePool (EfiBootServicesData,
Size,
(VOID *)&Ptr);
if (Status != EFI_SUCCESS) {
return EFI_OUT_OF_RESOURCES;
}
//
// Save the start address of the buffer.
//
ThunkBase = Ptr;
//
// Make sure it's aligned for code execution. If not, then
// round up.
//
if ((UINT32)(UINTN)Ptr & (EBC_THUNK_ALIGNMENT - 1)) {
Ptr = (UINT8 *)(((UINTN)Ptr + (EBC_THUNK_ALIGNMENT - 1)) &
~(UINT64)(EBC_THUNK_ALIGNMENT-1));
}
//
// Return the pointer to the thunk to the caller to user as the
// image entry point.
//
*Thunk = (VOID *)Ptr;
// Clear out the thunk entry
//EfiZeroMem(Ptr, Size);
//
// For IPF, when you do a call via a function pointer, the function pointer
// actually points to a function descriptor which consists of a 64-bit
// address of the function, followed by a 64-bit gp for the function being
// called. See the the Software Conventions and Runtime Architecture Guide
// for details.
// So first off in our thunk, create a descriptor for our actual thunk code.
// This means we need to create a pointer to the thunk code (which follows
// the descriptor we're going to create), followed by the gp of the Vm
// interpret function we're going to eventually execute.
//
Data64Ptr = (UINT64 *)Ptr;
//
// Write the function's entry point (which is our thunk code that follows
// this descriptor we're creating).
//
*Data64Ptr = (UINT64)(Data64Ptr + 2);
//
// Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
// descriptor.
//
*(Data64Ptr + 1) = *(UINT64 *)((UINT64 *)(UINTN)EbcInterpret + 1);
//
// Advance our thunk data pointer past the descriptor. Since the
// descriptor consists of 16 bytes, the pointer is still aligned for
// IPF code execution (on 16-byte boundary).
//
Ptr += sizeof (UINT64) * 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.
//
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:
Standard EFI status
--*/
{
UINT8 *BPtr;
UINT32 i;
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 (i = 0; i < 8; i++) {
*BPtr = (UINT8)Low64;
Low64 = RightShiftU64 (Low64, 8);
BPtr++;
}
for (i = 0; i < 8; i++) {
*BPtr = (UINT8)High64;
High64 = RightShiftU64 (High64, 8);
BPtr++;
}
return EFI_SUCCESS;
}
VOID
EbcLLCALLEX (
IN UINTN CallAddr,
IN UINTN EbcSp,
IN VOID *FramePtr
)
/*++
Routine Description:
Implements the EBC CALLEX instruction to call an external function, which
may be native or EBC 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 + -
显示快捷键?