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