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