3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 This module contains EBC support routines that are customized based on
19 the target x64 processor.
24 #include "EbcExecute.h"
27 // NOTE: This is the stack size allocated for the interpreter
28 // when it executes an EBC image. The requirements can change
29 // based on whether or not a debugger is present, and other
30 // platform-specific configurations.
32 #define VM_STACK_SIZE (1024 * 8)
33 #define EBC_THUNK_SIZE 64
35 #define STACK_REMAIN_SIZE (1024 * 4)
47 Push a 64 bit unsigned value to the VM stack.
51 VmPtr - The pointer to current VM context.
52 Arg - The value to be pushed
61 // Advance the VM stack down, and then copy the argument to the stack.
64 VmPtr
->R
[0] -= sizeof (UINT64
);
65 *(UINT64
*) VmPtr
->R
[0] = Arg
;
93 Begin executing an EBC image. The address of the entry point is passed
94 in via a processor register, so we'll need to make a call to get the
99 This is a thunk function. Microsoft x64 compiler only provide fast_call
100 calling convention, so the first four arguments are passed by rcx, rdx,
101 r8, and r9, while other arguments are passed in stack.
105 The value returned by the EBC application we're going to run.
110 // Create a new VM context on the stack
112 VM_CONTEXT VmContext
;
118 // Get the EBC entry point from the processor register.
119 // Don't call any function before getting the EBC entry
120 // point because this will collab the return register.
122 Addr
= EbcLLGetEbcEntryPoint ();
125 // Now clear out our context
127 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
130 // Set the VM instruction pointer to the correct location in memory.
132 VmContext
.Ip
= (VMIP
) Addr
;
135 // Initialize the stack pointer for the EBC. Get the current system stack
136 // pointer and adjust it down by the max needed for the interpreter.
138 Addr
= EbcLLGetStackPointer ();
141 // Adjust the VM's stack pointer down.
144 Status
= GetEBCStack((EFI_HANDLE
)(UINTN
)-1, &VmContext
.StackPool
, &StackIndex
);
145 if (EFI_ERROR(Status
)) {
148 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
149 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
150 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
151 VmContext
.R
[0] -= sizeof (UINTN
);
154 // Align the stack on a natural boundary.
156 VmContext
.R
[0] &= ~(sizeof (UINTN
) - 1);
159 // Put a magic value in the stack gap, then adjust down again.
161 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
162 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
165 // The stack upper to LowStackTop is belong to the VM.
167 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
170 // For the worst case, assume there are 4 arguments passed in registers, store
171 // them to VM's stack.
173 PushU64 (&VmContext
, (UINT64
) Arg16
);
174 PushU64 (&VmContext
, (UINT64
) Arg15
);
175 PushU64 (&VmContext
, (UINT64
) Arg14
);
176 PushU64 (&VmContext
, (UINT64
) Arg13
);
177 PushU64 (&VmContext
, (UINT64
) Arg12
);
178 PushU64 (&VmContext
, (UINT64
) Arg11
);
179 PushU64 (&VmContext
, (UINT64
) Arg10
);
180 PushU64 (&VmContext
, (UINT64
) Arg9
);
181 PushU64 (&VmContext
, (UINT64
) Arg8
);
182 PushU64 (&VmContext
, (UINT64
) Arg7
);
183 PushU64 (&VmContext
, (UINT64
) Arg6
);
184 PushU64 (&VmContext
, (UINT64
) Arg5
);
185 PushU64 (&VmContext
, (UINT64
) Arg4
);
186 PushU64 (&VmContext
, (UINT64
) Arg3
);
187 PushU64 (&VmContext
, (UINT64
) Arg2
);
188 PushU64 (&VmContext
, (UINT64
) Arg1
);
191 // Interpreter assumes 64-bit return address is pushed on the stack.
192 // The x64 does not do this so pad the stack accordingly.
194 PushU64 (&VmContext
, (UINT64
) 0);
195 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
198 // For x64, this is where we say our return address is
200 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
203 // We need to keep track of where the EBC stack starts. This way, if the EBC
204 // accesses any stack variables above its initial stack setting, then we know
205 // it's accessing variables passed into it, which means the data is on the
207 // When we're called, on the stack (high to low) we have the parameters, the
208 // return address, then the saved ebp. Save the pointer to the return address.
209 // EBC code knows that's there, so should look above it for function parameters.
210 // The offset is the size of locals (VMContext + Addr + saved ebp).
211 // Note that the interpreter assumes there is a 16 bytes of return address on
212 // the stack too, so adjust accordingly.
213 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
217 // Begin executing the EBC code
219 EbcExecute (&VmContext
);
222 // Return the value in R[7] unless there was an error
224 ReturnEBCStack(StackIndex
);
225 return (UINT64
) VmContext
.R
[7];
230 ExecuteEbcImageEntryPoint (
231 IN EFI_HANDLE ImageHandle
,
232 IN EFI_SYSTEM_TABLE
*SystemTable
238 Begin executing an EBC image. The address of the entry point is passed
239 in via a processor register, so we'll need to make a call to get the
244 ImageHandle - image handle for the EBC application we're executing
245 SystemTable - standard system table passed into an driver's entry point
249 The value returned by the EBC application we're going to run.
254 // Create a new VM context on the stack
256 VM_CONTEXT VmContext
;
262 // Get the EBC entry point from the processor register. Make sure you don't
263 // call any functions before this or you could mess up the register the
264 // entry point is passed in.
266 Addr
= EbcLLGetEbcEntryPoint ();
269 // Now clear out our context
271 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
274 // Save the image handle so we can track the thunks created for this image
276 VmContext
.ImageHandle
= ImageHandle
;
277 VmContext
.SystemTable
= SystemTable
;
280 // Set the VM instruction pointer to the correct location in memory.
282 VmContext
.Ip
= (VMIP
) Addr
;
285 // Initialize the stack pointer for the EBC. Get the current system stack
286 // pointer and adjust it down by the max needed for the interpreter.
288 Addr
= EbcLLGetStackPointer ();
290 Status
= GetEBCStack(ImageHandle
, &VmContext
.StackPool
, &StackIndex
);
291 if (EFI_ERROR(Status
)) {
294 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
295 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
296 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
297 VmContext
.R
[0] -= sizeof (UINTN
);
301 // Put a magic value in the stack gap, then adjust down again
303 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
304 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
307 // Align the stack on a natural boundary
308 VmContext
.R
[0] &= ~(sizeof(UINTN
) - 1);
310 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
313 // Simply copy the image handle and system table onto the EBC stack.
314 // Greatly simplifies things by not having to spill the args.
316 PushU64 (&VmContext
, (UINT64
) SystemTable
);
317 PushU64 (&VmContext
, (UINT64
) ImageHandle
);
320 // VM pushes 16-bytes for return address. Simulate that here.
322 PushU64 (&VmContext
, (UINT64
) 0);
323 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
326 // For x64, this is where we say our return address is
328 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
331 // Entry function needn't access high stack context, simply
332 // put the stack pointer here.
336 // Begin executing the EBC code
338 EbcExecute (&VmContext
);
341 // Return the value in R[7] unless there was an error
343 ReturnEBCStack(StackIndex
);
344 return (UINT64
) VmContext
.R
[7];
349 IN EFI_HANDLE ImageHandle
,
350 IN VOID
*EbcEntryPoint
,
358 Create an IA32 thunk for the given EBC entry point.
362 ImageHandle - Handle of image for which this thunk is being created
363 EbcEntryPoint - Address of the EBC code that the thunk is to call
364 Thunk - Returned thunk we create here
380 // Check alignment of pointer to EBC code
382 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
383 return EFI_INVALID_PARAMETER
;
386 Size
= EBC_THUNK_SIZE
;
389 Ptr
= AllocatePool (Size
);
392 return EFI_OUT_OF_RESOURCES
;
395 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
397 // Save the start address so we can add a pointer to it to a list later.
402 // Give them the address of our buffer we're going to fix up
404 *Thunk
= (VOID
*) Ptr
;
407 // Add a magic code here to help the VM recognize the thunk..
408 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
416 Addr
= (UINT64
) 0xCA112EBCCA112EBCULL
;
417 for (I
= 0; I
< sizeof (Addr
); I
++) {
418 *Ptr
= (UINT8
) (UINTN
) Addr
;
425 // Add code bytes to load up a processor register with the EBC entry point.
426 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
427 // The first 8 bytes of the thunk entry is the address of the EBC
436 Addr
= (UINT64
) EbcEntryPoint
;
437 for (I
= 0; I
< sizeof (Addr
); I
++) {
438 *Ptr
= (UINT8
) (UINTN
) Addr
;
445 // Stick in a load of ecx with the address of appropriate VM function.
446 // Using r11 because it's a volatile register and won't be used in this
448 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
450 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
451 Addr
= (UINTN
) ExecuteEbcImageEntryPoint
;
453 Addr
= (UINTN
) EbcInterpret
;
457 // mov r11 Addr => 0x49 0xBB
465 for (I
= 0; I
< sizeof (Addr
); I
++) {
472 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
484 // Double check that our defined size is ok (application error)
488 return EFI_BUFFER_TOO_SMALL
;
491 // Add the thunk to the list for this image. Do this last since the add
492 // function flushes the cache for us.
494 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
501 IN VM_CONTEXT
*VmPtr
,
503 IN UINTN NewStackPointer
,
511 This function is called to execute an EBC CALLEX instruction.
512 The function check the callee's content to see whether it is common native
513 code or a thunk to another piece of EBC code.
514 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
515 otherwise, set the VM->IP to target EBC code directly to avoid another VM
516 be startup which cost time and stack space.
520 VmPtr - Pointer to a VM context.
521 FuncAddr - Callee's address
522 NewStackPointer - New stack pointer after the call
523 FramePtr - New frame pointer after the call
524 Size - The size of call instruction
539 // Processor specific code to check whether the callee is a thunk to EBC.
541 if (*((UINT8
*)FuncAddr
) != 0x48) {
545 if (*((UINT8
*)FuncAddr
+ 1) != 0xB8) {
549 if (*((UINT8
*)FuncAddr
+ 2) != 0xBC) {
553 if (*((UINT8
*)FuncAddr
+ 3) != 0x2E) {
557 if (*((UINT8
*)FuncAddr
+ 4) != 0x11) {
561 if (*((UINT8
*)FuncAddr
+ 5) != 0xCA) {
565 if (*((UINT8
*)FuncAddr
+ 6) != 0xBC) {
569 if (*((UINT8
*)FuncAddr
+ 7) != 0x2E) {
573 if (*((UINT8
*)FuncAddr
+ 8) != 0x11) {
577 if (*((UINT8
*)FuncAddr
+ 9) != 0xCA) {
581 if (*((UINT8
*)FuncAddr
+ 10) != 0x48) {
585 if (*((UINT8
*)FuncAddr
+ 11) != 0xB8) {
590 CopyMem (&TargetEbcAddr
, (UINT8
*)FuncAddr
+ 12, 8);
595 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
596 // put our return address and frame pointer on the VM stack.
597 // Then set the VM's IP to new EBC code.
600 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
601 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
603 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
605 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
608 // The callee is not a thunk to EBC, call native code.
610 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
613 // Get return value and advance the IP.
615 VmPtr
->R
[7] = EbcLLGetReturnValue ();