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
325 // Check alignment of pointer to EBC code, which must always be aligned
326 // on a 2-byte boundary.
328 if ((UINT32
) (UINTN
) EbcEntryPoint
& 0x01) {
329 return EFI_INVALID_PARAMETER
;
332 // Allocate memory for the thunk. Make the (most likely incorrect) assumption
333 // that the returned buffer is not aligned, so round up to the next
336 Size
= EBC_THUNK_SIZE
+ EBC_THUNK_ALIGNMENT
- 1;
338 Status
= gBS
->AllocatePool (
343 if (Status
!= EFI_SUCCESS
) {
344 return EFI_OUT_OF_RESOURCES
;
347 // Save the start address of the buffer.
352 // Make sure it's aligned for code execution. If not, then
355 if ((UINT32
) (UINTN
) Ptr
& (EBC_THUNK_ALIGNMENT
- 1)) {
356 Ptr
= (UINT8
*) (((UINTN
) Ptr
+ (EBC_THUNK_ALIGNMENT
- 1)) &~ (UINT64
) (EBC_THUNK_ALIGNMENT
- 1));
359 // Return the pointer to the thunk to the caller to user as the
360 // image entry point.
362 *Thunk
= (VOID
*) Ptr
;
365 // Clear out the thunk entry
366 // ZeroMem(Ptr, Size);
368 // For IPF, when you do a call via a function pointer, the function pointer
369 // actually points to a function descriptor which consists of a 64-bit
370 // address of the function, followed by a 64-bit gp for the function being
371 // called. See the the Software Conventions and Runtime Architecture Guide
373 // So first off in our thunk, create a descriptor for our actual thunk code.
374 // This means we need to create a pointer to the thunk code (which follows
375 // the descriptor we're going to create), followed by the gp of the Vm
376 // interpret function we're going to eventually execute.
378 Data64Ptr
= (UINT64
*) Ptr
;
381 // Write the function's entry point (which is our thunk code that follows
382 // this descriptor we're creating).
384 *Data64Ptr
= (UINT64
) (Data64Ptr
+ 2);
386 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
389 *(Data64Ptr
+ 1) = *(UINT64
*) ((UINT64
*) (UINTN
) EbcInterpret
+ 1);
391 // Advance our thunk data pointer past the descriptor. Since the
392 // descriptor consists of 16 bytes, the pointer is still aligned for
393 // IPF code execution (on 16-byte boundary).
395 Ptr
+= sizeof (UINT64
) * 2;
398 // *************************** MAGIC BUNDLE ********************************
400 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
401 // to recognize it is a thunk.
403 Addr
= (UINT64
) 0xCA112EBCCA112EBC;
406 // Now generate the code bytes. First is nop.m 0x0
408 Code
[0] = OPCODE_NOP
;
411 // Next is simply Addr[62:22] (41 bits) of the address
413 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
416 // Extract bits from the address for insertion into the instruction
419 I
= RShiftU64 (Addr
, 63) & 0x01;
423 Ic
= RShiftU64 (Addr
, 21) & 0x01;
425 // imm5c = Addr[20:16] for 5 bits
427 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
429 // imm9d = Addr[15:7] for 9 bits
431 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
433 // imm7b = Addr[6:0] for 7 bits
438 // The EBC entry point will be put into r8, so r8 can be used here
439 // temporary. R8 is general register and is auto-serialized.
444 // Next is jumbled data, including opcode and rest of address
446 Code
[2] = LShiftU64 (Imm7b
, 13);
447 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
448 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
449 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
450 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
451 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
452 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
453 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
455 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
458 // *************************** FIRST BUNDLE ********************************
460 // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass
461 // the ebc entry point in to the interpreter function via a processor
463 // Note -- we could easily change this to pass in a pointer to a structure
464 // that contained, among other things, the EBC image's entry point. But
465 // for now pass it directly.
468 Addr
= (UINT64
) EbcEntryPoint
;
471 // Now generate the code bytes. First is nop.m 0x0
473 Code
[0] = OPCODE_NOP
;
476 // Next is simply Addr[62:22] (41 bits) of the address
478 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
481 // Extract bits from the address for insertion into the instruction
484 I
= RShiftU64 (Addr
, 63) & 0x01;
488 Ic
= RShiftU64 (Addr
, 21) & 0x01;
490 // imm5c = Addr[20:16] for 5 bits
492 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
494 // imm9d = Addr[15:7] for 9 bits
496 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
498 // imm7b = Addr[6:0] for 7 bits
503 // Put the EBC entry point in r8, which is the location of the return value
509 // Next is jumbled data, including opcode and rest of address
511 Code
[2] = LShiftU64 (Imm7b
, 13);
512 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
513 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
514 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
515 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
516 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
517 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
518 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
520 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
523 // *************************** NEXT BUNDLE *********************************
525 // Write code bundle for:
526 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
528 // Advance pointer to next bundle, then compute the offset from this bundle
529 // to the address of the entry point of the interpreter.
532 if (Flags
& FLAG_THUNK_ENTRY_POINT
) {
533 Addr
= (UINT64
) ExecuteEbcImageEntryPoint
;
535 Addr
= (UINT64
) EbcInterpret
;
538 // Indirection on Itanium-based systems
540 Addr
= *(UINT64
*) Addr
;
543 // Now write the code to load the offset into a register
545 Code
[0] = OPCODE_NOP
;
548 // Next is simply Addr[62:22] (41 bits) of the address
550 Code
[1] = RShiftU64 (Addr
, 22) & 0x1ffffffffff;
553 // Extract bits from the address for insertion into the instruction
556 I
= RShiftU64 (Addr
, 63) & 0x01;
560 Ic
= RShiftU64 (Addr
, 21) & 0x01;
562 // imm5c = Addr[20:16] for 5 bits
564 Imm5c
= RShiftU64 (Addr
, 16) & 0x1F;
566 // imm9d = Addr[15:7] for 9 bits
568 Imm9d
= RShiftU64 (Addr
, 7) & 0x1FF;
570 // imm7b = Addr[6:0] for 7 bits
575 // Put it in r31, a scratch register
580 // Next is jumbled data, including opcode and rest of address
582 Code
[2] = LShiftU64(Imm7b
, 13);
583 Code
[2] = Code
[2] | LShiftU64 (0x00, 20); // vc
584 Code
[2] = Code
[2] | LShiftU64 (Ic
, 21);
585 Code
[2] = Code
[2] | LShiftU64 (Imm5c
, 22);
586 Code
[2] = Code
[2] | LShiftU64 (Imm9d
, 27);
587 Code
[2] = Code
[2] | LShiftU64 (I
, 36);
588 Code
[2] = Code
[2] | LShiftU64 ((UINT64
)MOVL_OPCODE
, 37);
589 Code
[2] = Code
[2] | LShiftU64 ((RegNum
& 0x7F), 6);
591 WriteBundle ((VOID
*) Ptr
, 0x05, Code
[0], Code
[1], Code
[2]);
594 // *************************** NEXT BUNDLE *********************************
596 // Load branch register with EbcInterpret() function offset from the bundle
597 // address: mov b6 = RegNum
599 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
601 // Advance pointer to next bundle
604 Code
[0] = OPCODE_NOP
;
605 Code
[1] = OPCODE_NOP
;
606 Code
[2] = OPCODE_MOV_BX_RX
;
609 // Pick a branch register to use. Then fill in the bits for the branch
610 // register and user register (same user register as previous bundle).
613 Code
[2] |= LShiftU64 (Br
, 6);
614 Code
[2] |= LShiftU64 (RegNum
, 13);
615 WriteBundle ((VOID
*) Ptr
, 0x0d, Code
[0], Code
[1], Code
[2]);
618 // *************************** NEXT BUNDLE *********************************
620 // Now do the branch: (p0) br.cond.sptk.few b6
622 // Advance pointer to next bundle.
623 // Fill in the bits for the branch register (same reg as previous bundle)
626 Code
[0] = OPCODE_NOP
;
627 Code
[1] = OPCODE_NOP
;
628 Code
[2] = OPCODE_BR_COND_SPTK_FEW
;
629 Code
[2] |= LShiftU64 (Br
, 13);
630 WriteBundle ((VOID
*) Ptr
, 0x1d, Code
[0], Code
[1], Code
[2]);
633 // Add the thunk to our list of allocated thunks so we can do some cleanup
634 // when the image is unloaded. Do this last since the Add function flushes
635 // the instruction cache for us.
637 EbcAddImageThunk (ImageHandle
, (VOID
*) ThunkBase
, ThunkSize
);
658 Given raw bytes of Itanium based code, format them into a bundle and
663 MemPtr - pointer to memory location to write the bundles to
664 Template - 5-bit template
665 Slot0-2 - instruction slot data for the bundle
669 EFI_INVALID_PARAMETER - Pointer is not aligned
670 - No more than 5 bits in template
671 - More than 41 bits used in code
672 EFI_SUCCESS - All data is written.
682 // Verify pointer is aligned
684 if ((UINT64
) MemPtr
& 0xF) {
685 return EFI_INVALID_PARAMETER
;
688 // Verify no more than 5 bits in template
690 if (Template
&~0x1F) {
691 return EFI_INVALID_PARAMETER
;
694 // Verify max of 41 bits used in code
696 if ((Slot0
| Slot1
| Slot2
) &~0x1ffffffffff) {
697 return EFI_INVALID_PARAMETER
;
700 Low64
= LShiftU64 (Slot1
, 46);
701 Low64
= Low64
| LShiftU64 (Slot0
, 5) | Template
;
703 High64
= RShiftU64 (Slot1
, 18);
704 High64
= High64
| LShiftU64 (Slot2
, 23);
707 // Now write it all out
709 BPtr
= (UINT8
*) MemPtr
;
710 for (Index
= 0; Index
< 8; Index
++) {
711 *BPtr
= (UINT8
) Low64
;
712 Low64
= RShiftU64 (Low64
, 8);
716 for (Index
= 0; Index
< 8; Index
++) {
717 *BPtr
= (UINT8
) High64
;
718 High64
= RShiftU64 (High64
, 8);
727 IN VM_CONTEXT
*VmPtr
,
729 IN UINTN NewStackPointer
,
737 This function is called to execute an EBC CALLEX instruction.
738 The function check the callee's content to see whether it is common native
739 code or a thunk to another piece of EBC code.
740 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
741 otherwise, set the VM->IP to target EBC code directly to avoid another VM
742 be startup which cost time and stack space.
746 VmPtr - Pointer to a VM context.
747 FuncAddr - Callee's address
748 NewStackPointer - New stack pointer after the call
749 FramePtr - New frame pointer after the call
750 Size - The size of call instruction
773 // FuncAddr points to the descriptor of the target instructions.
775 CalleeAddr
= *((UINT64
*)FuncAddr
);
778 // Processor specific code to check whether the callee is a thunk to EBC.
780 if (*((UINT64
*)CalleeAddr
) != 0xBCCA000100000005) {
784 if (*((UINT64
*)CalleeAddr
+ 1) != 0x697623C1004A112E) {
789 CodeOne18
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 2), 46) & 0x3FFFF;
790 CodeOne23
= (*((UINT64
*)CalleeAddr
+ 3)) & 0x7FFFFF;
791 CodeTwoI
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 59) & 0x1;
792 CodeTwoIc
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 44) & 0x1;
793 CodeTwo7b
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 36) & 0x7F;
794 CodeTwo5c
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 45) & 0x1F;
795 CodeTwo9d
= RShiftU64 (*((UINT64
*)CalleeAddr
+ 3), 50) & 0x1FF;
797 TargetEbcAddr
= CodeTwo7b
;
798 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo9d
, 7);
799 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwo5c
, 16);
800 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoIc
, 21);
801 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne18
, 22);
802 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeOne23
, 40);
803 TargetEbcAddr
= TargetEbcAddr
| LShiftU64 (CodeTwoI
, 63);
808 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
809 // put our return address and frame pointer on the VM stack.
810 // Then set the VM's IP to new EBC code.
813 VmWriteMemN (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINTN
) FramePtr
);
814 VmPtr
->FramePtr
= (VOID
*) (UINTN
) VmPtr
->R
[0];
816 VmWriteMem64 (VmPtr
, (UINTN
) VmPtr
->R
[0], (UINT64
) (VmPtr
->Ip
+ Size
));
818 VmPtr
->Ip
= (VMIP
) (UINTN
) TargetEbcAddr
;
821 // The callee is not a thunk to EBC, call native code.
823 EbcLLCALLEXNative (FuncAddr
, NewStackPointer
, FramePtr
);
826 // Get return value and advance the IP.
828 VmPtr
->R
[7] = EbcLLGetReturnValue ();
842 Implements the EBC CALLEX instruction to call an external function, which
843 seems to be native code.
845 We'll copy the entire EBC stack frame down below itself in memory and use
846 that copy for passing parameters.
849 CallAddr - address (function pointer) of function to call
850 EbcSp - current EBC stack pointer
851 FramePtr - current EBC frame pointer.
862 // The stack for an EBC function looks like this:
866 // Stack for passing args (m)
868 // Pad the frame size with 64 bytes because the low-level code we call
869 // will move the stack pointer up assuming worst-case 8 args in registers.
871 FrameSize
= (UINTN
) FramePtr
- (UINTN
) EbcSp
+ 64;
872 Source
= (VOID
*) EbcSp
;
873 Destination
= (VOID
*) ((UINT8
*) EbcSp
- FrameSize
- CPU_STACK_ALIGNMENT
);
874 Destination
= (VOID
*) ((UINTN
) ((UINTN
) Destination
+ CPU_STACK_ALIGNMENT
- 1) &~((UINTN
) CPU_STACK_ALIGNMENT
- 1));
875 gBS
->CopyMem (Destination
, Source
, FrameSize
);
876 EbcAsmLLCALLEX ((UINTN
) CallAddr
, (UINTN
) Destination
);