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