]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c
Remove assumption on EAX and R10 usage for IA32 compiler and X64 compiler.
[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 - 2011, 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 * 8)
26 #define EBC_THUNK_SIZE 64
27
28 #define STACK_REMAIN_SIZE (1024 * 4)
29
30 /**
31 Begin executing an EBC image.
32 This is used for Ebc Thunk call.
33
34 @return The value returned by the EBC application we're going to run.
35
36 **/
37 UINT64
38 EFIAPI
39 EbcLLEbcInterpret (
40 VOID
41 );
42
43 /**
44 Begin executing an EBC image.
45 This is used for Ebc image entrypoint.
46
47 @return The value returned by the EBC application we're going to run.
48
49 **/
50 UINT64
51 EFIAPI
52 EbcLLExecuteEbcImageEntryPoint (
53 VOID
54 );
55
56 /**
57 Pushes a 64 bit unsigned value to the VM stack.
58
59 @param VmPtr The pointer to current VM context.
60 @param Arg The value to be pushed.
61
62 **/
63 VOID
64 PushU64 (
65 IN VM_CONTEXT *VmPtr,
66 IN UINT64 Arg
67 )
68 {
69 //
70 // Advance the VM stack down, and then copy the argument to the stack.
71 // Hope it's aligned.
72 //
73 VmPtr->Gpr[0] -= sizeof (UINT64);
74 *(UINT64 *) VmPtr->Gpr[0] = Arg;
75 return;
76 }
77
78
79 /**
80 Begin executing an EBC image.
81
82 This is a thunk function. Microsoft x64 compiler only provide fast_call
83 calling convention, so the first four arguments are passed by rcx, rdx,
84 r8, and r9, while other arguments are passed in stack.
85
86 @param EntryPoint The entrypoint of EBC code.
87 @param Arg1 The 1st argument.
88 @param Arg2 The 2nd argument.
89 @param Arg3 The 3rd argument.
90 @param Arg4 The 4th argument.
91 @param Arg5 The 5th argument.
92 @param Arg6 The 6th argument.
93 @param Arg7 The 7th argument.
94 @param Arg8 The 8th argument.
95 @param Arg9 The 9th argument.
96 @param Arg10 The 10th argument.
97 @param Arg11 The 11th argument.
98 @param Arg12 The 12th argument.
99 @param Arg13 The 13th argument.
100 @param Arg14 The 14th argument.
101 @param Arg15 The 15th argument.
102 @param Arg16 The 16th argument.
103
104 @return The value returned by the EBC application we're going to run.
105
106 **/
107 UINT64
108 EFIAPI
109 EbcInterpret (
110 IN UINTN EntryPoint,
111 IN UINTN Arg1,
112 IN UINTN Arg2,
113 IN UINTN Arg3,
114 IN UINTN Arg4,
115 IN UINTN Arg5,
116 IN UINTN Arg6,
117 IN UINTN Arg7,
118 IN UINTN Arg8,
119 IN UINTN Arg9,
120 IN UINTN Arg10,
121 IN UINTN Arg11,
122 IN UINTN Arg12,
123 IN UINTN Arg13,
124 IN UINTN Arg14,
125 IN UINTN Arg15,
126 IN UINTN Arg16
127 )
128 {
129 //
130 // Create a new VM context on the stack
131 //
132 VM_CONTEXT VmContext;
133 UINTN Addr;
134 EFI_STATUS Status;
135 UINTN StackIndex;
136
137 //
138 // Get the EBC entry point
139 //
140 Addr = EntryPoint;
141
142 //
143 // Now clear out our context
144 //
145 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
146
147 //
148 // Set the VM instruction pointer to the correct location in memory.
149 //
150 VmContext.Ip = (VMIP) Addr;
151
152 //
153 // Initialize the stack pointer for the EBC. Get the current system stack
154 // pointer and adjust it down by the max needed for the interpreter.
155 //
156
157 //
158 // Adjust the VM's stack pointer down.
159 //
160
161 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
162 if (EFI_ERROR(Status)) {
163 return Status;
164 }
165 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
166 VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
167 VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
168 VmContext.Gpr[0] -= sizeof (UINTN);
169
170 //
171 // Align the stack on a natural boundary.
172 //
173 VmContext.Gpr[0] &= ~(sizeof (UINTN) - 1);
174
175 //
176 // Put a magic value in the stack gap, then adjust down again.
177 //
178 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
179 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
180
181 //
182 // The stack upper to LowStackTop is belong to the VM.
183 //
184 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
185
186 //
187 // For the worst case, assume there are 4 arguments passed in registers, store
188 // them to VM's stack.
189 //
190 PushU64 (&VmContext, (UINT64) Arg16);
191 PushU64 (&VmContext, (UINT64) Arg15);
192 PushU64 (&VmContext, (UINT64) Arg14);
193 PushU64 (&VmContext, (UINT64) Arg13);
194 PushU64 (&VmContext, (UINT64) Arg12);
195 PushU64 (&VmContext, (UINT64) Arg11);
196 PushU64 (&VmContext, (UINT64) Arg10);
197 PushU64 (&VmContext, (UINT64) Arg9);
198 PushU64 (&VmContext, (UINT64) Arg8);
199 PushU64 (&VmContext, (UINT64) Arg7);
200 PushU64 (&VmContext, (UINT64) Arg6);
201 PushU64 (&VmContext, (UINT64) Arg5);
202 PushU64 (&VmContext, (UINT64) Arg4);
203 PushU64 (&VmContext, (UINT64) Arg3);
204 PushU64 (&VmContext, (UINT64) Arg2);
205 PushU64 (&VmContext, (UINT64) Arg1);
206
207 //
208 // Interpreter assumes 64-bit return address is pushed on the stack.
209 // The x64 does not do this so pad the stack accordingly.
210 //
211 PushU64 (&VmContext, (UINT64) 0);
212 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
213
214 //
215 // For x64, this is where we say our return address is
216 //
217 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
218
219 //
220 // We need to keep track of where the EBC stack starts. This way, if the EBC
221 // accesses any stack variables above its initial stack setting, then we know
222 // it's accessing variables passed into it, which means the data is on the
223 // VM's stack.
224 // When we're called, on the stack (high to low) we have the parameters, the
225 // return address, then the saved ebp. Save the pointer to the return address.
226 // EBC code knows that's there, so should look above it for function parameters.
227 // The offset is the size of locals (VMContext + Addr + saved ebp).
228 // Note that the interpreter assumes there is a 16 bytes of return address on
229 // the stack too, so adjust accordingly.
230 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
231 //
232
233 //
234 // Begin executing the EBC code
235 //
236 EbcExecute (&VmContext);
237
238 //
239 // Return the value in R[7] unless there was an error
240 //
241 ReturnEBCStack(StackIndex);
242 return (UINT64) VmContext.Gpr[7];
243 }
244
245
246 /**
247 Begin executing an EBC image.
248
249 @param EntryPoint The entrypoint of EBC code.
250 @param ImageHandle image handle for the EBC application we're executing
251 @param SystemTable standard system table passed into an driver's entry
252 point
253
254 @return The value returned by the EBC application we're going to run.
255
256 **/
257 UINT64
258 EFIAPI
259 ExecuteEbcImageEntryPoint (
260 IN UINTN EntryPoint,
261 IN EFI_HANDLE ImageHandle,
262 IN EFI_SYSTEM_TABLE *SystemTable
263 )
264 {
265 //
266 // Create a new VM context on the stack
267 //
268 VM_CONTEXT VmContext;
269 UINTN Addr;
270 EFI_STATUS Status;
271 UINTN StackIndex;
272
273 //
274 // Get the EBC entry point
275 //
276 Addr = EntryPoint;
277
278 //
279 // Now clear out our context
280 //
281 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
282
283 //
284 // Save the image handle so we can track the thunks created for this image
285 //
286 VmContext.ImageHandle = ImageHandle;
287 VmContext.SystemTable = SystemTable;
288
289 //
290 // Set the VM instruction pointer to the correct location in memory.
291 //
292 VmContext.Ip = (VMIP) Addr;
293
294 //
295 // Initialize the stack pointer for the EBC. Get the current system stack
296 // pointer and adjust it down by the max needed for the interpreter.
297 //
298
299 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
300 if (EFI_ERROR(Status)) {
301 return Status;
302 }
303 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
304 VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
305 VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
306 VmContext.Gpr[0] -= sizeof (UINTN);
307
308
309 //
310 // Put a magic value in the stack gap, then adjust down again
311 //
312 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
313 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
314
315 //
316 // Align the stack on a natural boundary
317 VmContext.Gpr[0] &= ~(sizeof(UINTN) - 1);
318 //
319 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
320
321 //
322 // Simply copy the image handle and system table onto the EBC stack.
323 // Greatly simplifies things by not having to spill the args.
324 //
325 PushU64 (&VmContext, (UINT64) SystemTable);
326 PushU64 (&VmContext, (UINT64) ImageHandle);
327
328 //
329 // VM pushes 16-bytes for return address. Simulate that here.
330 //
331 PushU64 (&VmContext, (UINT64) 0);
332 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
333
334 //
335 // For x64, this is where we say our return address is
336 //
337 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
338
339 //
340 // Entry function needn't access high stack context, simply
341 // put the stack pointer here.
342 //
343
344 //
345 // Begin executing the EBC code
346 //
347 EbcExecute (&VmContext);
348
349 //
350 // Return the value in R[7] unless there was an error
351 //
352 ReturnEBCStack(StackIndex);
353 return (UINT64) VmContext.Gpr[7];
354 }
355
356
357 /**
358 Create thunks for an EBC image entry point, or an EBC protocol service.
359
360 @param ImageHandle Image handle for the EBC image. If not null, then
361 we're creating a thunk for an image entry point.
362 @param EbcEntryPoint Address of the EBC code that the thunk is to call
363 @param Thunk Returned thunk we create here
364 @param Flags Flags indicating options for creating the thunk
365
366 @retval EFI_SUCCESS The thunk was created successfully.
367 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
368 aligned.
369 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
370 Thunk.
371 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
372
373 **/
374 EFI_STATUS
375 EbcCreateThunks (
376 IN EFI_HANDLE ImageHandle,
377 IN VOID *EbcEntryPoint,
378 OUT VOID **Thunk,
379 IN UINT32 Flags
380 )
381 {
382 UINT8 *Ptr;
383 UINT8 *ThunkBase;
384 UINT32 Index;
385 UINT64 Addr;
386 INT32 Size;
387 INT32 ThunkSize;
388
389 //
390 // Check alignment of pointer to EBC code
391 //
392 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
393 return EFI_INVALID_PARAMETER;
394 }
395
396 Size = EBC_THUNK_SIZE;
397 ThunkSize = Size;
398
399 Ptr = AllocatePool (Size);
400
401 if (Ptr == NULL) {
402 return EFI_OUT_OF_RESOURCES;
403 }
404 //
405 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
406 //
407 // Save the start address so we can add a pointer to it to a list later.
408 //
409 ThunkBase = Ptr;
410
411 //
412 // Give them the address of our buffer we're going to fix up
413 //
414 *Thunk = (VOID *) Ptr;
415
416 //
417 // Add a magic code here to help the VM recognize the thunk..
418 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
419 //
420 *Ptr = 0x48;
421 Ptr++;
422 Size--;
423 *Ptr = 0xB8;
424 Ptr++;
425 Size--;
426 Addr = (UINT64) 0xCA112EBCCA112EBCULL;
427 for (Index = 0; Index < sizeof (Addr); Index++) {
428 *Ptr = (UINT8) (UINTN) Addr;
429 Addr >>= 8;
430 Ptr++;
431 Size--;
432 }
433
434 //
435 // Add code bytes to load up a processor register with the EBC entry point.
436 // mov r10, 123456789abcdef0h => 49 BA F0 DE BC 9A 78 56 34 12
437 // The first 8 bytes of the thunk entry is the address of the EBC
438 // entry point.
439 //
440 *Ptr = 0x49;
441 Ptr++;
442 Size--;
443 *Ptr = 0xBA;
444 Ptr++;
445 Size--;
446 Addr = (UINT64) EbcEntryPoint;
447 for (Index = 0; Index < sizeof (Addr); Index++) {
448 *Ptr = (UINT8) (UINTN) Addr;
449 Addr >>= 8;
450 Ptr++;
451 Size--;
452 }
453
454 //
455 // Stick in a load of ecx with the address of appropriate VM function.
456 // Using r11 because it's a volatile register and won't be used in this
457 // point.
458 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
459 //
460 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
461 Addr = (UINTN) EbcLLExecuteEbcImageEntryPoint;
462 } else {
463 Addr = (UINTN) EbcLLEbcInterpret;
464 }
465
466 //
467 // mov r11 Addr => 0x49 0xBB
468 //
469 *Ptr = 0x49;
470 Ptr++;
471 Size--;
472 *Ptr = 0xBB;
473 Ptr++;
474 Size--;
475 for (Index = 0; Index < sizeof (Addr); Index++) {
476 *Ptr = (UINT8) Addr;
477 Addr >>= 8;
478 Ptr++;
479 Size--;
480 }
481 //
482 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
483 //
484 *Ptr = 0x41;
485 Ptr++;
486 Size--;
487 *Ptr = 0xFF;
488 Ptr++;
489 Size--;
490 *Ptr = 0xE3;
491 Size--;
492
493 //
494 // Double check that our defined size is ok (application error)
495 //
496 if (Size < 0) {
497 ASSERT (FALSE);
498 return EFI_BUFFER_TOO_SMALL;
499 }
500 //
501 // Add the thunk to the list for this image. Do this last since the add
502 // function flushes the cache for us.
503 //
504 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
505
506 return EFI_SUCCESS;
507 }
508
509
510 /**
511 This function is called to execute an EBC CALLEX instruction.
512 The function check the callee's content to see whether it is common native
513 code or a thunk to another piece of EBC code.
514 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
515 otherwise, set the VM->IP to target EBC code directly to avoid another VM
516 be startup which cost time and stack space.
517
518 @param VmPtr Pointer to a VM context.
519 @param FuncAddr Callee's address
520 @param NewStackPointer New stack pointer after the call
521 @param FramePtr New frame pointer after the call
522 @param Size The size of call instruction
523
524 **/
525 VOID
526 EbcLLCALLEX (
527 IN VM_CONTEXT *VmPtr,
528 IN UINTN FuncAddr,
529 IN UINTN NewStackPointer,
530 IN VOID *FramePtr,
531 IN UINT8 Size
532 )
533 {
534 UINTN IsThunk;
535 UINTN TargetEbcAddr;
536
537 IsThunk = 1;
538 TargetEbcAddr = 0;
539
540 //
541 // Processor specific code to check whether the callee is a thunk to EBC.
542 //
543 if (*((UINT8 *)FuncAddr) != 0x48) {
544 IsThunk = 0;
545 goto Action;
546 }
547 if (*((UINT8 *)FuncAddr + 1) != 0xB8) {
548 IsThunk = 0;
549 goto Action;
550 }
551 if (*((UINT8 *)FuncAddr + 2) != 0xBC) {
552 IsThunk = 0;
553 goto Action;
554 }
555 if (*((UINT8 *)FuncAddr + 3) != 0x2E) {
556 IsThunk = 0;
557 goto Action;
558 }
559 if (*((UINT8 *)FuncAddr + 4) != 0x11) {
560 IsThunk = 0;
561 goto Action;
562 }
563 if (*((UINT8 *)FuncAddr + 5) != 0xCA) {
564 IsThunk = 0;
565 goto Action;
566 }
567 if (*((UINT8 *)FuncAddr + 6) != 0xBC) {
568 IsThunk = 0;
569 goto Action;
570 }
571 if (*((UINT8 *)FuncAddr + 7) != 0x2E) {
572 IsThunk = 0;
573 goto Action;
574 }
575 if (*((UINT8 *)FuncAddr + 8) != 0x11) {
576 IsThunk = 0;
577 goto Action;
578 }
579 if (*((UINT8 *)FuncAddr + 9) != 0xCA) {
580 IsThunk = 0;
581 goto Action;
582 }
583 if (*((UINT8 *)FuncAddr + 10) != 0x49) {
584 IsThunk = 0;
585 goto Action;
586 }
587 if (*((UINT8 *)FuncAddr + 11) != 0xBA) {
588 IsThunk = 0;
589 goto Action;
590 }
591
592 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + 12, 8);
593
594 Action:
595 if (IsThunk == 1){
596 //
597 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
598 // put our return address and frame pointer on the VM stack.
599 // Then set the VM's IP to new EBC code.
600 //
601 VmPtr->Gpr[0] -= 8;
602 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
603 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
604 VmPtr->Gpr[0] -= 8;
605 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (VmPtr->Ip + Size));
606
607 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
608 } else {
609 //
610 // The callee is not a thunk to EBC, call native code,
611 // and get return value.
612 //
613 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
614
615 //
616 // Advance the IP.
617 //
618 VmPtr->Ip += Size;
619 }
620 }
621