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
24 #include "EbcExecute.h"
25 #include "EbcSupport.h"
45 // Advance the VM stack down, and then copy the argument to the stack.
48 VmPtr
->R
[0] -= sizeof (UINT64
);
49 *(UINT64
*) VmPtr
->R
[0] = Arg
;
60 // Create a new VM context on the stack
83 // Get the EBC entry point from the processor register. Make sure you don't
84 // call any functions before this or you could mess up the register the
85 // entry point is passed in.
87 Addr
= EbcLLGetEbcEntryPoint ();
89 // Need the args off the stack.
91 VA_START (List
, Arg1
);
92 Arg2
= VA_ARG (List
, UINT64
);
93 Arg3
= VA_ARG (List
, UINT64
);
94 Arg4
= VA_ARG (List
, UINT64
);
95 Arg5
= VA_ARG (List
, UINT64
);
96 Arg6
= VA_ARG (List
, UINT64
);
97 Arg7
= VA_ARG (List
, UINT64
);
98 Arg8
= VA_ARG (List
, UINT64
);
99 Arg9
= VA_ARG (List
, UINT64
);
100 Arg10
= VA_ARG (List
, UINT64
);
101 Arg11
= VA_ARG (List
, UINT64
);
102 Arg12
= VA_ARG (List
, UINT64
);
103 Arg13
= VA_ARG (List
, UINT64
);
104 Arg14
= VA_ARG (List
, UINT64
);
105 Arg15
= VA_ARG (List
, UINT64
);
106 Arg16
= VA_ARG (List
, UINT64
);
108 // Now clear out our context
110 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
112 // Set the VM instruction pointer to the correct location in memory.
114 VmContext
.Ip
= (VMIP
) Addr
;
116 // Initialize the stack pointer for the EBC. Get the current system stack
117 // pointer and adjust it down by the max needed for the interpreter.
120 // NOTE: Eventually we should have the interpreter allocate memory
121 // for stack space which it will use during its execution. This
122 // would likely improve performance because the interpreter would
123 // no longer be required to test each memory access and adjust
124 // those reading from the stack gap.
126 // For IPF, the stack looks like (assuming 10 args passed)
128 // arg9 (Bottom of high stack)
129 // [ stack gap for interpreter execution ]
130 // [ magic value for detection of stack corruption ]
131 // arg8 (Top of low stack)
134 // [ 64-bit return address ]
136 // If the EBC accesses memory in the stack gap, then we assume that it's
137 // actually trying to access args9 and greater. Therefore we need to
138 // adjust memory accesses in this region to point above the stack gap.
141 // Now adjust the EBC stack pointer down to leave a gap for interpreter
142 // execution. Then stuff a magic value there.
145 Status
= GetEBCStack((EFI_HANDLE
)(UINTN
)-1, &VmContext
.StackPool
, &StackIndex
);
146 if (EFI_ERROR(Status
)) {
149 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
150 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
151 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
152 VmContext
.R
[0] -= sizeof (UINTN
);
155 PushU64 (&VmContext
, (UINT64
) VM_STACK_KEY_VALUE
);
156 VmContext
.StackMagicPtr
= (UINTN
*) VmContext
.R
[0];
157 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
159 // Push the EBC arguments on the stack. Does not matter that they may not
162 PushU64 (&VmContext
, Arg16
);
163 PushU64 (&VmContext
, Arg15
);
164 PushU64 (&VmContext
, Arg14
);
165 PushU64 (&VmContext
, Arg13
);
166 PushU64 (&VmContext
, Arg12
);
167 PushU64 (&VmContext
, Arg11
);
168 PushU64 (&VmContext
, Arg10
);
169 PushU64 (&VmContext
, Arg9
);
170 PushU64 (&VmContext
, Arg8
);
171 PushU64 (&VmContext
, Arg7
);
172 PushU64 (&VmContext
, Arg6
);
173 PushU64 (&VmContext
, Arg5
);
174 PushU64 (&VmContext
, Arg4
);
175 PushU64 (&VmContext
, Arg3
);
176 PushU64 (&VmContext
, Arg2
);
177 PushU64 (&VmContext
, Arg1
);
179 // Push a bogus return address on the EBC stack because the
180 // interpreter expects one there. For stack alignment purposes on IPF,
181 // EBC return addresses are always 16 bytes. Push a bogus value as well.
183 PushU64 (&VmContext
, 0);
184 PushU64 (&VmContext
, 0xDEADBEEFDEADBEEF);
185 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
187 // Begin executing the EBC code
189 EbcExecute (&VmContext
);
191 // Return the value in R[7] unless there was an error
193 ReturnEBCStack(StackIndex
);
194 return (UINT64
) VmContext
.R
[7];
199 ExecuteEbcImageEntryPoint (
200 IN EFI_HANDLE ImageHandle
,
201 IN EFI_SYSTEM_TABLE
*SystemTable
209 Begin executing an EBC image. The address of the entry point is passed
210 in via a processor register, so we'll need to make a call to get the
215 ImageHandle - image handle for the EBC application we're executing
216 SystemTable - standard system table passed into an driver's entry point
220 The value returned by the EBC application we're going to run.
225 // Create a new VM context on the stack
227 VM_CONTEXT VmContext
;
233 // Get the EBC entry point from the processor register. Make sure you don't
234 // call any functions before this or you could mess up the register the
235 // entry point is passed in.
237 Addr
= EbcLLGetEbcEntryPoint ();
240 // Now clear out our context
242 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
245 // Save the image handle so we can track the thunks created for this image
247 VmContext
.ImageHandle
= ImageHandle
;
248 VmContext
.SystemTable
= SystemTable
;
251 // Set the VM instruction pointer to the correct location in memory.
253 VmContext
.Ip
= (VMIP
) Addr
;
256 // Get the stack pointer. This is the bottom of the upper stack.
258 Addr
= EbcLLGetStackPointer ();
260 Status
= GetEBCStack(ImageHandle
, &VmContext
.StackPool
, &StackIndex
);
261 if (EFI_ERROR(Status
)) {
264 VmContext
.StackTop
= (UINT8
*)VmContext
.StackPool
+ (STACK_REMAIN_SIZE
);
265 VmContext
.R
[0] = (UINT64
) ((UINT8
*)VmContext
.StackPool
+ STACK_POOL_SIZE
);
266 VmContext
.HighStackBottom
= (UINTN
) VmContext
.R
[0];
267 VmContext
.R
[0] -= sizeof (UINTN
);
271 // Allocate stack space for the interpreter. Then put a magic value
272 // at the bottom so we can detect stack corruption.
274 PushU64 (&VmContext
, (UINT64
) VM_STACK_KEY_VALUE
);
275 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
278 // When we thunk to external native code, we copy the last 8 qwords from
279 // the EBC stack into the processor registers, and adjust the stack pointer
280 // up. If the caller is not passing 8 parameters, then we've moved the
281 // stack pointer up into the stack gap. If this happens, then the caller
282 // can mess up the stack gap contents (in particular our magic value).
283 // Therefore, leave another gap below the magic value. Pick 10 qwords down,
284 // just as a starting point.
286 VmContext
.R
[0] -= 10 * sizeof (UINT64
);
289 // Align the stack pointer such that after pushing the system table,
290 // image handle, and return address on the stack, it's aligned on a 16-byte
291 // boundary as required for IPF.
293 VmContext
.R
[0] &= (INT64
)~0x0f;
294 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
296 // Simply copy the image handle and system table onto the EBC stack.
297 // Greatly simplifies things by not having to spill the args
299 PushU64 (&VmContext
, (UINT64
) SystemTable
);
300 PushU64 (&VmContext
, (UINT64
) ImageHandle
);
303 // Interpreter assumes 64-bit return address is pushed on the stack.
304 // IPF does not do this so pad the stack accordingly. Also, a
305 // "return address" is 16 bytes as required for IPF stack alignments.
307 PushU64 (&VmContext
, (UINT64
) 0);
308 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321);
309 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
312 // Begin executing the EBC code
314 EbcExecute (&VmContext
);
317 // Return the value in R[7] unless there was an error
319 ReturnEBCStack(StackIndex
);
320 return (UINT64
) VmContext
.R
[7];
325 IN EFI_HANDLE ImageHandle
,
326 IN VOID
*EbcEntryPoint
,
334 Create thunks for an EBC image entry point, or an EBC protocol service.
338 ImageHandle - Image handle for the EBC image. If not null, then we're
339 creating a thunk for an image entry point.
340 EbcEntryPoint - Address of the EBC code that the thunk is to call
341 Thunk - Returned thunk we create here
342 Flags - Flags indicating options for creating the thunk
353 UINT64 Code
[3]; // Code in a bundle
354 UINT64 RegNum
; // register number for MOVL
355 UINT64 I
; // bits of MOVL immediate data
356 UINT64 Ic
; // bits of MOVL immediate data
357 UINT64 Imm5c
; // bits of MOVL immediate data
358 UINT64 Imm9d
; // bits of MOVL immediate data
359 UINT64 Imm7b
; // bits of MOVL immediate data
360 UINT64 Br
; // branch register for loading and jumping
366 // Check alignment of pointer to EBC code, which must always be aligned
367 // on a 2-byte boundary.
369 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
370 return EFI_INVALID_PARAMETER
;
373 // Allocate memory for the thunk. Make the (most likely incorrect) assumption
374 // that the returned buffer is not aligned, so round up to the next
377 Size
= EBC_THUNK_SIZE
+ EBC_THUNK_ALIGNMENT
- 1;
379 Ptr
= AllocatePool (Size
);
382 return EFI_OUT_OF_RESOURCES
;
385 // Save the start address of the buffer.
390 // Make sure it's aligned for code execution. If not, then
393 if ((UINT32
) (UINTN
) Ptr
& (EBC_THUNK_ALIGNMENT
- 1)) {
394 Ptr
= (UINT8
*) (((UINTN
) Ptr
+ (EBC_THUNK_ALIGNMENT
- 1)) &~ (UINT64
) (EBC_THUNK_ALIGNMENT
- 1));
397 // Return the pointer to the thunk to the caller to user as the
398 // image entry point.
400 *Thunk
= (VOID
*) Ptr
;
403 // Clear out the thunk entry
404 // ZeroMem(Ptr, Size);
406 // For IPF, when you do a call via a function pointer, the function pointer
407 // actually points to a function descriptor which consists of a 64-bit
408 // address of the function, followed by a 64-bit gp for the function being
409 // called. See the the Software Conventions and Runtime Architecture Guide
411 // So first off in our thunk, create a descriptor for our actual thunk code.
412 // This means we need to create a pointer to the thunk code (which follows
413 // the descriptor we're going to create), followed by the gp of the Vm
414 // interpret function we're going to eventually execute.
416 Data64Ptr
= (UINT64
*) Ptr
;
419 // Write the function's entry point (which is our thunk code that follows
420 // this descriptor we're creating).
422 *Data64Ptr
= (UINT64
) (Data64Ptr
+ 2);
424 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
427 *(Data64Ptr
+ 1) = *(UINT64
*) ((UINT64
*) (UINTN
) EbcInterpret
+ 1);
429 // Advance our thunk data pointer past the descriptor. Since the
430 // descriptor consists of 16 bytes, the pointer is still aligned for
431 // IPF code execution (on 16-byte boundary).
433 Ptr
+= sizeof (UINT64
) * 2;
436 // *************************** MAGIC BUNDLE ********************************
438 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
439 // to recognize it is a thunk.
441 Addr
= (UINT64
) 0xCA112EBCCA112EBC;
444 // Now generate the code bytes. First is nop.m 0x0
446 Code
[0] = OPCODE_NOP
;
449 // Next is simply Addr[62:22] (41 bits) of the address
451 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
454 // Extract bits from the address for insertion into the instruction
457 I
= RShiftU64 (Addr
, 63) & 0x01;
461 Ic
= RShiftU64 (Addr
, 21) & 0x01;
463 // imm5c = Addr[20:16] for 5 bits
465 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
467 // imm9d = Addr[15:7] for 9 bits
469 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
471 // imm7b = Addr[6:0] for 7 bits
476 // The EBC entry point will be put into r8, so r8 can be used here
477 // temporary. R8 is general register and is auto-serialized.
482 // Next is jumbled data, including opcode and rest of address
484 Code
[2] = LShiftU64 (Imm7b
, 13);
485 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
486 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
487 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
488 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
489 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
490 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
491 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
493 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
496 // *************************** FIRST BUNDLE ********************************
498 // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass
499 // the ebc entry point in to the interpreter function via a processor
501 // Note -- we could easily change this to pass in a pointer to a structure
502 // that contained, among other things, the EBC image's entry point. But
503 // for now pass it directly.
506 Addr
= (UINT64
) EbcEntryPoint
;
509 // Now generate the code bytes. First is nop.m 0x0
511 Code
[0] = OPCODE_NOP
;
514 // Next is simply Addr[62:22] (41 bits) of the address
516 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
519 // Extract bits from the address for insertion into the instruction
522 I
= RShiftU64 (Addr
, 63) & 0x01;
526 Ic
= RShiftU64 (Addr
, 21) & 0x01;
528 // imm5c = Addr[20:16] for 5 bits
530 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
532 // imm9d = Addr[15:7] for 9 bits
534 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
536 // imm7b = Addr[6:0] for 7 bits
541 // Put the EBC entry point in r8, which is the location of the return value
547 // Next is jumbled data, including opcode and rest of address
549 Code
[2] = LShiftU64 (Imm7b
, 13);
550 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
551 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
552 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
553 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
554 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
555 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
556 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
558 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
561 // *************************** NEXT BUNDLE *********************************
563 // Write code bundle for:
564 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
566 // Advance pointer to next bundle, then compute the offset from this bundle
567 // to the address of the entry point of the interpreter.
570 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
571 Addr
= (UINT64
) ExecuteEbcImageEntryPoint
;
573 Addr
= (UINT64
) EbcInterpret
;
576 // Indirection on Itanium-based systems
578 Addr
= *(UINT64
*) Addr
;
581 // Now write the code to load the offset into a register
583 Code
[0] = OPCODE_NOP
;
586 // Next is simply Addr[62:22] (41 bits) of the address
588 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
591 // Extract bits from the address for insertion into the instruction
594 I
= RShiftU64 (Addr
, 63) & 0x01;
598 Ic
= RShiftU64 (Addr
, 21) & 0x01;
600 // imm5c = Addr[20:16] for 5 bits
602 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
604 // imm9d = Addr[15:7] for 9 bits
606 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
608 // imm7b = Addr[6:0] for 7 bits
613 // Put it in r31, a scratch register
618 // Next is jumbled data, including opcode and rest of address
620 Code
[2] = LShiftU64(Imm7b
, 13);
621 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
622 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
623 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
624 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
625 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
626 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
627 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
629 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
632 // *************************** NEXT BUNDLE *********************************
634 // Load branch register with EbcInterpret() function offset from the bundle
635 // address: mov b6 = RegNum
637 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
639 // Advance pointer to next bundle
642 Code
[0] = OPCODE_NOP
;
643 Code
[1] = OPCODE_NOP
;
644 Code
[2] = OPCODE_MOV_BX_RX
;
647 // Pick a branch register to use. Then fill in the bits for the branch
648 // register and user register (same user register as previous bundle).
651 Code
[2] |= LShiftU64 (Br
, 6);
652 Code
[2] |= LShiftU64 (RegNum
, 13);
653 WriteBundle ((VOID
*) Ptr
, 0x0d, Code
[0], Code
[1], Code
[2]);
656 // *************************** NEXT BUNDLE *********************************
658 // Now do the branch: (p0) br.cond.sptk.few b6
660 // Advance pointer to next bundle.
661 // Fill in the bits for the branch register (same reg as previous bundle)
664 Code
[0] = OPCODE_NOP
;
665 Code
[1] = OPCODE_NOP
;
666 Code
[2] = OPCODE_BR_COND_SPTK_FEW
;
667 Code
[2] |= LShiftU64 (Br
, 13);
668 WriteBundle ((VOID
*) Ptr
, 0x1d, Code
[0], Code
[1], Code
[2]);
671 // Add the thunk to our list of allocated thunks so we can do some cleanup
672 // when the image is unloaded. Do this last since the Add function flushes
673 // the instruction cache for us.
675 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
696 Given raw bytes of Itanium based code, format them into a bundle and
701 MemPtr - pointer to memory location to write the bundles to
702 Template - 5-bit template
703 Slot0-2 - instruction slot data for the bundle
707 EFI_INVALID_PARAMETER - Pointer is not aligned
708 - No more than 5 bits in template
709 - More than 41 bits used in code
710 EFI_SUCCESS - All data is written.
720 // Verify pointer is aligned
722 if ((UINT64
) MemPtr
& 0xF) {
723 return EFI_INVALID_PARAMETER
;
726 // Verify no more than 5 bits in template
728 if (Template
&~0x1F) {
729 return EFI_INVALID_PARAMETER
;
732 // Verify max of 41 bits used in code
734 if ((Slot0
| Slot1
| Slot2
) &~0x1ffffffffff) {
735 return EFI_INVALID_PARAMETER
;
738 Low64
= LShiftU64 (Slot1
, 46);
739 Low64
= Low64
| LShiftU64 (Slot0
, 5) | Template
;
741 High64
= RShiftU64 (Slot1
, 18);
742 High64
= High64
| LShiftU64 (Slot2
, 23);
745 // Now write it all out
747 BPtr
= (UINT8
*) MemPtr
;
748 for (Index
= 0; Index
< 8; Index
++) {
749 *BPtr
= (UINT8
) Low64
;
750 Low64
= RShiftU64 (Low64
, 8);
754 for (Index
= 0; Index
< 8; Index
++) {
755 *BPtr
= (UINT8
) High64
;
756 High64
= RShiftU64 (High64
, 8);
765 IN VM_CONTEXT
*VmPtr
,
767 IN UINTN NewStackPointer
,
775 This function is called to execute an EBC CALLEX instruction.
776 The function check the callee's content to see whether it is common native
777 code or a thunk to another piece of EBC code.
778 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
779 otherwise, set the VM->IP to target EBC code directly to avoid another VM
780 be startup which cost time and stack space.
784 VmPtr - Pointer to a VM context.
785 FuncAddr - Callee's address
786 NewStackPointer - New stack pointer after the call
787 FramePtr - New frame pointer after the call
788 Size - The size of call instruction
811 // FuncAddr points to the descriptor of the target instructions.
813 CalleeAddr
= *((UINT64
*)FuncAddr
);
816 // Processor specific code to check whether the callee is a thunk to EBC.
818 if (*((UINT64
*)CalleeAddr
) != 0xBCCA000100000005) {
822 if (*((UINT64
*)CalleeAddr
+ 1) != 0x697623C1004A112E) {
827 CodeOne18
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 2), 46) & 0x3FFFF;
828 CodeOne23
= (*((UINT64
*)CalleeAddr
+ 3)) & 0x7FFFFF;
829 CodeTwoI
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 59) & 0x1;
830 CodeTwoIc
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 44) & 0x1;
831 CodeTwo7b
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 36) & 0x7F;
832 CodeTwo5c
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 45) & 0x1F;
833 CodeTwo9d
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 50) & 0x1FF;
835 TargetEbcAddr
= CodeTwo7b
;
836 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo9d
, 7);
837 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo5c
, 16);
838 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoIc
, 21);
839 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne18
, 22);
840 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne23
, 40);
841 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoI
, 63);
846 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
847 // put our return address and frame pointer on the VM stack.
848 // Then set the VM's IP to new EBC code.
851 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
852 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
854 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
856 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
859 // The callee is not a thunk to EBC, call native code.
861 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
864 // Get return value and advance the IP.
866 VmPtr
->R
[7] = EbcLLGetReturnValue ();