]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 Begin executing an EBC image.
120
121 This is a thunk function. Microsoft x64 compiler only provide fast_call
122 calling convention, so the first four arguments are passed by rcx, rdx,
123 r8, and r9, while other arguments are passed in stack.
124
125 @param EntryPoint The entrypoint of EBC code.
126 @param Arg1 The 1st argument.
127 @param Arg2 The 2nd argument.
128 @param Arg3 The 3rd argument.
129 @param Arg4 The 4th argument.
130 @param Arg5 The 5th argument.
131 @param Arg6 The 6th argument.
132 @param Arg7 The 7th argument.
133 @param Arg8 The 8th argument.
134 @param Arg9 The 9th argument.
135 @param Arg10 The 10th argument.
136 @param Arg11 The 11th argument.
137 @param Arg12 The 12th argument.
138 @param Arg13 The 13th argument.
139 @param Arg14 The 14th argument.
140 @param Arg15 The 15th argument.
141 @param Arg16 The 16th argument.
142
143 @return The value returned by the EBC application we're going to run.
144
145 **/
146 UINT64
147 EFIAPI
148 EbcInterpret (
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
286 /**
287 Begin executing an EBC image.
288
289 @param EntryPoint The entrypoint of EBC code.
290 @param ImageHandle image handle for the EBC application we're executing
291 @param SystemTable standard system table passed into an driver's entry
292 point
293
294 @return The value returned by the EBC application we're going to run.
295
296 **/
297 UINT64
298 EFIAPI
299 ExecuteEbcImageEntryPoint (
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 //
387 EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
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
397 /**
398 Create thunks for an EBC image entry point, or an EBC protocol service.
399
400 @param ImageHandle Image handle for the EBC image. If not null, then
401 we're creating a thunk for an image entry point.
402 @param EbcEntryPoint Address of the EBC code that the thunk is to call
403 @param Thunk Returned thunk we create here
404 @param Flags Flags indicating options for creating the thunk
405
406 @retval EFI_SUCCESS The thunk was created successfully.
407 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
408 aligned.
409 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
410 Thunk.
411 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
412
413 **/
414 EFI_STATUS
415 EbcCreateThunks (
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
485 /**
486 This function is called to execute an EBC CALLEX instruction.
487 The function check the callee's content to see whether it is common native
488 code or a thunk to another piece of EBC code.
489 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
490 otherwise, set the VM->IP to target EBC code directly to avoid another VM
491 be startup which cost time and stack space.
492
493 @param VmPtr Pointer to a VM context.
494 @param FuncAddr Callee's address
495 @param NewStackPointer New stack pointer after the call
496 @param FramePtr New frame pointer after the call
497 @param Size The size of call instruction
498
499 **/
500 VOID
501 EbcLLCALLEX (
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 }