]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c
MdeModulePkg/EbcDxe: prepare support for EBC Debugger
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / X64 / EbcSupport.c
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
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
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
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
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
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