1 /** @file
2   This module contains EBC support routines that are customized based on
3   the target x64 processor.
4 
5 Copyright (c) 2006 - 2014, 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 "EbcDebuggerHook.h"
19 
20 //
21 // NOTE: This is the stack size allocated for the interpreter
22 //       when it executes an EBC image. The requirements can change
23 //       based on whether or not a debugger is present, and other
24 //       platform-specific configurations.
25 //
26 #define VM_STACK_SIZE   (1024 * 8)
27 
28 #define STACK_REMAIN_SIZE (1024 * 4)
29 
30 //
31 // This is instruction buffer used to create EBC thunk
32 //
33 #define EBC_ENTRYPOINT_SIGNATURE           0xAFAFAFAFAFAFAFAFull
34 #define EBC_LL_EBC_ENTRYPOINT_SIGNATURE    0xFAFAFAFAFAFAFAFAull
35 UINT8  mInstructionBufferTemplate[] = {
36   //
37   // Add a magic code here to help the VM recognize the thunk..
38   // mov rax, 0xca112ebcca112ebc  => 48 B8 BC 2E 11 CA BC 2E 11 CA
39   //
40   0x48, 0xB8, 0xBC, 0x2E, 0x11, 0xCA, 0xBC, 0x2E, 0x11, 0xCA,
41   //
42   // Add code bytes to load up a processor register with the EBC entry point.
43   //  mov r10, EbcEntryPoint  => 49 BA XX XX XX XX XX XX XX XX (To be fixed at runtime)
44   // These 8 bytes of the thunk entry is the address of the EBC
45   // entry point.
46   //
47   0x49, 0xBA,
48     (UINT8)(EBC_ENTRYPOINT_SIGNATURE & 0xFF),
49     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),
50     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),
51     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),
52     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),
53     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),
54     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),
55     (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),
56   //
57   // Stick in a load of r11 with the address of appropriate VM function.
58   //  mov r11, EbcLLEbcInterpret  => 49 BB XX XX XX XX XX XX XX XX (To be fixed at runtime)
59   //
60   0x49, 0xBB,
61     (UINT8)(EBC_LL_EBC_ENTRYPOINT_SIGNATURE & 0xFF),
62     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),
63     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),
64     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),
65     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),
66     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),
67     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),
68     (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),
69   //
70   // Stick in jump opcode bytes
71   //  jmp r11 => 41 FF E3
72   //
73   0x41, 0xFF, 0xE3,
74 };
75 
76 /**
77   Begin executing an EBC image.
78   This is used for Ebc Thunk call.
79 
80   @return The value returned by the EBC application we're going to run.
81 
82 **/
83 UINT64
84 EFIAPI
85 EbcLLEbcInterpret (
86   VOID
87   );
88 
89 /**
90   Begin executing an EBC image.
91   This is used for Ebc image entrypoint.
92 
93   @return The value returned by the EBC application we're going to run.
94 
95 **/
96 UINT64
97 EFIAPI
98 EbcLLExecuteEbcImageEntryPoint (
99   VOID
100   );
101 
102 /**
103   Pushes a 64 bit unsigned value to the VM stack.
104 
105   @param VmPtr  The pointer to current VM context.
106   @param Arg    The value to be pushed.
107 
108 **/
109 VOID
PushU64(IN VM_CONTEXT * VmPtr,IN UINT64 Arg)110 PushU64 (
111   IN VM_CONTEXT *VmPtr,
112   IN UINT64     Arg
113   )
114 {
115   //
116   // Advance the VM stack down, and then copy the argument to the stack.
117   // Hope it's aligned.
118   //
119   VmPtr->Gpr[0] -= sizeof (UINT64);
120   *(UINT64 *) VmPtr->Gpr[0] = Arg;
121   return;
122 }
123 
124 
125 /**
126   Begin executing an EBC image.
127 
128   This is a thunk function. Microsoft x64 compiler only provide fast_call
129   calling convention, so the first four arguments are passed by rcx, rdx,
130   r8, and r9, while other arguments are passed in stack.
131 
132   @param  EntryPoint            The entrypoint of EBC code.
133   @param  Arg1                  The 1st argument.
134   @param  Arg2                  The 2nd argument.
135   @param  Arg3                  The 3rd argument.
136   @param  Arg4                  The 4th argument.
137   @param  Arg5                  The 5th argument.
138   @param  Arg6                  The 6th argument.
139   @param  Arg7                  The 7th argument.
140   @param  Arg8                  The 8th argument.
141   @param  Arg9                  The 9th argument.
142   @param  Arg10                 The 10th argument.
143   @param  Arg11                 The 11th argument.
144   @param  Arg12                 The 12th argument.
145   @param  Arg13                 The 13th argument.
146   @param  Arg14                 The 14th argument.
147   @param  Arg15                 The 15th argument.
148   @param  Arg16                 The 16th argument.
149 
150   @return The value returned by the EBC application we're going to run.
151 
152 **/
153 UINT64
154 EFIAPI
EbcInterpret(IN UINTN EntryPoint,IN UINTN Arg1,IN UINTN Arg2,IN UINTN Arg3,IN UINTN Arg4,IN UINTN Arg5,IN UINTN Arg6,IN UINTN Arg7,IN UINTN Arg8,IN UINTN Arg9,IN UINTN Arg10,IN UINTN Arg11,IN UINTN Arg12,IN UINTN Arg13,IN UINTN Arg14,IN UINTN Arg15,IN UINTN Arg16)155 EbcInterpret (
156   IN UINTN      EntryPoint,
157   IN UINTN      Arg1,
158   IN UINTN      Arg2,
159   IN UINTN      Arg3,
160   IN UINTN      Arg4,
161   IN UINTN      Arg5,
162   IN UINTN      Arg6,
163   IN UINTN      Arg7,
164   IN UINTN      Arg8,
165   IN UINTN      Arg9,
166   IN UINTN      Arg10,
167   IN UINTN      Arg11,
168   IN UINTN      Arg12,
169   IN UINTN      Arg13,
170   IN UINTN      Arg14,
171   IN UINTN      Arg15,
172   IN UINTN      Arg16
173   )
174 {
175   //
176   // Create a new VM context on the stack
177   //
178   VM_CONTEXT  VmContext;
179   UINTN       Addr;
180   EFI_STATUS  Status;
181   UINTN       StackIndex;
182 
183   //
184   // Get the EBC entry point
185   //
186   Addr = EntryPoint;
187 
188   //
189   // Now clear out our context
190   //
191   ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
192 
193   //
194   // Set the VM instruction pointer to the correct location in memory.
195   //
196   VmContext.Ip = (VMIP) Addr;
197 
198   //
199   // Initialize the stack pointer for the EBC. Get the current system stack
200   // pointer and adjust it down by the max needed for the interpreter.
201   //
202 
203   //
204   // Adjust the VM's stack pointer down.
205   //
206 
207   Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
208   if (EFI_ERROR(Status)) {
209     return Status;
210   }
211   VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
212   VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
213   VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
214   VmContext.Gpr[0] -= sizeof (UINTN);
215 
216   //
217   // Align the stack on a natural boundary.
218   //
219   VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);
220 
221   //
222   // Put a magic value in the stack gap, then adjust down again.
223   //
224   *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
225   VmContext.StackMagicPtr             = (UINTN *) (UINTN) VmContext.Gpr[0];
226 
227   //
228   // The stack upper to LowStackTop is belong to the VM.
229   //
230   VmContext.LowStackTop   = (UINTN) VmContext.Gpr[0];
231 
232   //
233   // For the worst case, assume there are 4 arguments passed in registers, store
234   // them to VM's stack.
235   //
236   PushU64 (&VmContext, (UINT64) Arg16);
237   PushU64 (&VmContext, (UINT64) Arg15);
238   PushU64 (&VmContext, (UINT64) Arg14);
239   PushU64 (&VmContext, (UINT64) Arg13);
240   PushU64 (&VmContext, (UINT64) Arg12);
241   PushU64 (&VmContext, (UINT64) Arg11);
242   PushU64 (&VmContext, (UINT64) Arg10);
243   PushU64 (&VmContext, (UINT64) Arg9);
244   PushU64 (&VmContext, (UINT64) Arg8);
245   PushU64 (&VmContext, (UINT64) Arg7);
246   PushU64 (&VmContext, (UINT64) Arg6);
247   PushU64 (&VmContext, (UINT64) Arg5);
248   PushU64 (&VmContext, (UINT64) Arg4);
249   PushU64 (&VmContext, (UINT64) Arg3);
250   PushU64 (&VmContext, (UINT64) Arg2);
251   PushU64 (&VmContext, (UINT64) Arg1);
252 
253   //
254   // Interpreter assumes 64-bit return address is pushed on the stack.
255   // The x64 does not do this so pad the stack accordingly.
256   //
257   PushU64 (&VmContext, (UINT64) 0);
258   PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
259 
260   //
261   // For x64, this is where we say our return address is
262   //
263   VmContext.StackRetAddr  = (UINT64) VmContext.Gpr[0];
264 
265   //
266   // We need to keep track of where the EBC stack starts. This way, if the EBC
267   // accesses any stack variables above its initial stack setting, then we know
268   // it's accessing variables passed into it, which means the data is on the
269   // VM's stack.
270   // When we're called, on the stack (high to low) we have the parameters, the
271   // return address, then the saved ebp. Save the pointer to the return address.
272   // EBC code knows that's there, so should look above it for function parameters.
273   // The offset is the size of locals (VMContext + Addr + saved ebp).
274   // Note that the interpreter assumes there is a 16 bytes of return address on
275   // the stack too, so adjust accordingly.
276   //  VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
277   //
278 
279   //
280   // Begin executing the EBC code
281   //
282   EbcDebuggerHookEbcInterpret (&VmContext);
283   EbcExecute (&VmContext);
284 
285   //
286   // Return the value in Gpr[7] unless there was an error
287   //
288   ReturnEBCStack(StackIndex);
289   return (UINT64) VmContext.Gpr[7];
290 }
291 
292 
293 /**
294   Begin executing an EBC image.
295 
296   @param  EntryPoint       The entrypoint of EBC code.
297   @param  ImageHandle      image handle for the EBC application we're executing
298   @param  SystemTable      standard system table passed into an driver's entry
299                            point
300 
301   @return The value returned by the EBC application we're going to run.
302 
303 **/
304 UINT64
305 EFIAPI
ExecuteEbcImageEntryPoint(IN UINTN EntryPoint,IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)306 ExecuteEbcImageEntryPoint (
307   IN UINTN                EntryPoint,
308   IN EFI_HANDLE           ImageHandle,
309   IN EFI_SYSTEM_TABLE     *SystemTable
310   )
311 {
312   //
313   // Create a new VM context on the stack
314   //
315   VM_CONTEXT  VmContext;
316   UINTN       Addr;
317   EFI_STATUS  Status;
318   UINTN       StackIndex;
319 
320   //
321   // Get the EBC entry point
322   //
323   Addr = EntryPoint;
324 
325   //
326   // Now clear out our context
327   //
328   ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
329 
330   //
331   // Save the image handle so we can track the thunks created for this image
332   //
333   VmContext.ImageHandle = ImageHandle;
334   VmContext.SystemTable = SystemTable;
335 
336   //
337   // Set the VM instruction pointer to the correct location in memory.
338   //
339   VmContext.Ip = (VMIP) Addr;
340 
341   //
342   // Initialize the stack pointer for the EBC. Get the current system stack
343   // pointer and adjust it down by the max needed for the interpreter.
344   //
345 
346   Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
347   if (EFI_ERROR(Status)) {
348     return Status;
349   }
350   VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
351   VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
352   VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
353   VmContext.Gpr[0] -= sizeof (UINTN);
354 
355 
356   //
357   // Put a magic value in the stack gap, then adjust down again
358   //
359   *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
360   VmContext.StackMagicPtr             = (UINTN *) (UINTN) VmContext.Gpr[0];
361 
362   //
363   // Align the stack on a natural boundary
364   VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1);
365   //
366   VmContext.LowStackTop   = (UINTN) VmContext.Gpr[0];
367 
368   //
369   // Simply copy the image handle and system table onto the EBC stack.
370   // Greatly simplifies things by not having to spill the args.
371   //
372   PushU64 (&VmContext, (UINT64) SystemTable);
373   PushU64 (&VmContext, (UINT64) ImageHandle);
374 
375   //
376   // VM pushes 16-bytes for return address. Simulate that here.
377   //
378   PushU64 (&VmContext, (UINT64) 0);
379   PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
380 
381   //
382   // For x64, this is where we say our return address is
383   //
384   VmContext.StackRetAddr  = (UINT64) VmContext.Gpr[0];
385 
386   //
387   // Entry function needn't access high stack context, simply
388   // put the stack pointer here.
389   //
390 
391   //
392   // Begin executing the EBC code
393   //
394   EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
395   EbcExecute (&VmContext);
396 
397   //
398   // Return the value in Gpr[7] unless there was an error
399   //
400   ReturnEBCStack(StackIndex);
401   return (UINT64) VmContext.Gpr[7];
402 }
403 
404 
405 /**
406   Create thunks for an EBC image entry point, or an EBC protocol service.
407 
408   @param  ImageHandle           Image handle for the EBC image. If not null, then
409                                 we're creating a thunk for an image entry point.
410   @param  EbcEntryPoint         Address of the EBC code that the thunk is to call
411   @param  Thunk                 Returned thunk we create here
412   @param  Flags                 Flags indicating options for creating the thunk
413 
414   @retval EFI_SUCCESS           The thunk was created successfully.
415   @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
416                                 aligned.
417   @retval EFI_OUT_OF_RESOURCES  There is not enough memory to created the EBC
418                                 Thunk.
419   @retval EFI_BUFFER_TOO_SMALL  EBC_THUNK_SIZE is not larger enough.
420 
421 **/
422 EFI_STATUS
EbcCreateThunks(IN EFI_HANDLE ImageHandle,IN VOID * EbcEntryPoint,OUT VOID ** Thunk,IN UINT32 Flags)423 EbcCreateThunks (
424   IN EFI_HANDLE           ImageHandle,
425   IN VOID                 *EbcEntryPoint,
426   OUT VOID                **Thunk,
427   IN  UINT32              Flags
428   )
429 {
430   UINT8       *Ptr;
431   UINT8       *ThunkBase;
432   UINT32      Index;
433   INT32       ThunkSize;
434 
435   //
436   // Check alignment of pointer to EBC code
437   //
438   if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
439     return EFI_INVALID_PARAMETER;
440   }
441 
442   ThunkSize = sizeof(mInstructionBufferTemplate);
443 
444   Ptr = AllocatePool (sizeof(mInstructionBufferTemplate));
445 
446   if (Ptr == NULL) {
447     return EFI_OUT_OF_RESOURCES;
448   }
449   //
450   //  Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
451   //
452   // Save the start address so we can add a pointer to it to a list later.
453   //
454   ThunkBase = Ptr;
455 
456   //
457   // Give them the address of our buffer we're going to fix up
458   //
459   *Thunk = (VOID *) Ptr;
460 
461   //
462   // Copy whole thunk instruction buffer template
463   //
464   CopyMem (Ptr, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate));
465 
466   //
467   // Patch EbcEntryPoint and EbcLLEbcInterpret
468   //
469   for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {
470     if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) {
471       *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint;
472     }
473     if (*(UINTN *)&Ptr[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {
474       if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
475         *(UINTN *)&Ptr[Index] = (UINTN)EbcLLExecuteEbcImageEntryPoint;
476       } else {
477         *(UINTN *)&Ptr[Index] = (UINTN)EbcLLEbcInterpret;
478       }
479     }
480   }
481 
482   //
483   // Add the thunk to the list for this image. Do this last since the add
484   // function flushes the cache for us.
485   //
486   EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
487 
488   return EFI_SUCCESS;
489 }
490 
491 
492 /**
493   This function is called to execute an EBC CALLEX instruction.
494   The function check the callee's content to see whether it is common native
495   code or a thunk to another piece of EBC code.
496   If the callee is common native code, use EbcLLCAllEXASM to manipulate,
497   otherwise, set the VM->IP to target EBC code directly to avoid another VM
498   be startup which cost time and stack space.
499 
500   @param  VmPtr            Pointer to a VM context.
501   @param  FuncAddr         Callee's address
502   @param  NewStackPointer  New stack pointer after the call
503   @param  FramePtr         New frame pointer after the call
504   @param  Size             The size of call instruction
505 
506 **/
507 VOID
EbcLLCALLEX(IN VM_CONTEXT * VmPtr,IN UINTN FuncAddr,IN UINTN NewStackPointer,IN VOID * FramePtr,IN UINT8 Size)508 EbcLLCALLEX (
509   IN VM_CONTEXT   *VmPtr,
510   IN UINTN        FuncAddr,
511   IN UINTN        NewStackPointer,
512   IN VOID         *FramePtr,
513   IN UINT8        Size
514   )
515 {
516   UINTN    IsThunk;
517   UINTN    TargetEbcAddr;
518   UINT8    InstructionBuffer[sizeof(mInstructionBufferTemplate)];
519   UINTN    Index;
520   UINTN    IndexOfEbcEntrypoint;
521 
522   IsThunk       = 1;
523   TargetEbcAddr = 0;
524   IndexOfEbcEntrypoint = 0;
525 
526   //
527   // Processor specific code to check whether the callee is a thunk to EBC.
528   //
529   CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof(InstructionBuffer));
530   //
531   // Fill the signature according to mInstructionBufferTemplate
532   //
533   for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {
534     if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) {
535       *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE;
536       IndexOfEbcEntrypoint = Index;
537     }
538     if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {
539       *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE;
540     }
541   }
542   //
543   // Check if we need thunk to native
544   //
545   if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)) != 0) {
546     IsThunk = 0;
547   }
548 
549   if (IsThunk == 1){
550     //
551     // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
552     // put our return address and frame pointer on the VM stack.
553     // Then set the VM's IP to new EBC code.
554     //
555     VmPtr->Gpr[0] -= 8;
556     VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
557     VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
558     VmPtr->Gpr[0] -= 8;
559     VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
560 
561     CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof(UINTN));
562     VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
563   } else {
564     //
565     // The callee is not a thunk to EBC, call native code,
566     // and get return value.
567     //
568     VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
569 
570     //
571     // Advance the IP.
572     //
573     VmPtr->Ip += Size;
574   }
575 }
576 
577