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