]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/EbcDxe/Ipf/EbcSupport.c
apply for doxgen format.
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / Ipf / EbcSupport.c
CommitLineData
fb0b259e 1/** @file\r
53c71d09 2 This module contains EBC support routines that are customized based on\r
3 the target processor.\r
4\r
fb0b259e 5Copyright (c) 2006, Intel Corporation\r
6All rights reserved. This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
53c71d09 15\r
16#include "EbcInt.h"\r
17#include "EbcExecute.h"\r
18#include "EbcSupport.h"\r
19\r
20STATIC\r
21EFI_STATUS\r
22WriteBundle (\r
23 IN VOID *MemPtr,\r
24 IN UINT8 Template,\r
25 IN UINT64 Slot0,\r
26 IN UINT64 Slot1,\r
27 IN UINT64 Slot2\r
28 );\r
29\r
30STATIC\r
31VOID\r
32PushU64 (\r
33 VM_CONTEXT *VmPtr,\r
34 UINT64 Arg\r
35 )\r
36{\r
37 //\r
38 // Advance the VM stack down, and then copy the argument to the stack.\r
39 // Hope it's aligned.\r
40 //\r
41 VmPtr->R[0] -= sizeof (UINT64);\r
42 *(UINT64 *) VmPtr->R[0] = Arg;\r
43}\r
44\r
45STATIC\r
46UINT64\r
47EbcInterpret (\r
48 UINT64 Arg1,\r
49 ...\r
50 )\r
51{\r
52 //\r
53 // Create a new VM context on the stack\r
54 //\r
55 VM_CONTEXT VmContext;\r
56 UINTN Addr;\r
57 EFI_STATUS Status;\r
58 UINTN StackIndex;\r
59 VA_LIST List;\r
60 UINT64 Arg2;\r
61 UINT64 Arg3;\r
62 UINT64 Arg4;\r
63 UINT64 Arg5;\r
64 UINT64 Arg6;\r
65 UINT64 Arg7;\r
66 UINT64 Arg8;\r
67 UINT64 Arg9;\r
68 UINT64 Arg10;\r
69 UINT64 Arg11;\r
70 UINT64 Arg12;\r
71 UINT64 Arg13;\r
72 UINT64 Arg14;\r
73 UINT64 Arg15;\r
74 UINT64 Arg16;\r
75 //\r
76 // Get the EBC entry point from the processor register. Make sure you don't\r
77 // call any functions before this or you could mess up the register the\r
78 // entry point is passed in.\r
79 //\r
80 Addr = EbcLLGetEbcEntryPoint ();\r
81 //\r
82 // Need the args off the stack.\r
83 //\r
84 VA_START (List, Arg1);\r
85 Arg2 = VA_ARG (List, UINT64);\r
86 Arg3 = VA_ARG (List, UINT64);\r
87 Arg4 = VA_ARG (List, UINT64);\r
88 Arg5 = VA_ARG (List, UINT64);\r
89 Arg6 = VA_ARG (List, UINT64);\r
90 Arg7 = VA_ARG (List, UINT64);\r
91 Arg8 = VA_ARG (List, UINT64);\r
92 Arg9 = VA_ARG (List, UINT64);\r
93 Arg10 = VA_ARG (List, UINT64);\r
94 Arg11 = VA_ARG (List, UINT64);\r
95 Arg12 = VA_ARG (List, UINT64);\r
96 Arg13 = VA_ARG (List, UINT64);\r
97 Arg14 = VA_ARG (List, UINT64);\r
98 Arg15 = VA_ARG (List, UINT64);\r
99 Arg16 = VA_ARG (List, UINT64);\r
100 //\r
101 // Now clear out our context\r
102 //\r
103 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));\r
104 //\r
105 // Set the VM instruction pointer to the correct location in memory.\r
106 //\r
107 VmContext.Ip = (VMIP) Addr;\r
108 //\r
109 // Initialize the stack pointer for the EBC. Get the current system stack\r
110 // pointer and adjust it down by the max needed for the interpreter.\r
111 //\r
112 //\r
113 // NOTE: Eventually we should have the interpreter allocate memory\r
114 // for stack space which it will use during its execution. This\r
115 // would likely improve performance because the interpreter would\r
116 // no longer be required to test each memory access and adjust\r
117 // those reading from the stack gap.\r
118 //\r
119 // For IPF, the stack looks like (assuming 10 args passed)\r
120 // arg10\r
121 // arg9 (Bottom of high stack)\r
122 // [ stack gap for interpreter execution ]\r
123 // [ magic value for detection of stack corruption ]\r
124 // arg8 (Top of low stack)\r
125 // arg7....\r
126 // arg1\r
127 // [ 64-bit return address ]\r
128 // [ ebc stack ]\r
129 // If the EBC accesses memory in the stack gap, then we assume that it's\r
130 // actually trying to access args9 and greater. Therefore we need to\r
131 // adjust memory accesses in this region to point above the stack gap.\r
132 //\r
133 //\r
134 // Now adjust the EBC stack pointer down to leave a gap for interpreter\r
135 // execution. Then stuff a magic value there.\r
136 //\r
fb0b259e 137\r
53c71d09 138 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);\r
139 if (EFI_ERROR(Status)) {\r
140 return Status;\r
141 }\r
142 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
143 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);\r
144 VmContext.HighStackBottom = (UINTN) VmContext.R[0];\r
145 VmContext.R[0] -= sizeof (UINTN);\r
146\r
fb0b259e 147\r
53c71d09 148 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);\r
149 VmContext.StackMagicPtr = (UINTN *) VmContext.R[0];\r
150 VmContext.LowStackTop = (UINTN) VmContext.R[0];\r
151 //\r
152 // Push the EBC arguments on the stack. Does not matter that they may not\r
153 // all be valid.\r
154 //\r
155 PushU64 (&VmContext, Arg16);\r
156 PushU64 (&VmContext, Arg15);\r
157 PushU64 (&VmContext, Arg14);\r
158 PushU64 (&VmContext, Arg13);\r
159 PushU64 (&VmContext, Arg12);\r
160 PushU64 (&VmContext, Arg11);\r
161 PushU64 (&VmContext, Arg10);\r
162 PushU64 (&VmContext, Arg9);\r
163 PushU64 (&VmContext, Arg8);\r
164 PushU64 (&VmContext, Arg7);\r
165 PushU64 (&VmContext, Arg6);\r
166 PushU64 (&VmContext, Arg5);\r
167 PushU64 (&VmContext, Arg4);\r
168 PushU64 (&VmContext, Arg3);\r
169 PushU64 (&VmContext, Arg2);\r
170 PushU64 (&VmContext, Arg1);\r
171 //\r
172 // Push a bogus return address on the EBC stack because the\r
173 // interpreter expects one there. For stack alignment purposes on IPF,\r
174 // EBC return addresses are always 16 bytes. Push a bogus value as well.\r
175 //\r
176 PushU64 (&VmContext, 0);\r
177 PushU64 (&VmContext, 0xDEADBEEFDEADBEEF);\r
178 VmContext.StackRetAddr = (UINT64) VmContext.R[0];\r
179 //\r
180 // Begin executing the EBC code\r
181 //\r
182 EbcExecute (&VmContext);\r
183 //\r
184 // Return the value in R[7] unless there was an error\r
185 //\r
186 ReturnEBCStack(StackIndex);\r
187 return (UINT64) VmContext.R[7];\r
188}\r
189\r
53c71d09 190\r
fb0b259e 191/**\r
53c71d09 192 IPF implementation.\r
53c71d09 193 Begin executing an EBC image. The address of the entry point is passed\r
194 in via a processor register, so we'll need to make a call to get the\r
195 value.\r
53c71d09 196\r
fb0b259e 197 @param ImageHandle image handle for the EBC application we're\r
198 executing\r
199 @param SystemTable standard system table passed into an driver's\r
200 entry point\r
53c71d09 201\r
fb0b259e 202 @return The value returned by the EBC application we're going to run.\r
53c71d09 203\r
fb0b259e 204**/\r
205STATIC\r
206UINT64\r
207ExecuteEbcImageEntryPoint (\r
208 IN EFI_HANDLE ImageHandle,\r
209 IN EFI_SYSTEM_TABLE *SystemTable\r
210 )\r
53c71d09 211{\r
212 //\r
213 // Create a new VM context on the stack\r
214 //\r
215 VM_CONTEXT VmContext;\r
216 UINTN Addr;\r
217 EFI_STATUS Status;\r
218 UINTN StackIndex;\r
219\r
220 //\r
221 // Get the EBC entry point from the processor register. Make sure you don't\r
222 // call any functions before this or you could mess up the register the\r
223 // entry point is passed in.\r
224 //\r
225 Addr = EbcLLGetEbcEntryPoint ();\r
226\r
227 //\r
228 // Now clear out our context\r
229 //\r
230 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));\r
231\r
232 //\r
233 // Save the image handle so we can track the thunks created for this image\r
234 //\r
235 VmContext.ImageHandle = ImageHandle;\r
236 VmContext.SystemTable = SystemTable;\r
237\r
238 //\r
239 // Set the VM instruction pointer to the correct location in memory.\r
240 //\r
241 VmContext.Ip = (VMIP) Addr;\r
242\r
243 //\r
244 // Get the stack pointer. This is the bottom of the upper stack.\r
245 //\r
246 Addr = EbcLLGetStackPointer ();\r
fb0b259e 247\r
53c71d09 248 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);\r
249 if (EFI_ERROR(Status)) {\r
250 return Status;\r
251 }\r
252 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
253 VmContext.R[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);\r
254 VmContext.HighStackBottom = (UINTN) VmContext.R[0];\r
255 VmContext.R[0] -= sizeof (UINTN);\r
256\r
fb0b259e 257\r
53c71d09 258 //\r
259 // Allocate stack space for the interpreter. Then put a magic value\r
260 // at the bottom so we can detect stack corruption.\r
261 //\r
262 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);\r
263 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];\r
264\r
265 //\r
266 // When we thunk to external native code, we copy the last 8 qwords from\r
267 // the EBC stack into the processor registers, and adjust the stack pointer\r
268 // up. If the caller is not passing 8 parameters, then we've moved the\r
269 // stack pointer up into the stack gap. If this happens, then the caller\r
270 // can mess up the stack gap contents (in particular our magic value).\r
271 // Therefore, leave another gap below the magic value. Pick 10 qwords down,\r
272 // just as a starting point.\r
273 //\r
274 VmContext.R[0] -= 10 * sizeof (UINT64);\r
275\r
276 //\r
277 // Align the stack pointer such that after pushing the system table,\r
278 // image handle, and return address on the stack, it's aligned on a 16-byte\r
279 // boundary as required for IPF.\r
280 //\r
281 VmContext.R[0] &= (INT64)~0x0f;\r
282 VmContext.LowStackTop = (UINTN) VmContext.R[0];\r
283 //\r
284 // Simply copy the image handle and system table onto the EBC stack.\r
285 // Greatly simplifies things by not having to spill the args\r
286 //\r
287 PushU64 (&VmContext, (UINT64) SystemTable);\r
288 PushU64 (&VmContext, (UINT64) ImageHandle);\r
289\r
290 //\r
291 // Interpreter assumes 64-bit return address is pushed on the stack.\r
292 // IPF does not do this so pad the stack accordingly. Also, a\r
293 // "return address" is 16 bytes as required for IPF stack alignments.\r
294 //\r
295 PushU64 (&VmContext, (UINT64) 0);\r
296 PushU64 (&VmContext, (UINT64) 0x1234567887654321);\r
297 VmContext.StackRetAddr = (UINT64) VmContext.R[0];\r
298\r
299 //\r
300 // Begin executing the EBC code\r
301 //\r
302 EbcExecute (&VmContext);\r
303\r
304 //\r
305 // Return the value in R[7] unless there was an error\r
306 //\r
307 ReturnEBCStack(StackIndex);\r
308 return (UINT64) VmContext.R[7];\r
309}\r
310\r
fb0b259e 311\r
312/**\r
313 Create thunks for an EBC image entry point, or an EBC protocol service.\r
314\r
315 @param ImageHandle Image handle for the EBC image. If not null, then\r
316 we're creating a thunk for an image entry point.\r
317 @param EbcEntryPoint Address of the EBC code that the thunk is to call\r
318 @param Thunk Returned thunk we create here\r
319 @param Flags Flags indicating options for creating the thunk\r
320\r
321 @return Standard EFI status.\r
322\r
323**/\r
53c71d09 324EFI_STATUS\r
325EbcCreateThunks (\r
326 IN EFI_HANDLE ImageHandle,\r
327 IN VOID *EbcEntryPoint,\r
328 OUT VOID **Thunk,\r
329 IN UINT32 Flags\r
330 )\r
53c71d09 331{\r
332 UINT8 *Ptr;\r
333 UINT8 *ThunkBase;\r
334 UINT64 Addr;\r
335 UINT64 Code[3]; // Code in a bundle\r
336 UINT64 RegNum; // register number for MOVL\r
337 UINT64 I; // bits of MOVL immediate data\r
338 UINT64 Ic; // bits of MOVL immediate data\r
339 UINT64 Imm5c; // bits of MOVL immediate data\r
340 UINT64 Imm9d; // bits of MOVL immediate data\r
341 UINT64 Imm7b; // bits of MOVL immediate data\r
342 UINT64 Br; // branch register for loading and jumping\r
343 UINT64 *Data64Ptr;\r
344 UINT32 ThunkSize;\r
345 UINT32 Size;\r
346\r
347 //\r
348 // Check alignment of pointer to EBC code, which must always be aligned\r
349 // on a 2-byte boundary.\r
350 //\r
351 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {\r
352 return EFI_INVALID_PARAMETER;\r
353 }\r
354 //\r
355 // Allocate memory for the thunk. Make the (most likely incorrect) assumption\r
356 // that the returned buffer is not aligned, so round up to the next\r
357 // alignment size.\r
358 //\r
359 Size = EBC_THUNK_SIZE + EBC_THUNK_ALIGNMENT - 1;\r
360 ThunkSize = Size;\r
361 Ptr = AllocatePool (Size);\r
362\r
363 if (Ptr == NULL) {\r
364 return EFI_OUT_OF_RESOURCES;\r
365 }\r
366 //\r
367 // Save the start address of the buffer.\r
368 //\r
369 ThunkBase = Ptr;\r
370\r
371 //\r
372 // Make sure it's aligned for code execution. If not, then\r
373 // round up.\r
374 //\r
375 if ((UINT32) (UINTN) Ptr & (EBC_THUNK_ALIGNMENT - 1)) {\r
376 Ptr = (UINT8 *) (((UINTN) Ptr + (EBC_THUNK_ALIGNMENT - 1)) &~ (UINT64) (EBC_THUNK_ALIGNMENT - 1));\r
377 }\r
378 //\r
379 // Return the pointer to the thunk to the caller to user as the\r
380 // image entry point.\r
381 //\r
382 *Thunk = (VOID *) Ptr;\r
383\r
384 //\r
385 // Clear out the thunk entry\r
386 // ZeroMem(Ptr, Size);\r
387 //\r
388 // For IPF, when you do a call via a function pointer, the function pointer\r
389 // actually points to a function descriptor which consists of a 64-bit\r
390 // address of the function, followed by a 64-bit gp for the function being\r
391 // called. See the the Software Conventions and Runtime Architecture Guide\r
392 // for details.\r
393 // So first off in our thunk, create a descriptor for our actual thunk code.\r
394 // This means we need to create a pointer to the thunk code (which follows\r
395 // the descriptor we're going to create), followed by the gp of the Vm\r
396 // interpret function we're going to eventually execute.\r
397 //\r
398 Data64Ptr = (UINT64 *) Ptr;\r
399\r
400 //\r
401 // Write the function's entry point (which is our thunk code that follows\r
402 // this descriptor we're creating).\r
403 //\r
404 *Data64Ptr = (UINT64) (Data64Ptr + 2);\r
405 //\r
406 // Get the gp from the descriptor for EbcInterpret and stuff it in our thunk\r
407 // descriptor.\r
408 //\r
409 *(Data64Ptr + 1) = *(UINT64 *) ((UINT64 *) (UINTN) EbcInterpret + 1);\r
410 //\r
411 // Advance our thunk data pointer past the descriptor. Since the\r
412 // descriptor consists of 16 bytes, the pointer is still aligned for\r
413 // IPF code execution (on 16-byte boundary).\r
414 //\r
415 Ptr += sizeof (UINT64) * 2;\r
416\r
417 //\r
418 // *************************** MAGIC BUNDLE ********************************\r
419 //\r
420 // Write magic code bundle for: movl r8 = 0xca112ebcca112ebc to help the VM\r
421 // to recognize it is a thunk.\r
422 //\r
423 Addr = (UINT64) 0xCA112EBCCA112EBC;\r
424\r
425 //\r
426 // Now generate the code bytes. First is nop.m 0x0\r
427 //\r
428 Code[0] = OPCODE_NOP;\r
429\r
430 //\r
431 // Next is simply Addr[62:22] (41 bits) of the address\r
432 //\r
433 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;\r
434\r
435 //\r
436 // Extract bits from the address for insertion into the instruction\r
437 // i = Addr[63:63]\r
438 //\r
439 I = RShiftU64 (Addr, 63) & 0x01;\r
440 //\r
441 // ic = Addr[21:21]\r
442 //\r
443 Ic = RShiftU64 (Addr, 21) & 0x01;\r
444 //\r
445 // imm5c = Addr[20:16] for 5 bits\r
446 //\r
447 Imm5c = RShiftU64 (Addr, 16) & 0x1F;\r
448 //\r
449 // imm9d = Addr[15:7] for 9 bits\r
450 //\r
451 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
452 //\r
453 // imm7b = Addr[6:0] for 7 bits\r
454 //\r
455 Imm7b = Addr & 0x7F;\r
456\r
457 //\r
458 // The EBC entry point will be put into r8, so r8 can be used here\r
459 // temporary. R8 is general register and is auto-serialized.\r
460 //\r
461 RegNum = 8;\r
462\r
463 //\r
464 // Next is jumbled data, including opcode and rest of address\r
465 //\r
466 Code[2] = LShiftU64 (Imm7b, 13);\r
467 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
468 Code[2] = Code[2] | LShiftU64 (Ic, 21);\r
469 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);\r
470 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);\r
471 Code[2] = Code[2] | LShiftU64 (I, 36);\r
472 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);\r
473 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);\r
474\r
475 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);\r
476\r
477 //\r
478 // *************************** FIRST BUNDLE ********************************\r
479 //\r
480 // Write code bundle for: movl r8 = EBC_ENTRY_POINT so we pass\r
481 // the ebc entry point in to the interpreter function via a processor\r
482 // register.\r
483 // Note -- we could easily change this to pass in a pointer to a structure\r
484 // that contained, among other things, the EBC image's entry point. But\r
485 // for now pass it directly.\r
486 //\r
487 Ptr += 16;\r
488 Addr = (UINT64) EbcEntryPoint;\r
489\r
490 //\r
491 // Now generate the code bytes. First is nop.m 0x0\r
492 //\r
493 Code[0] = OPCODE_NOP;\r
494\r
495 //\r
496 // Next is simply Addr[62:22] (41 bits) of the address\r
497 //\r
498 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;\r
499\r
500 //\r
501 // Extract bits from the address for insertion into the instruction\r
502 // i = Addr[63:63]\r
503 //\r
504 I = RShiftU64 (Addr, 63) & 0x01;\r
505 //\r
506 // ic = Addr[21:21]\r
507 //\r
508 Ic = RShiftU64 (Addr, 21) & 0x01;\r
509 //\r
510 // imm5c = Addr[20:16] for 5 bits\r
511 //\r
512 Imm5c = RShiftU64 (Addr, 16) & 0x1F;\r
513 //\r
514 // imm9d = Addr[15:7] for 9 bits\r
515 //\r
516 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
517 //\r
518 // imm7b = Addr[6:0] for 7 bits\r
519 //\r
520 Imm7b = Addr & 0x7F;\r
521\r
522 //\r
523 // Put the EBC entry point in r8, which is the location of the return value\r
524 // for functions.\r
525 //\r
526 RegNum = 8;\r
527\r
528 //\r
529 // Next is jumbled data, including opcode and rest of address\r
530 //\r
531 Code[2] = LShiftU64 (Imm7b, 13);\r
532 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
533 Code[2] = Code[2] | LShiftU64 (Ic, 21);\r
534 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);\r
535 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);\r
536 Code[2] = Code[2] | LShiftU64 (I, 36);\r
537 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);\r
538 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);\r
539\r
540 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);\r
541\r
542 //\r
543 // *************************** NEXT BUNDLE *********************************\r
544 //\r
545 // Write code bundle for:\r
546 // movl rx = offset_of(EbcInterpret|ExecuteEbcImageEntryPoint)\r
547 //\r
548 // Advance pointer to next bundle, then compute the offset from this bundle\r
549 // to the address of the entry point of the interpreter.\r
550 //\r
551 Ptr += 16;\r
552 if (Flags & FLAG_THUNK_ENTRY_POINT) {\r
553 Addr = (UINT64) ExecuteEbcImageEntryPoint;\r
554 } else {\r
555 Addr = (UINT64) EbcInterpret;\r
556 }\r
557 //\r
558 // Indirection on Itanium-based systems\r
559 //\r
560 Addr = *(UINT64 *) Addr;\r
561\r
562 //\r
563 // Now write the code to load the offset into a register\r
564 //\r
565 Code[0] = OPCODE_NOP;\r
566\r
567 //\r
568 // Next is simply Addr[62:22] (41 bits) of the address\r
569 //\r
570 Code[1] = RShiftU64 (Addr, 22) & 0x1ffffffffff;\r
571\r
572 //\r
573 // Extract bits from the address for insertion into the instruction\r
574 // i = Addr[63:63]\r
575 //\r
576 I = RShiftU64 (Addr, 63) & 0x01;\r
577 //\r
578 // ic = Addr[21:21]\r
579 //\r
580 Ic = RShiftU64 (Addr, 21) & 0x01;\r
581 //\r
582 // imm5c = Addr[20:16] for 5 bits\r
583 //\r
584 Imm5c = RShiftU64 (Addr, 16) & 0x1F;\r
585 //\r
586 // imm9d = Addr[15:7] for 9 bits\r
587 //\r
588 Imm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
589 //\r
590 // imm7b = Addr[6:0] for 7 bits\r
591 //\r
592 Imm7b = Addr & 0x7F;\r
593\r
594 //\r
595 // Put it in r31, a scratch register\r
596 //\r
597 RegNum = 31;\r
598\r
599 //\r
600 // Next is jumbled data, including opcode and rest of address\r
601 //\r
602 Code[2] = LShiftU64(Imm7b, 13);\r
603 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
604 Code[2] = Code[2] | LShiftU64 (Ic, 21);\r
605 Code[2] = Code[2] | LShiftU64 (Imm5c, 22);\r
606 Code[2] = Code[2] | LShiftU64 (Imm9d, 27);\r
607 Code[2] = Code[2] | LShiftU64 (I, 36);\r
608 Code[2] = Code[2] | LShiftU64 ((UINT64)MOVL_OPCODE, 37);\r
609 Code[2] = Code[2] | LShiftU64 ((RegNum & 0x7F), 6);\r
610\r
611 WriteBundle ((VOID *) Ptr, 0x05, Code[0], Code[1], Code[2]);\r
612\r
613 //\r
614 // *************************** NEXT BUNDLE *********************************\r
615 //\r
616 // Load branch register with EbcInterpret() function offset from the bundle\r
617 // address: mov b6 = RegNum\r
618 //\r
619 // See volume 3 page 4-29 of the Arch. Software Developer's Manual.\r
620 //\r
621 // Advance pointer to next bundle\r
622 //\r
623 Ptr += 16;\r
624 Code[0] = OPCODE_NOP;\r
625 Code[1] = OPCODE_NOP;\r
626 Code[2] = OPCODE_MOV_BX_RX;\r
627\r
628 //\r
629 // Pick a branch register to use. Then fill in the bits for the branch\r
630 // register and user register (same user register as previous bundle).\r
631 //\r
632 Br = 6;\r
633 Code[2] |= LShiftU64 (Br, 6);\r
634 Code[2] |= LShiftU64 (RegNum, 13);\r
635 WriteBundle ((VOID *) Ptr, 0x0d, Code[0], Code[1], Code[2]);\r
636\r
637 //\r
638 // *************************** NEXT BUNDLE *********************************\r
639 //\r
640 // Now do the branch: (p0) br.cond.sptk.few b6\r
641 //\r
642 // Advance pointer to next bundle.\r
643 // Fill in the bits for the branch register (same reg as previous bundle)\r
644 //\r
645 Ptr += 16;\r
646 Code[0] = OPCODE_NOP;\r
647 Code[1] = OPCODE_NOP;\r
648 Code[2] = OPCODE_BR_COND_SPTK_FEW;\r
649 Code[2] |= LShiftU64 (Br, 13);\r
650 WriteBundle ((VOID *) Ptr, 0x1d, Code[0], Code[1], Code[2]);\r
651\r
652 //\r
653 // Add the thunk to our list of allocated thunks so we can do some cleanup\r
654 // when the image is unloaded. Do this last since the Add function flushes\r
655 // the instruction cache for us.\r
656 //\r
657 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);\r
658\r
659 //\r
660 // Done\r
661 //\r
662 return EFI_SUCCESS;\r
663}\r
664\r
fb0b259e 665\r
666/**\r
667 Given raw bytes of Itanium based code, format them into a bundle and\r
668 write them out.\r
669\r
670 @param MemPtr pointer to memory location to write the bundles to\r
671 @param Template 5-bit template\r
672 @param Slot0-2 instruction slot data for the bundle\r
673\r
674 @retval EFI_INVALID_PARAMETER Pointer is not aligned\r
675 @retval No more than 5 bits in template\r
676 @retval More than 41 bits used in code\r
677 @retval EFI_SUCCESS All data is written.\r
678\r
679**/\r
53c71d09 680STATIC\r
681EFI_STATUS\r
682WriteBundle (\r
683 IN VOID *MemPtr,\r
684 IN UINT8 Template,\r
685 IN UINT64 Slot0,\r
686 IN UINT64 Slot1,\r
687 IN UINT64 Slot2\r
688 )\r
53c71d09 689{\r
690 UINT8 *BPtr;\r
691 UINT32 Index;\r
692 UINT64 Low64;\r
693 UINT64 High64;\r
694\r
695 //\r
696 // Verify pointer is aligned\r
697 //\r
698 if ((UINT64) MemPtr & 0xF) {\r
699 return EFI_INVALID_PARAMETER;\r
700 }\r
701 //\r
702 // Verify no more than 5 bits in template\r
703 //\r
704 if (Template &~0x1F) {\r
705 return EFI_INVALID_PARAMETER;\r
706 }\r
707 //\r
708 // Verify max of 41 bits used in code\r
709 //\r
710 if ((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) {\r
711 return EFI_INVALID_PARAMETER;\r
712 }\r
713\r
714 Low64 = LShiftU64 (Slot1, 46);\r
715 Low64 = Low64 | LShiftU64 (Slot0, 5) | Template;\r
716\r
717 High64 = RShiftU64 (Slot1, 18);\r
718 High64 = High64 | LShiftU64 (Slot2, 23);\r
719\r
720 //\r
721 // Now write it all out\r
722 //\r
723 BPtr = (UINT8 *) MemPtr;\r
724 for (Index = 0; Index < 8; Index++) {\r
725 *BPtr = (UINT8) Low64;\r
726 Low64 = RShiftU64 (Low64, 8);\r
727 BPtr++;\r
728 }\r
729\r
730 for (Index = 0; Index < 8; Index++) {\r
731 *BPtr = (UINT8) High64;\r
732 High64 = RShiftU64 (High64, 8);\r
733 BPtr++;\r
734 }\r
735\r
736 return EFI_SUCCESS;\r
737}\r
738\r
53c71d09 739\r
fb0b259e 740/**\r
741 This function is called to execute an EBC CALLEX instruction.\r
53c71d09 742 The function check the callee's content to see whether it is common native\r
743 code or a thunk to another piece of EBC code.\r
744 If the callee is common native code, use EbcLLCAllEXASM to manipulate,\r
745 otherwise, set the VM->IP to target EBC code directly to avoid another VM\r
746 be startup which cost time and stack space.\r
53c71d09 747\r
fb0b259e 748 @param VmPtr Pointer to a VM context.\r
749 @param FuncAddr Callee's address\r
750 @param NewStackPointer New stack pointer after the call\r
751 @param FramePtr New frame pointer after the call\r
752 @param Size The size of call instruction\r
53c71d09 753\r
fb0b259e 754 @return None.\r
53c71d09 755\r
fb0b259e 756**/\r
757VOID\r
758EbcLLCALLEX (\r
759 IN VM_CONTEXT *VmPtr,\r
760 IN UINTN FuncAddr,\r
761 IN UINTN NewStackPointer,\r
762 IN VOID *FramePtr,\r
763 IN UINT8 Size\r
764 )\r
53c71d09 765{\r
766 UINTN IsThunk;\r
767 UINTN TargetEbcAddr;\r
768 UINTN CodeOne18;\r
769 UINTN CodeOne23;\r
770 UINTN CodeTwoI;\r
771 UINTN CodeTwoIc;\r
772 UINTN CodeTwo7b;\r
773 UINTN CodeTwo5c;\r
774 UINTN CodeTwo9d;\r
775 UINTN CalleeAddr;\r
776\r
777 IsThunk = 1;\r
778 TargetEbcAddr = 0;\r
779\r
780 //\r
781 // FuncAddr points to the descriptor of the target instructions.\r
782 //\r
783 CalleeAddr = *((UINT64 *)FuncAddr);\r
784\r
785 //\r
786 // Processor specific code to check whether the callee is a thunk to EBC.\r
787 //\r
788 if (*((UINT64 *)CalleeAddr) != 0xBCCA000100000005) {\r
789 IsThunk = 0;\r
790 goto Action;\r
791 }\r
792 if (*((UINT64 *)CalleeAddr + 1) != 0x697623C1004A112E) {\r
793 IsThunk = 0;\r
794 goto Action;\r
795 }\r
796\r
797 CodeOne18 = RShiftU64 (*((UINT64 *)CalleeAddr + 2), 46) & 0x3FFFF;\r
798 CodeOne23 = (*((UINT64 *)CalleeAddr + 3)) & 0x7FFFFF;\r
799 CodeTwoI = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 59) & 0x1;\r
800 CodeTwoIc = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 44) & 0x1;\r
801 CodeTwo7b = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 36) & 0x7F;\r
802 CodeTwo5c = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 45) & 0x1F;\r
803 CodeTwo9d = RShiftU64 (*((UINT64 *)CalleeAddr + 3), 50) & 0x1FF;\r
804\r
805 TargetEbcAddr = CodeTwo7b;\r
806 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwo9d, 7);\r
807 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwo5c, 16);\r
808 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwoIc, 21);\r
809 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeOne18, 22);\r
810 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeOne23, 40);\r
811 TargetEbcAddr = TargetEbcAddr | LShiftU64 (CodeTwoI, 63);\r
812\r
813Action:\r
814 if (IsThunk == 1){\r
815 //\r
816 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and\r
817 // put our return address and frame pointer on the VM stack.\r
818 // Then set the VM's IP to new EBC code.\r
819 //\r
820 VmPtr->R[0] -= 8;\r
821 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);\r
822 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];\r
823 VmPtr->R[0] -= 8;\r
824 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (VmPtr->Ip + Size));\r
825\r
826 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;\r
827 } else {\r
828 //\r
829 // The callee is not a thunk to EBC, call native code.\r
830 //\r
831 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);\r
832\r
833 //\r
834 // Get return value and advance the IP.\r
835 //\r
836 VmPtr->R[7] = EbcLLGetReturnValue ();\r
837 VmPtr->Ip += Size;\r
838 }\r
839}\r