]> git.proxmox.com Git - mirror_edk2.git/blame - 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
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
3bbe68a3 5Copyright (c) 2006 - 2012, 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
3bbe68a3 137 VA_END (List);\r
53c71d09 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
fb0b259e 175\r
53c71d09 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
1ccdbf2a 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
53c71d09 184\r
fb0b259e 185\r
53c71d09 186 PushU64 (&VmContext, (UINT64) VM_STACK_KEY_VALUE);\r
1ccdbf2a 187 VmContext.StackMagicPtr = (UINTN *) VmContext.Gpr[0];\r
188 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];\r
53c71d09 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
1ccdbf2a 216 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];\r
53c71d09 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
1ccdbf2a 225 return (UINT64) VmContext.Gpr[7];\r
53c71d09 226}\r
227\r
53c71d09 228\r
fb0b259e 229/**\r
53c71d09 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
53c71d09 233\r
8e3bc754 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
53c71d09 237\r
fb0b259e 238 @return The value returned by the EBC application we're going to run.\r
53c71d09 239\r
fb0b259e 240**/\r
fb0b259e 241UINT64\r
fa97cbf4 242EFIAPI\r
fb0b259e 243ExecuteEbcImageEntryPoint (\r
244 IN EFI_HANDLE ImageHandle,\r
245 IN EFI_SYSTEM_TABLE *SystemTable\r
246 )\r
53c71d09 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
fb0b259e 282\r
53c71d09 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
1ccdbf2a 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
53c71d09 291\r
fb0b259e 292\r
53c71d09 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
1ccdbf2a 298 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];\r
53c71d09 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
1ccdbf2a 309 VmContext.Gpr[0] -= 10 * sizeof (UINT64);\r
53c71d09 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
1ccdbf2a 316 VmContext.Gpr[0] &= (INT64)~0x0f;\r
317 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];\r
53c71d09 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
1ccdbf2a 332 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];\r
53c71d09 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
1ccdbf2a 343 return (UINT64) VmContext.Gpr[7];\r
53c71d09 344}\r
345\r
fb0b259e 346\r
347/**\r
348 Create thunks for an EBC image entry point, or an EBC protocol service.\r
349\r
8e3bc754 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
fb0b259e 355\r
8e3bc754 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
fb0b259e 362\r
363**/\r
53c71d09 364EFI_STATUS\r
365EbcCreateThunks (\r
f45af90b 366 IN EFI_HANDLE ImageHandle,\r
367 IN VOID *EbcEntryPoint,\r
368 OUT VOID **Thunk,\r
369 IN UINT32 Flags\r
53c71d09 370 )\r
53c71d09 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
8e3bc754 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
53c71d09 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
8e3bc754 479 BitI = RShiftU64 (Addr, 63) & 0x01;\r
53c71d09 480 //\r
481 // ic = Addr[21:21]\r
482 //\r
8e3bc754 483 BitIc = RShiftU64 (Addr, 21) & 0x01;\r
53c71d09 484 //\r
485 // imm5c = Addr[20:16] for 5 bits\r
486 //\r
8e3bc754 487 BitImm5c = RShiftU64 (Addr, 16) & 0x1F;\r
53c71d09 488 //\r
489 // imm9d = Addr[15:7] for 9 bits\r
490 //\r
8e3bc754 491 BitImm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
53c71d09 492 //\r
493 // imm7b = Addr[6:0] for 7 bits\r
494 //\r
8e3bc754 495 BitImm7b = Addr & 0x7F;\r
53c71d09 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
8e3bc754 506 Code[2] = LShiftU64 (BitImm7b, 13);\r
53c71d09 507 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
8e3bc754 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
53c71d09 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
8e3bc754 544 BitI = RShiftU64 (Addr, 63) & 0x01;\r
53c71d09 545 //\r
546 // ic = Addr[21:21]\r
547 //\r
8e3bc754 548 BitIc = RShiftU64 (Addr, 21) & 0x01;\r
53c71d09 549 //\r
550 // imm5c = Addr[20:16] for 5 bits\r
551 //\r
8e3bc754 552 BitImm5c = RShiftU64 (Addr, 16) & 0x1F;\r
53c71d09 553 //\r
554 // imm9d = Addr[15:7] for 9 bits\r
555 //\r
8e3bc754 556 BitImm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
53c71d09 557 //\r
558 // imm7b = Addr[6:0] for 7 bits\r
559 //\r
8e3bc754 560 BitImm7b = Addr & 0x7F;\r
53c71d09 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
8e3bc754 571 Code[2] = LShiftU64 (BitImm7b, 13);\r
53c71d09 572 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
8e3bc754 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
53c71d09 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
366219ab 592 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {\r
53c71d09 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
8e3bc754 616 BitI = RShiftU64 (Addr, 63) & 0x01;\r
53c71d09 617 //\r
618 // ic = Addr[21:21]\r
619 //\r
8e3bc754 620 BitIc = RShiftU64 (Addr, 21) & 0x01;\r
53c71d09 621 //\r
622 // imm5c = Addr[20:16] for 5 bits\r
623 //\r
8e3bc754 624 BitImm5c = RShiftU64 (Addr, 16) & 0x1F;\r
53c71d09 625 //\r
626 // imm9d = Addr[15:7] for 9 bits\r
627 //\r
8e3bc754 628 BitImm9d = RShiftU64 (Addr, 7) & 0x1FF;\r
53c71d09 629 //\r
630 // imm7b = Addr[6:0] for 7 bits\r
631 //\r
8e3bc754 632 BitImm7b = Addr & 0x7F;\r
53c71d09 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
8e3bc754 642 Code[2] = LShiftU64(BitImm7b, 13);\r
53c71d09 643 Code[2] = Code[2] | LShiftU64 (0x00, 20); // vc\r
8e3bc754 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
53c71d09 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
fb0b259e 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
8e3bc754 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
fb0b259e 716\r
717 @retval EFI_INVALID_PARAMETER Pointer is not aligned\r
8e3bc754 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
fb0b259e 720 @retval EFI_SUCCESS All data is written.\r
721\r
722**/\r
53c71d09 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
53c71d09 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
366219ab 746 if ((Template &~0x1F) != 0) {\r
53c71d09 747 return EFI_INVALID_PARAMETER;\r
748 }\r
749 //\r
750 // Verify max of 41 bits used in code\r
751 //\r
366219ab 752 if (((Slot0 | Slot1 | Slot2) &~0x1ffffffffff) != 0) {\r
53c71d09 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
53c71d09 781\r
fb0b259e 782/**\r
783 This function is called to execute an EBC CALLEX instruction.\r
53c71d09 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
53c71d09 789\r
8e3bc754 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
53c71d09 795\r
fb0b259e 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
53c71d09 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
1ccdbf2a 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
53c71d09 865\r
866 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;\r
867 } else {\r
868 //\r
fa97cbf4
JY
869 // The callee is not a thunk to EBC, call native code,\r
870 // and get return value.\r
53c71d09 871 //\r
fa97cbf4 872 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);\r
53c71d09 873\r
874 //\r
fa97cbf4 875 // Advance the IP.\r
53c71d09 876 //\r
53c71d09 877 VmPtr->Ip += Size;\r
878 }\r
879}\r