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