]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
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
29 UINT8 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
70 /**
71 Begin executing an EBC image.
72 This is used for Ebc Thunk call.
73
74 @return The value returned by the EBC application we're going to run.
75
76 **/
77 UINT64
78 EFIAPI
79 EbcLLEbcInterpret (
80 VOID
81 );
82
83 /**
84 Begin executing an EBC image.
85 This is used for Ebc image entrypoint.
86
87 @return The value returned by the EBC application we're going to run.
88
89 **/
90 UINT64
91 EFIAPI
92 EbcLLExecuteEbcImageEntryPoint (
93 VOID
94 );
95
96 /**
97 Pushes a 64 bit unsigned value to the VM stack.
98
99 @param VmPtr The pointer to current VM context.
100 @param Arg The value to be pushed.
101
102 **/
103 VOID
104 PushU64 (
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
118
119 /**
120 Begin executing an EBC image.
121
122 This is a thunk function. Microsoft x64 compiler only provide fast_call
123 calling convention, so the first four arguments are passed by rcx, rdx,
124 r8, and r9, while other arguments are passed in stack.
125
126 @param EntryPoint The entrypoint of EBC code.
127 @param Arg1 The 1st argument.
128 @param Arg2 The 2nd argument.
129 @param Arg3 The 3rd argument.
130 @param Arg4 The 4th argument.
131 @param Arg5 The 5th argument.
132 @param Arg6 The 6th argument.
133 @param Arg7 The 7th argument.
134 @param Arg8 The 8th argument.
135 @param Arg9 The 9th argument.
136 @param Arg10 The 10th argument.
137 @param Arg11 The 11th argument.
138 @param Arg12 The 12th argument.
139 @param Arg13 The 13th argument.
140 @param Arg14 The 14th argument.
141 @param Arg15 The 15th argument.
142 @param Arg16 The 16th argument.
143
144 @return The value returned by the EBC application we're going to run.
145
146 **/
147 UINT64
148 EFIAPI
149 EbcInterpret (
150 IN UINTN EntryPoint,
151 IN UINTN Arg1,
152 IN UINTN Arg2,
153 IN UINTN Arg3,
154 IN UINTN Arg4,
155 IN UINTN Arg5,
156 IN UINTN Arg6,
157 IN UINTN Arg7,
158 IN UINTN Arg8,
159 IN UINTN Arg9,
160 IN UINTN Arg10,
161 IN UINTN Arg11,
162 IN UINTN Arg12,
163 IN UINTN Arg13,
164 IN UINTN Arg14,
165 IN UINTN Arg15,
166 IN UINTN Arg16
167 )
168 {
169 //
170 // Create a new VM context on the stack
171 //
172 VM_CONTEXT VmContext;
173 UINTN Addr;
174 EFI_STATUS Status;
175 UINTN StackIndex;
176
177 //
178 // Get the EBC entry point
179 //
180 Addr = EntryPoint;
181
182 //
183 // Now clear out our context
184 //
185 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
186
187 //
188 // Set the VM instruction pointer to the correct location in memory.
189 //
190 VmContext.Ip = (VMIP) Addr;
191
192 //
193 // Initialize the stack pointer for the EBC. Get the current system stack
194 // pointer and adjust it down by the max needed for the interpreter.
195 //
196
197 //
198 // Adjust the VM's stack pointer down.
199 //
200
201 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
202 if (EFI_ERROR(Status)) {
203 return Status;
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
286
287 /**
288 Begin executing an EBC image.
289
290 @param EntryPoint The entrypoint of EBC code.
291 @param ImageHandle image handle for the EBC application we're executing
292 @param SystemTable standard system table passed into an driver's entry
293 point
294
295 @return The value returned by the EBC application we're going to run.
296
297 **/
298 UINT64
299 EFIAPI
300 ExecuteEbcImageEntryPoint (
301 IN UINTN EntryPoint,
302 IN EFI_HANDLE ImageHandle,
303 IN EFI_SYSTEM_TABLE *SystemTable
304 )
305 {
306 //
307 // Create a new VM context on the stack
308 //
309 VM_CONTEXT VmContext;
310 UINTN Addr;
311 EFI_STATUS Status;
312 UINTN StackIndex;
313
314 //
315 // Get the EBC entry point
316 //
317 Addr = EntryPoint;
318
319 //
320 // Now clear out our context
321 //
322 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
323
324 //
325 // Save the image handle so we can track the thunks created for this image
326 //
327 VmContext.ImageHandle = ImageHandle;
328 VmContext.SystemTable = SystemTable;
329
330 //
331 // Set the VM instruction pointer to the correct location in memory.
332 //
333 VmContext.Ip = (VMIP) Addr;
334
335 //
336 // Initialize the stack pointer for the EBC. Get the current system stack
337 // pointer and adjust it down by the max needed for the interpreter.
338 //
339
340 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
341 if (EFI_ERROR(Status)) {
342 return Status;
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 //
351 // Put a magic value in the stack gap, then adjust down again
352 //
353 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
354 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
355
356 //
357 // Align the stack on a natural boundary
358 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1);
359 //
360 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
361
362 //
363 // Simply copy the image handle and system table onto the EBC stack.
364 // Greatly simplifies things by not having to spill the args.
365 //
366 PushU64 (&VmContext, (UINT64) SystemTable);
367 PushU64 (&VmContext, (UINT64) ImageHandle);
368
369 //
370 // VM pushes 16-bytes for return address. Simulate that here.
371 //
372 PushU64 (&VmContext, (UINT64) 0);
373 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
374
375 //
376 // For x64, this is where we say our return address is
377 //
378 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
379
380 //
381 // Entry function needn't access high stack context, simply
382 // put the stack pointer here.
383 //
384
385 //
386 // Begin executing the EBC code
387 //
388 EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
389 EbcExecute (&VmContext);
390
391 //
392 // Return the value in Gpr[7] unless there was an error
393 //
394 ReturnEBCStack(StackIndex);
395 return (UINT64) VmContext.Gpr[7];
396 }
397
398
399 /**
400 Create thunks for an EBC image entry point, or an EBC protocol service.
401
402 @param ImageHandle Image handle for the EBC image. If not null, then
403 we're creating a thunk for an image entry point.
404 @param EbcEntryPoint Address of the EBC code that the thunk is to call
405 @param Thunk Returned thunk we create here
406 @param Flags Flags indicating options for creating the thunk
407
408 @retval EFI_SUCCESS The thunk was created successfully.
409 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
410 aligned.
411 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
412 Thunk.
413 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
414
415 **/
416 EFI_STATUS
417 EbcCreateThunks (
418 IN EFI_HANDLE ImageHandle,
419 IN VOID *EbcEntryPoint,
420 OUT VOID **Thunk,
421 IN UINT32 Flags
422 )
423 {
424 UINT8 *Ptr;
425 UINT8 *ThunkBase;
426 UINT32 Index;
427 INT32 ThunkSize;
428
429 //
430 // Check alignment of pointer to EBC code
431 //
432 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
433 return EFI_INVALID_PARAMETER;
434 }
435
436 ThunkSize = sizeof(mInstructionBufferTemplate);
437
438 Ptr = EbcAllocatePoolForThunk (sizeof(mInstructionBufferTemplate));
439
440 if (Ptr == NULL) {
441 return EFI_OUT_OF_RESOURCES;
442 }
443 //
444 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
445 //
446 // Save the start address so we can add a pointer to it to a list later.
447 //
448 ThunkBase = Ptr;
449
450 //
451 // Give them the address of our buffer we're going to fix up
452 //
453 *Thunk = (VOID *) Ptr;
454
455 //
456 // Copy whole thunk instruction buffer template
457 //
458 CopyMem (Ptr, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate));
459
460 //
461 // Patch EbcEntryPoint and EbcLLEbcInterpret
462 //
463 for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {
464 if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) {
465 *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint;
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
485
486 /**
487 This function is called to execute an EBC CALLEX instruction.
488 The function check the callee's content to see whether it is common native
489 code or a thunk to another piece of EBC code.
490 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
491 otherwise, set the VM->IP to target EBC code directly to avoid another VM
492 be startup which cost time and stack space.
493
494 @param VmPtr Pointer to a VM context.
495 @param FuncAddr Callee's address
496 @param NewStackPointer New stack pointer after the call
497 @param FramePtr New frame pointer after the call
498 @param Size The size of call instruction
499
500 **/
501 VOID
502 EbcLLCALLEX (
503 IN VM_CONTEXT *VmPtr,
504 IN UINTN FuncAddr,
505 IN UINTN NewStackPointer,
506 IN VOID *FramePtr,
507 IN UINT8 Size
508 )
509 {
510 UINTN IsThunk;
511 UINTN TargetEbcAddr;
512 UINT8 InstructionBuffer[sizeof(mInstructionBufferTemplate)];
513 UINTN Index;
514 UINTN IndexOfEbcEntrypoint;
515
516 IsThunk = 1;
517 TargetEbcAddr = 0;
518 IndexOfEbcEntrypoint = 0;
519
520 //
521 // Processor specific code to check whether the callee is a thunk to EBC.
522 //
523 CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof(InstructionBuffer));
524 //
525 // Fill the signature according to mInstructionBufferTemplate
526 //
527 for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {
528 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) {
529 *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE;
530 IndexOfEbcEntrypoint = Index;
531 }
532 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {
533 *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE;
534 }
535 }
536 //
537 // Check if we need thunk to native
538 //
539 if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)) != 0) {
540 IsThunk = 0;
541 }
542
543 if (IsThunk == 1){
544 //
545 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
546 // put our return address and frame pointer on the VM stack.
547 // Then set the VM's IP to new EBC code.
548 //
549 VmPtr->Gpr[0] -= 8;
550 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
551 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
552 VmPtr->Gpr[0] -= 8;
553 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
554
555 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof(UINTN));
556 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
557 } else {
558 //
559 // The callee is not a thunk to EBC, call native code,
560 // and get return value.
561 //
562 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
563
564 //
565 // Advance the IP.
566 //
567 VmPtr->Ip += Size;
568 }
569 }
570