]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/Ebc/Dxe/Ipf/EbcSupport.c
29ee55b8640d1bd0bdb62663244088f38b518a68
[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 EFI_STATUS Status;
323
324 //
325 // Check alignment of pointer to EBC code, which must always be aligned
326 // on a 2-byte boundary.
327 //
328 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
329 return EFI_INVALID_PARAMETER;
330 }
331 //
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
334 // alignment size.
335 //
336 Size = EBC_THUNK_SIZE + EBC_THUNK_ALIGNMENT - 1;
337 ThunkSize = Size;
338 Status = gBS->AllocatePool (
339 EfiBootServicesData,
340 Size,
341 (VOID *) &Ptr
342 );
343 if (Status != EFI_SUCCESS) {
344 return EFI_OUT_OF_RESOURCES;
345 }
346 //
347 // Save the start address of the buffer.
348 //
349 ThunkBase = Ptr;
350
351 //
352 // Make sure it's aligned for code execution. If not, then
353 // round up.
354 //
355 if ((UINT32) (UINTN) Ptr & (EBC_THUNK_ALIGNMENT - 1)) {
356 Ptr = (UINT8 *) (((UINTN) Ptr + (EBC_THUNK_ALIGNMENT - 1)) &~ (UINT64) (EBC_THUNK_ALIGNMENT - 1));
357 }
358 //
359 // Return the pointer to the thunk to the caller to user as the
360 // image entry point.
361 //
362 *Thunk = (VOID *) Ptr;
363
364 //
365 // Clear out the thunk entry
366 // ZeroMem(Ptr, Size);
367 //
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
372 // for details.
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.
377 //
378 Data64Ptr = (UINT64 *) Ptr;
379
380 //
381 // Write the function's entry point (which is our thunk code that follows
382 // this descriptor we're creating).
383 //
384 *Data64Ptr = (UINT64) (Data64Ptr + 2);
385 //
386 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
387 // descriptor.
388 //
389 *(Data64Ptr + 1) = *(UINT64 *) ((UINT64 *) (UINTN) EbcInterpret + 1);
390 //
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).
394 //
395 Ptr += sizeof (UINT64) * 2;
396
397 //
398 // *************************** MAGIC BUNDLE ********************************
399 //
400 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
401 // to recognize it is a thunk.
402 //
403 Addr = (UINT64) 0xCA112EBCCA112EBC;
404
405 //
406 // Now generate the code bytes. First is nop.m 0x0
407 //
408 Code[0] = OPCODE_NOP;
409
410 //
411 // Next is simply Addr[62:22] (41 bits) of the address
412 //
413 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
414
415 //
416 // Extract bits from the address for insertion into the instruction
417 // i = Addr[63:63]
418 //
419 I = RShiftU64 (Addr, 63) & 0x01;
420 //
421 // ic = Addr[21:21]
422 //
423 Ic = RShiftU64 (Addr, 21) & 0x01;
424 //
425 // imm5c = Addr[20:16] for 5 bits
426 //
427 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
428 //
429 // imm9d = Addr[15:7] for 9 bits
430 //
431 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
432 //
433 // imm7b = Addr[6:0] for 7 bits
434 //
435 Imm7b = Addr & 0x7F;
436
437 //
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.
440 //
441 RegNum = 8;
442
443 //
444 // Next is jumbled data, including opcode and rest of address
445 //
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);
454
455 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
456
457 //
458 // *************************** FIRST BUNDLE ********************************
459 //
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
462 // register.
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.
466 //
467 Ptr += 16;
468 Addr = (UINT64) EbcEntryPoint;
469
470 //
471 // Now generate the code bytes. First is nop.m 0x0
472 //
473 Code[0] = OPCODE_NOP;
474
475 //
476 // Next is simply Addr[62:22] (41 bits) of the address
477 //
478 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
479
480 //
481 // Extract bits from the address for insertion into the instruction
482 // i = Addr[63:63]
483 //
484 I = RShiftU64 (Addr, 63) & 0x01;
485 //
486 // ic = Addr[21:21]
487 //
488 Ic = RShiftU64 (Addr, 21) & 0x01;
489 //
490 // imm5c = Addr[20:16] for 5 bits
491 //
492 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
493 //
494 // imm9d = Addr[15:7] for 9 bits
495 //
496 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
497 //
498 // imm7b = Addr[6:0] for 7 bits
499 //
500 Imm7b = Addr & 0x7F;
501
502 //
503 // Put the EBC entry point in r8, which is the location of the return value
504 // for functions.
505 //
506 RegNum = 8;
507
508 //
509 // Next is jumbled data, including opcode and rest of address
510 //
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);
519
520 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
521
522 //
523 // *************************** NEXT BUNDLE *********************************
524 //
525 // Write code bundle for:
526 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
527 //
528 // Advance pointer to next bundle, then compute the offset from this bundle
529 // to the address of the entry point of the interpreter.
530 //
531 Ptr += 16;
532 if (Flags & FLAG_THUNK_ENTRY_POINT) {
533 Addr = (UINT64) ExecuteEbcImageEntryPoint;
534 } else {
535 Addr = (UINT64) EbcInterpret;
536 }
537 //
538 // Indirection on Itanium-based systems
539 //
540 Addr = *(UINT64 *) Addr;
541
542 //
543 // Now write the code to load the offset into a register
544 //
545 Code[0] = OPCODE_NOP;
546
547 //
548 // Next is simply Addr[62:22] (41 bits) of the address
549 //
550 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
551
552 //
553 // Extract bits from the address for insertion into the instruction
554 // i = Addr[63:63]
555 //
556 I = RShiftU64 (Addr, 63) & 0x01;
557 //
558 // ic = Addr[21:21]
559 //
560 Ic = RShiftU64 (Addr, 21) & 0x01;
561 //
562 // imm5c = Addr[20:16] for 5 bits
563 //
564 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
565 //
566 // imm9d = Addr[15:7] for 9 bits
567 //
568 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
569 //
570 // imm7b = Addr[6:0] for 7 bits
571 //
572 Imm7b = Addr & 0x7F;
573
574 //
575 // Put it in r31, a scratch register
576 //
577 RegNum = 31;
578
579 //
580 // Next is jumbled data, including opcode and rest of address
581 //
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);
590
591 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
592
593 //
594 // *************************** NEXT BUNDLE *********************************
595 //
596 // Load branch register with EbcInterpret() function offset from the bundle
597 // address: mov b6 = RegNum
598 //
599 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
600 //
601 // Advance pointer to next bundle
602 //
603 Ptr += 16;
604 Code[0] = OPCODE_NOP;
605 Code[1] = OPCODE_NOP;
606 Code[2] = OPCODE_MOV_BX_RX;
607
608 //
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).
611 //
612 Br = 6;
613 Code[2] |= LShiftU64 (Br, 6);
614 Code[2] |= LShiftU64 (RegNum, 13);
615 WriteBundle ((VOID *) Ptr, 0x0d, Code[0], Code[1], Code[2]);
616
617 //
618 // *************************** NEXT BUNDLE *********************************
619 //
620 // Now do the branch: (p0) br.cond.sptk.few b6
621 //
622 // Advance pointer to next bundle.
623 // Fill in the bits for the branch register (same reg as previous bundle)
624 //
625 Ptr += 16;
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]);
631
632 //
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.
636 //
637 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
638
639 //
640 // Done
641 //
642 return EFI_SUCCESS;
643 }
644
645 STATIC
646 EFI_STATUS
647 WriteBundle (
648 IN VOID *MemPtr,
649 IN UINT8 Template,
650 IN UINT64 Slot0,
651 IN UINT64 Slot1,
652 IN UINT64 Slot2
653 )
654 /*++
655
656 Routine Description:
657
658 Given raw bytes of Itanium based code, format them into a bundle and
659 write them out.
660
661 Arguments:
662
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
666
667 Returns:
668
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.
673
674 --*/
675 {
676 UINT8 *BPtr;
677 UINT32 Index;
678 UINT64 Low64;
679 UINT64 High64;
680
681 //
682 // Verify pointer is aligned
683 //
684 if ((UINT64) MemPtr & 0xF) {
685 return EFI_INVALID_PARAMETER;
686 }
687 //
688 // Verify no more than 5 bits in template
689 //
690 if (Template &~0x1F) {
691 return EFI_INVALID_PARAMETER;
692 }
693 //
694 // Verify max of 41 bits used in code
695 //
696 if ((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) {
697 return EFI_INVALID_PARAMETER;
698 }
699
700 Low64 = LShiftU64 (Slot1, 46);
701 Low64 = Low64 | LShiftU64 (Slot0, 5) | Template;
702
703 High64 = RShiftU64 (Slot1, 18);
704 High64 = High64 | LShiftU64 (Slot2, 23);
705
706 //
707 // Now write it all out
708 //
709 BPtr = (UINT8 *) MemPtr;
710 for (Index = 0; Index < 8; Index++) {
711 *BPtr = (UINT8) Low64;
712 Low64 = RShiftU64 (Low64, 8);
713 BPtr++;
714 }
715
716 for (Index = 0; Index < 8; Index++) {
717 *BPtr = (UINT8) High64;
718 High64 = RShiftU64 (High64, 8);
719 BPtr++;
720 }
721
722 return EFI_SUCCESS;
723 }
724
725 VOID
726 EbcLLCALLEX (
727 IN VM_CONTEXT *VmPtr,
728 IN UINTN FuncAddr,
729 IN UINTN NewStackPointer,
730 IN VOID *FramePtr,
731 IN UINT8 Size
732 )
733 /*++
734
735 Routine Description:
736
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.
743
744 Arguments:
745
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
751
752 Returns:
753
754 None.
755
756 --*/
757 {
758 UINTN IsThunk;
759 UINTN TargetEbcAddr;
760 UINTN CodeOne18;
761 UINTN CodeOne23;
762 UINTN CodeTwoI;
763 UINTN CodeTwoIc;
764 UINTN CodeTwo7b;
765 UINTN CodeTwo5c;
766 UINTN CodeTwo9d;
767 UINTN CalleeAddr;
768
769 IsThunk = 1;
770 TargetEbcAddr = 0;
771
772 //
773 // FuncAddr points to the descriptor of the target instructions.
774 //
775 CalleeAddr = *((UINT64 *)FuncAddr);
776
777 //
778 // Processor specific code to check whether the callee is a thunk to EBC.
779 //
780 if (*((UINT64 *)CalleeAddr) != 0xBCCA000100000005) {
781 IsThunk = 0;
782 goto Action;
783 }
784 if (*((UINT64 *)CalleeAddr + 1) != 0x697623C1004A112E) {
785 IsThunk = 0;
786 goto Action;
787 }
788
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;
796
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);
804
805 Action:
806 if (IsThunk == 1){
807 //
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.
811 //
812 VmPtr->R[0] -= 8;
813 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
814 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
815 VmPtr->R[0] -= 8;
816 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));
817
818 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
819 } else {
820 //
821 // The callee is not a thunk to EBC, call native code.
822 //
823 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
824
825 //
826 // Get return value and advance the IP.
827 //
828 VmPtr->R[7] = EbcLLGetReturnValue ();
829 VmPtr->Ip += Size;
830 }
831 }
832
833 VOID
834 EbcLLCALLEXNative (
835 IN UINTN CallAddr,
836 IN UINTN EbcSp,
837 IN VOID *FramePtr
838 )
839 /*++
840
841 Routine Description:
842 Implements the EBC CALLEX instruction to call an external function, which
843 seems to be native code.
844
845 We'll copy the entire EBC stack frame down below itself in memory and use
846 that copy for passing parameters.
847
848 Arguments:
849 CallAddr - address (function pointer) of function to call
850 EbcSp - current EBC stack pointer
851 FramePtr - current EBC frame pointer.
852
853 Returns:
854 NA
855
856 --*/
857 {
858 UINTN FrameSize;
859 VOID *Destination;
860 VOID *Source;
861 //
862 // The stack for an EBC function looks like this:
863 // FramePtr (8)
864 // RetAddr (8)
865 // Locals (n)
866 // Stack for passing args (m)
867 //
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.
870 //
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);
877 }