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