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