]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/Ia32/EbcSupport.c
Fix the prediction warnings in EBC.
[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 @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->R[0] -= 8;
112 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
113 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
114 VmPtr->R[0] -= 8;
115 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[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->R[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 STATIC
163 UINT64
164 EbcInterpret (
165 IN OUT UINTN Arg1,
166 IN OUT UINTN Arg2,
167 IN OUT UINTN Arg3,
168 IN OUT UINTN Arg4,
169 IN OUT UINTN Arg5,
170 IN OUT UINTN Arg6,
171 IN OUT UINTN Arg7,
172 IN OUT UINTN Arg8,
173 IN OUT UINTN Arg9,
174 IN OUT UINTN Arg10,
175 IN OUT UINTN Arg11,
176 IN OUT UINTN Arg12,
177 IN OUT UINTN Arg13,
178 IN OUT UINTN Arg14,
179 IN OUT UINTN Arg15,
180 IN OUT UINTN Arg16
181 )
182 {
183 //
184 // Create a new VM context on the stack
185 //
186 VM_CONTEXT VmContext;
187 UINTN Addr;
188 EFI_STATUS Status;
189 UINTN StackIndex;
190
191 //
192 // Get the EBC entry point from the processor register.
193 //
194 Addr = EbcLLGetEbcEntryPoint ();
195
196 //
197 // Now clear out our context
198 //
199 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
200
201 //
202 // Set the VM instruction pointer to the correct location in memory.
203 //
204 VmContext.Ip = (VMIP) Addr;
205 //
206 // Initialize the stack pointer for the EBC. Get the current system stack
207 // pointer and adjust it down by the max needed for the interpreter.
208 //
209
210 //
211 // Align the stack on a natural boundary
212 //
213
214 //
215 // Allocate stack pool
216 //
217 Status = GetEBCStack((EFI_HANDLE)-1, &VmContext.StackPool, &StackIndex);
218 if (EFI_ERROR(Status)) {
219 return Status;
220 }
221 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
222 VmContext.R[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
223 VmContext.HighStackBottom = (UINTN)VmContext.R[0];
224 VmContext.R[0] &= ~(sizeof (UINTN) - 1);
225 VmContext.R[0] -= sizeof (UINTN);
226
227 //
228 // Put a magic value in the stack gap, then adjust down again
229 //
230 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
231 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
232 VmContext.LowStackTop = (UINTN) VmContext.R[0];
233
234 //
235 // For IA32, this is where we say our return address is
236 //
237 VmContext.R[0] -= sizeof (UINTN);
238 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg16;
239 VmContext.R[0] -= sizeof (UINTN);
240 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg15;
241 VmContext.R[0] -= sizeof (UINTN);
242 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg14;
243 VmContext.R[0] -= sizeof (UINTN);
244 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg13;
245 VmContext.R[0] -= sizeof (UINTN);
246 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg12;
247 VmContext.R[0] -= sizeof (UINTN);
248 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg11;
249 VmContext.R[0] -= sizeof (UINTN);
250 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg10;
251 VmContext.R[0] -= sizeof (UINTN);
252 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg9;
253 VmContext.R[0] -= sizeof (UINTN);
254 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg8;
255 VmContext.R[0] -= sizeof (UINTN);
256 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg7;
257 VmContext.R[0] -= sizeof (UINTN);
258 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg6;
259 VmContext.R[0] -= sizeof (UINTN);
260 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg5;
261 VmContext.R[0] -= sizeof (UINTN);
262 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg4;
263 VmContext.R[0] -= sizeof (UINTN);
264 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg3;
265 VmContext.R[0] -= sizeof (UINTN);
266 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg2;
267 VmContext.R[0] -= sizeof (UINTN);
268 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg1;
269 VmContext.R[0] -= 16;
270 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
271
272 //
273 // We need to keep track of where the EBC stack starts. This way, if the EBC
274 // accesses any stack variables above its initial stack setting, then we know
275 // it's accessing variables passed into it, which means the data is on the
276 // VM's stack.
277 // When we're called, on the stack (high to low) we have the parameters, the
278 // return address, then the saved ebp. Save the pointer to the return address.
279 // EBC code knows that's there, so should look above it for function parameters.
280 // The offset is the size of locals (VMContext + Addr + saved ebp).
281 // Note that the interpreter assumes there is a 16 bytes of return address on
282 // the stack too, so adjust accordingly.
283 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
284 //
285
286 //
287 // Begin executing the EBC code
288 //
289 EbcExecute (&VmContext);
290
291 //
292 // Return the value in R[7] unless there was an error
293 //
294 ReturnEBCStack(StackIndex);
295 return (UINT64) VmContext.R[7];
296 }
297
298
299 /**
300 Begin executing an EBC image. The address of the entry point is passed
301 in via a processor register, so we'll need to make a call to get the
302 value.
303
304 @param ImageHandle image handle for the EBC application we're executing
305 @param SystemTable standard system table passed into an driver's entry
306 point
307
308 @return The value returned by the EBC application we're going to run.
309
310 **/
311 STATIC
312 UINT64
313 ExecuteEbcImageEntryPoint (
314 IN EFI_HANDLE ImageHandle,
315 IN EFI_SYSTEM_TABLE *SystemTable
316 )
317 {
318 //
319 // Create a new VM context on the stack
320 //
321 VM_CONTEXT VmContext;
322 UINTN Addr;
323 EFI_STATUS Status;
324 UINTN StackIndex;
325
326 //
327 // Get the EBC entry point from the processor register. Make sure you don't
328 // call any functions before this or you could mess up the register the
329 // entry point is passed in.
330 //
331 Addr = EbcLLGetEbcEntryPoint ();
332
333 //
334 // Print(L"*** Thunked into EBC entry point - ImageHandle = 0x%X\n", (UINTN)ImageHandle);
335 // Print(L"EBC entry point is 0x%X\n", (UINT32)(UINTN)Addr);
336 //
337 // Now clear out our context
338 //
339 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
340
341 //
342 // Save the image handle so we can track the thunks created for this image
343 //
344 VmContext.ImageHandle = ImageHandle;
345 VmContext.SystemTable = SystemTable;
346
347 //
348 // Set the VM instruction pointer to the correct location in memory.
349 //
350 VmContext.Ip = (VMIP) Addr;
351
352 //
353 // Initialize the stack pointer for the EBC. Get the current system stack
354 // pointer and adjust it down by the max needed for the interpreter.
355 //
356
357 //
358 // Allocate stack pool
359 //
360 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
361 if (EFI_ERROR(Status)) {
362 return Status;
363 }
364 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
365 VmContext.R[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
366 VmContext.HighStackBottom = (UINTN)VmContext.R[0];
367 VmContext.R[0] -= sizeof (UINTN);
368
369 //
370 // Put a magic value in the stack gap, then adjust down again
371 //
372 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
373 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
374
375 //
376 // Align the stack on a natural boundary
377 // VmContext.R[0] &= ~(sizeof(UINTN) - 1);
378 //
379 VmContext.LowStackTop = (UINTN) VmContext.R[0];
380 VmContext.R[0] -= sizeof (UINTN);
381 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) SystemTable;
382 VmContext.R[0] -= sizeof (UINTN);
383 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) ImageHandle;
384
385 VmContext.R[0] -= 16;
386 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
387 //
388 // VM pushes 16-bytes for return address. Simulate that here.
389 //
390
391 //
392 // Begin executing the EBC code
393 //
394 EbcExecute (&VmContext);
395
396 //
397 // Return the value in R[7] unless there was an error
398 //
399 return (UINT64) VmContext.R[7];
400 }
401
402
403 /**
404 Create thunks for an EBC image entry point, or an EBC protocol service.
405
406 @param ImageHandle Image handle for the EBC image. If not null, then
407 we're creating a thunk for an image entry point.
408 @param EbcEntryPoint Address of the EBC code that the thunk is to call
409 @param Thunk Returned thunk we create here
410 @param Flags Flags indicating options for creating the thunk
411
412 @retval EFI_SUCCESS The thunk was created successfully.
413 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
414 aligned.
415 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
416 Thunk.
417 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
418
419 **/
420 EFI_STATUS
421 EbcCreateThunks (
422 IN EFI_HANDLE ImageHandle,
423 IN VOID *EbcEntryPoint,
424 OUT VOID **Thunk,
425 IN UINT32 Flags
426 )
427 {
428 UINT8 *Ptr;
429 UINT8 *ThunkBase;
430 UINT32 Index;
431 UINT32 Addr;
432 INT32 Size;
433 INT32 ThunkSize;
434
435 //
436 // Check alignment of pointer to EBC code
437 //
438 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
439 return EFI_INVALID_PARAMETER;
440 }
441
442 Size = EBC_THUNK_SIZE;
443 ThunkSize = Size;
444
445 Ptr = AllocatePool (Size);
446
447 if (Ptr == NULL) {
448 return EFI_OUT_OF_RESOURCES;
449 }
450 //
451 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
452 //
453 // Save the start address so we can add a pointer to it to a list later.
454 //
455 ThunkBase = Ptr;
456
457 //
458 // Give them the address of our buffer we're going to fix up
459 //
460 *Thunk = (VOID *) Ptr;
461
462 //
463 // Add a magic code here to help the VM recognize the thunk..
464 // mov eax, 0xca112ebc => B8 BC 2E 11 CA
465 //
466 *Ptr = 0xB8;
467 Ptr++;
468 Size--;
469 Addr = (UINT32) 0xCA112EBC;
470 for (Index = 0; Index < sizeof (Addr); Index++) {
471 *Ptr = (UINT8) (UINTN) Addr;
472 Addr >>= 8;
473 Ptr++;
474 Size--;
475 }
476
477 //
478 // Add code bytes to load up a processor register with the EBC entry point.
479 // mov eax, 0xaa55aa55 => B8 55 AA 55 AA
480 // The first 8 bytes of the thunk entry is the address of the EBC
481 // entry point.
482 //
483 *Ptr = 0xB8;
484 Ptr++;
485 Size--;
486 Addr = (UINT32) EbcEntryPoint;
487 for (Index = 0; Index < sizeof (Addr); Index++) {
488 *Ptr = (UINT8) (UINTN) Addr;
489 Addr >>= 8;
490 Ptr++;
491 Size--;
492 }
493 //
494 // Stick in a load of ecx with the address of appropriate VM function.
495 // mov ecx 12345678h => 0xB9 0x78 0x56 0x34 0x12
496 //
497 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
498 Addr = (UINT32) (UINTN) ExecuteEbcImageEntryPoint;
499 } else {
500 Addr = (UINT32) (UINTN) EbcInterpret;
501 }
502
503 //
504 // MOV ecx
505 //
506 *Ptr = 0xB9;
507 Ptr++;
508 Size--;
509 for (Index = 0; Index < sizeof (Addr); Index++) {
510 *Ptr = (UINT8) Addr;
511 Addr >>= 8;
512 Ptr++;
513 Size--;
514 }
515 //
516 // Stick in jump opcode bytes for jmp ecx => 0xFF 0xE1
517 //
518 *Ptr = 0xFF;
519 Ptr++;
520 Size--;
521 *Ptr = 0xE1;
522 Size--;
523
524 //
525 // Double check that our defined size is ok (application error)
526 //
527 if (Size < 0) {
528 ASSERT (FALSE);
529 return EFI_BUFFER_TOO_SMALL;
530 }
531 //
532 // Add the thunk to the list for this image. Do this last since the add
533 // function flushes the cache for us.
534 //
535 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
536
537 return EFI_SUCCESS;
538 }