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