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