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
45 Push a 64 bit unsigned value to the VM stack.
49 VmPtr - The pointer to current VM context.
50 Arg - The value to be pushed
59 // Advance the VM stack down, and then copy the argument to the stack.
62 VmPtr
->R
[0] -= sizeof (UINT64
);
63 *(UINT64
*) VmPtr
->R
[0] = Arg
;
80 Begin executing an EBC image. The address of the entry point is passed
81 in via a processor register, so we'll need to make a call to get the
86 This is a thunk function. Microsoft x64 compiler only provide fast_call
87 calling convention, so the first four arguments are passed by rcx, rdx,
88 r8, and r9, while other arguments are passed in stack.
92 The value returned by the EBC application we're going to run.
97 // Create a new VM context on the stack
103 // Get the EBC entry point from the processor register.
104 // Don't call any function before getting the EBC entry
105 // point because this will collab the return register.
107 Addr
= EbcLLGetEbcEntryPoint ();
110 // Now clear out our context
112 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
115 // Set the VM instruction pointer to the correct location in memory.
117 VmContext
.Ip
= (VMIP
) Addr
;
120 // Initialize the stack pointer for the EBC. Get the current system stack
121 // pointer and adjust it down by the max needed for the interpreter.
123 Addr
= EbcLLGetStackPointer ();
126 // Adjust the VM's stack pointer down.
128 VmContext
.R
[0] = (UINT64
) Addr
;
129 VmContext
.R
[0] -= VM_STACK_SIZE
;
132 // Align the stack on a natural boundary.
134 VmContext
.R
[0] &= ~(sizeof (UINTN
) - 1);
137 // Put a magic value in the stack gap, then adjust down again.
139 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
140 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
143 // The stack upper to LowStackTop is belong to the VM.
145 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
148 // For the worst case, assume there are 4 arguments passed in registers, store
149 // them to VM's stack.
151 PushU64 (&VmContext
, (UINT64
) Arg4
);
152 PushU64 (&VmContext
, (UINT64
) Arg3
);
153 PushU64 (&VmContext
, (UINT64
) Arg2
);
154 PushU64 (&VmContext
, (UINT64
) Arg1
);
157 // Interpreter assumes 64-bit return address is pushed on the stack.
158 // The x64 does not do this so pad the stack accordingly.
160 PushU64 (&VmContext
, (UINT64
) 0);
161 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
164 // For x64, this is where we say our return address is
166 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
169 // We need to keep track of where the EBC stack starts. This way, if the EBC
170 // accesses any stack variables above its initial stack setting, then we know
171 // it's accessing variables passed into it, which means the data is on the
173 // When we're called, on the stack (high to low) we have the parameters, the
174 // return address, then the saved ebp. Save the pointer to the return address.
175 // EBC code knows that's there, so should look above it for function parameters.
176 // The offset is the size of locals (VMContext + Addr + saved ebp).
177 // Note that the interpreter assumes there is a 16 bytes of return address on
178 // the stack too, so adjust accordingly.
179 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
181 VmContext
.HighStackBottom
= (UINTN
) &Arg5
;
184 // Begin executing the EBC code
186 EbcExecute (&VmContext
);
189 // Return the value in R[7] unless there was an error
191 return (UINT64
) VmContext
.R
[7];
196 ExecuteEbcImageEntryPoint (
197 IN EFI_HANDLE ImageHandle
,
198 IN EFI_SYSTEM_TABLE
*SystemTable
204 Begin executing an EBC image. The address of the entry point is passed
205 in via a processor register, so we'll need to make a call to get the
210 ImageHandle - image handle for the EBC application we're executing
211 SystemTable - standard system table passed into an driver's entry point
215 The value returned by the EBC application we're going to run.
220 // Create a new VM context on the stack
222 VM_CONTEXT VmContext
;
226 // Get the EBC entry point from the processor register. Make sure you don't
227 // call any functions before this or you could mess up the register the
228 // entry point is passed in.
230 Addr
= EbcLLGetEbcEntryPoint ();
233 // Now clear out our context
235 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
238 // Save the image handle so we can track the thunks created for this image
240 VmContext
.ImageHandle
= ImageHandle
;
241 VmContext
.SystemTable
= SystemTable
;
244 // Set the VM instruction pointer to the correct location in memory.
246 VmContext
.Ip
= (VMIP
) Addr
;
249 // Initialize the stack pointer for the EBC. Get the current system stack
250 // pointer and adjust it down by the max needed for the interpreter.
252 Addr
= EbcLLGetStackPointer ();
253 VmContext
.R
[0] = (UINT64
) Addr
;
254 VmContext
.R
[0] -= VM_STACK_SIZE
;
257 // Put a magic value in the stack gap, then adjust down again
259 *(UINTN
*) (UINTN
) (VmContext
.R
[0]) = (UINTN
) VM_STACK_KEY_VALUE
;
260 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
263 // Align the stack on a natural boundary
264 VmContext
.R
[0] &= ~(sizeof(UINTN
) - 1);
266 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
269 // Simply copy the image handle and system table onto the EBC stack.
270 // Greatly simplifies things by not having to spill the args.
272 PushU64 (&VmContext
, (UINT64
) SystemTable
);
273 PushU64 (&VmContext
, (UINT64
) ImageHandle
);
276 // VM pushes 16-bytes for return address. Simulate that here.
278 PushU64 (&VmContext
, (UINT64
) 0);
279 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321ULL
);
282 // For x64, this is where we say our return address is
284 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
287 // Entry function needn't access high stack context, simply
288 // put the stack pointer here.
290 VmContext
.HighStackBottom
= (UINTN
) Addr
;
293 // Begin executing the EBC code
295 EbcExecute (&VmContext
);
298 // Return the value in R[7] unless there was an error
300 return (UINT64
) VmContext
.R
[7];
305 IN EFI_HANDLE ImageHandle
,
306 IN VOID
*EbcEntryPoint
,
314 Create an IA32 thunk for the given EBC entry point.
318 ImageHandle - Handle of image for which this thunk is being created
319 EbcEntryPoint - Address of the EBC code that the thunk is to call
320 Thunk - Returned thunk we create here
336 // Check alignment of pointer to EBC code
338 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
339 return EFI_INVALID_PARAMETER
;
342 Size
= EBC_THUNK_SIZE
;
345 Ptr
= AllocatePool (Size
);
348 return EFI_OUT_OF_RESOURCES
;
351 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
353 // Save the start address so we can add a pointer to it to a list later.
358 // Give them the address of our buffer we're going to fix up
360 *Thunk
= (VOID
*) Ptr
;
363 // Add a magic code here to help the VM recognize the thunk..
364 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
372 Addr
= (UINT64
) 0xCA112EBCCA112EBCULL
;
373 for (I
= 0; I
< sizeof (Addr
); I
++) {
374 *Ptr
= (UINT8
) (UINTN
) Addr
;
381 // Add code bytes to load up a processor register with the EBC entry point.
382 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
383 // The first 8 bytes of the thunk entry is the address of the EBC
392 Addr
= (UINT64
) EbcEntryPoint
;
393 for (I
= 0; I
< sizeof (Addr
); I
++) {
394 *Ptr
= (UINT8
) (UINTN
) Addr
;
401 // Stick in a load of ecx with the address of appropriate VM function.
402 // Using r11 because it's a volatile register and won't be used in this
404 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
406 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
407 Addr
= (UINTN
) ExecuteEbcImageEntryPoint
;
409 Addr
= (UINTN
) EbcInterpret
;
413 // mov r11 Addr => 0x49 0xBB
421 for (I
= 0; I
< sizeof (Addr
); I
++) {
428 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
440 // Double check that our defined size is ok (application error)
444 return EFI_BUFFER_TOO_SMALL
;
447 // Add the thunk to the list for this image. Do this last since the add
448 // function flushes the cache for us.
450 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
457 IN VM_CONTEXT
*VmPtr
,
459 IN UINTN NewStackPointer
,
467 This function is called to execute an EBC CALLEX instruction.
468 The function check the callee's content to see whether it is common native
469 code or a thunk to another piece of EBC code.
470 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
471 otherwise, set the VM->IP to target EBC code directly to avoid another VM
472 be startup which cost time and stack space.
476 VmPtr - Pointer to a VM context.
477 FuncAddr - Callee's address
478 NewStackPointer - New stack pointer after the call
479 FramePtr - New frame pointer after the call
480 Size - The size of call instruction
495 // Processor specific code to check whether the callee is a thunk to EBC.
497 if (*((UINT8
*)FuncAddr
) != 0x48) {
501 if (*((UINT8
*)FuncAddr
+ 1) != 0xB8) {
505 if (*((UINT8
*)FuncAddr
+ 2) != 0xBC) {
509 if (*((UINT8
*)FuncAddr
+ 3) != 0x2E) {
513 if (*((UINT8
*)FuncAddr
+ 4) != 0x11) {
517 if (*((UINT8
*)FuncAddr
+ 5) != 0xCA) {
521 if (*((UINT8
*)FuncAddr
+ 6) != 0xBC) {
525 if (*((UINT8
*)FuncAddr
+ 7) != 0x2E) {
529 if (*((UINT8
*)FuncAddr
+ 8) != 0x11) {
533 if (*((UINT8
*)FuncAddr
+ 9) != 0xCA) {
537 if (*((UINT8
*)FuncAddr
+ 10) != 0x48) {
541 if (*((UINT8
*)FuncAddr
+ 11) != 0xB8) {
546 CopyMem (&TargetEbcAddr
, (UINT8
*)FuncAddr
+ 12, 8);
551 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
552 // put our return address and frame pointer on the VM stack.
553 // Then set the VM's IP to new EBC code.
556 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
557 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
559 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
561 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
564 // The callee is not a thunk to EBC, call native code.
566 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
569 // Get return value and advance the IP.
571 VmPtr
->R
[7] = EbcLLGetReturnValue ();