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