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