TianoCore EDK2 master
Loading...
Searching...
No Matches
EbcSupport.c
Go to the documentation of this file.
1
10#include "EbcInt.h"
11#include "EbcExecute.h"
12#include "EbcDebuggerHook.h"
13
14//
15// NOTE: This is the stack size allocated for the interpreter
16// when it executes an EBC image. The requirements can change
17// based on whether or not a debugger is present, and other
18// platform-specific configurations.
19//
20#define VM_STACK_SIZE (1024 * 8)
21
22#define STACK_REMAIN_SIZE (1024 * 4)
23
24//
25// This is instruction buffer used to create EBC thunk
26//
27#define EBC_ENTRYPOINT_SIGNATURE 0xAFAFAFAFAFAFAFAFull
28#define EBC_LL_EBC_ENTRYPOINT_SIGNATURE 0xFAFAFAFAFAFAFAFAull
29UINT8 mInstructionBufferTemplate[] = {
30 //
31 // Add a magic code here to help the VM recognize the thunk..
32 // mov rax, 0xca112ebcca112ebc => 48 B8 BC 2E 11 CA BC 2E 11 CA
33 //
34 0x48, 0xB8, 0xBC, 0x2E, 0x11, 0xCA, 0xBC, 0x2E, 0x11, 0xCA,
35 //
36 // Add code bytes to load up a processor register with the EBC entry point.
37 // mov r10, EbcEntryPoint => 49 BA XX XX XX XX XX XX XX XX (To be fixed at runtime)
38 // These 8 bytes of the thunk entry is the address of the EBC
39 // entry point.
40 //
41 0x49, 0xBA,
42 (UINT8)(EBC_ENTRYPOINT_SIGNATURE & 0xFF),
43 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),
44 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),
45 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),
46 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),
47 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),
48 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),
49 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),
50 //
51 // Stick in a load of r11 with the address of appropriate VM function.
52 // mov r11, EbcLLEbcInterpret => 49 BB XX XX XX XX XX XX XX XX (To be fixed at runtime)
53 //
54 0x49, 0xBB,
55 (UINT8)(EBC_LL_EBC_ENTRYPOINT_SIGNATURE & 0xFF),
56 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),
57 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),
58 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),
59 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),
60 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),
61 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),
62 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),
63 //
64 // Stick in jump opcode bytes
65 // jmp r11 => 41 FF E3
66 //
67 0x41, 0xFF, 0xE3,
68};
69
77UINT64
78EFIAPI
80 VOID
81 );
82
90UINT64
91EFIAPI
93 VOID
94 );
95
103VOID
105 IN VM_CONTEXT *VmPtr,
106 IN UINT64 Arg
107 )
108{
109 //
110 // Advance the VM stack down, and then copy the argument to the stack.
111 // Hope it's aligned.
112 //
113 VmPtr->Gpr[0] -= sizeof (UINT64);
114 *(UINT64 *)VmPtr->Gpr[0] = Arg;
115 return;
116}
117
146UINT64
147EFIAPI
149 IN UINTN EntryPoint,
150 IN UINTN Arg1,
151 IN UINTN Arg2,
152 IN UINTN Arg3,
153 IN UINTN Arg4,
154 IN UINTN Arg5,
155 IN UINTN Arg6,
156 IN UINTN Arg7,
157 IN UINTN Arg8,
158 IN UINTN Arg9,
159 IN UINTN Arg10,
160 IN UINTN Arg11,
161 IN UINTN Arg12,
162 IN UINTN Arg13,
163 IN UINTN Arg14,
164 IN UINTN Arg15,
165 IN UINTN Arg16
166 )
167{
168 //
169 // Create a new VM context on the stack
170 //
171 VM_CONTEXT VmContext;
172 UINTN Addr;
173 EFI_STATUS Status;
174 UINTN StackIndex;
175
176 //
177 // Get the EBC entry point
178 //
179 Addr = EntryPoint;
180
181 //
182 // Now clear out our context
183 //
184 ZeroMem ((VOID *)&VmContext, sizeof (VM_CONTEXT));
185
186 //
187 // Set the VM instruction pointer to the correct location in memory.
188 //
189 VmContext.Ip = (VMIP)Addr;
190
191 //
192 // Initialize the stack pointer for the EBC. Get the current system stack
193 // pointer and adjust it down by the max needed for the interpreter.
194 //
195
196 //
197 // Adjust the VM's stack pointer down.
198 //
199
200 Status = GetEBCStack ((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
201 if (EFI_ERROR (Status)) {
202 return Status;
203 }
204
205 VmContext.StackTop = (UINT8 *)VmContext.StackPool + (STACK_REMAIN_SIZE);
206 VmContext.Gpr[0] = (UINT64)((UINT8 *)VmContext.StackPool + STACK_POOL_SIZE);
207 VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
208 VmContext.Gpr[0] -= sizeof (UINTN);
209
210 //
211 // Align the stack on a natural boundary.
212 //
213 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);
214
215 //
216 // Put a magic value in the stack gap, then adjust down again.
217 //
218 *(UINTN *)(UINTN)(VmContext.Gpr[0]) = (UINTN)VM_STACK_KEY_VALUE;
219 VmContext.StackMagicPtr = (UINTN *)(UINTN)VmContext.Gpr[0];
220
221 //
222 // The stack upper to LowStackTop is belong to the VM.
223 //
224 VmContext.LowStackTop = (UINTN)VmContext.Gpr[0];
225
226 //
227 // For the worst case, assume there are 4 arguments passed in registers, store
228 // them to VM's stack.
229 //
230 PushU64 (&VmContext, (UINT64)Arg16);
231 PushU64 (&VmContext, (UINT64)Arg15);
232 PushU64 (&VmContext, (UINT64)Arg14);
233 PushU64 (&VmContext, (UINT64)Arg13);
234 PushU64 (&VmContext, (UINT64)Arg12);
235 PushU64 (&VmContext, (UINT64)Arg11);
236 PushU64 (&VmContext, (UINT64)Arg10);
237 PushU64 (&VmContext, (UINT64)Arg9);
238 PushU64 (&VmContext, (UINT64)Arg8);
239 PushU64 (&VmContext, (UINT64)Arg7);
240 PushU64 (&VmContext, (UINT64)Arg6);
241 PushU64 (&VmContext, (UINT64)Arg5);
242 PushU64 (&VmContext, (UINT64)Arg4);
243 PushU64 (&VmContext, (UINT64)Arg3);
244 PushU64 (&VmContext, (UINT64)Arg2);
245 PushU64 (&VmContext, (UINT64)Arg1);
246
247 //
248 // Interpreter assumes 64-bit return address is pushed on the stack.
249 // The x64 does not do this so pad the stack accordingly.
250 //
251 PushU64 (&VmContext, (UINT64)0);
252 PushU64 (&VmContext, (UINT64)0x1234567887654321ULL);
253
254 //
255 // For x64, this is where we say our return address is
256 //
257 VmContext.StackRetAddr = (UINT64)VmContext.Gpr[0];
258
259 //
260 // We need to keep track of where the EBC stack starts. This way, if the EBC
261 // accesses any stack variables above its initial stack setting, then we know
262 // it's accessing variables passed into it, which means the data is on the
263 // VM's stack.
264 // When we're called, on the stack (high to low) we have the parameters, the
265 // return address, then the saved ebp. Save the pointer to the return address.
266 // EBC code knows that's there, so should look above it for function parameters.
267 // The offset is the size of locals (VMContext + Addr + saved ebp).
268 // Note that the interpreter assumes there is a 16 bytes of return address on
269 // the stack too, so adjust accordingly.
270 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
271 //
272
273 //
274 // Begin executing the EBC code
275 //
276 EbcDebuggerHookEbcInterpret (&VmContext);
277 EbcExecute (&VmContext);
278
279 //
280 // Return the value in Gpr[7] unless there was an error
281 //
282 ReturnEBCStack (StackIndex);
283 return (UINT64)VmContext.Gpr[7];
284}
285
297UINT64
298EFIAPI
300 IN UINTN EntryPoint,
301 IN EFI_HANDLE ImageHandle,
302 IN EFI_SYSTEM_TABLE *SystemTable
303 )
304{
305 //
306 // Create a new VM context on the stack
307 //
308 VM_CONTEXT VmContext;
309 UINTN Addr;
310 EFI_STATUS Status;
311 UINTN StackIndex;
312
313 //
314 // Get the EBC entry point
315 //
316 Addr = EntryPoint;
317
318 //
319 // Now clear out our context
320 //
321 ZeroMem ((VOID *)&VmContext, sizeof (VM_CONTEXT));
322
323 //
324 // Save the image handle so we can track the thunks created for this image
325 //
326 VmContext.ImageHandle = ImageHandle;
327 VmContext.SystemTable = SystemTable;
328
329 //
330 // Set the VM instruction pointer to the correct location in memory.
331 //
332 VmContext.Ip = (VMIP)Addr;
333
334 //
335 // Initialize the stack pointer for the EBC. Get the current system stack
336 // pointer and adjust it down by the max needed for the interpreter.
337 //
338
339 Status = GetEBCStack (ImageHandle, &VmContext.StackPool, &StackIndex);
340 if (EFI_ERROR (Status)) {
341 return Status;
342 }
343
344 VmContext.StackTop = (UINT8 *)VmContext.StackPool + (STACK_REMAIN_SIZE);
345 VmContext.Gpr[0] = (UINT64)((UINT8 *)VmContext.StackPool + STACK_POOL_SIZE);
346 VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
347 VmContext.Gpr[0] -= sizeof (UINTN);
348
349 //
350 // Put a magic value in the stack gap, then adjust down again
351 //
352 *(UINTN *)(UINTN)(VmContext.Gpr[0]) = (UINTN)VM_STACK_KEY_VALUE;
353 VmContext.StackMagicPtr = (UINTN *)(UINTN)VmContext.Gpr[0];
354
355 //
356 // Align the stack on a natural boundary
357 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);
358 //
359 VmContext.LowStackTop = (UINTN)VmContext.Gpr[0];
360
361 //
362 // Simply copy the image handle and system table onto the EBC stack.
363 // Greatly simplifies things by not having to spill the args.
364 //
365 PushU64 (&VmContext, (UINT64)SystemTable);
366 PushU64 (&VmContext, (UINT64)ImageHandle);
367
368 //
369 // VM pushes 16-bytes for return address. Simulate that here.
370 //
371 PushU64 (&VmContext, (UINT64)0);
372 PushU64 (&VmContext, (UINT64)0x1234567887654321ULL);
373
374 //
375 // For x64, this is where we say our return address is
376 //
377 VmContext.StackRetAddr = (UINT64)VmContext.Gpr[0];
378
379 //
380 // Entry function needn't access high stack context, simply
381 // put the stack pointer here.
382 //
383
384 //
385 // Begin executing the EBC code
386 //
388 EbcExecute (&VmContext);
389
390 //
391 // Return the value in Gpr[7] unless there was an error
392 //
393 ReturnEBCStack (StackIndex);
394 return (UINT64)VmContext.Gpr[7];
395}
396
416 IN EFI_HANDLE ImageHandle,
417 IN VOID *EbcEntryPoint,
418 OUT VOID **Thunk,
419 IN UINT32 Flags
420 )
421{
422 UINT8 *Ptr;
423 UINT8 *ThunkBase;
424 UINT32 Index;
425 INT32 ThunkSize;
426
427 //
428 // Check alignment of pointer to EBC code
429 //
430 if ((UINT32)(UINTN)EbcEntryPoint & 0x01) {
431 return EFI_INVALID_PARAMETER;
432 }
433
434 ThunkSize = sizeof (mInstructionBufferTemplate);
435
436 Ptr = EbcAllocatePoolForThunk (sizeof (mInstructionBufferTemplate));
437
438 if (Ptr == NULL) {
439 return EFI_OUT_OF_RESOURCES;
440 }
441
442 //
443 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
444 //
445 // Save the start address so we can add a pointer to it to a list later.
446 //
447 ThunkBase = Ptr;
448
449 //
450 // Give them the address of our buffer we're going to fix up
451 //
452 *Thunk = (VOID *)Ptr;
453
454 //
455 // Copy whole thunk instruction buffer template
456 //
457 CopyMem (Ptr, mInstructionBufferTemplate, sizeof (mInstructionBufferTemplate));
458
459 //
460 // Patch EbcEntryPoint and EbcLLEbcInterpret
461 //
462 for (Index = 0; Index < sizeof (mInstructionBufferTemplate) - sizeof (UINTN); Index++) {
463 if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) {
464 *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint;
465 }
466
467 if (*(UINTN *)&Ptr[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {
468 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
469 *(UINTN *)&Ptr[Index] = (UINTN)EbcLLExecuteEbcImageEntryPoint;
470 } else {
471 *(UINTN *)&Ptr[Index] = (UINTN)EbcLLEbcInterpret;
472 }
473 }
474 }
475
476 //
477 // Add the thunk to the list for this image. Do this last since the add
478 // function flushes the cache for us.
479 //
480 EbcAddImageThunk (ImageHandle, (VOID *)ThunkBase, ThunkSize);
481
482 return EFI_SUCCESS;
483}
484
500VOID
502 IN VM_CONTEXT *VmPtr,
503 IN UINTN FuncAddr,
504 IN UINTN NewStackPointer,
505 IN VOID *FramePtr,
506 IN UINT8 Size
507 )
508{
509 UINTN IsThunk;
510 UINTN TargetEbcAddr;
511 UINT8 InstructionBuffer[sizeof (mInstructionBufferTemplate)];
512 UINTN Index;
513 UINTN IndexOfEbcEntrypoint;
514
515 IsThunk = 1;
516 TargetEbcAddr = 0;
517 IndexOfEbcEntrypoint = 0;
518
519 //
520 // Processor specific code to check whether the callee is a thunk to EBC.
521 //
522 CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof (InstructionBuffer));
523 //
524 // Fill the signature according to mInstructionBufferTemplate
525 //
526 for (Index = 0; Index < sizeof (mInstructionBufferTemplate) - sizeof (UINTN); Index++) {
527 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) {
528 *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE;
529 IndexOfEbcEntrypoint = Index;
530 }
531
532 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {
533 *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE;
534 }
535 }
536
537 //
538 // Check if we need thunk to native
539 //
540 if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof (mInstructionBufferTemplate)) != 0) {
541 IsThunk = 0;
542 }
543
544 if (IsThunk == 1) {
545 //
546 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
547 // put our return address and frame pointer on the VM stack.
548 // Then set the VM's IP to new EBC code.
549 //
550 VmPtr->Gpr[0] -= 8;
551 VmWriteMemN (VmPtr, (UINTN)VmPtr->Gpr[0], (UINTN)FramePtr);
552 VmPtr->FramePtr = (VOID *)(UINTN)VmPtr->Gpr[0];
553 VmPtr->Gpr[0] -= 8;
554 VmWriteMem64 (VmPtr, (UINTN)VmPtr->Gpr[0], (UINT64)(UINTN)(VmPtr->Ip + Size));
555
556 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof (UINTN));
557 VmPtr->Ip = (VMIP)(UINTN)TargetEbcAddr;
558 } else {
559 //
560 // The callee is not a thunk to EBC, call native code,
561 // and get return value.
562 //
563 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
564
565 //
566 // Advance the IP.
567 //
568 VmPtr->Ip += Size;
569 }
570}
UINT64 EFIAPI ExecuteEbcImageEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable, IN UINTN EntryPoint)
Definition: EbcSupport.c:246
UINT64 EFIAPI EbcLLExecuteEbcImageEntryPoint(VOID)
VOID PushU64(IN VM_CONTEXT *VmPtr, IN UINT64 Arg)
Definition: EbcSupport.c:67
VOID EbcLLCALLEX(IN VM_CONTEXT *VmPtr, IN UINTN FuncAddr, IN UINTN NewStackPointer, IN VOID *FramePtr, IN UINT8 Size)
Definition: EbcSupport.c:436
UINT64 EFIAPI EbcInterpret(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 EntryPoint, IN CONST UINTN Args9_16[])
Definition: EbcSupport.c:102
UINT64 EFIAPI EbcLLEbcInterpret(VOID)
EFI_STATUS EbcCreateThunks(IN EFI_HANDLE ImageHandle, IN VOID *EbcEntryPoint, OUT VOID **Thunk, IN UINT32 Flags)
Definition: EbcSupport.c:362
UINT64 UINTN
INTN EFIAPI CompareMem(IN CONST VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
EFI_STATUS EbcExecute(IN VM_CONTEXT *VmPtr)
Definition: EbcExecute.c:1418
EFI_STATUS VmWriteMemN(IN VM_CONTEXT *VmPtr, IN UINTN Addr, IN UINTN Data)
Definition: EbcExecute.c:4906
EFI_STATUS VmWriteMem64(IN VM_CONTEXT *VmPtr, IN UINTN Addr, IN UINT64 Data)
Definition: EbcExecute.c:4844
EFI_STATUS EbcAddImageThunk(IN EFI_HANDLE ImageHandle, IN VOID *ThunkBuffer, IN UINT32 ThunkSize)
Definition: EbcInt.c:1239
VOID *EFIAPI EbcAllocatePoolForThunk(IN UINTN AllocationSize)
Definition: EbcInt.c:1554
EFI_STATUS ReturnEBCStack(IN UINTN Index)
Definition: EbcInt.c:1400
EFI_STATUS GetEBCStack(IN EFI_HANDLE Handle, OUT VOID **StackBuffer, OUT UINTN *BufferIndex)
Definition: EbcInt.c:1364
INT64 EFIAPI EbcLLCALLEXNative(IN UINTN CallAddr, IN UINTN EbcSp, IN VOID *FramePtr)
UINT8 * VMIP
Definition: EbcVmTest.h:78
VOID EbcDebuggerHookEbcInterpret(IN VM_CONTEXT *VmPtr)
Definition: EdbHook.c:529
VOID EbcDebuggerHookExecuteEbcImageEntryPoint(IN VM_CONTEXT *VmPtr)
Definition: EdbHook.c:508
#define NULL
Definition: Base.h:319
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
UINT64 StackRetAddr
location of final return address on stack
Definition: EbcVmTest.h:96
UINTN HighStackBottom
bottom of the upper stack
Definition: EbcVmTest.h:94
UINTN * StackMagicPtr
pointer to magic value on stack to detect corruption
Definition: EbcVmTest.h:97
VM_REGISTER Gpr[8]
Definition: EbcVmTest.h:84
UINTN LowStackTop
top of the lower stack
Definition: EbcVmTest.h:95
VMIP Ip
Instruction pointer.
Definition: EbcVmTest.h:89
EFI_HANDLE ImageHandle
for this EBC driver
Definition: EbcVmTest.h:98
EFI_SYSTEM_TABLE * SystemTable
for debugging only
Definition: EbcVmTest.h:99