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
74 // Get the EBC entry point from the processor register. Make sure you don't
75 // call any functions before this or you could mess up the register the
76 // entry point is passed in.
78 Addr
= EbcLLGetEbcEntryPoint ();
80 // Need the args off the stack.
82 VA_START (List
, Arg1
);
83 Arg2
= VA_ARG (List
, UINT64
);
84 Arg3
= VA_ARG (List
, UINT64
);
85 Arg4
= VA_ARG (List
, UINT64
);
86 Arg5
= VA_ARG (List
, UINT64
);
87 Arg6
= VA_ARG (List
, UINT64
);
88 Arg7
= VA_ARG (List
, UINT64
);
89 Arg8
= VA_ARG (List
, UINT64
);
90 Arg9Addr
= (UINTN
) List
;
92 // Now clear out our context
94 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
96 // Set the VM instruction pointer to the correct location in memory.
98 VmContext
.Ip
= (VMIP
) Addr
;
100 // Initialize the stack pointer for the EBC. Get the current system stack
101 // pointer and adjust it down by the max needed for the interpreter.
103 Addr
= (UINTN
) Arg9Addr
;
105 // NOTE: Eventually we should have the interpreter allocate memory
106 // for stack space which it will use during its execution. This
107 // would likely improve performance because the interpreter would
108 // no longer be required to test each memory access and adjust
109 // those reading from the stack gap.
111 // For IPF, the stack looks like (assuming 10 args passed)
113 // arg9 (Bottom of high stack)
114 // [ stack gap for interpreter execution ]
115 // [ magic value for detection of stack corruption ]
116 // arg8 (Top of low stack)
119 // [ 64-bit return address ]
121 // If the EBC accesses memory in the stack gap, then we assume that it's
122 // actually trying to access args9 and greater. Therefore we need to
123 // adjust memory accesses in this region to point above the stack gap.
125 VmContext
.HighStackBottom
= (UINTN
) Addr
;
127 // Now adjust the EBC stack pointer down to leave a gap for interpreter
128 // execution. Then stuff a magic value there.
130 VmContext
.R
[0] = (UINT64
) Addr
;
131 VmContext
.R
[0] -= VM_STACK_SIZE
;
132 PushU64 (&VmContext
, (UINT64
) VM_STACK_KEY_VALUE
);
133 VmContext
.StackMagicPtr
= (UINTN
*) VmContext
.R
[0];
134 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
136 // Push the EBC arguments on the stack. Does not matter that they may not
139 PushU64 (&VmContext
, Arg8
);
140 PushU64 (&VmContext
, Arg7
);
141 PushU64 (&VmContext
, Arg6
);
142 PushU64 (&VmContext
, Arg5
);
143 PushU64 (&VmContext
, Arg4
);
144 PushU64 (&VmContext
, Arg3
);
145 PushU64 (&VmContext
, Arg2
);
146 PushU64 (&VmContext
, Arg1
);
148 // Push a bogus return address on the EBC stack because the
149 // interpreter expects one there. For stack alignment purposes on IPF,
150 // EBC return addresses are always 16 bytes. Push a bogus value as well.
152 PushU64 (&VmContext
, 0);
153 PushU64 (&VmContext
, 0xDEADBEEFDEADBEEF);
154 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
156 // Begin executing the EBC code
158 EbcExecute (&VmContext
);
160 // Return the value in R[7] unless there was an error
162 return (UINT64
) VmContext
.R
[7];
167 ExecuteEbcImageEntryPoint (
168 IN EFI_HANDLE ImageHandle
,
169 IN EFI_SYSTEM_TABLE
*SystemTable
177 Begin executing an EBC image. The address of the entry point is passed
178 in via a processor register, so we'll need to make a call to get the
183 ImageHandle - image handle for the EBC application we're executing
184 SystemTable - standard system table passed into an driver's entry point
188 The value returned by the EBC application we're going to run.
193 // Create a new VM context on the stack
195 VM_CONTEXT VmContext
;
199 // Get the EBC entry point from the processor register. Make sure you don't
200 // call any functions before this or you could mess up the register the
201 // entry point is passed in.
203 Addr
= EbcLLGetEbcEntryPoint ();
206 // Now clear out our context
208 ZeroMem ((VOID
*) &VmContext
, sizeof (VM_CONTEXT
));
211 // Save the image handle so we can track the thunks created for this image
213 VmContext
.ImageHandle
= ImageHandle
;
214 VmContext
.SystemTable
= SystemTable
;
217 // Set the VM instruction pointer to the correct location in memory.
219 VmContext
.Ip
= (VMIP
) Addr
;
222 // Get the stack pointer. This is the bottom of the upper stack.
224 Addr
= EbcLLGetStackPointer ();
225 VmContext
.HighStackBottom
= (UINTN
) Addr
;
226 VmContext
.R
[0] = (INT64
) Addr
;
229 // Allocate stack space for the interpreter. Then put a magic value
230 // at the bottom so we can detect stack corruption.
232 VmContext
.R
[0] -= VM_STACK_SIZE
;
233 PushU64 (&VmContext
, (UINT64
) VM_STACK_KEY_VALUE
);
234 VmContext
.StackMagicPtr
= (UINTN
*) (UINTN
) VmContext
.R
[0];
237 // When we thunk to external native code, we copy the last 8 qwords from
238 // the EBC stack into the processor registers, and adjust the stack pointer
239 // up. If the caller is not passing 8 parameters, then we've moved the
240 // stack pointer up into the stack gap. If this happens, then the caller
241 // can mess up the stack gap contents (in particular our magic value).
242 // Therefore, leave another gap below the magic value. Pick 10 qwords down,
243 // just as a starting point.
245 VmContext
.R
[0] -= 10 * sizeof (UINT64
);
248 // Align the stack pointer such that after pushing the system table,
249 // image handle, and return address on the stack, it's aligned on a 16-byte
250 // boundary as required for IPF.
252 VmContext
.R
[0] &= (INT64
)~0x0f;
253 VmContext
.LowStackTop
= (UINTN
) VmContext
.R
[0];
255 // Simply copy the image handle and system table onto the EBC stack.
256 // Greatly simplifies things by not having to spill the args
258 PushU64 (&VmContext
, (UINT64
) SystemTable
);
259 PushU64 (&VmContext
, (UINT64
) ImageHandle
);
262 // Interpreter assumes 64-bit return address is pushed on the stack.
263 // IPF does not do this so pad the stack accordingly. Also, a
264 // "return address" is 16 bytes as required for IPF stack alignments.
266 PushU64 (&VmContext
, (UINT64
) 0);
267 PushU64 (&VmContext
, (UINT64
) 0x1234567887654321);
268 VmContext
.StackRetAddr
= (UINT64
) VmContext
.R
[0];
271 // Begin executing the EBC code
273 EbcExecute (&VmContext
);
276 // Return the value in R[7] unless there was an error
278 return (UINT64
) VmContext
.R
[7];
283 IN EFI_HANDLE ImageHandle
,
284 IN VOID
*EbcEntryPoint
,
292 Create thunks for an EBC image entry point, or an EBC protocol service.
296 ImageHandle - Image handle for the EBC image. If not null, then we're
297 creating a thunk for an image entry point.
298 EbcEntryPoint - Address of the EBC code that the thunk is to call
299 Thunk - Returned thunk we create here
300 Flags - Flags indicating options for creating the thunk
311 UINT64 Code
[3]; // Code in a bundle
312 UINT64 RegNum
; // register number for MOVL
313 UINT64 I
; // bits of MOVL immediate data
314 UINT64 Ic
; // bits of MOVL immediate data
315 UINT64 Imm5c
; // bits of MOVL immediate data
316 UINT64 Imm9d
; // bits of MOVL immediate data
317 UINT64 Imm7b
; // bits of MOVL immediate data
318 UINT64 Br
; // branch register for loading and jumping
324 // Check alignment of pointer to EBC code, which must always be aligned
325 // on a 2-byte boundary.
327 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
328 return EFI_INVALID_PARAMETER
;
331 // Allocate memory for the thunk. Make the (most likely incorrect) assumption
332 // that the returned buffer is not aligned, so round up to the next
335 Size
= EBC_THUNK_SIZE
+ EBC_THUNK_ALIGNMENT
- 1;
337 Ptr
= AllocatePool (Size
);
340 return EFI_OUT_OF_RESOURCES
;
343 // Save the start address of the buffer.
348 // Make sure it's aligned for code execution. If not, then
351 if ((UINT32
) (UINTN
) Ptr
& (EBC_THUNK_ALIGNMENT
- 1)) {
352 Ptr
= (UINT8
*) (((UINTN
) Ptr
+ (EBC_THUNK_ALIGNMENT
- 1)) &~ (UINT64
) (EBC_THUNK_ALIGNMENT
- 1));
355 // Return the pointer to the thunk to the caller to user as the
356 // image entry point.
358 *Thunk
= (VOID
*) Ptr
;
361 // Clear out the thunk entry
362 // ZeroMem(Ptr, Size);
364 // For IPF, when you do a call via a function pointer, the function pointer
365 // actually points to a function descriptor which consists of a 64-bit
366 // address of the function, followed by a 64-bit gp for the function being
367 // called. See the the Software Conventions and Runtime Architecture Guide
369 // So first off in our thunk, create a descriptor for our actual thunk code.
370 // This means we need to create a pointer to the thunk code (which follows
371 // the descriptor we're going to create), followed by the gp of the Vm
372 // interpret function we're going to eventually execute.
374 Data64Ptr
= (UINT64
*) Ptr
;
377 // Write the function's entry point (which is our thunk code that follows
378 // this descriptor we're creating).
380 *Data64Ptr
= (UINT64
) (Data64Ptr
+ 2);
382 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
385 *(Data64Ptr
+ 1) = *(UINT64
*) ((UINT64
*) (UINTN
) EbcInterpret
+ 1);
387 // Advance our thunk data pointer past the descriptor. Since the
388 // descriptor consists of 16 bytes, the pointer is still aligned for
389 // IPF code execution (on 16-byte boundary).
391 Ptr
+= sizeof (UINT64
) * 2;
394 // *************************** MAGIC BUNDLE ********************************
396 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
397 // to recognize it is a thunk.
399 Addr
= (UINT64
) 0xCA112EBCCA112EBC;
402 // Now generate the code bytes. First is nop.m 0x0
404 Code
[0] = OPCODE_NOP
;
407 // Next is simply Addr[62:22] (41 bits) of the address
409 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
412 // Extract bits from the address for insertion into the instruction
415 I
= RShiftU64 (Addr
, 63) & 0x01;
419 Ic
= RShiftU64 (Addr
, 21) & 0x01;
421 // imm5c = Addr[20:16] for 5 bits
423 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
425 // imm9d = Addr[15:7] for 9 bits
427 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
429 // imm7b = Addr[6:0] for 7 bits
434 // The EBC entry point will be put into r8, so r8 can be used here
435 // temporary. R8 is general register and is auto-serialized.
440 // Next is jumbled data, including opcode and rest of address
442 Code
[2] = LShiftU64 (Imm7b
, 13);
443 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
444 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
445 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
446 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
447 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
448 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
449 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
451 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
454 // *************************** FIRST BUNDLE ********************************
456 // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass
457 // the ebc entry point in to the interpreter function via a processor
459 // Note -- we could easily change this to pass in a pointer to a structure
460 // that contained, among other things, the EBC image's entry point. But
461 // for now pass it directly.
464 Addr
= (UINT64
) EbcEntryPoint
;
467 // Now generate the code bytes. First is nop.m 0x0
469 Code
[0] = OPCODE_NOP
;
472 // Next is simply Addr[62:22] (41 bits) of the address
474 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
477 // Extract bits from the address for insertion into the instruction
480 I
= RShiftU64 (Addr
, 63) & 0x01;
484 Ic
= RShiftU64 (Addr
, 21) & 0x01;
486 // imm5c = Addr[20:16] for 5 bits
488 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
490 // imm9d = Addr[15:7] for 9 bits
492 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
494 // imm7b = Addr[6:0] for 7 bits
499 // Put the EBC entry point in r8, which is the location of the return value
505 // Next is jumbled data, including opcode and rest of address
507 Code
[2] = LShiftU64 (Imm7b
, 13);
508 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
509 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
510 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
511 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
512 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
513 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
514 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
516 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
519 // *************************** NEXT BUNDLE *********************************
521 // Write code bundle for:
522 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
524 // Advance pointer to next bundle, then compute the offset from this bundle
525 // to the address of the entry point of the interpreter.
528 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
529 Addr
= (UINT64
) ExecuteEbcImageEntryPoint
;
531 Addr
= (UINT64
) EbcInterpret
;
534 // Indirection on Itanium-based systems
536 Addr
= *(UINT64
*) Addr
;
539 // Now write the code to load the offset into a register
541 Code
[0] = OPCODE_NOP
;
544 // Next is simply Addr[62:22] (41 bits) of the address
546 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
549 // Extract bits from the address for insertion into the instruction
552 I
= RShiftU64 (Addr
, 63) & 0x01;
556 Ic
= RShiftU64 (Addr
, 21) & 0x01;
558 // imm5c = Addr[20:16] for 5 bits
560 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
562 // imm9d = Addr[15:7] for 9 bits
564 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
566 // imm7b = Addr[6:0] for 7 bits
571 // Put it in r31, a scratch register
576 // Next is jumbled data, including opcode and rest of address
578 Code
[2] = LShiftU64(Imm7b
, 13);
579 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
580 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
581 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
582 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
583 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
584 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
585 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
587 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
590 // *************************** NEXT BUNDLE *********************************
592 // Load branch register with EbcInterpret() function offset from the bundle
593 // address: mov b6 = RegNum
595 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
597 // Advance pointer to next bundle
600 Code
[0] = OPCODE_NOP
;
601 Code
[1] = OPCODE_NOP
;
602 Code
[2] = OPCODE_MOV_BX_RX
;
605 // Pick a branch register to use. Then fill in the bits for the branch
606 // register and user register (same user register as previous bundle).
609 Code
[2] |= LShiftU64 (Br
, 6);
610 Code
[2] |= LShiftU64 (RegNum
, 13);
611 WriteBundle ((VOID
*) Ptr
, 0x0d, Code
[0], Code
[1], Code
[2]);
614 // *************************** NEXT BUNDLE *********************************
616 // Now do the branch: (p0) br.cond.sptk.few b6
618 // Advance pointer to next bundle.
619 // Fill in the bits for the branch register (same reg as previous bundle)
622 Code
[0] = OPCODE_NOP
;
623 Code
[1] = OPCODE_NOP
;
624 Code
[2] = OPCODE_BR_COND_SPTK_FEW
;
625 Code
[2] |= LShiftU64 (Br
, 13);
626 WriteBundle ((VOID
*) Ptr
, 0x1d, Code
[0], Code
[1], Code
[2]);
629 // Add the thunk to our list of allocated thunks so we can do some cleanup
630 // when the image is unloaded. Do this last since the Add function flushes
631 // the instruction cache for us.
633 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
654 Given raw bytes of Itanium based code, format them into a bundle and
659 MemPtr - pointer to memory location to write the bundles to
660 Template - 5-bit template
661 Slot0-2 - instruction slot data for the bundle
665 EFI_INVALID_PARAMETER - Pointer is not aligned
666 - No more than 5 bits in template
667 - More than 41 bits used in code
668 EFI_SUCCESS - All data is written.
678 // Verify pointer is aligned
680 if ((UINT64
) MemPtr
& 0xF) {
681 return EFI_INVALID_PARAMETER
;
684 // Verify no more than 5 bits in template
686 if (Template
&~0x1F) {
687 return EFI_INVALID_PARAMETER
;
690 // Verify max of 41 bits used in code
692 if ((Slot0
| Slot1
| Slot2
) &~0x1ffffffffff) {
693 return EFI_INVALID_PARAMETER
;
696 Low64
= LShiftU64 (Slot1
, 46);
697 Low64
= Low64
| LShiftU64 (Slot0
, 5) | Template
;
699 High64
= RShiftU64 (Slot1
, 18);
700 High64
= High64
| LShiftU64 (Slot2
, 23);
703 // Now write it all out
705 BPtr
= (UINT8
*) MemPtr
;
706 for (Index
= 0; Index
< 8; Index
++) {
707 *BPtr
= (UINT8
) Low64
;
708 Low64
= RShiftU64 (Low64
, 8);
712 for (Index
= 0; Index
< 8; Index
++) {
713 *BPtr
= (UINT8
) High64
;
714 High64
= RShiftU64 (High64
, 8);
723 IN VM_CONTEXT
*VmPtr
,
725 IN UINTN NewStackPointer
,
733 This function is called to execute an EBC CALLEX instruction.
734 The function check the callee's content to see whether it is common native
735 code or a thunk to another piece of EBC code.
736 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
737 otherwise, set the VM->IP to target EBC code directly to avoid another VM
738 be startup which cost time and stack space.
742 VmPtr - Pointer to a VM context.
743 FuncAddr - Callee's address
744 NewStackPointer - New stack pointer after the call
745 FramePtr - New frame pointer after the call
746 Size - The size of call instruction
769 // FuncAddr points to the descriptor of the target instructions.
771 CalleeAddr
= *((UINT64
*)FuncAddr
);
774 // Processor specific code to check whether the callee is a thunk to EBC.
776 if (*((UINT64
*)CalleeAddr
) != 0xBCCA000100000005) {
780 if (*((UINT64
*)CalleeAddr
+ 1) != 0x697623C1004A112E) {
785 CodeOne18
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 2), 46) & 0x3FFFF;
786 CodeOne23
= (*((UINT64
*)CalleeAddr
+ 3)) & 0x7FFFFF;
787 CodeTwoI
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 59) & 0x1;
788 CodeTwoIc
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 44) & 0x1;
789 CodeTwo7b
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 36) & 0x7F;
790 CodeTwo5c
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 45) & 0x1F;
791 CodeTwo9d
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 50) & 0x1FF;
793 TargetEbcAddr
= CodeTwo7b
;
794 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo9d
, 7);
795 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo5c
, 16);
796 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoIc
, 21);
797 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne18
, 22);
798 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne23
, 40);
799 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoI
, 63);
804 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
805 // put our return address and frame pointer on the VM stack.
806 // Then set the VM's IP to new EBC code.
809 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
810 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
812 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
814 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
817 // The callee is not a thunk to EBC, call native code.
819 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
822 // Get return value and advance the IP.
824 VmPtr
->R
[7] = EbcLLGetReturnValue ();
838 Implements the EBC CALLEX instruction to call an external function, which
839 seems to be native code.
841 We'll copy the entire EBC stack frame down below itself in memory and use
842 that copy for passing parameters.
845 CallAddr - address (function pointer) of function to call
846 EbcSp - current EBC stack pointer
847 FramePtr - current EBC frame pointer.
858 // The stack for an EBC function looks like this:
862 // Stack for passing args (m)
864 // Pad the frame size with 64 bytes because the low-level code we call
865 // will move the stack pointer up assuming worst-case 8 args in registers.
867 FrameSize
= (UINTN
) FramePtr
- (UINTN
) EbcSp
+ 64;
868 Source
= (VOID
*) EbcSp
;
869 Destination
= (VOID
*) ((UINT8
*) EbcSp
- FrameSize
- CPU_STACK_ALIGNMENT
);
870 Destination
= (VOID
*) ((UINTN
) ((UINTN
) Destination
+ CPU_STACK_ALIGNMENT
- 1) &~((UINTN
) CPU_STACK_ALIGNMENT
- 1));
871 CopyMem (Destination
, Source
, FrameSize
);
872 EbcAsmLLCALLEX ((UINTN
) CallAddr
, (UINTN
) Destination
);