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