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