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