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