]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/Ebc/Dxe/x64/EbcSupport.c
Perfect the msa of the following modules, DiskIo, Partition, English and Ebc.
[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) 0x1234567887654321ULL);
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) 0x1234567887654321ULL);
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
335 //
336 // Check alignment of pointer to EBC code
337 //
338 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
339 return EFI_INVALID_PARAMETER;
340 }
341
342 Size = EBC_THUNK_SIZE;
343 ThunkSize = Size;
344
345 Ptr = AllocatePool (Size);
346
347 if (Ptr == NULL) {
348 return EFI_OUT_OF_RESOURCES;
349 }
350 //
351 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
352 //
353 // Save the start address so we can add a pointer to it to a list later.
354 //
355 ThunkBase = Ptr;
356
357 //
358 // Give them the address of our buffer we're going to fix up
359 //
360 *Thunk = (VOID *) Ptr;
361
362 //
363 // Add a magic code here to help the VM recognize the thunk..
364 // mov rax, ca112ebccall2ebch => 48 B8 BC 2E 11 CA BC 2E 11 CA
365 //
366 *Ptr = 0x48;
367 Ptr++;
368 Size--;
369 *Ptr = 0xB8;
370 Ptr++;
371 Size--;
372 Addr = (UINT64) 0xCA112EBCCA112EBCULL;
373 for (I = 0; I < sizeof (Addr); I++) {
374 *Ptr = (UINT8) (UINTN) Addr;
375 Addr >>= 8;
376 Ptr++;
377 Size--;
378 }
379
380 //
381 // Add code bytes to load up a processor register with the EBC entry point.
382 // mov rax, 123456789abcdef0h => 48 B8 F0 DE BC 9A 78 56 34 12
383 // The first 8 bytes of the thunk entry is the address of the EBC
384 // entry point.
385 //
386 *Ptr = 0x48;
387 Ptr++;
388 Size--;
389 *Ptr = 0xB8;
390 Ptr++;
391 Size--;
392 Addr = (UINT64) EbcEntryPoint;
393 for (I = 0; I < sizeof (Addr); I++) {
394 *Ptr = (UINT8) (UINTN) Addr;
395 Addr >>= 8;
396 Ptr++;
397 Size--;
398 }
399
400 //
401 // Stick in a load of ecx with the address of appropriate VM function.
402 // Using r11 because it's a volatile register and won't be used in this
403 // point.
404 // mov r11 123456789abcdef0h => 49 BB F0 DE BC 9A 78 56 34 12
405 //
406 if (Flags & FLAG_THUNK_ENTRY_POINT) {
407 Addr = (UINTN) ExecuteEbcImageEntryPoint;
408 } else {
409 Addr = (UINTN) EbcInterpret;
410 }
411
412 //
413 // mov r11 Addr => 0x49 0xBB
414 //
415 *Ptr = 0x49;
416 Ptr++;
417 Size--;
418 *Ptr = 0xBB;
419 Ptr++;
420 Size--;
421 for (I = 0; I < sizeof (Addr); I++) {
422 *Ptr = (UINT8) Addr;
423 Addr >>= 8;
424 Ptr++;
425 Size--;
426 }
427 //
428 // Stick in jump opcode bytes for jmp r11 => 0x41 0xFF 0xE3
429 //
430 *Ptr = 0x41;
431 Ptr++;
432 Size--;
433 *Ptr = 0xFF;
434 Ptr++;
435 Size--;
436 *Ptr = 0xE3;
437 Size--;
438
439 //
440 // Double check that our defined size is ok (application error)
441 //
442 if (Size < 0) {
443 ASSERT (FALSE);
444 return EFI_BUFFER_TOO_SMALL;
445 }
446 //
447 // Add the thunk to the list for this image. Do this last since the add
448 // function flushes the cache for us.
449 //
450 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
451
452 return EFI_SUCCESS;
453 }
454
455 VOID
456 EbcLLCALLEX (
457 IN VM_CONTEXT *VmPtr,
458 IN UINTN FuncAddr,
459 IN UINTN NewStackPointer,
460 IN VOID *FramePtr,
461 IN UINT8 Size
462 )
463 /*++
464
465 Routine Description:
466
467 This function is called to execute an EBC CALLEX instruction.
468 The function check the callee's content to see whether it is common native
469 code or a thunk to another piece of EBC code.
470 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
471 otherwise, set the VM->IP to target EBC code directly to avoid another VM
472 be startup which cost time and stack space.
473
474 Arguments:
475
476 VmPtr - Pointer to a VM context.
477 FuncAddr - Callee's address
478 NewStackPointer - New stack pointer after the call
479 FramePtr - New frame pointer after the call
480 Size - The size of call instruction
481
482 Returns:
483
484 None.
485
486 --*/
487 {
488 UINTN IsThunk;
489 UINTN TargetEbcAddr;
490
491 IsThunk = 1;
492 TargetEbcAddr = 0;
493
494 //
495 // Processor specific code to check whether the callee is a thunk to EBC.
496 //
497 if (*((UINT8 *)FuncAddr) != 0x48) {
498 IsThunk = 0;
499 goto Action;
500 }
501 if (*((UINT8 *)FuncAddr + 1) != 0xB8) {
502 IsThunk = 0;
503 goto Action;
504 }
505 if (*((UINT8 *)FuncAddr + 2) != 0xBC) {
506 IsThunk = 0;
507 goto Action;
508 }
509 if (*((UINT8 *)FuncAddr + 3) != 0x2E) {
510 IsThunk = 0;
511 goto Action;
512 }
513 if (*((UINT8 *)FuncAddr + 4) != 0x11) {
514 IsThunk = 0;
515 goto Action;
516 }
517 if (*((UINT8 *)FuncAddr + 5) != 0xCA) {
518 IsThunk = 0;
519 goto Action;
520 }
521 if (*((UINT8 *)FuncAddr + 6) != 0xBC) {
522 IsThunk = 0;
523 goto Action;
524 }
525 if (*((UINT8 *)FuncAddr + 7) != 0x2E) {
526 IsThunk = 0;
527 goto Action;
528 }
529 if (*((UINT8 *)FuncAddr + 8) != 0x11) {
530 IsThunk = 0;
531 goto Action;
532 }
533 if (*((UINT8 *)FuncAddr + 9) != 0xCA) {
534 IsThunk = 0;
535 goto Action;
536 }
537 if (*((UINT8 *)FuncAddr + 10) != 0x48) {
538 IsThunk = 0;
539 goto Action;
540 }
541 if (*((UINT8 *)FuncAddr + 11) != 0xB8) {
542 IsThunk = 0;
543 goto Action;
544 }
545
546 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + 12, 8);
547
548 Action:
549 if (IsThunk == 1){
550 //
551 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
552 // put our return address and frame pointer on the VM stack.
553 // Then set the VM's IP to new EBC code.
554 //
555 VmPtr->R[0] -= 8;
556 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
557 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
558 VmPtr->R[0] -= 8;
559 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));
560
561 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
562 } else {
563 //
564 // The callee is not a thunk to EBC, call native code.
565 //
566 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
567
568 //
569 // Get return value and advance the IP.
570 //
571 VmPtr->R[7] = EbcLLGetReturnValue ();
572 VmPtr->Ip += Size;
573 }
574 }
575