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