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