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
337 // Check alignment of pointer to EBC code
339 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
340 return EFI_INVALID_PARAMETER
;
343 Size
= EBC_THUNK_SIZE
;
346 Status
= gBS
->AllocatePool (
351 if (Status
!= EFI_SUCCESS
) {
352 return EFI_OUT_OF_RESOURCES
;
355 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
357 // Save the start address so we can add a pointer to it to a list later.
362 // Give them the address of our buffer we're going to fix up
364 *Thunk
= (VOID
*) Ptr
;
367 // Add a magic code here to help the VM recognize the thunk..
368 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
376 Addr
= (UINT64
) 0xCA112EBCCA112EBCULL
;
377 for (I
= 0; I
< sizeof (Addr
); I
++) {
378 *Ptr
= (UINT8
) (UINTN
) Addr
;
385 // Add code bytes to load up a processor register with the EBC entry point.
386 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
387 // The first 8 bytes of the thunk entry is the address of the EBC
396 Addr
= (UINT64
) EbcEntryPoint
;
397 for (I
= 0; I
< sizeof (Addr
); I
++) {
398 *Ptr
= (UINT8
) (UINTN
) Addr
;
405 // Stick in a load of ecx with the address of appropriate VM function.
406 // Using r11 because it's a volatile register and won't be used in this
408 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
410 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
411 Addr
= (UINTN
) ExecuteEbcImageEntryPoint
;
413 Addr
= (UINTN
) EbcInterpret
;
417 // mov r11 Addr => 0x49 0xBB
425 for (I
= 0; I
< sizeof (Addr
); I
++) {
432 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
444 // Double check that our defined size is ok (application error)
448 return EFI_BUFFER_TOO_SMALL
;
451 // Add the thunk to the list for this image. Do this last since the add
452 // function flushes the cache for us.
454 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
461 IN VM_CONTEXT
*VmPtr
,
463 IN UINTN NewStackPointer
,
471 This function is called to execute an EBC CALLEX instruction.
472 The function check the callee's content to see whether it is common native
473 code or a thunk to another piece of EBC code.
474 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
475 otherwise, set the VM->IP to target EBC code directly to avoid another VM
476 be startup which cost time and stack space.
480 VmPtr - Pointer to a VM context.
481 FuncAddr - Callee's address
482 NewStackPointer - New stack pointer after the call
483 FramePtr - New frame pointer after the call
484 Size - The size of call instruction
499 // Processor specific code to check whether the callee is a thunk to EBC.
501 if (*((UINT8
*)FuncAddr
) != 0x48) {
505 if (*((UINT8
*)FuncAddr
+ 1) != 0xB8) {
509 if (*((UINT8
*)FuncAddr
+ 2) != 0xBC) {
513 if (*((UINT8
*)FuncAddr
+ 3) != 0x2E) {
517 if (*((UINT8
*)FuncAddr
+ 4) != 0x11) {
521 if (*((UINT8
*)FuncAddr
+ 5) != 0xCA) {
525 if (*((UINT8
*)FuncAddr
+ 6) != 0xBC) {
529 if (*((UINT8
*)FuncAddr
+ 7) != 0x2E) {
533 if (*((UINT8
*)FuncAddr
+ 8) != 0x11) {
537 if (*((UINT8
*)FuncAddr
+ 9) != 0xCA) {
541 if (*((UINT8
*)FuncAddr
+ 10) != 0x48) {
545 if (*((UINT8
*)FuncAddr
+ 11) != 0xB8) {
550 CopyMem (&TargetEbcAddr
, (UINT8
*)FuncAddr
+ 12, 8);
555 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
556 // put our return address and frame pointer on the VM stack.
557 // Then set the VM's IP to new EBC code.
560 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
561 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
563 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
565 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
568 // The callee is not a thunk to EBC, call native code.
570 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
573 // Get return value and advance the IP.
575 VmPtr
->R
[7] = EbcLLGetReturnValue ();