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