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