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