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