]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/Ipf/EbcSupport.c
apply for doxgen format.
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / Ipf / EbcSupport.c
1 /** @file
2 This module contains EBC support routines that are customized based on
3 the target processor.
4
5 Copyright (c) 2006, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "EbcInt.h"
17 #include "EbcExecute.h"
18 #include "EbcSupport.h"
19
20 STATIC
21 EFI_STATUS
22 WriteBundle (
23 IN VOID *MemPtr,
24 IN UINT8 Template,
25 IN UINT64 Slot0,
26 IN UINT64 Slot1,
27 IN UINT64 Slot2
28 );
29
30 STATIC
31 VOID
32 PushU64 (
33 VM_CONTEXT *VmPtr,
34 UINT64 Arg
35 )
36 {
37 //
38 // Advance the VM stack down, and then copy the argument to the stack.
39 // Hope it's aligned.
40 //
41 VmPtr->R[0] -= sizeof (UINT64);
42 *(UINT64 *) VmPtr->R[0] = Arg;
43 }
44
45 STATIC
46 UINT64
47 EbcInterpret (
48 UINT64 Arg1,
49 ...
50 )
51 {
52 //
53 // Create a new VM context on the stack
54 //
55 VM_CONTEXT VmContext;
56 UINTN Addr;
57 EFI_STATUS Status;
58 UINTN StackIndex;
59 VA_LIST List;
60 UINT64 Arg2;
61 UINT64 Arg3;
62 UINT64 Arg4;
63 UINT64 Arg5;
64 UINT64 Arg6;
65 UINT64 Arg7;
66 UINT64 Arg8;
67 UINT64 Arg9;
68 UINT64 Arg10;
69 UINT64 Arg11;
70 UINT64 Arg12;
71 UINT64 Arg13;
72 UINT64 Arg14;
73 UINT64 Arg15;
74 UINT64 Arg16;
75 //
76 // Get the EBC entry point from the processor register. Make sure you don't
77 // call any functions before this or you could mess up the register the
78 // entry point is passed in.
79 //
80 Addr = EbcLLGetEbcEntryPoint ();
81 //
82 // Need the args off the stack.
83 //
84 VA_START (List, Arg1);
85 Arg2 = VA_ARG (List, UINT64);
86 Arg3 = VA_ARG (List, UINT64);
87 Arg4 = VA_ARG (List, UINT64);
88 Arg5 = VA_ARG (List, UINT64);
89 Arg6 = VA_ARG (List, UINT64);
90 Arg7 = VA_ARG (List, UINT64);
91 Arg8 = VA_ARG (List, UINT64);
92 Arg9 = VA_ARG (List, UINT64);
93 Arg10 = VA_ARG (List, UINT64);
94 Arg11 = VA_ARG (List, UINT64);
95 Arg12 = VA_ARG (List, UINT64);
96 Arg13 = VA_ARG (List, UINT64);
97 Arg14 = VA_ARG (List, UINT64);
98 Arg15 = VA_ARG (List, UINT64);
99 Arg16 = VA_ARG (List, UINT64);
100 //
101 // Now clear out our context
102 //
103 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
104 //
105 // Set the VM instruction pointer to the correct location in memory.
106 //
107 VmContext.Ip = (VMIP) Addr;
108 //
109 // Initialize the stack pointer for the EBC. Get the current system stack
110 // pointer and adjust it down by the max needed for the interpreter.
111 //
112 //
113 // NOTE: Eventually we should have the interpreter allocate memory
114 // for stack space which it will use during its execution. This
115 // would likely improve performance because the interpreter would
116 // no longer be required to test each memory access and adjust
117 // those reading from the stack gap.
118 //
119 // For IPF, the stack looks like (assuming 10 args passed)
120 // arg10
121 // arg9 (Bottom of high stack)
122 // [ stack gap for interpreter execution ]
123 // [ magic value for detection of stack corruption ]
124 // arg8 (Top of low stack)
125 // arg7....
126 // arg1
127 // [ 64-bit return address ]
128 // [ ebc stack ]
129 // If the EBC accesses memory in the stack gap, then we assume that it's
130 // actually trying to access args9 and greater. Therefore we need to
131 // adjust memory accesses in this region to point above the stack gap.
132 //
133 //
134 // Now adjust the EBC stack pointer down to leave a gap for interpreter
135 // execution. Then stuff a magic value there.
136 //
137
138 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
139 if (EFI_ERROR(Status)) {
140 return Status;
141 }
142 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
143 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
144 VmContext.HighStackBottom = (UINTN) VmContext.R[0];
145 VmContext.R[0] -= sizeof (UINTN);
146
147
148 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);
149 VmContext.StackMagicPtr = (UINTN *) VmContext.R[0];
150 VmContext.LowStackTop = (UINTN) VmContext.R[0];
151 //
152 // Push the EBC arguments on the stack. Does not matter that they may not
153 // all be valid.
154 //
155 PushU64 (&VmContext, Arg16);
156 PushU64 (&VmContext, Arg15);
157 PushU64 (&VmContext, Arg14);
158 PushU64 (&VmContext, Arg13);
159 PushU64 (&VmContext, Arg12);
160 PushU64 (&VmContext, Arg11);
161 PushU64 (&VmContext, Arg10);
162 PushU64 (&VmContext, Arg9);
163 PushU64 (&VmContext, Arg8);
164 PushU64 (&VmContext, Arg7);
165 PushU64 (&VmContext, Arg6);
166 PushU64 (&VmContext, Arg5);
167 PushU64 (&VmContext, Arg4);
168 PushU64 (&VmContext, Arg3);
169 PushU64 (&VmContext, Arg2);
170 PushU64 (&VmContext, Arg1);
171 //
172 // Push a bogus return address on the EBC stack because the
173 // interpreter expects one there. For stack alignment purposes on IPF,
174 // EBC return addresses are always 16 bytes. Push a bogus value as well.
175 //
176 PushU64 (&VmContext, 0);
177 PushU64 (&VmContext, 0xDEADBEEFDEADBEEF);
178 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
179 //
180 // Begin executing the EBC code
181 //
182 EbcExecute (&VmContext);
183 //
184 // Return the value in R[7] unless there was an error
185 //
186 ReturnEBCStack(StackIndex);
187 return (UINT64) VmContext.R[7];
188 }
189
190
191 /**
192 IPF implementation.
193 Begin executing an EBC image. The address of the entry point is passed
194 in via a processor register, so we'll need to make a call to get the
195 value.
196
197 @param ImageHandle image handle for the EBC application we're
198 executing
199 @param SystemTable standard system table passed into an driver's
200 entry point
201
202 @return The value returned by the EBC application we're going to run.
203
204 **/
205 STATIC
206 UINT64
207 ExecuteEbcImageEntryPoint (
208 IN EFI_HANDLE ImageHandle,
209 IN EFI_SYSTEM_TABLE *SystemTable
210 )
211 {
212 //
213 // Create a new VM context on the stack
214 //
215 VM_CONTEXT VmContext;
216 UINTN Addr;
217 EFI_STATUS Status;
218 UINTN StackIndex;
219
220 //
221 // Get the EBC entry point from the processor register. Make sure you don't
222 // call any functions before this or you could mess up the register the
223 // entry point is passed in.
224 //
225 Addr = EbcLLGetEbcEntryPoint ();
226
227 //
228 // Now clear out our context
229 //
230 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
231
232 //
233 // Save the image handle so we can track the thunks created for this image
234 //
235 VmContext.ImageHandle = ImageHandle;
236 VmContext.SystemTable = SystemTable;
237
238 //
239 // Set the VM instruction pointer to the correct location in memory.
240 //
241 VmContext.Ip = (VMIP) Addr;
242
243 //
244 // Get the stack pointer. This is the bottom of the upper stack.
245 //
246 Addr = EbcLLGetStackPointer ();
247
248 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
249 if (EFI_ERROR(Status)) {
250 return Status;
251 }
252 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
253 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
254 VmContext.HighStackBottom = (UINTN) VmContext.R[0];
255 VmContext.R[0] -= sizeof (UINTN);
256
257
258 //
259 // Allocate stack space for the interpreter. Then put a magic value
260 // at the bottom so we can detect stack corruption.
261 //
262 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);
263 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
264
265 //
266 // When we thunk to external native code, we copy the last 8 qwords from
267 // the EBC stack into the processor registers, and adjust the stack pointer
268 // up. If the caller is not passing 8 parameters, then we've moved the
269 // stack pointer up into the stack gap. If this happens, then the caller
270 // can mess up the stack gap contents (in particular our magic value).
271 // Therefore, leave another gap below the magic value. Pick 10 qwords down,
272 // just as a starting point.
273 //
274 VmContext.R[0] -= 10 * sizeof (UINT64);
275
276 //
277 // Align the stack pointer such that after pushing the system table,
278 // image handle, and return address on the stack, it's aligned on a 16-byte
279 // boundary as required for IPF.
280 //
281 VmContext.R[0] &= (INT64)~0x0f;
282 VmContext.LowStackTop = (UINTN) VmContext.R[0];
283 //
284 // Simply copy the image handle and system table onto the EBC stack.
285 // Greatly simplifies things by not having to spill the args
286 //
287 PushU64 (&VmContext, (UINT64) SystemTable);
288 PushU64 (&VmContext, (UINT64) ImageHandle);
289
290 //
291 // Interpreter assumes 64-bit return address is pushed on the stack.
292 // IPF does not do this so pad the stack accordingly. Also, a
293 // "return address" is 16 bytes as required for IPF stack alignments.
294 //
295 PushU64 (&VmContext, (UINT64) 0);
296 PushU64 (&VmContext, (UINT64) 0x1234567887654321);
297 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
298
299 //
300 // Begin executing the EBC code
301 //
302 EbcExecute (&VmContext);
303
304 //
305 // Return the value in R[7] unless there was an error
306 //
307 ReturnEBCStack(StackIndex);
308 return (UINT64) VmContext.R[7];
309 }
310
311
312 /**
313 Create thunks for an EBC image entry point, or an EBC protocol service.
314
315 @param ImageHandle Image handle for the EBC image. If not null, then
316 we're creating a thunk for an image entry point.
317 @param EbcEntryPoint Address of the EBC code that the thunk is to call
318 @param Thunk Returned thunk we create here
319 @param Flags Flags indicating options for creating the thunk
320
321 @return Standard EFI status.
322
323 **/
324 EFI_STATUS
325 EbcCreateThunks (
326 IN EFI_HANDLE ImageHandle,
327 IN VOID *EbcEntryPoint,
328 OUT VOID **Thunk,
329 IN UINT32 Flags
330 )
331 {
332 UINT8 *Ptr;
333 UINT8 *ThunkBase;
334 UINT64 Addr;
335 UINT64 Code[3]; // Code in a bundle
336 UINT64 RegNum; // register number for MOVL
337 UINT64 I; // bits of MOVL immediate data
338 UINT64 Ic; // bits of MOVL immediate data
339 UINT64 Imm5c; // bits of MOVL immediate data
340 UINT64 Imm9d; // bits of MOVL immediate data
341 UINT64 Imm7b; // bits of MOVL immediate data
342 UINT64 Br; // branch register for loading and jumping
343 UINT64 *Data64Ptr;
344 UINT32 ThunkSize;
345 UINT32 Size;
346
347 //
348 // Check alignment of pointer to EBC code, which must always be aligned
349 // on a 2-byte boundary.
350 //
351 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
352 return EFI_INVALID_PARAMETER;
353 }
354 //
355 // Allocate memory for the thunk. Make the (most likely incorrect) assumption
356 // that the returned buffer is not aligned, so round up to the next
357 // alignment size.
358 //
359 Size = EBC_THUNK_SIZE + EBC_THUNK_ALIGNMENT - 1;
360 ThunkSize = Size;
361 Ptr = AllocatePool (Size);
362
363 if (Ptr == NULL) {
364 return EFI_OUT_OF_RESOURCES;
365 }
366 //
367 // Save the start address of the buffer.
368 //
369 ThunkBase = Ptr;
370
371 //
372 // Make sure it's aligned for code execution. If not, then
373 // round up.
374 //
375 if ((UINT32) (UINTN) Ptr & (EBC_THUNK_ALIGNMENT - 1)) {
376 Ptr = (UINT8 *) (((UINTN) Ptr + (EBC_THUNK_ALIGNMENT - 1)) &~ (UINT64) (EBC_THUNK_ALIGNMENT - 1));
377 }
378 //
379 // Return the pointer to the thunk to the caller to user as the
380 // image entry point.
381 //
382 *Thunk = (VOID *) Ptr;
383
384 //
385 // Clear out the thunk entry
386 // ZeroMem(Ptr, Size);
387 //
388 // For IPF, when you do a call via a function pointer, the function pointer
389 // actually points to a function descriptor which consists of a 64-bit
390 // address of the function, followed by a 64-bit gp for the function being
391 // called. See the the Software Conventions and Runtime Architecture Guide
392 // for details.
393 // So first off in our thunk, create a descriptor for our actual thunk code.
394 // This means we need to create a pointer to the thunk code (which follows
395 // the descriptor we're going to create), followed by the gp of the Vm
396 // interpret function we're going to eventually execute.
397 //
398 Data64Ptr = (UINT64 *) Ptr;
399
400 //
401 // Write the function's entry point (which is our thunk code that follows
402 // this descriptor we're creating).
403 //
404 *Data64Ptr = (UINT64) (Data64Ptr + 2);
405 //
406 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk
407 // descriptor.
408 //
409 *(Data64Ptr + 1) = *(UINT64 *) ((UINT64 *) (UINTN) EbcInterpret + 1);
410 //
411 // Advance our thunk data pointer past the descriptor. Since the
412 // descriptor consists of 16 bytes, the pointer is still aligned for
413 // IPF code execution (on 16-byte boundary).
414 //
415 Ptr += sizeof (UINT64) * 2;
416
417 //
418 // *************************** MAGIC BUNDLE ********************************
419 //
420 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM
421 // to recognize it is a thunk.
422 //
423 Addr = (UINT64) 0xCA112EBCCA112EBC;
424
425 //
426 // Now generate the code bytes. First is nop.m 0x0
427 //
428 Code[0] = OPCODE_NOP;
429
430 //
431 // Next is simply Addr[62:22] (41 bits) of the address
432 //
433 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
434
435 //
436 // Extract bits from the address for insertion into the instruction
437 // i = Addr[63:63]
438 //
439 I = RShiftU64 (Addr, 63) & 0x01;
440 //
441 // ic = Addr[21:21]
442 //
443 Ic = RShiftU64 (Addr, 21) & 0x01;
444 //
445 // imm5c = Addr[20:16] for 5 bits
446 //
447 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
448 //
449 // imm9d = Addr[15:7] for 9 bits
450 //
451 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
452 //
453 // imm7b = Addr[6:0] for 7 bits
454 //
455 Imm7b = Addr & 0x7F;
456
457 //
458 // The EBC entry point will be put into r8, so r8 can be used here
459 // temporary. R8 is general register and is auto-serialized.
460 //
461 RegNum = 8;
462
463 //
464 // Next is jumbled data, including opcode and rest of address
465 //
466 Code[2] = LShiftU64 (Imm7b, 13);
467 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc
468 Code[2] = Code[2] | LShiftU64 (Ic, 21);
469 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);
470 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);
471 Code[2] = Code[2] | LShiftU64 (I, 36);
472 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);
473 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);
474
475 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
476
477 //
478 // *************************** FIRST BUNDLE ********************************
479 //
480 // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass
481 // the ebc entry point in to the interpreter function via a processor
482 // register.
483 // Note -- we could easily change this to pass in a pointer to a structure
484 // that contained, among other things, the EBC image's entry point. But
485 // for now pass it directly.
486 //
487 Ptr += 16;
488 Addr = (UINT64) EbcEntryPoint;
489
490 //
491 // Now generate the code bytes. First is nop.m 0x0
492 //
493 Code[0] = OPCODE_NOP;
494
495 //
496 // Next is simply Addr[62:22] (41 bits) of the address
497 //
498 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
499
500 //
501 // Extract bits from the address for insertion into the instruction
502 // i = Addr[63:63]
503 //
504 I = RShiftU64 (Addr, 63) & 0x01;
505 //
506 // ic = Addr[21:21]
507 //
508 Ic = RShiftU64 (Addr, 21) & 0x01;
509 //
510 // imm5c = Addr[20:16] for 5 bits
511 //
512 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
513 //
514 // imm9d = Addr[15:7] for 9 bits
515 //
516 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
517 //
518 // imm7b = Addr[6:0] for 7 bits
519 //
520 Imm7b = Addr & 0x7F;
521
522 //
523 // Put the EBC entry point in r8, which is the location of the return value
524 // for functions.
525 //
526 RegNum = 8;
527
528 //
529 // Next is jumbled data, including opcode and rest of address
530 //
531 Code[2] = LShiftU64 (Imm7b, 13);
532 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc
533 Code[2] = Code[2] | LShiftU64 (Ic, 21);
534 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);
535 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);
536 Code[2] = Code[2] | LShiftU64 (I, 36);
537 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);
538 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);
539
540 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
541
542 //
543 // *************************** NEXT BUNDLE *********************************
544 //
545 // Write code bundle for:
546 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)
547 //
548 // Advance pointer to next bundle, then compute the offset from this bundle
549 // to the address of the entry point of the interpreter.
550 //
551 Ptr += 16;
552 if (Flags & FLAG_THUNK_ENTRY_POINT) {
553 Addr = (UINT64) ExecuteEbcImageEntryPoint;
554 } else {
555 Addr = (UINT64) EbcInterpret;
556 }
557 //
558 // Indirection on Itanium-based systems
559 //
560 Addr = *(UINT64 *) Addr;
561
562 //
563 // Now write the code to load the offset into a register
564 //
565 Code[0] = OPCODE_NOP;
566
567 //
568 // Next is simply Addr[62:22] (41 bits) of the address
569 //
570 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;
571
572 //
573 // Extract bits from the address for insertion into the instruction
574 // i = Addr[63:63]
575 //
576 I = RShiftU64 (Addr, 63) & 0x01;
577 //
578 // ic = Addr[21:21]
579 //
580 Ic = RShiftU64 (Addr, 21) & 0x01;
581 //
582 // imm5c = Addr[20:16] for 5 bits
583 //
584 Imm5c = RShiftU64 (Addr, 16) & 0x1F;
585 //
586 // imm9d = Addr[15:7] for 9 bits
587 //
588 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;
589 //
590 // imm7b = Addr[6:0] for 7 bits
591 //
592 Imm7b = Addr & 0x7F;
593
594 //
595 // Put it in r31, a scratch register
596 //
597 RegNum = 31;
598
599 //
600 // Next is jumbled data, including opcode and rest of address
601 //
602 Code[2] = LShiftU64(Imm7b, 13);
603 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc
604 Code[2] = Code[2] | LShiftU64 (Ic, 21);
605 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);
606 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);
607 Code[2] = Code[2] | LShiftU64 (I, 36);
608 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);
609 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);
610
611 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);
612
613 //
614 // *************************** NEXT BUNDLE *********************************
615 //
616 // Load branch register with EbcInterpret() function offset from the bundle
617 // address: mov b6 = RegNum
618 //
619 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.
620 //
621 // Advance pointer to next bundle
622 //
623 Ptr += 16;
624 Code[0] = OPCODE_NOP;
625 Code[1] = OPCODE_NOP;
626 Code[2] = OPCODE_MOV_BX_RX;
627
628 //
629 // Pick a branch register to use. Then fill in the bits for the branch
630 // register and user register (same user register as previous bundle).
631 //
632 Br = 6;
633 Code[2] |= LShiftU64 (Br, 6);
634 Code[2] |= LShiftU64 (RegNum, 13);
635 WriteBundle ((VOID *) Ptr, 0x0d, Code[0], Code[1], Code[2]);
636
637 //
638 // *************************** NEXT BUNDLE *********************************
639 //
640 // Now do the branch: (p0) br.cond.sptk.few b6
641 //
642 // Advance pointer to next bundle.
643 // Fill in the bits for the branch register (same reg as previous bundle)
644 //
645 Ptr += 16;
646 Code[0] = OPCODE_NOP;
647 Code[1] = OPCODE_NOP;
648 Code[2] = OPCODE_BR_COND_SPTK_FEW;
649 Code[2] |= LShiftU64 (Br, 13);
650 WriteBundle ((VOID *) Ptr, 0x1d, Code[0], Code[1], Code[2]);
651
652 //
653 // Add the thunk to our list of allocated thunks so we can do some cleanup
654 // when the image is unloaded. Do this last since the Add function flushes
655 // the instruction cache for us.
656 //
657 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
658
659 //
660 // Done
661 //
662 return EFI_SUCCESS;
663 }
664
665
666 /**
667 Given raw bytes of Itanium based code, format them into a bundle and
668 write them out.
669
670 @param MemPtr pointer to memory location to write the bundles to
671 @param Template 5-bit template
672 @param Slot0-2 instruction slot data for the bundle
673
674 @retval EFI_INVALID_PARAMETER Pointer is not aligned
675 @retval No more than 5 bits in template
676 @retval More than 41 bits used in code
677 @retval EFI_SUCCESS All data is written.
678
679 **/
680 STATIC
681 EFI_STATUS
682 WriteBundle (
683 IN VOID *MemPtr,
684 IN UINT8 Template,
685 IN UINT64 Slot0,
686 IN UINT64 Slot1,
687 IN UINT64 Slot2
688 )
689 {
690 UINT8 *BPtr;
691 UINT32 Index;
692 UINT64 Low64;
693 UINT64 High64;
694
695 //
696 // Verify pointer is aligned
697 //
698 if ((UINT64) MemPtr & 0xF) {
699 return EFI_INVALID_PARAMETER;
700 }
701 //
702 // Verify no more than 5 bits in template
703 //
704 if (Template &~0x1F) {
705 return EFI_INVALID_PARAMETER;
706 }
707 //
708 // Verify max of 41 bits used in code
709 //
710 if ((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) {
711 return EFI_INVALID_PARAMETER;
712 }
713
714 Low64 = LShiftU64 (Slot1, 46);
715 Low64 = Low64 | LShiftU64 (Slot0, 5) | Template;
716
717 High64 = RShiftU64 (Slot1, 18);
718 High64 = High64 | LShiftU64 (Slot2, 23);
719
720 //
721 // Now write it all out
722 //
723 BPtr = (UINT8 *) MemPtr;
724 for (Index = 0; Index < 8; Index++) {
725 *BPtr = (UINT8) Low64;
726 Low64 = RShiftU64 (Low64, 8);
727 BPtr++;
728 }
729
730 for (Index = 0; Index < 8; Index++) {
731 *BPtr = (UINT8) High64;
732 High64 = RShiftU64 (High64, 8);
733 BPtr++;
734 }
735
736 return EFI_SUCCESS;
737 }
738
739
740 /**
741 This function is called to execute an EBC CALLEX instruction.
742 The function check the callee's content to see whether it is common native
743 code or a thunk to another piece of EBC code.
744 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
745 otherwise, set the VM->IP to target EBC code directly to avoid another VM
746 be startup which cost time and stack space.
747
748 @param VmPtr Pointer to a VM context.
749 @param FuncAddr Callee's address
750 @param NewStackPointer New stack pointer after the call
751 @param FramePtr New frame pointer after the call
752 @param Size The size of call instruction
753
754 @return None.
755
756 **/
757 VOID
758 EbcLLCALLEX (
759 IN VM_CONTEXT *VmPtr,
760 IN UINTN FuncAddr,
761 IN UINTN NewStackPointer,
762 IN VOID *FramePtr,
763 IN UINT8 Size
764 )
765 {
766 UINTN IsThunk;
767 UINTN TargetEbcAddr;
768 UINTN CodeOne18;
769 UINTN CodeOne23;
770 UINTN CodeTwoI;
771 UINTN CodeTwoIc;
772 UINTN CodeTwo7b;
773 UINTN CodeTwo5c;
774 UINTN CodeTwo9d;
775 UINTN CalleeAddr;
776
777 IsThunk = 1;
778 TargetEbcAddr = 0;
779
780 //
781 // FuncAddr points to the descriptor of the target instructions.
782 //
783 CalleeAddr = *((UINT64 *)FuncAddr);
784
785 //
786 // Processor specific code to check whether the callee is a thunk to EBC.
787 //
788 if (*((UINT64 *)CalleeAddr) != 0xBCCA000100000005) {
789 IsThunk = 0;
790 goto Action;
791 }
792 if (*((UINT64 *)CalleeAddr + 1) != 0x697623C1004A112E) {
793 IsThunk = 0;
794 goto Action;
795 }
796
797 CodeOne18 = RShiftU64 (*((UINT64 *)CalleeAddr + 2), 46) & 0x3FFFF;
798 CodeOne23 = (*((UINT64 *)CalleeAddr + 3)) & 0x7FFFFF;
799 CodeTwoI = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 59) & 0x1;
800 CodeTwoIc = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 44) & 0x1;
801 CodeTwo7b = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 36) & 0x7F;
802 CodeTwo5c = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 45) & 0x1F;
803 CodeTwo9d = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 50) & 0x1FF;
804
805 TargetEbcAddr = CodeTwo7b;
806 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwo9d, 7);
807 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwo5c, 16);
808 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwoIc, 21);
809 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeOne18, 22);
810 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeOne23, 40);
811 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwoI, 63);
812
813 Action:
814 if (IsThunk == 1){
815 //
816 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
817 // put our return address and frame pointer on the VM stack.
818 // Then set the VM's IP to new EBC code.
819 //
820 VmPtr->R[0] -= 8;
821 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
822 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
823 VmPtr->R[0] -= 8;
824 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));
825
826 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
827 } else {
828 //
829 // The callee is not a thunk to EBC, call native code.
830 //
831 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
832
833 //
834 // Get return value and advance the IP.
835 //
836 VmPtr->R[7] = EbcLLGetReturnValue ();
837 VmPtr->Ip += Size;
838 }
839 }