]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/x64/EbcSupport.c
apply for doxgen format.
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / x64 / EbcSupport.c
1 /** @file
2 This module contains EBC support routines that are customized based on
3 the target x64 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 * 8)
26 #define EBC_THUNK_SIZE 64
27
28 #define STACK_REMAIN_SIZE (1024 * 4)
29
30 STATIC
31 VOID
32 PushU64 (
33 VM_CONTEXT *VmPtr,
34 UINT64 Arg
35 )
36 /*++
37
38 Routine Description:
39
40 Push a 64 bit unsigned value to the VM stack.
41
42 Arguments:
43
44 VmPtr - The pointer to current VM context.
45 Arg - The value to be pushed
46
47 Returns:
48
49 VOID
50
51 --*/
52 {
53 //
54 // Advance the VM stack down, and then copy the argument to the stack.
55 // Hope it's aligned.
56 //
57 VmPtr->R[0] -= sizeof (UINT64);
58 *(UINT64 *) VmPtr->R[0] = Arg;
59 return;
60 }
61
62
63 /**
64 Begin executing an EBC image. The address of the entry point is passed
65 in via a processor register, so we'll need to make a call to get the
66 value.
67
68 This is a thunk function. Microsoft x64 compiler only provide fast_call
69 calling convention, so the first four arguments are passed by rcx, rdx,
70 r8, and r9, while other arguments are passed in stack.
71
72 @return The value returned by the EBC application we're going to run.
73
74 **/
75 STATIC
76 UINT64
77 EbcInterpret (
78 UINTN Arg1,
79 UINTN Arg2,
80 UINTN Arg3,
81 UINTN Arg4,
82 UINTN Arg5,
83 UINTN Arg6,
84 UINTN Arg7,
85 UINTN Arg8,
86 UINTN Arg9,
87 UINTN Arg10,
88 UINTN Arg11,
89 UINTN Arg12,
90 UINTN Arg13,
91 UINTN Arg14,
92 UINTN Arg15,
93 UINTN Arg16
94 )
95 {
96 //
97 // Create a new VM context on the stack
98 //
99 VM_CONTEXT VmContext;
100 UINTN Addr;
101 EFI_STATUS Status;
102 UINTN StackIndex;
103
104 //
105 // Get the EBC entry point from the processor register.
106 // Don't call any function before getting the EBC entry
107 // point because this will collab the return register.
108 //
109 Addr = EbcLLGetEbcEntryPoint ();
110
111 //
112 // Now clear out our context
113 //
114 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
115
116 //
117 // Set the VM instruction pointer to the correct location in memory.
118 //
119 VmContext.Ip = (VMIP) Addr;
120
121 //
122 // Initialize the stack pointer for the EBC. Get the current system stack
123 // pointer and adjust it down by the max needed for the interpreter.
124 //
125 Addr = EbcLLGetStackPointer ();
126
127 //
128 // Adjust the VM's stack pointer down.
129 //
130
131 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
132 if (EFI_ERROR(Status)) {
133 return Status;
134 }
135 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
136 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
137 VmContext.HighStackBottom = (UINTN) VmContext.R[0];
138 VmContext.R[0] -= sizeof (UINTN);
139
140 //
141 // Align the stack on a natural boundary.
142 //
143 VmContext.R[0] &= ~(sizeof (UINTN) - 1);
144
145 //
146 // Put a magic value in the stack gap, then adjust down again.
147 //
148 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
149 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
150
151 //
152 // The stack upper to LowStackTop is belong to the VM.
153 //
154 VmContext.LowStackTop = (UINTN) VmContext.R[0];
155
156 //
157 // For the worst case, assume there are 4 arguments passed in registers, store
158 // them to VM's stack.
159 //
160 PushU64 (&VmContext, (UINT64) Arg16);
161 PushU64 (&VmContext, (UINT64) Arg15);
162 PushU64 (&VmContext, (UINT64) Arg14);
163 PushU64 (&VmContext, (UINT64) Arg13);
164 PushU64 (&VmContext, (UINT64) Arg12);
165 PushU64 (&VmContext, (UINT64) Arg11);
166 PushU64 (&VmContext, (UINT64) Arg10);
167 PushU64 (&VmContext, (UINT64) Arg9);
168 PushU64 (&VmContext, (UINT64) Arg8);
169 PushU64 (&VmContext, (UINT64) Arg7);
170 PushU64 (&VmContext, (UINT64) Arg6);
171 PushU64 (&VmContext, (UINT64) Arg5);
172 PushU64 (&VmContext, (UINT64) Arg4);
173 PushU64 (&VmContext, (UINT64) Arg3);
174 PushU64 (&VmContext, (UINT64) Arg2);
175 PushU64 (&VmContext, (UINT64) Arg1);
176
177 //
178 // Interpreter assumes 64-bit return address is pushed on the stack.
179 // The x64 does not do this so pad the stack accordingly.
180 //
181 PushU64 (&VmContext, (UINT64) 0);
182 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
183
184 //
185 // For x64, this is where we say our return address is
186 //
187 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
188
189 //
190 // We need to keep track of where the EBC stack starts. This way, if the EBC
191 // accesses any stack variables above its initial stack setting, then we know
192 // it's accessing variables passed into it, which means the data is on the
193 // VM's stack.
194 // When we're called, on the stack (high to low) we have the parameters, the
195 // return address, then the saved ebp. Save the pointer to the return address.
196 // EBC code knows that's there, so should look above it for function parameters.
197 // The offset is the size of locals (VMContext + Addr + saved ebp).
198 // Note that the interpreter assumes there is a 16 bytes of return address on
199 // the stack too, so adjust accordingly.
200 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
201 //
202
203 //
204 // Begin executing the EBC code
205 //
206 EbcExecute (&VmContext);
207
208 //
209 // Return the value in R[7] unless there was an error
210 //
211 ReturnEBCStack(StackIndex);
212 return (UINT64) VmContext.R[7];
213 }
214
215
216 /**
217 Begin executing an EBC image. The address of the entry point is passed
218 in via a processor register, so we'll need to make a call to get the
219 value.
220
221 @param ImageHandle image handle for the EBC application we're executing
222 @param SystemTable standard system table passed into an driver's entry
223 point
224
225 @return The value returned by the EBC application we're going to run.
226
227 **/
228 STATIC
229 UINT64
230 ExecuteEbcImageEntryPoint (
231 IN EFI_HANDLE ImageHandle,
232 IN EFI_SYSTEM_TABLE *SystemTable
233 )
234 {
235 //
236 // Create a new VM context on the stack
237 //
238 VM_CONTEXT VmContext;
239 UINTN Addr;
240 EFI_STATUS Status;
241 UINTN StackIndex;
242
243 //
244 // Get the EBC entry point from the processor register. Make sure you don't
245 // call any functions before this or you could mess up the register the
246 // entry point is passed in.
247 //
248 Addr = EbcLLGetEbcEntryPoint ();
249
250 //
251 // Now clear out our context
252 //
253 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
254
255 //
256 // Save the image handle so we can track the thunks created for this image
257 //
258 VmContext.ImageHandle = ImageHandle;
259 VmContext.SystemTable = SystemTable;
260
261 //
262 // Set the VM instruction pointer to the correct location in memory.
263 //
264 VmContext.Ip = (VMIP) Addr;
265
266 //
267 // Initialize the stack pointer for the EBC. Get the current system stack
268 // pointer and adjust it down by the max needed for the interpreter.
269 //
270 Addr = EbcLLGetStackPointer ();
271
272 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
273 if (EFI_ERROR(Status)) {
274 return Status;
275 }
276 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
277 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
278 VmContext.HighStackBottom = (UINTN) VmContext.R[0];
279 VmContext.R[0] -= sizeof (UINTN);
280
281
282 //
283 // Put a magic value in the stack gap, then adjust down again
284 //
285 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
286 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
287
288 //
289 // Align the stack on a natural boundary
290 VmContext.R[0] &= ~(sizeof(UINTN) - 1);
291 //
292 VmContext.LowStackTop = (UINTN) VmContext.R[0];
293
294 //
295 // Simply copy the image handle and system table onto the EBC stack.
296 // Greatly simplifies things by not having to spill the args.
297 //
298 PushU64 (&VmContext, (UINT64) SystemTable);
299 PushU64 (&VmContext, (UINT64) ImageHandle);
300
301 //
302 // VM pushes 16-bytes for return address. Simulate that here.
303 //
304 PushU64 (&VmContext, (UINT64) 0);
305 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
306
307 //
308 // For x64, this is where we say our return address is
309 //
310 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
311
312 //
313 // Entry function needn't access high stack context, simply
314 // put the stack pointer here.
315 //
316
317 //
318 // Begin executing the EBC code
319 //
320 EbcExecute (&VmContext);
321
322 //
323 // Return the value in R[7] unless there was an error
324 //
325 ReturnEBCStack(StackIndex);
326 return (UINT64) VmContext.R[7];
327 }
328
329
330 /**
331 Create an IA32 thunk for the given EBC entry point.
332
333 @param ImageHandle Handle of image for which this thunk is being created
334 @param EbcEntryPoint Address of the EBC code that the thunk is to call
335 @param Thunk Returned thunk we create here
336
337 @return Standard EFI status.
338
339 **/
340 EFI_STATUS
341 EbcCreateThunks (
342 IN EFI_HANDLE ImageHandle,
343 IN VOID *EbcEntryPoint,
344 OUT VOID **Thunk,
345 IN UINT32 Flags
346 )
347 {
348 UINT8 *Ptr;
349 UINT8 *ThunkBase;
350 UINT32 I;
351 UINT64 Addr;
352 INT32 Size;
353 INT32 ThunkSize;
354
355 //
356 // Check alignment of pointer to EBC code
357 //
358 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
359 return EFI_INVALID_PARAMETER;
360 }
361
362 Size = EBC_THUNK_SIZE;
363 ThunkSize = Size;
364
365 Ptr = AllocatePool (Size);
366
367 if (Ptr == NULL) {
368 return EFI_OUT_OF_RESOURCES;
369 }
370 //
371 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
372 //
373 // Save the start address so we can add a pointer to it to a list later.
374 //
375 ThunkBase = Ptr;
376
377 //
378 // Give them the address of our buffer we're going to fix up
379 //
380 *Thunk = (VOID *) Ptr;
381
382 //
383 // Add a magic code here to help the VM recognize the thunk..
384 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
385 //
386 *Ptr = 0x48;
387 Ptr++;
388 Size--;
389 *Ptr = 0xB8;
390 Ptr++;
391 Size--;
392 Addr = (UINT64) 0xCA112EBCCA112EBCULL;
393 for (I = 0; I < sizeof (Addr); I++) {
394 *Ptr = (UINT8) (UINTN) Addr;
395 Addr >>= 8;
396 Ptr++;
397 Size--;
398 }
399
400 //
401 // Add code bytes to load up a processor register with the EBC entry point.
402 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
403 // The first 8 bytes of the thunk entry is the address of the EBC
404 // entry point.
405 //
406 *Ptr = 0x48;
407 Ptr++;
408 Size--;
409 *Ptr = 0xB8;
410 Ptr++;
411 Size--;
412 Addr = (UINT64) EbcEntryPoint;
413 for (I = 0; I < sizeof (Addr); I++) {
414 *Ptr = (UINT8) (UINTN) Addr;
415 Addr >>= 8;
416 Ptr++;
417 Size--;
418 }
419
420 //
421 // Stick in a load of ecx with the address of appropriate VM function.
422 // Using r11 because it's a volatile register and won't be used in this
423 // point.
424 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
425 //
426 if (Flags & FLAG_THUNK_ENTRY_POINT) {
427 Addr = (UINTN) ExecuteEbcImageEntryPoint;
428 } else {
429 Addr = (UINTN) EbcInterpret;
430 }
431
432 //
433 // mov r11 Addr => 0x49 0xBB
434 //
435 *Ptr = 0x49;
436 Ptr++;
437 Size--;
438 *Ptr = 0xBB;
439 Ptr++;
440 Size--;
441 for (I = 0; I < sizeof (Addr); I++) {
442 *Ptr = (UINT8) Addr;
443 Addr >>= 8;
444 Ptr++;
445 Size--;
446 }
447 //
448 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
449 //
450 *Ptr = 0x41;
451 Ptr++;
452 Size--;
453 *Ptr = 0xFF;
454 Ptr++;
455 Size--;
456 *Ptr = 0xE3;
457 Size--;
458
459 //
460 // Double check that our defined size is ok (application error)
461 //
462 if (Size < 0) {
463 ASSERT (FALSE);
464 return EFI_BUFFER_TOO_SMALL;
465 }
466 //
467 // Add the thunk to the list for this image. Do this last since the add
468 // function flushes the cache for us.
469 //
470 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
471
472 return EFI_SUCCESS;
473 }
474
475
476 /**
477 This function is called to execute an EBC CALLEX instruction.
478 The function check the callee's content to see whether it is common native
479 code or a thunk to another piece of EBC code.
480 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
481 otherwise, set the VM->IP to target EBC code directly to avoid another VM
482 be startup which cost time and stack space.
483
484 @param VmPtr Pointer to a VM context.
485 @param FuncAddr Callee's address
486 @param NewStackPointer New stack pointer after the call
487 @param FramePtr New frame pointer after the call
488 @param Size The size of call instruction
489
490 @return None.
491
492 **/
493 VOID
494 EbcLLCALLEX (
495 IN VM_CONTEXT *VmPtr,
496 IN UINTN FuncAddr,
497 IN UINTN NewStackPointer,
498 IN VOID *FramePtr,
499 IN UINT8 Size
500 )
501 {
502 UINTN IsThunk;
503 UINTN TargetEbcAddr;
504
505 IsThunk = 1;
506 TargetEbcAddr = 0;
507
508 //
509 // Processor specific code to check whether the callee is a thunk to EBC.
510 //
511 if (*((UINT8 *)FuncAddr) != 0x48) {
512 IsThunk = 0;
513 goto Action;
514 }
515 if (*((UINT8 *)FuncAddr + 1) != 0xB8) {
516 IsThunk = 0;
517 goto Action;
518 }
519 if (*((UINT8 *)FuncAddr + 2) != 0xBC) {
520 IsThunk = 0;
521 goto Action;
522 }
523 if (*((UINT8 *)FuncAddr + 3) != 0x2E) {
524 IsThunk = 0;
525 goto Action;
526 }
527 if (*((UINT8 *)FuncAddr + 4) != 0x11) {
528 IsThunk = 0;
529 goto Action;
530 }
531 if (*((UINT8 *)FuncAddr + 5) != 0xCA) {
532 IsThunk = 0;
533 goto Action;
534 }
535 if (*((UINT8 *)FuncAddr + 6) != 0xBC) {
536 IsThunk = 0;
537 goto Action;
538 }
539 if (*((UINT8 *)FuncAddr + 7) != 0x2E) {
540 IsThunk = 0;
541 goto Action;
542 }
543 if (*((UINT8 *)FuncAddr + 8) != 0x11) {
544 IsThunk = 0;
545 goto Action;
546 }
547 if (*((UINT8 *)FuncAddr + 9) != 0xCA) {
548 IsThunk = 0;
549 goto Action;
550 }
551 if (*((UINT8 *)FuncAddr + 10) != 0x48) {
552 IsThunk = 0;
553 goto Action;
554 }
555 if (*((UINT8 *)FuncAddr + 11) != 0xB8) {
556 IsThunk = 0;
557 goto Action;
558 }
559
560 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + 12, 8);
561
562 Action:
563 if (IsThunk == 1){
564 //
565 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
566 // put our return address and frame pointer on the VM stack.
567 // Then set the VM's IP to new EBC code.
568 //
569 VmPtr->R[0] -= 8;
570 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
571 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
572 VmPtr->R[0] -= 8;
573 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));
574
575 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
576 } else {
577 //
578 // The callee is not a thunk to EBC, call native code.
579 //
580 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
581
582 //
583 // Get return value and advance the IP.
584 //
585 VmPtr->R[7] = EbcLLGetReturnValue ();
586 VmPtr->Ip += Size;
587 }
588 }
589