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