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