2 This module contains EBC support routines that are customized based on
3 the target x64 processor.
5 Copyright (c) 2006, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "EbcExecute.h"
20 // NOTE: This is the stack size allocated for the interpreter
21 // when it executes an EBC image. The requirements can change
22 // based on whether or not a debugger is present, and other
23 // platform-specific configurations.
25 #define VM_STACK_SIZE (1024 * 8)
26 #define EBC_THUNK_SIZE 64
28 #define STACK_REMAIN_SIZE (1024 * 4)
40 Push a 64 bit unsigned value to the VM stack.
44 VmPtr - The pointer to current VM context.
45 Arg - The value to be pushed
54 // Advance the VM stack down, and then copy the argument to the stack.
57 VmPtr
->R
[0] -= sizeof (UINT64
);
58 *(UINT64
*) VmPtr
->R
[0] = Arg
;
64 Begin executing an EBC image. The address of the entry point is passed
65 in via a processor register, so we'll need to make a call to get the
68 This is a thunk function. Microsoft x64 compiler only provide fast_call
69 calling convention, so the first four arguments are passed by rcx, rdx,
70 r8, and r9, while other arguments are passed in stack.
72 @return The value returned by the EBC application we're going to run.
97 // Create a new VM context on the stack
105 // Get the EBC entry point from the processor register.
106 // Don't call any function before getting the EBC entry
107 // point because this will collab the return register.
109 Addr
= EbcLLGetEbcEntryPoint ();
112 // Now clear out our context
114 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
117 // Set the VM instruction pointer to the correct location in memory.
119 VmContext
.Ip
= (VMIP
) Addr
;
122 // Initialize the stack pointer for the EBC. Get the current system stack
123 // pointer and adjust it down by the max needed for the interpreter.
125 Addr
= EbcLLGetStackPointer ();
128 // Adjust the VM's stack pointer down.
131 Status
= GetEBCStack((EFI_HANDLE
)(UINTN
)-1, &VmContext
.StackPool
, &StackIndex
);
132 if (EFI_ERROR(Status
)) {
135 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
136 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
137 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
138 VmContext
.R
[0] -= sizeof (UINTN
);
141 // Align the stack on a natural boundary.
143 VmContext
.R
[0] &= ~(sizeof (UINTN
) - 1);
146 // Put a magic value in the stack gap, then adjust down again.
148 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
149 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
152 // The stack upper to LowStackTop is belong to the VM.
154 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
157 // For the worst case, assume there are 4 arguments passed in registers, store
158 // them to VM's stack.
160 PushU64 (&VmContext
, (UINT64
) Arg16
);
161 PushU64 (&VmContext
, (UINT64
) Arg15
);
162 PushU64 (&VmContext
, (UINT64
) Arg14
);
163 PushU64 (&VmContext
, (UINT64
) Arg13
);
164 PushU64 (&VmContext
, (UINT64
) Arg12
);
165 PushU64 (&VmContext
, (UINT64
) Arg11
);
166 PushU64 (&VmContext
, (UINT64
) Arg10
);
167 PushU64 (&VmContext
, (UINT64
) Arg9
);
168 PushU64 (&VmContext
, (UINT64
) Arg8
);
169 PushU64 (&VmContext
, (UINT64
) Arg7
);
170 PushU64 (&VmContext
, (UINT64
) Arg6
);
171 PushU64 (&VmContext
, (UINT64
) Arg5
);
172 PushU64 (&VmContext
, (UINT64
) Arg4
);
173 PushU64 (&VmContext
, (UINT64
) Arg3
);
174 PushU64 (&VmContext
, (UINT64
) Arg2
);
175 PushU64 (&VmContext
, (UINT64
) Arg1
);
178 // Interpreter assumes 64-bit return address is pushed on the stack.
179 // The x64 does not do this so pad the stack accordingly.
181 PushU64 (&VmContext
, (UINT64
) 0);
182 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
185 // For x64, this is where we say our return address is
187 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
190 // We need to keep track of where the EBC stack starts. This way, if the EBC
191 // accesses any stack variables above its initial stack setting, then we know
192 // it's accessing variables passed into it, which means the data is on the
194 // When we're called, on the stack (high to low) we have the parameters, the
195 // return address, then the saved ebp. Save the pointer to the return address.
196 // EBC code knows that's there, so should look above it for function parameters.
197 // The offset is the size of locals (VMContext + Addr + saved ebp).
198 // Note that the interpreter assumes there is a 16 bytes of return address on
199 // the stack too, so adjust accordingly.
200 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
204 // Begin executing the EBC code
206 EbcExecute (&VmContext
);
209 // Return the value in R[7] unless there was an error
211 ReturnEBCStack(StackIndex
);
212 return (UINT64
) VmContext
.R
[7];
217 Begin executing an EBC image. The address of the entry point is passed
218 in via a processor register, so we'll need to make a call to get the
221 @param ImageHandle image handle for the EBC application we're executing
222 @param SystemTable standard system table passed into an driver's entry
225 @return The value returned by the EBC application we're going to run.
230 ExecuteEbcImageEntryPoint (
231 IN EFI_HANDLE ImageHandle
,
232 IN EFI_SYSTEM_TABLE
*SystemTable
236 // Create a new VM context on the stack
238 VM_CONTEXT VmContext
;
244 // Get the EBC entry point from the processor register. Make sure you don't
245 // call any functions before this or you could mess up the register the
246 // entry point is passed in.
248 Addr
= EbcLLGetEbcEntryPoint ();
251 // Now clear out our context
253 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
256 // Save the image handle so we can track the thunks created for this image
258 VmContext
.ImageHandle
= ImageHandle
;
259 VmContext
.SystemTable
= SystemTable
;
262 // Set the VM instruction pointer to the correct location in memory.
264 VmContext
.Ip
= (VMIP
) Addr
;
267 // Initialize the stack pointer for the EBC. Get the current system stack
268 // pointer and adjust it down by the max needed for the interpreter.
270 Addr
= EbcLLGetStackPointer ();
272 Status
= GetEBCStack(ImageHandle
, &VmContext
.StackPool
, &StackIndex
);
273 if (EFI_ERROR(Status
)) {
276 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
277 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
278 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
279 VmContext
.R
[0] -= sizeof (UINTN
);
283 // Put a magic value in the stack gap, then adjust down again
285 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
286 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
289 // Align the stack on a natural boundary
290 VmContext
.R
[0] &= ~(sizeof(UINTN
) - 1);
292 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
295 // Simply copy the image handle and system table onto the EBC stack.
296 // Greatly simplifies things by not having to spill the args.
298 PushU64 (&VmContext
, (UINT64
) SystemTable
);
299 PushU64 (&VmContext
, (UINT64
) ImageHandle
);
302 // VM pushes 16-bytes for return address. Simulate that here.
304 PushU64 (&VmContext
, (UINT64
) 0);
305 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
308 // For x64, this is where we say our return address is
310 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
313 // Entry function needn't access high stack context, simply
314 // put the stack pointer here.
318 // Begin executing the EBC code
320 EbcExecute (&VmContext
);
323 // Return the value in R[7] unless there was an error
325 ReturnEBCStack(StackIndex
);
326 return (UINT64
) VmContext
.R
[7];
331 Create an IA32 thunk for the given EBC entry point.
333 @param ImageHandle Handle of image for which this thunk is being created
334 @param EbcEntryPoint Address of the EBC code that the thunk is to call
335 @param Thunk Returned thunk we create here
337 @return Standard EFI status.
342 IN EFI_HANDLE ImageHandle
,
343 IN VOID
*EbcEntryPoint
,
356 // Check alignment of pointer to EBC code
358 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
359 return EFI_INVALID_PARAMETER
;
362 Size
= EBC_THUNK_SIZE
;
365 Ptr
= AllocatePool (Size
);
368 return EFI_OUT_OF_RESOURCES
;
371 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
373 // Save the start address so we can add a pointer to it to a list later.
378 // Give them the address of our buffer we're going to fix up
380 *Thunk
= (VOID
*) Ptr
;
383 // Add a magic code here to help the VM recognize the thunk..
384 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
392 Addr
= (UINT64
) 0xCA112EBCCA112EBCULL
;
393 for (I
= 0; I
< sizeof (Addr
); I
++) {
394 *Ptr
= (UINT8
) (UINTN
) Addr
;
401 // Add code bytes to load up a processor register with the EBC entry point.
402 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
403 // The first 8 bytes of the thunk entry is the address of the EBC
412 Addr
= (UINT64
) EbcEntryPoint
;
413 for (I
= 0; I
< sizeof (Addr
); I
++) {
414 *Ptr
= (UINT8
) (UINTN
) Addr
;
421 // Stick in a load of ecx with the address of appropriate VM function.
422 // Using r11 because it's a volatile register and won't be used in this
424 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
426 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
427 Addr
= (UINTN
) ExecuteEbcImageEntryPoint
;
429 Addr
= (UINTN
) EbcInterpret
;
433 // mov r11 Addr => 0x49 0xBB
441 for (I
= 0; I
< sizeof (Addr
); I
++) {
448 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
460 // Double check that our defined size is ok (application error)
464 return EFI_BUFFER_TOO_SMALL
;
467 // Add the thunk to the list for this image. Do this last since the add
468 // function flushes the cache for us.
470 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
477 This function is called to execute an EBC CALLEX instruction.
478 The function check the callee's content to see whether it is common native
479 code or a thunk to another piece of EBC code.
480 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
481 otherwise, set the VM->IP to target EBC code directly to avoid another VM
482 be startup which cost time and stack space.
484 @param VmPtr Pointer to a VM context.
485 @param FuncAddr Callee's address
486 @param NewStackPointer New stack pointer after the call
487 @param FramePtr New frame pointer after the call
488 @param Size The size of call instruction
495 IN VM_CONTEXT
*VmPtr
,
497 IN UINTN NewStackPointer
,
509 // Processor specific code to check whether the callee is a thunk to EBC.
511 if (*((UINT8
*)FuncAddr
) != 0x48) {
515 if (*((UINT8
*)FuncAddr
+ 1) != 0xB8) {
519 if (*((UINT8
*)FuncAddr
+ 2) != 0xBC) {
523 if (*((UINT8
*)FuncAddr
+ 3) != 0x2E) {
527 if (*((UINT8
*)FuncAddr
+ 4) != 0x11) {
531 if (*((UINT8
*)FuncAddr
+ 5) != 0xCA) {
535 if (*((UINT8
*)FuncAddr
+ 6) != 0xBC) {
539 if (*((UINT8
*)FuncAddr
+ 7) != 0x2E) {
543 if (*((UINT8
*)FuncAddr
+ 8) != 0x11) {
547 if (*((UINT8
*)FuncAddr
+ 9) != 0xCA) {
551 if (*((UINT8
*)FuncAddr
+ 10) != 0x48) {
555 if (*((UINT8
*)FuncAddr
+ 11) != 0xB8) {
560 CopyMem (&TargetEbcAddr
, (UINT8
*)FuncAddr
+ 12, 8);
565 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
566 // put our return address and frame pointer on the VM stack.
567 // Then set the VM's IP to new EBC code.
570 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
571 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
573 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
575 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
578 // The callee is not a thunk to EBC, call native code.
580 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
583 // Get return value and advance the IP.
585 VmPtr
->R
[7] = EbcLLGetReturnValue ();