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