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