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