]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/Ebc/Dxe/Ipf/EbcSupport.c
d60ab09d8a0630ec2abcec56a277999716f13c4c
[mirror_edk2.git] / EdkModulePkg / Universal / Ebc / Dxe / Ipf / EbcSupport.c
1 /*++
2
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
8
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.
11
12 Module Name:
13
14 EbcSupport.c
15
16 Abstract:
17
18 This module contains EBC support routines that are customized based on
19 the target processor.
20
21 --*/
22
23 #include "EbcInt.h"
24 #include "EbcExecute.h"
25 #include "EbcSupport.h"
26
27 STATIC
28 EFI_STATUS
29 WriteBundle (
30 IN VOID *MemPtr,
31 IN UINT8 Template,
32 IN UINT64 Slot0,
33 IN UINT64 Slot1,
34 IN UINT64 Slot2
35 );
36
37 STATIC
38 VOID
39 PushU64 (
40 VM_CONTEXT *VmPtr,
41 UINT64 Arg
42 )
43 {
44 //
45 // Advance the VM stack down, and then copy the argument to the stack.
46 // Hope it's aligned.
47 //
48 VmPtr->R[0] -= sizeof (UINT64);
49 *(UINT64 *) VmPtr->R[0] = Arg;
50 }
51
52 STATIC
53 UINT64
54 EbcInterpret (
55 UINT64 Arg1,
56 ...
57 )
58 {
59 //
60 // Create a new VM context on the stack
61 //
62 VM_CONTEXT VmContext;
63 UINTN Addr;
64 VA_LIST List;
65 UINT64 Arg2;
66 UINT64 Arg3;
67 UINT64 Arg4;
68 UINT64 Arg5;
69 UINT64 Arg6;
70 UINT64 Arg7;
71 UINT64 Arg8;
72 UINTN Arg9Addr;
73 //
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.
77 //
78 Addr = EbcLLGetEbcEntryPoint ();
79 //
80 // Need the args off the stack.
81 //
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;
91 //
92 // Now clear out our context
93 //
94 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
95 //
96 // Set the VM instruction pointer to the correct location in memory.
97 //
98 VmContext.Ip = (VMIP) Addr;
99 //
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.
102 //
103 Addr = (UINTN) Arg9Addr;
104 //
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.
110 //
111 // For IPF, the stack looks like (assuming 10 args passed)
112 // arg10
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)
117 // arg7....
118 // arg1
119 // [ 64-bit return address ]
120 // [ ebc stack ]
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.
124 //
125 VmContext.HighStackBottom = (UINTN) Addr;
126 //
127 // Now adjust the EBC stack pointer down to leave a gap for interpreter
128 // execution. Then stuff a magic value there.
129 //
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];
135 //
136 // Push the EBC arguments on the stack. Does not matter that they may not
137 // all be valid.
138 //
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);
147 //
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.
151 //
152 PushU64 (&VmContext, 0);
153 PushU64 (&VmContext, 0xDEADBEEFDEADBEEF);
154 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
155 //
156 // Begin executing the EBC code
157 //
158 EbcExecute (&VmContext);
159 //
160 // Return the value in R[7] unless there was an error
161 //
162 return (UINT64) VmContext.R[7];
163 }
164
165 STATIC
166 UINT64
167 ExecuteEbcImageEntryPoint (
168 IN EFI_HANDLE ImageHandle,
169 IN EFI_SYSTEM_TABLE *SystemTable
170 )
171 /*++
172
173 Routine Description:
174
175 IPF implementation.
176
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
179 value.
180
181 Arguments:
182
183 ImageHandle - image handle for the EBC application we're executing
184 SystemTable - standard system table passed into an driver's entry point
185
186 Returns:
187
188 The value returned by the EBC application we're going to run.
189
190 --*/
191 {
192 //
193 // Create a new VM context on the stack
194 //
195 VM_CONTEXT VmContext;
196 UINTN Addr;
197
198 //
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.
202 //
203 Addr = EbcLLGetEbcEntryPoint ();
204
205 //
206 // Now clear out our context
207 //
208 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
209
210 //
211 // Save the image handle so we can track the thunks created for this image
212 //
213 VmContext.ImageHandle = ImageHandle;
214 VmContext.SystemTable = SystemTable;
215
216 //
217 // Set the VM instruction pointer to the correct location in memory.
218 //
219 VmContext.Ip = (VMIP) Addr;
220
221 //
222 // Get the stack pointer. This is the bottom of the upper stack.
223 //
224 Addr = EbcLLGetStackPointer ();
225 VmContext.HighStackBottom = (UINTN) Addr;
226 VmContext.R[0] = (INT64) Addr;
227
228 //
229 // Allocate stack space for the interpreter. Then put a magic value
230 // at the bottom so we can detect stack corruption.
231 //
232 VmContext.R[0] -= VM_STACK_SIZE;
233 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);
234 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
235
236 //
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.
244 //
245 VmContext.R[0] -= 10 * sizeof (UINT64);
246
247 //
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.
251 //
252 VmContext.R[0] &= (INT64)~0x0f;
253 VmContext.LowStackTop = (UINTN) VmContext.R[0];
254 //
255 // Simply copy the image handle and system table onto the EBC stack.
256 // Greatly simplifies things by not having to spill the args
257 //
258 PushU64 (&VmContext, (UINT64) SystemTable);
259 PushU64 (&VmContext, (UINT64) ImageHandle);
260
261 //
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.
265 //
266 PushU64 (&VmContext, (UINT64) 0);
267 PushU64 (&VmContext, (UINT64) 0x1234567887654321);
268 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
269
270 //
271 // Begin executing the EBC code
272 //
273 EbcExecute (&VmContext);
274
275 //
276 // Return the value in R[7] unless there was an error
277 //
278 return (UINT64) VmContext.R[7];
279 }
280
281 EFI_STATUS
282 EbcCreateThunks (
283 IN EFI_HANDLE ImageHandle,
284 IN VOID *EbcEntryPoint,
285 OUT VOID **Thunk,
286 IN UINT32 Flags
287 )
288 /*++
289
290 Routine Description:
291
292 Create thunks for an EBC image entry point, or an EBC protocol service.
293
294 Arguments:
295
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
301
302 Returns:
303
304 Standard EFI status.
305
306 --*/
307 {
308 UINT8 *Ptr;
309 UINT8 *ThunkBase;
310 UINT64 Addr;
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
319 UINT64 *Data64Ptr;
320 UINT32 ThunkSize;
321 UINT32 Size;
322
323 //
324 // Check alignment of pointer to EBC code, which must always be aligned
325 // on a 2-byte boundary.
326 //
327 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
328 return EFI_INVALID_PARAMETER;
329 }
330 //
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
333 // alignment size.
334 //
335 Size = EBC_THUNK_SIZE + EBC_THUNK_ALIGNMENT - 1;
336 ThunkSize = Size;
337 Ptr = AllocatePool (Size);
338
339 if (Ptr == NULL) {
340 return EFI_OUT_OF_RESOURCES;
341 }
342 //
343 // Save the start address of the buffer.
344 //
345 ThunkBase = Ptr;
346
347 //
348 // Make sure it's aligned for code execution. If not, then
349 // round up.
350 //
351 if ((UINT32) (UINTN) Ptr & (EBC_THUNK_ALIGNMENT - 1)) {
352 Ptr = (UINT8 *) (((UINTN) Ptr + (EBC_THUNK_ALIGNMENT - 1)) &~ (UINT64) (EBC_THUNK_ALIGNMENT - 1));
353 }
354 //
355 // Return the pointer to the thunk to the caller to user as the
356 // image entry point.
357 //
358 *Thunk = (VOID *) Ptr;
359
360 //
361 // Clear out the thunk entry
362 // ZeroMem(Ptr, Size);
363 //
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
368 // for details.
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.
373 //
374 Data64Ptr = (UINT64 *) Ptr;
375
376 //
377 // Write the function's entry point (which is our thunk code that follows
378 // this descriptor we're creating).
379 //
380 *Data64Ptr = (UINT64) (Data64Ptr + 2);
381 //
382 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
383 // descriptor.
384 //
385 *(Data64Ptr + 1) = *(UINT64 *) ((UINT64 *) (UINTN) EbcInterpret + 1);
386 //
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).
390 //
391 Ptr += sizeof (UINT64) * 2;
392
393 //
394 // *************************** MAGIC BUNDLE ********************************
395 //
396 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
397 // to recognize it is a thunk.
398 //
399 Addr = (UINT64) 0xCA112EBCCA112EBC;
400
401 //
402 // Now generate the code bytes. First is nop.m 0x0
403 //
404 Code[0] = OPCODE_NOP;
405
406 //
407 // Next is simply Addr[62:22] (41 bits) of the address
408 //
409 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
410
411 //
412 // Extract bits from the address for insertion into the instruction
413 // i = Addr[63:63]
414 //
415 I = RShiftU64 (Addr, 63) & 0x01;
416 //
417 // ic = Addr[21:21]
418 //
419 Ic = RShiftU64 (Addr, 21) & 0x01;
420 //
421 // imm5c = Addr[20:16] for 5 bits
422 //
423 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
424 //
425 // imm9d = Addr[15:7] for 9 bits
426 //
427 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
428 //
429 // imm7b = Addr[6:0] for 7 bits
430 //
431 Imm7b = Addr & 0x7F;
432
433 //
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.
436 //
437 RegNum = 8;
438
439 //
440 // Next is jumbled data, including opcode and rest of address
441 //
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);
450
451 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
452
453 //
454 // *************************** FIRST BUNDLE ********************************
455 //
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
458 // register.
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.
462 //
463 Ptr += 16;
464 Addr = (UINT64) EbcEntryPoint;
465
466 //
467 // Now generate the code bytes. First is nop.m 0x0
468 //
469 Code[0] = OPCODE_NOP;
470
471 //
472 // Next is simply Addr[62:22] (41 bits) of the address
473 //
474 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
475
476 //
477 // Extract bits from the address for insertion into the instruction
478 // i = Addr[63:63]
479 //
480 I = RShiftU64 (Addr, 63) & 0x01;
481 //
482 // ic = Addr[21:21]
483 //
484 Ic = RShiftU64 (Addr, 21) & 0x01;
485 //
486 // imm5c = Addr[20:16] for 5 bits
487 //
488 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
489 //
490 // imm9d = Addr[15:7] for 9 bits
491 //
492 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
493 //
494 // imm7b = Addr[6:0] for 7 bits
495 //
496 Imm7b = Addr & 0x7F;
497
498 //
499 // Put the EBC entry point in r8, which is the location of the return value
500 // for functions.
501 //
502 RegNum = 8;
503
504 //
505 // Next is jumbled data, including opcode and rest of address
506 //
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);
515
516 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
517
518 //
519 // *************************** NEXT BUNDLE *********************************
520 //
521 // Write code bundle for:
522 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
523 //
524 // Advance pointer to next bundle, then compute the offset from this bundle
525 // to the address of the entry point of the interpreter.
526 //
527 Ptr += 16;
528 if (Flags & FLAG_THUNK_ENTRY_POINT) {
529 Addr = (UINT64) ExecuteEbcImageEntryPoint;
530 } else {
531 Addr = (UINT64) EbcInterpret;
532 }
533 //
534 // Indirection on Itanium-based systems
535 //
536 Addr = *(UINT64 *) Addr;
537
538 //
539 // Now write the code to load the offset into a register
540 //
541 Code[0] = OPCODE_NOP;
542
543 //
544 // Next is simply Addr[62:22] (41 bits) of the address
545 //
546 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
547
548 //
549 // Extract bits from the address for insertion into the instruction
550 // i = Addr[63:63]
551 //
552 I = RShiftU64 (Addr, 63) & 0x01;
553 //
554 // ic = Addr[21:21]
555 //
556 Ic = RShiftU64 (Addr, 21) & 0x01;
557 //
558 // imm5c = Addr[20:16] for 5 bits
559 //
560 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
561 //
562 // imm9d = Addr[15:7] for 9 bits
563 //
564 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
565 //
566 // imm7b = Addr[6:0] for 7 bits
567 //
568 Imm7b = Addr & 0x7F;
569
570 //
571 // Put it in r31, a scratch register
572 //
573 RegNum = 31;
574
575 //
576 // Next is jumbled data, including opcode and rest of address
577 //
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);
586
587 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
588
589 //
590 // *************************** NEXT BUNDLE *********************************
591 //
592 // Load branch register with EbcInterpret() function offset from the bundle
593 // address: mov b6 = RegNum
594 //
595 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
596 //
597 // Advance pointer to next bundle
598 //
599 Ptr += 16;
600 Code[0] = OPCODE_NOP;
601 Code[1] = OPCODE_NOP;
602 Code[2] = OPCODE_MOV_BX_RX;
603
604 //
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).
607 //
608 Br = 6;
609 Code[2] |= LShiftU64 (Br, 6);
610 Code[2] |= LShiftU64 (RegNum, 13);
611 WriteBundle ((VOID *) Ptr, 0x0d, Code[0], Code[1], Code[2]);
612
613 //
614 // *************************** NEXT BUNDLE *********************************
615 //
616 // Now do the branch: (p0) br.cond.sptk.few b6
617 //
618 // Advance pointer to next bundle.
619 // Fill in the bits for the branch register (same reg as previous bundle)
620 //
621 Ptr += 16;
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]);
627
628 //
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.
632 //
633 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
634
635 //
636 // Done
637 //
638 return EFI_SUCCESS;
639 }
640
641 STATIC
642 EFI_STATUS
643 WriteBundle (
644 IN VOID *MemPtr,
645 IN UINT8 Template,
646 IN UINT64 Slot0,
647 IN UINT64 Slot1,
648 IN UINT64 Slot2
649 )
650 /*++
651
652 Routine Description:
653
654 Given raw bytes of Itanium based code, format them into a bundle and
655 write them out.
656
657 Arguments:
658
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
662
663 Returns:
664
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.
669
670 --*/
671 {
672 UINT8 *BPtr;
673 UINT32 Index;
674 UINT64 Low64;
675 UINT64 High64;
676
677 //
678 // Verify pointer is aligned
679 //
680 if ((UINT64) MemPtr & 0xF) {
681 return EFI_INVALID_PARAMETER;
682 }
683 //
684 // Verify no more than 5 bits in template
685 //
686 if (Template &~0x1F) {
687 return EFI_INVALID_PARAMETER;
688 }
689 //
690 // Verify max of 41 bits used in code
691 //
692 if ((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) {
693 return EFI_INVALID_PARAMETER;
694 }
695
696 Low64 = LShiftU64 (Slot1, 46);
697 Low64 = Low64 | LShiftU64 (Slot0, 5) | Template;
698
699 High64 = RShiftU64 (Slot1, 18);
700 High64 = High64 | LShiftU64 (Slot2, 23);
701
702 //
703 // Now write it all out
704 //
705 BPtr = (UINT8 *) MemPtr;
706 for (Index = 0; Index < 8; Index++) {
707 *BPtr = (UINT8) Low64;
708 Low64 = RShiftU64 (Low64, 8);
709 BPtr++;
710 }
711
712 for (Index = 0; Index < 8; Index++) {
713 *BPtr = (UINT8) High64;
714 High64 = RShiftU64 (High64, 8);
715 BPtr++;
716 }
717
718 return EFI_SUCCESS;
719 }
720
721 VOID
722 EbcLLCALLEX (
723 IN VM_CONTEXT *VmPtr,
724 IN UINTN FuncAddr,
725 IN UINTN NewStackPointer,
726 IN VOID *FramePtr,
727 IN UINT8 Size
728 )
729 /*++
730
731 Routine Description:
732
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.
739
740 Arguments:
741
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
747
748 Returns:
749
750 None.
751
752 --*/
753 {
754 UINTN IsThunk;
755 UINTN TargetEbcAddr;
756 UINTN CodeOne18;
757 UINTN CodeOne23;
758 UINTN CodeTwoI;
759 UINTN CodeTwoIc;
760 UINTN CodeTwo7b;
761 UINTN CodeTwo5c;
762 UINTN CodeTwo9d;
763 UINTN CalleeAddr;
764
765 IsThunk = 1;
766 TargetEbcAddr = 0;
767
768 //
769 // FuncAddr points to the descriptor of the target instructions.
770 //
771 CalleeAddr = *((UINT64 *)FuncAddr);
772
773 //
774 // Processor specific code to check whether the callee is a thunk to EBC.
775 //
776 if (*((UINT64 *)CalleeAddr) != 0xBCCA000100000005) {
777 IsThunk = 0;
778 goto Action;
779 }
780 if (*((UINT64 *)CalleeAddr + 1) != 0x697623C1004A112E) {
781 IsThunk = 0;
782 goto Action;
783 }
784
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;
792
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);
800
801 Action:
802 if (IsThunk == 1){
803 //
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.
807 //
808 VmPtr->R[0] -= 8;
809 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
810 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
811 VmPtr->R[0] -= 8;
812 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));
813
814 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
815 } else {
816 //
817 // The callee is not a thunk to EBC, call native code.
818 //
819 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
820
821 //
822 // Get return value and advance the IP.
823 //
824 VmPtr->R[7] = EbcLLGetReturnValue ();
825 VmPtr->Ip += Size;
826 }
827 }
828
829 VOID
830 EbcLLCALLEXNative (
831 IN UINTN CallAddr,
832 IN UINTN EbcSp,
833 IN VOID *FramePtr
834 )
835 /*++
836
837 Routine Description:
838 Implements the EBC CALLEX instruction to call an external function, which
839 seems to be native code.
840
841 We'll copy the entire EBC stack frame down below itself in memory and use
842 that copy for passing parameters.
843
844 Arguments:
845 CallAddr - address (function pointer) of function to call
846 EbcSp - current EBC stack pointer
847 FramePtr - current EBC frame pointer.
848
849 Returns:
850 NA
851
852 --*/
853 {
854 UINTN FrameSize;
855 VOID *Destination;
856 VOID *Source;
857 //
858 // The stack for an EBC function looks like this:
859 // FramePtr (8)
860 // RetAddr (8)
861 // Locals (n)
862 // Stack for passing args (m)
863 //
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.
866 //
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);
873 }