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