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