1 /** @file 2 Contains code that implements the virtual machine. 3 4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "EbcInt.h" 16 #include "EbcExecute.h" 17 #include "EbcDebuggerHook.h" 18 19 20 // 21 // Define some useful data size constants to allow switch statements based on 22 // size of operands or data. 23 // 24 #define DATA_SIZE_INVALID 0 25 #define DATA_SIZE_8 1 26 #define DATA_SIZE_16 2 27 #define DATA_SIZE_32 4 28 #define DATA_SIZE_64 8 29 #define DATA_SIZE_N 48 // 4 or 8 30 // 31 // Structure we'll use to dispatch opcodes to execute functions. 32 // 33 typedef struct { 34 EFI_STATUS (*ExecuteFunction) (IN VM_CONTEXT * VmPtr); 35 } 36 VM_TABLE_ENTRY; 37 38 typedef 39 UINT64 40 (*DATA_MANIP_EXEC_FUNCTION) ( 41 IN VM_CONTEXT * VmPtr, 42 IN UINT64 Op1, 43 IN UINT64 Op2 44 ); 45 46 /** 47 Decode a 16-bit index to determine the offset. Given an index value: 48 49 b15 - sign bit 50 b14:12 - number of bits in this index assigned to natural units (=a) 51 ba:11 - constant units = ConstUnits 52 b0:a - natural units = NaturalUnits 53 54 Given this info, the offset can be computed by: 55 offset = sign_bit * (ConstUnits + NaturalUnits * sizeof(UINTN)) 56 57 Max offset is achieved with index = 0x7FFF giving an offset of 58 0x27B (32-bit machine) or 0x477 (64-bit machine). 59 Min offset is achieved with index = 60 61 @param VmPtr A pointer to VM context. 62 @param CodeOffset Offset from IP of the location of the 16-bit index 63 to decode. 64 65 @return The decoded offset. 66 67 **/ 68 INT16 69 VmReadIndex16 ( 70 IN VM_CONTEXT *VmPtr, 71 IN UINT32 CodeOffset 72 ); 73 74 /** 75 Decode a 32-bit index to determine the offset. 76 77 @param VmPtr A pointer to VM context. 78 @param CodeOffset Offset from IP of the location of the 32-bit index 79 to decode. 80 81 @return Converted index per EBC VM specification. 82 83 **/ 84 INT32 85 VmReadIndex32 ( 86 IN VM_CONTEXT *VmPtr, 87 IN UINT32 CodeOffset 88 ); 89 90 /** 91 Decode a 64-bit index to determine the offset. 92 93 @param VmPtr A pointer to VM context.s 94 @param CodeOffset Offset from IP of the location of the 64-bit index 95 to decode. 96 97 @return Converted index per EBC VM specification 98 99 **/ 100 INT64 101 VmReadIndex64 ( 102 IN VM_CONTEXT *VmPtr, 103 IN UINT32 CodeOffset 104 ); 105 106 /** 107 Reads 8-bit data form the memory address. 108 109 @param VmPtr A pointer to VM context. 110 @param Addr The memory address. 111 112 @return The 8-bit value from the memory address. 113 114 **/ 115 UINT8 116 VmReadMem8 ( 117 IN VM_CONTEXT *VmPtr, 118 IN UINTN Addr 119 ); 120 121 /** 122 Reads 16-bit data form the memory address. 123 124 @param VmPtr A pointer to VM context. 125 @param Addr The memory address. 126 127 @return The 16-bit value from the memory address. 128 129 **/ 130 UINT16 131 VmReadMem16 ( 132 IN VM_CONTEXT *VmPtr, 133 IN UINTN Addr 134 ); 135 136 /** 137 Reads 32-bit data form the memory address. 138 139 @param VmPtr A pointer to VM context. 140 @param Addr The memory address. 141 142 @return The 32-bit value from the memory address. 143 144 **/ 145 UINT32 146 VmReadMem32 ( 147 IN VM_CONTEXT *VmPtr, 148 IN UINTN Addr 149 ); 150 151 /** 152 Reads 64-bit data form the memory address. 153 154 @param VmPtr A pointer to VM context. 155 @param Addr The memory address. 156 157 @return The 64-bit value from the memory address. 158 159 **/ 160 UINT64 161 VmReadMem64 ( 162 IN VM_CONTEXT *VmPtr, 163 IN UINTN Addr 164 ); 165 166 /** 167 Read a natural value from memory. May or may not be aligned. 168 169 @param VmPtr current VM context 170 @param Addr the address to read from 171 172 @return The natural value at address Addr. 173 174 **/ 175 UINTN 176 VmReadMemN ( 177 IN VM_CONTEXT *VmPtr, 178 IN UINTN Addr 179 ); 180 181 /** 182 Writes 8-bit data to memory address. 183 184 This routine is called by the EBC data 185 movement instructions that write to memory. Since these writes 186 may be to the stack, which looks like (high address on top) this, 187 188 [EBC entry point arguments] 189 [VM stack] 190 [EBC stack] 191 192 we need to detect all attempts to write to the EBC entry point argument 193 stack area and adjust the address (which will initially point into the 194 VM stack) to point into the EBC entry point arguments. 195 196 @param VmPtr A pointer to a VM context. 197 @param Addr Address to write to. 198 @param Data Value to write to Addr. 199 200 @retval EFI_SUCCESS The instruction is executed successfully. 201 @retval Other Some error occurs when writing data to the address. 202 203 **/ 204 EFI_STATUS 205 VmWriteMem8 ( 206 IN VM_CONTEXT *VmPtr, 207 IN UINTN Addr, 208 IN UINT8 Data 209 ); 210 211 /** 212 Writes 16-bit data to memory address. 213 214 This routine is called by the EBC data 215 movement instructions that write to memory. Since these writes 216 may be to the stack, which looks like (high address on top) this, 217 218 [EBC entry point arguments] 219 [VM stack] 220 [EBC stack] 221 222 we need to detect all attempts to write to the EBC entry point argument 223 stack area and adjust the address (which will initially point into the 224 VM stack) to point into the EBC entry point arguments. 225 226 @param VmPtr A pointer to a VM context. 227 @param Addr Address to write to. 228 @param Data Value to write to Addr. 229 230 @retval EFI_SUCCESS The instruction is executed successfully. 231 @retval Other Some error occurs when writing data to the address. 232 233 **/ 234 EFI_STATUS 235 VmWriteMem16 ( 236 IN VM_CONTEXT *VmPtr, 237 IN UINTN Addr, 238 IN UINT16 Data 239 ); 240 241 /** 242 Writes 32-bit data to memory address. 243 244 This routine is called by the EBC data 245 movement instructions that write to memory. Since these writes 246 may be to the stack, which looks like (high address on top) this, 247 248 [EBC entry point arguments] 249 [VM stack] 250 [EBC stack] 251 252 we need to detect all attempts to write to the EBC entry point argument 253 stack area and adjust the address (which will initially point into the 254 VM stack) to point into the EBC entry point arguments. 255 256 @param VmPtr A pointer to a VM context. 257 @param Addr Address to write to. 258 @param Data Value to write to Addr. 259 260 @retval EFI_SUCCESS The instruction is executed successfully. 261 @retval Other Some error occurs when writing data to the address. 262 263 **/ 264 EFI_STATUS 265 VmWriteMem32 ( 266 IN VM_CONTEXT *VmPtr, 267 IN UINTN Addr, 268 IN UINT32 Data 269 ); 270 271 /** 272 Reads 16-bit unsigned data from the code stream. 273 274 This routine provides the ability to read raw unsigned data from the code 275 stream. 276 277 @param VmPtr A pointer to VM context 278 @param Offset Offset from current IP to the raw data to read. 279 280 @return The raw unsigned 16-bit value from the code stream. 281 282 **/ 283 UINT16 284 VmReadCode16 ( 285 IN VM_CONTEXT *VmPtr, 286 IN UINT32 Offset 287 ); 288 289 /** 290 Reads 32-bit unsigned data from the code stream. 291 292 This routine provides the ability to read raw unsigned data from the code 293 stream. 294 295 @param VmPtr A pointer to VM context 296 @param Offset Offset from current IP to the raw data to read. 297 298 @return The raw unsigned 32-bit value from the code stream. 299 300 **/ 301 UINT32 302 VmReadCode32 ( 303 IN VM_CONTEXT *VmPtr, 304 IN UINT32 Offset 305 ); 306 307 /** 308 Reads 64-bit unsigned data from the code stream. 309 310 This routine provides the ability to read raw unsigned data from the code 311 stream. 312 313 @param VmPtr A pointer to VM context 314 @param Offset Offset from current IP to the raw data to read. 315 316 @return The raw unsigned 64-bit value from the code stream. 317 318 **/ 319 UINT64 320 VmReadCode64 ( 321 IN VM_CONTEXT *VmPtr, 322 IN UINT32 Offset 323 ); 324 325 /** 326 Reads 8-bit immediate value at the offset. 327 328 This routine is called by the EBC execute 329 functions to read EBC immediate values from the code stream. 330 Since we can't assume alignment, each tries to read in the biggest 331 chunks size available, but will revert to smaller reads if necessary. 332 333 @param VmPtr A pointer to a VM context. 334 @param Offset offset from IP of the code bytes to read. 335 336 @return Signed data of the requested size from the specified address. 337 338 **/ 339 INT8 340 VmReadImmed8 ( 341 IN VM_CONTEXT *VmPtr, 342 IN UINT32 Offset 343 ); 344 345 /** 346 Reads 16-bit immediate value at the offset. 347 348 This routine is called by the EBC execute 349 functions to read EBC immediate values from the code stream. 350 Since we can't assume alignment, each tries to read in the biggest 351 chunks size available, but will revert to smaller reads if necessary. 352 353 @param VmPtr A pointer to a VM context. 354 @param Offset offset from IP of the code bytes to read. 355 356 @return Signed data of the requested size from the specified address. 357 358 **/ 359 INT16 360 VmReadImmed16 ( 361 IN VM_CONTEXT *VmPtr, 362 IN UINT32 Offset 363 ); 364 365 /** 366 Reads 32-bit immediate value at the offset. 367 368 This routine is called by the EBC execute 369 functions to read EBC immediate values from the code stream. 370 Since we can't assume alignment, each tries to read in the biggest 371 chunks size available, but will revert to smaller reads if necessary. 372 373 @param VmPtr A pointer to a VM context. 374 @param Offset offset from IP of the code bytes to read. 375 376 @return Signed data of the requested size from the specified address. 377 378 **/ 379 INT32 380 VmReadImmed32 ( 381 IN VM_CONTEXT *VmPtr, 382 IN UINT32 Offset 383 ); 384 385 /** 386 Reads 64-bit immediate value at the offset. 387 388 This routine is called by the EBC execute 389 functions to read EBC immediate values from the code stream. 390 Since we can't assume alignment, each tries to read in the biggest 391 chunks size available, but will revert to smaller reads if necessary. 392 393 @param VmPtr A pointer to a VM context. 394 @param Offset offset from IP of the code bytes to read. 395 396 @return Signed data of the requested size from the specified address. 397 398 **/ 399 INT64 400 VmReadImmed64 ( 401 IN VM_CONTEXT *VmPtr, 402 IN UINT32 Offset 403 ); 404 405 /** 406 Given an address that EBC is going to read from or write to, return 407 an appropriate address that accounts for a gap in the stack. 408 The stack for this application looks like this (high addr on top) 409 [EBC entry point arguments] 410 [VM stack] 411 [EBC stack] 412 The EBC assumes that its arguments are at the top of its stack, which 413 is where the VM stack is really. Therefore if the EBC does memory 414 accesses into the VM stack area, then we need to convert the address 415 to point to the EBC entry point arguments area. Do this here. 416 417 @param VmPtr A Pointer to VM context. 418 @param Addr Address of interest 419 420 @return The unchanged address if it's not in the VM stack region. Otherwise, 421 adjust for the stack gap and return the modified address. 422 423 **/ 424 UINTN 425 ConvertStackAddr ( 426 IN VM_CONTEXT *VmPtr, 427 IN UINTN Addr 428 ); 429 430 /** 431 Execute all the EBC data manipulation instructions. 432 Since the EBC data manipulation instructions all have the same basic form, 433 they can share the code that does the fetch of operands and the write-back 434 of the result. This function performs the fetch of the operands (even if 435 both are not needed to be fetched, like NOT instruction), dispatches to the 436 appropriate subfunction, then writes back the returned result. 437 438 Format: 439 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 440 441 @param VmPtr A pointer to VM context. 442 @param IsSignedOp Indicates whether the operand is signed or not. 443 444 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 445 @retval EFI_SUCCESS The instruction is executed successfully. 446 447 **/ 448 EFI_STATUS 449 ExecuteDataManip ( 450 IN VM_CONTEXT *VmPtr, 451 IN BOOLEAN IsSignedOp 452 ); 453 454 // 455 // Functions that execute VM opcodes 456 // 457 /** 458 Execute the EBC BREAK instruction. 459 460 @param VmPtr A pointer to a VM context. 461 462 @retval EFI_SUCCESS The instruction is executed successfully. 463 464 **/ 465 EFI_STATUS 466 ExecuteBREAK ( 467 IN VM_CONTEXT *VmPtr 468 ); 469 470 /** 471 Execute the JMP instruction. 472 473 Instruction syntax: 474 JMP64{cs|cc} Immed64 475 JMP32{cs|cc} {@}R1 {Immed32|Index32} 476 477 Encoding: 478 b0.7 - immediate data present 479 b0.6 - 1 = 64 bit immediate data 480 0 = 32 bit immediate data 481 b1.7 - 1 = conditional 482 b1.6 1 = CS (condition set) 483 0 = CC (condition clear) 484 b1.4 1 = relative address 485 0 = absolute address 486 b1.3 1 = operand1 indirect 487 b1.2-0 operand 1 488 489 @param VmPtr A pointer to a VM context. 490 491 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 492 @retval EFI_SUCCESS The instruction is executed successfully. 493 494 **/ 495 EFI_STATUS 496 ExecuteJMP ( 497 IN VM_CONTEXT *VmPtr 498 ); 499 500 /** 501 Execute the EBC JMP8 instruction. 502 503 Instruction syntax: 504 JMP8{cs|cc} Offset/2 505 506 @param VmPtr A pointer to a VM context. 507 508 @retval EFI_SUCCESS The instruction is executed successfully. 509 510 **/ 511 EFI_STATUS 512 ExecuteJMP8 ( 513 IN VM_CONTEXT *VmPtr 514 ); 515 516 /** 517 Implements the EBC CALL instruction. 518 519 Instruction format: 520 CALL64 Immed64 521 CALL32 {@}R1 {Immed32|Index32} 522 CALLEX64 Immed64 523 CALLEX16 {@}R1 {Immed32} 524 525 If Rx == R0, then it's a PC relative call to PC = PC + imm32. 526 527 @param VmPtr A pointer to a VM context. 528 529 @retval EFI_SUCCESS The instruction is executed successfully. 530 531 **/ 532 EFI_STATUS 533 ExecuteCALL ( 534 IN VM_CONTEXT *VmPtr 535 ); 536 537 /** 538 Execute the EBC RET instruction. 539 540 Instruction syntax: 541 RET 542 543 @param VmPtr A pointer to a VM context. 544 545 @retval EFI_SUCCESS The instruction is executed successfully. 546 547 **/ 548 EFI_STATUS 549 ExecuteRET ( 550 IN VM_CONTEXT *VmPtr 551 ); 552 553 /** 554 Execute the EBC CMP instruction. 555 556 Instruction syntax: 557 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16} 558 559 @param VmPtr A pointer to a VM context. 560 561 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 562 @retval EFI_SUCCESS The instruction is executed successfully. 563 564 **/ 565 EFI_STATUS 566 ExecuteCMP ( 567 IN VM_CONTEXT *VmPtr 568 ); 569 570 /** 571 Execute the EBC CMPI instruction 572 573 Instruction syntax: 574 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32 575 576 @param VmPtr A pointer to a VM context. 577 578 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 579 @retval EFI_SUCCESS The instruction is executed successfully. 580 581 **/ 582 EFI_STATUS 583 ExecuteCMPI ( 584 IN VM_CONTEXT *VmPtr 585 ); 586 587 /** 588 Execute the MOVxx instructions. 589 590 Instruction format: 591 592 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32} 593 MOVqq {@}R1 {Index64}, {@}R2 {Index64} 594 595 Copies contents of [R2] -> [R1], zero extending where required. 596 597 First character indicates the size of the move. 598 Second character indicates the size of the index(s). 599 600 Invalid to have R1 direct with index. 601 602 @param VmPtr A pointer to a VM context. 603 604 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 605 @retval EFI_SUCCESS The instruction is executed successfully. 606 607 **/ 608 EFI_STATUS 609 ExecuteMOVxx ( 610 IN VM_CONTEXT *VmPtr 611 ); 612 613 /** 614 Execute the EBC MOVI. 615 616 Instruction syntax: 617 618 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64 619 620 First variable character specifies the move size 621 Second variable character specifies size of the immediate data 622 623 Sign-extend the immediate data to the size of the operation, and zero-extend 624 if storing to a register. 625 626 Operand1 direct with index/immed is invalid. 627 628 @param VmPtr A pointer to a VM context. 629 630 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 631 @retval EFI_SUCCESS The instruction is executed successfully. 632 633 **/ 634 EFI_STATUS 635 ExecuteMOVI ( 636 IN VM_CONTEXT *VmPtr 637 ); 638 639 /** 640 Execute the EBC MOV immediate natural. This instruction moves an immediate 641 index value into a register or memory location. 642 643 Instruction syntax: 644 645 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64 646 647 @param VmPtr A pointer to a VM context. 648 649 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 650 @retval EFI_SUCCESS The instruction is executed successfully. 651 652 **/ 653 EFI_STATUS 654 ExecuteMOVIn ( 655 IN VM_CONTEXT *VmPtr 656 ); 657 658 /** 659 Execute the EBC MOVREL instruction. 660 Dest <- Ip + ImmData 661 662 Instruction syntax: 663 664 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64 665 666 @param VmPtr A pointer to a VM context. 667 668 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 669 @retval EFI_SUCCESS The instruction is executed successfully. 670 671 **/ 672 EFI_STATUS 673 ExecuteMOVREL ( 674 IN VM_CONTEXT *VmPtr 675 ); 676 677 /** 678 Execute the EBC PUSHn instruction 679 680 Instruction syntax: 681 PUSHn {@}R1 {Index16|Immed16} 682 683 @param VmPtr A pointer to a VM context. 684 685 @retval EFI_SUCCESS The instruction is executed successfully. 686 687 **/ 688 EFI_STATUS 689 ExecutePUSHn ( 690 IN VM_CONTEXT *VmPtr 691 ); 692 693 /** 694 Execute the EBC PUSH instruction. 695 696 Instruction syntax: 697 PUSH[32|64] {@}R1 {Index16|Immed16} 698 699 @param VmPtr A pointer to a VM context. 700 701 @retval EFI_SUCCESS The instruction is executed successfully. 702 703 **/ 704 EFI_STATUS 705 ExecutePUSH ( 706 IN VM_CONTEXT *VmPtr 707 ); 708 709 /** 710 Execute the EBC POPn instruction. 711 712 Instruction syntax: 713 POPn {@}R1 {Index16|Immed16} 714 715 @param VmPtr A pointer to a VM context. 716 717 @retval EFI_SUCCESS The instruction is executed successfully. 718 719 **/ 720 EFI_STATUS 721 ExecutePOPn ( 722 IN VM_CONTEXT *VmPtr 723 ); 724 725 /** 726 Execute the EBC POP instruction. 727 728 Instruction syntax: 729 POPn {@}R1 {Index16|Immed16} 730 731 @param VmPtr A pointer to a VM context. 732 733 @retval EFI_SUCCESS The instruction is executed successfully. 734 735 **/ 736 EFI_STATUS 737 ExecutePOP ( 738 IN VM_CONTEXT *VmPtr 739 ); 740 741 /** 742 Execute all the EBC signed data manipulation instructions. 743 Since the EBC data manipulation instructions all have the same basic form, 744 they can share the code that does the fetch of operands and the write-back 745 of the result. This function performs the fetch of the operands (even if 746 both are not needed to be fetched, like NOT instruction), dispatches to the 747 appropriate subfunction, then writes back the returned result. 748 749 Format: 750 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 751 752 @param VmPtr A pointer to VM context. 753 754 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 755 @retval EFI_SUCCESS The instruction is executed successfully. 756 757 **/ 758 EFI_STATUS 759 ExecuteSignedDataManip ( 760 IN VM_CONTEXT *VmPtr 761 ); 762 763 /** 764 Execute all the EBC unsigned data manipulation instructions. 765 Since the EBC data manipulation instructions all have the same basic form, 766 they can share the code that does the fetch of operands and the write-back 767 of the result. This function performs the fetch of the operands (even if 768 both are not needed to be fetched, like NOT instruction), dispatches to the 769 appropriate subfunction, then writes back the returned result. 770 771 Format: 772 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 773 774 @param VmPtr A pointer to VM context. 775 776 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 777 @retval EFI_SUCCESS The instruction is executed successfully. 778 779 **/ 780 EFI_STATUS 781 ExecuteUnsignedDataManip ( 782 IN VM_CONTEXT *VmPtr 783 ); 784 785 /** 786 Execute the EBC LOADSP instruction. 787 788 Instruction syntax: 789 LOADSP SP1, R2 790 791 @param VmPtr A pointer to a VM context. 792 793 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 794 @retval EFI_SUCCESS The instruction is executed successfully. 795 796 **/ 797 EFI_STATUS 798 ExecuteLOADSP ( 799 IN VM_CONTEXT *VmPtr 800 ); 801 802 /** 803 Execute the EBC STORESP instruction. 804 805 Instruction syntax: 806 STORESP Rx, FLAGS|IP 807 808 @param VmPtr A pointer to a VM context. 809 810 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 811 @retval EFI_SUCCESS The instruction is executed successfully. 812 813 **/ 814 EFI_STATUS 815 ExecuteSTORESP ( 816 IN VM_CONTEXT *VmPtr 817 ); 818 819 /** 820 Execute the EBC MOVsnw instruction. This instruction loads a signed 821 natural value from memory or register to another memory or register. On 822 32-bit machines, the value gets sign-extended to 64 bits if the destination 823 is a register. 824 825 Instruction syntax: 826 827 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32} 828 829 0:7 1=>operand1 index present 830 0:6 1=>operand2 index present 831 832 @param VmPtr A pointer to a VM context. 833 834 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 835 @retval EFI_SUCCESS The instruction is executed successfully. 836 837 **/ 838 EFI_STATUS 839 ExecuteMOVsnd ( 840 IN VM_CONTEXT *VmPtr 841 ); 842 843 /** 844 Execute the EBC MOVsnw instruction. This instruction loads a signed 845 natural value from memory or register to another memory or register. On 846 32-bit machines, the value gets sign-extended to 64 bits if the destination 847 is a register. 848 849 Instruction syntax: 850 851 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16} 852 853 0:7 1=>operand1 index present 854 0:6 1=>operand2 index present 855 856 @param VmPtr A pointer to a VM context. 857 858 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 859 @retval EFI_SUCCESS The instruction is executed successfully. 860 861 **/ 862 EFI_STATUS 863 ExecuteMOVsnw ( 864 IN VM_CONTEXT *VmPtr 865 ); 866 867 // 868 // Data manipulation subfunctions 869 // 870 /** 871 Execute the EBC NOT instruction.s 872 873 Instruction syntax: 874 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16} 875 876 @param VmPtr A pointer to a VM context. 877 @param Op1 Operand 1 from the instruction 878 @param Op2 Operand 2 from the instruction 879 880 @return ~Op2 881 882 **/ 883 UINT64 884 ExecuteNOT ( 885 IN VM_CONTEXT *VmPtr, 886 IN UINT64 Op1, 887 IN UINT64 Op2 888 ); 889 890 /** 891 Execute the EBC NEG instruction. 892 893 Instruction syntax: 894 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16} 895 896 @param VmPtr A pointer to a VM context. 897 @param Op1 Operand 1 from the instruction 898 @param Op2 Operand 2 from the instruction 899 900 @return Op2 * -1 901 902 **/ 903 UINT64 904 ExecuteNEG ( 905 IN VM_CONTEXT *VmPtr, 906 IN UINT64 Op1, 907 IN UINT64 Op2 908 ); 909 910 /** 911 Execute the EBC ADD instruction. 912 913 Instruction syntax: 914 ADD[32|64] {@}R1, {@}R2 {Index16} 915 916 @param VmPtr A pointer to a VM context. 917 @param Op1 Operand 1 from the instruction 918 @param Op2 Operand 2 from the instruction 919 920 @return Op1 + Op2 921 922 **/ 923 UINT64 924 ExecuteADD ( 925 IN VM_CONTEXT *VmPtr, 926 IN UINT64 Op1, 927 IN UINT64 Op2 928 ); 929 930 /** 931 Execute the EBC SUB instruction. 932 933 Instruction syntax: 934 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} 935 936 @param VmPtr A pointer to a VM context. 937 @param Op1 Operand 1 from the instruction 938 @param Op2 Operand 2 from the instruction 939 940 @return Op1 - Op2 941 942 **/ 943 UINT64 944 ExecuteSUB ( 945 IN VM_CONTEXT *VmPtr, 946 IN UINT64 Op1, 947 IN UINT64 Op2 948 ); 949 950 /** 951 Execute the EBC MUL instruction. 952 953 Instruction syntax: 954 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} 955 956 @param VmPtr A pointer to a VM context. 957 @param Op1 Operand 1 from the instruction 958 @param Op2 Operand 2 from the instruction 959 960 @return Op1 * Op2 961 962 **/ 963 UINT64 964 ExecuteMUL ( 965 IN VM_CONTEXT *VmPtr, 966 IN UINT64 Op1, 967 IN UINT64 Op2 968 ); 969 970 /** 971 Execute the EBC MULU instruction 972 973 Instruction syntax: 974 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16} 975 976 @param VmPtr A pointer to a VM context. 977 @param Op1 Operand 1 from the instruction 978 @param Op2 Operand 2 from the instruction 979 980 @return (unsigned)Op1 * (unsigned)Op2 981 982 **/ 983 UINT64 984 ExecuteMULU ( 985 IN VM_CONTEXT *VmPtr, 986 IN UINT64 Op1, 987 IN UINT64 Op2 988 ); 989 990 /** 991 Execute the EBC DIV instruction. 992 993 Instruction syntax: 994 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16} 995 996 @param VmPtr A pointer to a VM context. 997 @param Op1 Operand 1 from the instruction 998 @param Op2 Operand 2 from the instruction 999 1000 @return Op1 / Op2 1001 1002 **/ 1003 UINT64 1004 ExecuteDIV ( 1005 IN VM_CONTEXT *VmPtr, 1006 IN UINT64 Op1, 1007 IN UINT64 Op2 1008 ); 1009 1010 /** 1011 Execute the EBC DIVU instruction 1012 1013 Instruction syntax: 1014 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16} 1015 1016 @param VmPtr A pointer to a VM context. 1017 @param Op1 Operand 1 from the instruction 1018 @param Op2 Operand 2 from the instruction 1019 1020 @return (unsigned)Op1 / (unsigned)Op2 1021 1022 **/ 1023 UINT64 1024 ExecuteDIVU ( 1025 IN VM_CONTEXT *VmPtr, 1026 IN UINT64 Op1, 1027 IN UINT64 Op2 1028 ); 1029 1030 /** 1031 Execute the EBC MOD instruction. 1032 1033 Instruction syntax: 1034 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16} 1035 1036 @param VmPtr A pointer to a VM context. 1037 @param Op1 Operand 1 from the instruction 1038 @param Op2 Operand 2 from the instruction 1039 1040 @return Op1 MODULUS Op2 1041 1042 **/ 1043 UINT64 1044 ExecuteMOD ( 1045 IN VM_CONTEXT *VmPtr, 1046 IN UINT64 Op1, 1047 IN UINT64 Op2 1048 ); 1049 1050 /** 1051 Execute the EBC MODU instruction. 1052 1053 Instruction syntax: 1054 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16} 1055 1056 @param VmPtr A pointer to a VM context. 1057 @param Op1 Operand 1 from the instruction 1058 @param Op2 Operand 2 from the instruction 1059 1060 @return Op1 UNSIGNED_MODULUS Op2 1061 1062 **/ 1063 UINT64 1064 ExecuteMODU ( 1065 IN VM_CONTEXT *VmPtr, 1066 IN UINT64 Op1, 1067 IN UINT64 Op2 1068 ); 1069 1070 /** 1071 Execute the EBC AND instruction. 1072 1073 Instruction syntax: 1074 AND[32|64] {@}R1, {@}R2 {Index16|Immed16} 1075 1076 @param VmPtr A pointer to a VM context. 1077 @param Op1 Operand 1 from the instruction 1078 @param Op2 Operand 2 from the instruction 1079 1080 @return Op1 AND Op2 1081 1082 **/ 1083 UINT64 1084 ExecuteAND ( 1085 IN VM_CONTEXT *VmPtr, 1086 IN UINT64 Op1, 1087 IN UINT64 Op2 1088 ); 1089 1090 /** 1091 Execute the EBC OR instruction. 1092 1093 Instruction syntax: 1094 OR[32|64] {@}R1, {@}R2 {Index16|Immed16} 1095 1096 @param VmPtr A pointer to a VM context. 1097 @param Op1 Operand 1 from the instruction 1098 @param Op2 Operand 2 from the instruction 1099 1100 @return Op1 OR Op2 1101 1102 **/ 1103 UINT64 1104 ExecuteOR ( 1105 IN VM_CONTEXT *VmPtr, 1106 IN UINT64 Op1, 1107 IN UINT64 Op2 1108 ); 1109 1110 /** 1111 Execute the EBC XOR instruction. 1112 1113 Instruction syntax: 1114 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16} 1115 1116 @param VmPtr A pointer to a VM context. 1117 @param Op1 Operand 1 from the instruction 1118 @param Op2 Operand 2 from the instruction 1119 1120 @return Op1 XOR Op2 1121 1122 **/ 1123 UINT64 1124 ExecuteXOR ( 1125 IN VM_CONTEXT *VmPtr, 1126 IN UINT64 Op1, 1127 IN UINT64 Op2 1128 ); 1129 1130 /** 1131 Execute the EBC SHL shift left instruction. 1132 1133 Instruction syntax: 1134 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16} 1135 1136 @param VmPtr A pointer to a VM context. 1137 @param Op1 Operand 1 from the instruction 1138 @param Op2 Operand 2 from the instruction 1139 1140 @return Op1 << Op2 1141 1142 **/ 1143 UINT64 1144 ExecuteSHL ( 1145 IN VM_CONTEXT *VmPtr, 1146 IN UINT64 Op1, 1147 IN UINT64 Op2 1148 ); 1149 1150 /** 1151 Execute the EBC SHR instruction. 1152 1153 Instruction syntax: 1154 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16} 1155 1156 @param VmPtr A pointer to a VM context. 1157 @param Op1 Operand 1 from the instruction 1158 @param Op2 Operand 2 from the instruction 1159 1160 @return Op1 >> Op2 (unsigned operands) 1161 1162 **/ 1163 UINT64 1164 ExecuteSHR ( 1165 IN VM_CONTEXT *VmPtr, 1166 IN UINT64 Op1, 1167 IN UINT64 Op2 1168 ); 1169 1170 /** 1171 Execute the EBC ASHR instruction. 1172 1173 Instruction syntax: 1174 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16} 1175 1176 @param VmPtr A pointer to a VM context. 1177 @param Op1 Operand 1 from the instruction 1178 @param Op2 Operand 2 from the instruction 1179 1180 @return Op1 >> Op2 (signed) 1181 1182 **/ 1183 UINT64 1184 ExecuteASHR ( 1185 IN VM_CONTEXT *VmPtr, 1186 IN UINT64 Op1, 1187 IN UINT64 Op2 1188 ); 1189 1190 /** 1191 Execute the EBC EXTNDB instruction to sign-extend a byte value. 1192 1193 Instruction syntax: 1194 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16} 1195 1196 @param VmPtr A pointer to a VM context. 1197 @param Op1 Operand 1 from the instruction 1198 @param Op2 Operand 2 from the instruction 1199 1200 @return (INT64)(INT8)Op2 1201 1202 **/ 1203 UINT64 1204 ExecuteEXTNDB ( 1205 IN VM_CONTEXT *VmPtr, 1206 IN UINT64 Op1, 1207 IN UINT64 Op2 1208 ); 1209 1210 /** 1211 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value. 1212 1213 Instruction syntax: 1214 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16} 1215 1216 @param VmPtr A pointer to a VM context. 1217 @param Op1 Operand 1 from the instruction 1218 @param Op2 Operand 2 from the instruction 1219 1220 @return (INT64)(INT16)Op2 1221 1222 **/ 1223 UINT64 1224 ExecuteEXTNDW ( 1225 IN VM_CONTEXT *VmPtr, 1226 IN UINT64 Op1, 1227 IN UINT64 Op2 1228 ); 1229 1230 /** 1231 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value. 1232 1233 Instruction syntax: 1234 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16} 1235 1236 @param VmPtr A pointer to a VM context. 1237 @param Op1 Operand 1 from the instruction 1238 @param Op2 Operand 2 from the instruction 1239 1240 @return (INT64)(INT32)Op2 1241 1242 **/ 1243 UINT64 1244 ExecuteEXTNDD ( 1245 IN VM_CONTEXT *VmPtr, 1246 IN UINT64 Op1, 1247 IN UINT64 Op2 1248 ); 1249 1250 // 1251 // Once we retrieve the operands for the data manipulation instructions, 1252 // call these functions to perform the operation. 1253 // 1254 CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable[] = { 1255 ExecuteNOT, 1256 ExecuteNEG, 1257 ExecuteADD, 1258 ExecuteSUB, 1259 ExecuteMUL, 1260 ExecuteMULU, 1261 ExecuteDIV, 1262 ExecuteDIVU, 1263 ExecuteMOD, 1264 ExecuteMODU, 1265 ExecuteAND, 1266 ExecuteOR, 1267 ExecuteXOR, 1268 ExecuteSHL, 1269 ExecuteSHR, 1270 ExecuteASHR, 1271 ExecuteEXTNDB, 1272 ExecuteEXTNDW, 1273 ExecuteEXTNDD, 1274 }; 1275 1276 CONST VM_TABLE_ENTRY mVmOpcodeTable[] = { 1277 { ExecuteBREAK }, // opcode 0x00 1278 { ExecuteJMP }, // opcode 0x01 1279 { ExecuteJMP8 }, // opcode 0x02 1280 { ExecuteCALL }, // opcode 0x03 1281 { ExecuteRET }, // opcode 0x04 1282 { ExecuteCMP }, // opcode 0x05 CMPeq 1283 { ExecuteCMP }, // opcode 0x06 CMPlte 1284 { ExecuteCMP }, // opcode 0x07 CMPgte 1285 { ExecuteCMP }, // opcode 0x08 CMPulte 1286 { ExecuteCMP }, // opcode 0x09 CMPugte 1287 { ExecuteUnsignedDataManip }, // opcode 0x0A NOT 1288 { ExecuteSignedDataManip }, // opcode 0x0B NEG 1289 { ExecuteSignedDataManip }, // opcode 0x0C ADD 1290 { ExecuteSignedDataManip }, // opcode 0x0D SUB 1291 { ExecuteSignedDataManip }, // opcode 0x0E MUL 1292 { ExecuteUnsignedDataManip }, // opcode 0x0F MULU 1293 { ExecuteSignedDataManip }, // opcode 0x10 DIV 1294 { ExecuteUnsignedDataManip }, // opcode 0x11 DIVU 1295 { ExecuteSignedDataManip }, // opcode 0x12 MOD 1296 { ExecuteUnsignedDataManip }, // opcode 0x13 MODU 1297 { ExecuteUnsignedDataManip }, // opcode 0x14 AND 1298 { ExecuteUnsignedDataManip }, // opcode 0x15 OR 1299 { ExecuteUnsignedDataManip }, // opcode 0x16 XOR 1300 { ExecuteUnsignedDataManip }, // opcode 0x17 SHL 1301 { ExecuteUnsignedDataManip }, // opcode 0x18 SHR 1302 { ExecuteSignedDataManip }, // opcode 0x19 ASHR 1303 { ExecuteUnsignedDataManip }, // opcode 0x1A EXTNDB 1304 { ExecuteUnsignedDataManip }, // opcode 0x1B EXTNDW 1305 { ExecuteUnsignedDataManip }, // opcode 0x1C EXTNDD 1306 { ExecuteMOVxx }, // opcode 0x1D MOVBW 1307 { ExecuteMOVxx }, // opcode 0x1E MOVWW 1308 { ExecuteMOVxx }, // opcode 0x1F MOVDW 1309 { ExecuteMOVxx }, // opcode 0x20 MOVQW 1310 { ExecuteMOVxx }, // opcode 0x21 MOVBD 1311 { ExecuteMOVxx }, // opcode 0x22 MOVWD 1312 { ExecuteMOVxx }, // opcode 0x23 MOVDD 1313 { ExecuteMOVxx }, // opcode 0x24 MOVQD 1314 { ExecuteMOVsnw }, // opcode 0x25 MOVsnw 1315 { ExecuteMOVsnd }, // opcode 0x26 MOVsnd 1316 { NULL }, // opcode 0x27 1317 { ExecuteMOVxx }, // opcode 0x28 MOVqq 1318 { ExecuteLOADSP }, // opcode 0x29 LOADSP SP1, R2 1319 { ExecuteSTORESP }, // opcode 0x2A STORESP R1, SP2 1320 { ExecutePUSH }, // opcode 0x2B PUSH {@}R1 [imm16] 1321 { ExecutePOP }, // opcode 0x2C POP {@}R1 [imm16] 1322 { ExecuteCMPI }, // opcode 0x2D CMPIEQ 1323 { ExecuteCMPI }, // opcode 0x2E CMPILTE 1324 { ExecuteCMPI }, // opcode 0x2F CMPIGTE 1325 { ExecuteCMPI }, // opcode 0x30 CMPIULTE 1326 { ExecuteCMPI }, // opcode 0x31 CMPIUGTE 1327 { ExecuteMOVxx }, // opcode 0x32 MOVN 1328 { ExecuteMOVxx }, // opcode 0x33 MOVND 1329 { NULL }, // opcode 0x34 1330 { ExecutePUSHn }, // opcode 0x35 1331 { ExecutePOPn }, // opcode 0x36 1332 { ExecuteMOVI }, // opcode 0x37 - mov immediate data 1333 { ExecuteMOVIn }, // opcode 0x38 - mov immediate natural 1334 { ExecuteMOVREL }, // opcode 0x39 - move data relative to PC 1335 { NULL }, // opcode 0x3a 1336 { NULL }, // opcode 0x3b 1337 { NULL }, // opcode 0x3c 1338 { NULL }, // opcode 0x3d 1339 { NULL }, // opcode 0x3e 1340 { NULL } // opcode 0x3f 1341 }; 1342 1343 // 1344 // Length of JMP instructions, depending on upper two bits of opcode. 1345 // 1346 CONST UINT8 mJMPLen[] = { 2, 2, 6, 10 }; 1347 1348 /** 1349 Given a pointer to a new VM context, execute one or more instructions. This 1350 function is only used for test purposes via the EBC VM test protocol. 1351 1352 @param This A pointer to the EFI_EBC_VM_TEST_PROTOCOL structure. 1353 @param VmPtr A pointer to a VM context. 1354 @param InstructionCount A pointer to a UINTN value holding the number of 1355 instructions to execute. If it holds value of 0, 1356 then the instruction to be executed is 1. 1357 1358 @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. 1359 @retval EFI_SUCCESS All of the instructions are executed successfully. 1360 1361 **/ 1362 EFI_STATUS 1363 EFIAPI 1364 EbcExecuteInstructions ( 1365 IN EFI_EBC_VM_TEST_PROTOCOL *This, 1366 IN VM_CONTEXT *VmPtr, 1367 IN OUT UINTN *InstructionCount 1368 ) 1369 { 1370 UINTN ExecFunc; 1371 EFI_STATUS Status; 1372 UINTN InstructionsLeft; 1373 UINTN SavedInstructionCount; 1374 1375 Status = EFI_SUCCESS; 1376 1377 if (*InstructionCount == 0) { 1378 InstructionsLeft = 1; 1379 } else { 1380 InstructionsLeft = *InstructionCount; 1381 } 1382 1383 SavedInstructionCount = *InstructionCount; 1384 *InstructionCount = 0; 1385 1386 // 1387 // Index into the opcode table using the opcode byte for this instruction. 1388 // This gives you the execute function, which we first test for null, then 1389 // call it if it's not null. 1390 // 1391 while (InstructionsLeft != 0) { 1392 ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction; 1393 if (ExecFunc == (UINTN) NULL) { 1394 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr); 1395 return EFI_UNSUPPORTED; 1396 } else { 1397 mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr); 1398 *InstructionCount = *InstructionCount + 1; 1399 } 1400 1401 // 1402 // Decrement counter if applicable 1403 // 1404 if (SavedInstructionCount != 0) { 1405 InstructionsLeft--; 1406 } 1407 } 1408 1409 return Status; 1410 } 1411 1412 1413 /** 1414 Execute an EBC image from an entry point or from a published protocol. 1415 1416 @param VmPtr A pointer to a VM context. 1417 1418 @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. 1419 @retval EFI_SUCCESS All of the instructions are executed successfully. 1420 1421 **/ 1422 EFI_STATUS 1423 EbcExecute ( 1424 IN VM_CONTEXT *VmPtr 1425 ) 1426 { 1427 UINTN ExecFunc; 1428 UINT8 StackCorrupted; 1429 EFI_STATUS Status; 1430 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL *EbcSimpleDebugger; 1431 1432 mVmPtr = VmPtr; 1433 EbcSimpleDebugger = NULL; 1434 Status = EFI_SUCCESS; 1435 StackCorrupted = 0; 1436 1437 // 1438 // Make sure the magic value has been put on the stack before we got here. 1439 // 1440 if (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE) { 1441 StackCorrupted = 1; 1442 } 1443 1444 VmPtr->FramePtr = (VOID *) ((UINT8 *) (UINTN) VmPtr->Gpr[0] + 8); 1445 1446 // 1447 // Try to get the debug support for EBC 1448 // 1449 DEBUG_CODE_BEGIN (); 1450 Status = gBS->LocateProtocol ( 1451 &gEfiEbcSimpleDebuggerProtocolGuid, 1452 NULL, 1453 (VOID **) &EbcSimpleDebugger 1454 ); 1455 if (EFI_ERROR (Status)) { 1456 EbcSimpleDebugger = NULL; 1457 } 1458 DEBUG_CODE_END (); 1459 1460 // 1461 // Save the start IP for debug. For example, if we take an exception we 1462 // can print out the location of the exception relative to the entry point, 1463 // which could then be used in a disassembly listing to find the problem. 1464 // 1465 VmPtr->EntryPoint = (VOID *) VmPtr->Ip; 1466 1467 // 1468 // We'll wait for this flag to know when we're done. The RET 1469 // instruction sets it if it runs out of stack. 1470 // 1471 VmPtr->StopFlags = 0; 1472 while ((VmPtr->StopFlags & STOPFLAG_APP_DONE) == 0) { 1473 // 1474 // If we've found a simple debugger protocol, call it 1475 // 1476 DEBUG_CODE_BEGIN (); 1477 if (EbcSimpleDebugger != NULL) { 1478 EbcSimpleDebugger->Debugger (EbcSimpleDebugger, VmPtr); 1479 } 1480 DEBUG_CODE_END (); 1481 1482 // 1483 // Use the opcode bits to index into the opcode dispatch table. If the 1484 // function pointer is null then generate an exception. 1485 // 1486 ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction; 1487 if (ExecFunc == (UINTN) NULL) { 1488 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr); 1489 Status = EFI_UNSUPPORTED; 1490 goto Done; 1491 } 1492 1493 EbcDebuggerHookExecuteStart (VmPtr); 1494 1495 // 1496 // The EBC VM is a strongly ordered processor, so perform a fence operation before 1497 // and after each instruction is executed. 1498 // 1499 MemoryFence (); 1500 1501 mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr); 1502 1503 MemoryFence (); 1504 1505 EbcDebuggerHookExecuteEnd (VmPtr); 1506 1507 // 1508 // If the step flag is set, signal an exception and continue. We don't 1509 // clear it here. Assuming the debugger is responsible for clearing it. 1510 // 1511 if (VMFLAG_ISSET (VmPtr, VMFLAGS_STEP)) { 1512 EbcDebugSignalException (EXCEPT_EBC_STEP, EXCEPTION_FLAG_NONE, VmPtr); 1513 } 1514 // 1515 // Make sure stack has not been corrupted. Only report it once though. 1516 // 1517 if ((StackCorrupted == 0) && (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE)) { 1518 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr); 1519 StackCorrupted = 1; 1520 } 1521 if ((StackCorrupted == 0) && ((UINT64)VmPtr->Gpr[0] <= (UINT64)(UINTN) VmPtr->StackTop)) { 1522 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr); 1523 StackCorrupted = 1; 1524 } 1525 } 1526 1527 Done: 1528 mVmPtr = NULL; 1529 1530 return Status; 1531 } 1532 1533 1534 /** 1535 Execute the MOVxx instructions. 1536 1537 Instruction format: 1538 1539 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32} 1540 MOVqq {@}R1 {Index64}, {@}R2 {Index64} 1541 1542 Copies contents of [R2] -> [R1], zero extending where required. 1543 1544 First character indicates the size of the move. 1545 Second character indicates the size of the index(s). 1546 1547 Invalid to have R1 direct with index. 1548 1549 @param VmPtr A pointer to a VM context. 1550 1551 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 1552 @retval EFI_SUCCESS The instruction is executed successfully. 1553 1554 **/ 1555 EFI_STATUS 1556 ExecuteMOVxx ( 1557 IN VM_CONTEXT *VmPtr 1558 ) 1559 { 1560 UINT8 Opcode; 1561 UINT8 OpcMasked; 1562 UINT8 Operands; 1563 UINT8 Size; 1564 UINT8 MoveSize; 1565 INT16 Index16; 1566 INT32 Index32; 1567 INT64 Index64Op1; 1568 INT64 Index64Op2; 1569 UINT64 Data64; 1570 UINT64 DataMask; 1571 UINTN Source; 1572 1573 Opcode = GETOPCODE (VmPtr); 1574 OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE); 1575 1576 // 1577 // Get the operands byte so we can get R1 and R2 1578 // 1579 Operands = GETOPERANDS (VmPtr); 1580 1581 // 1582 // Assume no indexes 1583 // 1584 Index64Op1 = 0; 1585 Index64Op2 = 0; 1586 Data64 = 0; 1587 1588 // 1589 // Determine if we have an index/immediate data. Base instruction size 1590 // is 2 (opcode + operands). Add to this size each index specified. 1591 // 1592 Size = 2; 1593 if ((Opcode & (OPCODE_M_IMMED_OP1 | OPCODE_M_IMMED_OP2)) != 0) { 1594 // 1595 // Determine size of the index from the opcode. Then get it. 1596 // 1597 if ((OpcMasked <= OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVNW)) { 1598 // 1599 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index. 1600 // Get one or both index values. 1601 // 1602 if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { 1603 Index16 = VmReadIndex16 (VmPtr, 2); 1604 Index64Op1 = (INT64) Index16; 1605 Size += sizeof (UINT16); 1606 } 1607 1608 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 1609 Index16 = VmReadIndex16 (VmPtr, Size); 1610 Index64Op2 = (INT64) Index16; 1611 Size += sizeof (UINT16); 1612 } 1613 } else if ((OpcMasked <= OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVND)) { 1614 // 1615 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index 1616 // 1617 if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { 1618 Index32 = VmReadIndex32 (VmPtr, 2); 1619 Index64Op1 = (INT64) Index32; 1620 Size += sizeof (UINT32); 1621 } 1622 1623 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 1624 Index32 = VmReadIndex32 (VmPtr, Size); 1625 Index64Op2 = (INT64) Index32; 1626 Size += sizeof (UINT32); 1627 } 1628 } else if (OpcMasked == OPCODE_MOVQQ) { 1629 // 1630 // MOVqq -- only form with a 64-bit index 1631 // 1632 if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { 1633 Index64Op1 = VmReadIndex64 (VmPtr, 2); 1634 Size += sizeof (UINT64); 1635 } 1636 1637 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 1638 Index64Op2 = VmReadIndex64 (VmPtr, Size); 1639 Size += sizeof (UINT64); 1640 } 1641 } else { 1642 // 1643 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index 1644 // 1645 EbcDebugSignalException ( 1646 EXCEPT_EBC_INSTRUCTION_ENCODING, 1647 EXCEPTION_FLAG_FATAL, 1648 VmPtr 1649 ); 1650 return EFI_UNSUPPORTED; 1651 } 1652 } 1653 // 1654 // Determine the size of the move, and create a mask for it so we can 1655 // clear unused bits. 1656 // 1657 if ((OpcMasked == OPCODE_MOVBW) || (OpcMasked == OPCODE_MOVBD)) { 1658 MoveSize = DATA_SIZE_8; 1659 DataMask = 0xFF; 1660 } else if ((OpcMasked == OPCODE_MOVWW) || (OpcMasked == OPCODE_MOVWD)) { 1661 MoveSize = DATA_SIZE_16; 1662 DataMask = 0xFFFF; 1663 } else if ((OpcMasked == OPCODE_MOVDW) || (OpcMasked == OPCODE_MOVDD)) { 1664 MoveSize = DATA_SIZE_32; 1665 DataMask = 0xFFFFFFFF; 1666 } else if ((OpcMasked == OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVQQ)) { 1667 MoveSize = DATA_SIZE_64; 1668 DataMask = (UINT64)~0; 1669 } else if ((OpcMasked == OPCODE_MOVNW) || (OpcMasked == OPCODE_MOVND)) { 1670 MoveSize = DATA_SIZE_N; 1671 DataMask = (UINT64)~0 >> (64 - 8 * sizeof (UINTN)); 1672 } else { 1673 // 1674 // We were dispatched to this function and we don't recognize the opcode 1675 // 1676 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED, EXCEPTION_FLAG_FATAL, VmPtr); 1677 return EFI_UNSUPPORTED; 1678 } 1679 // 1680 // Now get the source address 1681 // 1682 if (OPERAND2_INDIRECT (Operands)) { 1683 // 1684 // Indirect form @R2. Compute address of operand2 1685 // 1686 Source = (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index64Op2); 1687 // 1688 // Now get the data from the source. Always 0-extend and let the compiler 1689 // sign-extend where required. 1690 // 1691 switch (MoveSize) { 1692 case DATA_SIZE_8: 1693 Data64 = (UINT64) (UINT8) VmReadMem8 (VmPtr, Source); 1694 break; 1695 1696 case DATA_SIZE_16: 1697 Data64 = (UINT64) (UINT16) VmReadMem16 (VmPtr, Source); 1698 break; 1699 1700 case DATA_SIZE_32: 1701 Data64 = (UINT64) (UINT32) VmReadMem32 (VmPtr, Source); 1702 break; 1703 1704 case DATA_SIZE_64: 1705 Data64 = (UINT64) VmReadMem64 (VmPtr, Source); 1706 break; 1707 1708 case DATA_SIZE_N: 1709 Data64 = (UINT64) (UINTN) VmReadMemN (VmPtr, Source); 1710 break; 1711 1712 default: 1713 // 1714 // not reached 1715 // 1716 break; 1717 } 1718 } else { 1719 // 1720 // Not indirect source: MOVxx {@}Rx, Ry [Index] 1721 // 1722 Data64 = (UINT64) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index64Op2); 1723 // 1724 // Did Operand2 have an index? If so, treat as two signed values since 1725 // indexes are signed values. 1726 // 1727 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 1728 // 1729 // NOTE: need to find a way to fix this, most likely by changing the VM 1730 // implementation to remove the stack gap. To do that, we'd need to 1731 // allocate stack space for the VM and actually set the system 1732 // stack pointer to the allocated buffer when the VM starts. 1733 // 1734 // Special case -- if someone took the address of a function parameter 1735 // then we need to make sure it's not in the stack gap. We can identify 1736 // this situation if (Operand2 register == 0) && (Operand2 is direct) 1737 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0) 1738 // Situations that to be aware of: 1739 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize 1740 // 1741 if ((OPERAND2_REGNUM (Operands) == 0) && 1742 (!OPERAND2_INDIRECT (Operands)) && 1743 (Index64Op2 > 0) && 1744 (OPERAND1_REGNUM (Operands) == 0) && 1745 (OPERAND1_INDIRECT (Operands)) 1746 ) { 1747 Data64 = (UINT64) ConvertStackAddr (VmPtr, (UINTN) (INT64) Data64); 1748 } 1749 } 1750 } 1751 // 1752 // Now write it back 1753 // 1754 if (OPERAND1_INDIRECT (Operands)) { 1755 // 1756 // Reuse the Source variable to now be dest. 1757 // 1758 Source = (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index64Op1); 1759 // 1760 // Do the write based on the size 1761 // 1762 switch (MoveSize) { 1763 case DATA_SIZE_8: 1764 VmWriteMem8 (VmPtr, Source, (UINT8) Data64); 1765 break; 1766 1767 case DATA_SIZE_16: 1768 VmWriteMem16 (VmPtr, Source, (UINT16) Data64); 1769 break; 1770 1771 case DATA_SIZE_32: 1772 VmWriteMem32 (VmPtr, Source, (UINT32) Data64); 1773 break; 1774 1775 case DATA_SIZE_64: 1776 VmWriteMem64 (VmPtr, Source, Data64); 1777 break; 1778 1779 case DATA_SIZE_N: 1780 VmWriteMemN (VmPtr, Source, (UINTN) Data64); 1781 break; 1782 1783 default: 1784 // 1785 // not reached 1786 // 1787 break; 1788 } 1789 } else { 1790 // 1791 // Operand1 direct. 1792 // Make sure we didn't have an index on operand1. 1793 // 1794 if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { 1795 EbcDebugSignalException ( 1796 EXCEPT_EBC_INSTRUCTION_ENCODING, 1797 EXCEPTION_FLAG_FATAL, 1798 VmPtr 1799 ); 1800 return EFI_UNSUPPORTED; 1801 } 1802 // 1803 // Direct storage in register. Clear unused bits and store back to 1804 // register. 1805 // 1806 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Data64 & DataMask; 1807 } 1808 // 1809 // Advance the instruction pointer 1810 // 1811 VmPtr->Ip += Size; 1812 return EFI_SUCCESS; 1813 } 1814 1815 1816 /** 1817 Execute the EBC BREAK instruction. 1818 1819 @param VmPtr A pointer to a VM context. 1820 1821 @retval EFI_SUCCESS The instruction is executed successfully. 1822 1823 **/ 1824 EFI_STATUS 1825 ExecuteBREAK ( 1826 IN VM_CONTEXT *VmPtr 1827 ) 1828 { 1829 EFI_STATUS Status; 1830 UINT8 Operands; 1831 VOID *EbcEntryPoint; 1832 VOID *Thunk; 1833 UINT64 U64EbcEntryPoint; 1834 INT32 Offset; 1835 1836 Thunk = NULL; 1837 Operands = GETOPERANDS (VmPtr); 1838 switch (Operands) { 1839 // 1840 // Runaway program break. Generate an exception and terminate 1841 // 1842 case 0: 1843 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr); 1844 break; 1845 1846 // 1847 // Get VM version -- return VM revision number in R7 1848 // 1849 case 1: 1850 // 1851 // Bits: 1852 // 63-17 = 0 1853 // 16-8 = Major version 1854 // 7-0 = Minor version 1855 // 1856 VmPtr->Gpr[7] = GetVmVersion (); 1857 break; 1858 1859 // 1860 // Debugger breakpoint 1861 // 1862 case 3: 1863 VmPtr->StopFlags |= STOPFLAG_BREAKPOINT; 1864 // 1865 // See if someone has registered a handler 1866 // 1867 EbcDebugSignalException ( 1868 EXCEPT_EBC_BREAKPOINT, 1869 EXCEPTION_FLAG_NONE, 1870 VmPtr 1871 ); 1872 break; 1873 1874 // 1875 // System call, which there are none, so NOP it. 1876 // 1877 case 4: 1878 break; 1879 1880 // 1881 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot) 1882 // "offset from self" pointer to the EBC entry point. 1883 // After we're done, *(UINT64 *)R7 will be the address of the new thunk. 1884 // 1885 case 5: 1886 Offset = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7]); 1887 U64EbcEntryPoint = (UINT64) (VmPtr->Gpr[7] + Offset + 4); 1888 EbcEntryPoint = (VOID *) (UINTN) U64EbcEntryPoint; 1889 1890 // 1891 // Now create a new thunk 1892 // 1893 Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0); 1894 if (EFI_ERROR (Status)) { 1895 return Status; 1896 } 1897 1898 // 1899 // Finally replace the EBC entry point memory with the thunk address 1900 // 1901 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[7], (UINT64) (UINTN) Thunk); 1902 break; 1903 1904 // 1905 // Compiler setting version per value in R7 1906 // 1907 case 6: 1908 VmPtr->CompilerVersion = (UINT32) VmPtr->Gpr[7]; 1909 // 1910 // Check compiler version against VM version? 1911 // 1912 break; 1913 1914 // 1915 // Unhandled break code. Signal exception. 1916 // 1917 default: 1918 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr); 1919 break; 1920 } 1921 // 1922 // Advance IP 1923 // 1924 VmPtr->Ip += 2; 1925 return EFI_SUCCESS; 1926 } 1927 1928 1929 /** 1930 Execute the JMP instruction. 1931 1932 Instruction syntax: 1933 JMP64{cs|cc} Immed64 1934 JMP32{cs|cc} {@}R1 {Immed32|Index32} 1935 1936 Encoding: 1937 b0.7 - immediate data present 1938 b0.6 - 1 = 64 bit immediate data 1939 0 = 32 bit immediate data 1940 b1.7 - 1 = conditional 1941 b1.6 1 = CS (condition set) 1942 0 = CC (condition clear) 1943 b1.4 1 = relative address 1944 0 = absolute address 1945 b1.3 1 = operand1 indirect 1946 b1.2-0 operand 1 1947 1948 @param VmPtr A pointer to a VM context. 1949 1950 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 1951 @retval EFI_SUCCESS The instruction is executed successfully. 1952 1953 **/ 1954 EFI_STATUS 1955 ExecuteJMP ( 1956 IN VM_CONTEXT *VmPtr 1957 ) 1958 { 1959 UINT8 Opcode; 1960 UINT8 CompareSet; 1961 UINT8 ConditionFlag; 1962 UINT8 Size; 1963 UINT8 Operand; 1964 UINT64 Data64; 1965 INT32 Index32; 1966 UINTN Addr; 1967 1968 Operand = GETOPERANDS (VmPtr); 1969 Opcode = GETOPCODE (VmPtr); 1970 1971 // 1972 // Get instruction length from the opcode. The upper two bits are used here 1973 // to index into the length array. 1974 // 1975 Size = mJMPLen[(Opcode >> 6) & 0x03]; 1976 1977 // 1978 // Decode instruction conditions 1979 // If we haven't met the condition, then simply advance the IP and return. 1980 // 1981 CompareSet = (UINT8) (((Operand & JMP_M_CS) != 0) ? 1 : 0); 1982 ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC); 1983 if ((Operand & CONDITION_M_CONDITIONAL) != 0) { 1984 if (CompareSet != ConditionFlag) { 1985 EbcDebuggerHookJMPStart (VmPtr); 1986 VmPtr->Ip += Size; 1987 EbcDebuggerHookJMPEnd (VmPtr); 1988 return EFI_SUCCESS; 1989 } 1990 } 1991 // 1992 // Check for 64-bit form and do it right away since it's the most 1993 // straight-forward form. 1994 // 1995 if ((Opcode & OPCODE_M_IMMDATA64) != 0) { 1996 // 1997 // Double check for immediate-data, which is required. If not there, 1998 // then signal an exception 1999 // 2000 if ((Opcode & OPCODE_M_IMMDATA) == 0) { 2001 EbcDebugSignalException ( 2002 EXCEPT_EBC_INSTRUCTION_ENCODING, 2003 EXCEPTION_FLAG_ERROR, 2004 VmPtr 2005 ); 2006 return EFI_UNSUPPORTED; 2007 } 2008 // 2009 // 64-bit immediate data is full address. Read the immediate data, 2010 // check for alignment, and jump absolute. 2011 // 2012 Data64 = (UINT64) VmReadImmed64 (VmPtr, 2); 2013 if (!IS_ALIGNED ((UINTN) Data64, sizeof (UINT16))) { 2014 EbcDebugSignalException ( 2015 EXCEPT_EBC_ALIGNMENT_CHECK, 2016 EXCEPTION_FLAG_FATAL, 2017 VmPtr 2018 ); 2019 2020 return EFI_UNSUPPORTED; 2021 } 2022 2023 // 2024 // Take jump -- relative or absolute 2025 // 2026 EbcDebuggerHookJMPStart (VmPtr); 2027 if ((Operand & JMP_M_RELATIVE) != 0) { 2028 VmPtr->Ip += (UINTN) Data64 + Size; 2029 } else { 2030 VmPtr->Ip = (VMIP) (UINTN) Data64; 2031 } 2032 EbcDebuggerHookJMPEnd (VmPtr); 2033 2034 return EFI_SUCCESS; 2035 } 2036 // 2037 // 32-bit forms: 2038 // Get the index if there is one. May be either an index, or an immediate 2039 // offset depending on indirect operand. 2040 // JMP32 @R1 Index32 -- immediate data is an index 2041 // JMP32 R1 Immed32 -- immedate data is an offset 2042 // 2043 if ((Opcode & OPCODE_M_IMMDATA) != 0) { 2044 if (OPERAND1_INDIRECT (Operand)) { 2045 Index32 = VmReadIndex32 (VmPtr, 2); 2046 } else { 2047 Index32 = VmReadImmed32 (VmPtr, 2); 2048 } 2049 } else { 2050 Index32 = 0; 2051 } 2052 // 2053 // Get the register data. If R == 0, then special case where it's ignored. 2054 // 2055 if (OPERAND1_REGNUM (Operand) == 0) { 2056 Data64 = 0; 2057 } else { 2058 Data64 = (UINT64) OPERAND1_REGDATA (VmPtr, Operand); 2059 } 2060 // 2061 // Decode the forms 2062 // 2063 if (OPERAND1_INDIRECT (Operand)) { 2064 // 2065 // Form: JMP32 @Rx {Index32} 2066 // 2067 Addr = VmReadMemN (VmPtr, (UINTN) Data64 + Index32); 2068 if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) { 2069 EbcDebugSignalException ( 2070 EXCEPT_EBC_ALIGNMENT_CHECK, 2071 EXCEPTION_FLAG_FATAL, 2072 VmPtr 2073 ); 2074 2075 return EFI_UNSUPPORTED; 2076 } 2077 2078 EbcDebuggerHookJMPStart (VmPtr); 2079 if ((Operand & JMP_M_RELATIVE) != 0) { 2080 VmPtr->Ip += (UINTN) Addr + Size; 2081 } else { 2082 VmPtr->Ip = (VMIP) Addr; 2083 } 2084 EbcDebuggerHookJMPEnd (VmPtr); 2085 2086 } else { 2087 // 2088 // Form: JMP32 Rx {Immed32} 2089 // 2090 Addr = (UINTN) (Data64 + Index32); 2091 if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) { 2092 EbcDebugSignalException ( 2093 EXCEPT_EBC_ALIGNMENT_CHECK, 2094 EXCEPTION_FLAG_FATAL, 2095 VmPtr 2096 ); 2097 2098 return EFI_UNSUPPORTED; 2099 } 2100 2101 EbcDebuggerHookJMPStart (VmPtr); 2102 if ((Operand & JMP_M_RELATIVE) != 0) { 2103 VmPtr->Ip += (UINTN) Addr + Size; 2104 } else { 2105 VmPtr->Ip = (VMIP) Addr; 2106 } 2107 EbcDebuggerHookJMPEnd (VmPtr); 2108 2109 } 2110 2111 return EFI_SUCCESS; 2112 } 2113 2114 2115 /** 2116 Execute the EBC JMP8 instruction. 2117 2118 Instruction syntax: 2119 JMP8{cs|cc} Offset/2 2120 2121 @param VmPtr A pointer to a VM context. 2122 2123 @retval EFI_SUCCESS The instruction is executed successfully. 2124 2125 **/ 2126 EFI_STATUS 2127 ExecuteJMP8 ( 2128 IN VM_CONTEXT *VmPtr 2129 ) 2130 { 2131 UINT8 Opcode; 2132 UINT8 ConditionFlag; 2133 UINT8 CompareSet; 2134 INT8 Offset; 2135 2136 // 2137 // Decode instruction. 2138 // 2139 Opcode = GETOPCODE (VmPtr); 2140 CompareSet = (UINT8) (((Opcode & JMP_M_CS) != 0) ? 1 : 0); 2141 ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC); 2142 2143 // 2144 // If we haven't met the condition, then simply advance the IP and return 2145 // 2146 if ((Opcode & CONDITION_M_CONDITIONAL) != 0) { 2147 if (CompareSet != ConditionFlag) { 2148 EbcDebuggerHookJMP8Start (VmPtr); 2149 VmPtr->Ip += 2; 2150 EbcDebuggerHookJMP8End (VmPtr); 2151 return EFI_SUCCESS; 2152 } 2153 } 2154 // 2155 // Get the offset from the instruction stream. It's relative to the 2156 // following instruction, and divided by 2. 2157 // 2158 Offset = VmReadImmed8 (VmPtr, 1); 2159 // 2160 // Want to check for offset == -2 and then raise an exception? 2161 // 2162 EbcDebuggerHookJMP8Start (VmPtr); 2163 VmPtr->Ip += (Offset * 2) + 2; 2164 EbcDebuggerHookJMP8End (VmPtr); 2165 return EFI_SUCCESS; 2166 } 2167 2168 2169 /** 2170 Execute the EBC MOVI. 2171 2172 Instruction syntax: 2173 2174 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64 2175 2176 First variable character specifies the move size 2177 Second variable character specifies size of the immediate data 2178 2179 Sign-extend the immediate data to the size of the operation, and zero-extend 2180 if storing to a register. 2181 2182 Operand1 direct with index/immed is invalid. 2183 2184 @param VmPtr A pointer to a VM context. 2185 2186 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 2187 @retval EFI_SUCCESS The instruction is executed successfully. 2188 2189 **/ 2190 EFI_STATUS 2191 ExecuteMOVI ( 2192 IN VM_CONTEXT *VmPtr 2193 ) 2194 { 2195 UINT8 Opcode; 2196 UINT8 Operands; 2197 UINT8 Size; 2198 INT16 Index16; 2199 INT64 ImmData64; 2200 UINT64 Op1; 2201 UINT64 Mask64; 2202 2203 // 2204 // Get the opcode and operands byte so we can get R1 and R2 2205 // 2206 Opcode = GETOPCODE (VmPtr); 2207 Operands = GETOPERANDS (VmPtr); 2208 2209 // 2210 // Get the index (16-bit) if present 2211 // 2212 if ((Operands & MOVI_M_IMMDATA) != 0) { 2213 Index16 = VmReadIndex16 (VmPtr, 2); 2214 Size = 4; 2215 } else { 2216 Index16 = 0; 2217 Size = 2; 2218 } 2219 // 2220 // Extract the immediate data. Sign-extend always. 2221 // 2222 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { 2223 ImmData64 = (INT64) (INT16) VmReadImmed16 (VmPtr, Size); 2224 Size += 2; 2225 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { 2226 ImmData64 = (INT64) (INT32) VmReadImmed32 (VmPtr, Size); 2227 Size += 4; 2228 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { 2229 ImmData64 = (INT64) VmReadImmed64 (VmPtr, Size); 2230 Size += 8; 2231 } else { 2232 // 2233 // Invalid encoding 2234 // 2235 EbcDebugSignalException ( 2236 EXCEPT_EBC_INSTRUCTION_ENCODING, 2237 EXCEPTION_FLAG_FATAL, 2238 VmPtr 2239 ); 2240 return EFI_UNSUPPORTED; 2241 } 2242 // 2243 // Now write back the result 2244 // 2245 if (!OPERAND1_INDIRECT (Operands)) { 2246 // 2247 // Operand1 direct. Make sure it didn't have an index. 2248 // 2249 if ((Operands & MOVI_M_IMMDATA) != 0) { 2250 EbcDebugSignalException ( 2251 EXCEPT_EBC_INSTRUCTION_ENCODING, 2252 EXCEPTION_FLAG_FATAL, 2253 VmPtr 2254 ); 2255 return EFI_UNSUPPORTED; 2256 } 2257 // 2258 // Writing directly to a register. Clear unused bits. 2259 // 2260 if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) { 2261 Mask64 = 0x000000FF; 2262 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) { 2263 Mask64 = 0x0000FFFF; 2264 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) { 2265 Mask64 = 0x00000000FFFFFFFF; 2266 } else { 2267 Mask64 = (UINT64)~0; 2268 } 2269 2270 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmData64 & Mask64; 2271 } else { 2272 // 2273 // Get the address then write back based on size of the move 2274 // 2275 Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; 2276 if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) { 2277 VmWriteMem8 (VmPtr, (UINTN) Op1, (UINT8) ImmData64); 2278 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) { 2279 VmWriteMem16 (VmPtr, (UINTN) Op1, (UINT16) ImmData64); 2280 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) { 2281 VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) ImmData64); 2282 } else { 2283 VmWriteMem64 (VmPtr, (UINTN) Op1, (UINT64) ImmData64); 2284 } 2285 } 2286 // 2287 // Advance the instruction pointer 2288 // 2289 VmPtr->Ip += Size; 2290 return EFI_SUCCESS; 2291 } 2292 2293 2294 /** 2295 Execute the EBC MOV immediate natural. This instruction moves an immediate 2296 index value into a register or memory location. 2297 2298 Instruction syntax: 2299 2300 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64 2301 2302 @param VmPtr A pointer to a VM context. 2303 2304 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 2305 @retval EFI_SUCCESS The instruction is executed successfully. 2306 2307 **/ 2308 EFI_STATUS 2309 ExecuteMOVIn ( 2310 IN VM_CONTEXT *VmPtr 2311 ) 2312 { 2313 UINT8 Opcode; 2314 UINT8 Operands; 2315 UINT8 Size; 2316 INT16 Index16; 2317 INT16 ImmedIndex16; 2318 INT32 ImmedIndex32; 2319 INT64 ImmedIndex64; 2320 UINT64 Op1; 2321 2322 // 2323 // Get the opcode and operands byte so we can get R1 and R2 2324 // 2325 Opcode = GETOPCODE (VmPtr); 2326 Operands = GETOPERANDS (VmPtr); 2327 2328 // 2329 // Get the operand1 index (16-bit) if present 2330 // 2331 if ((Operands & MOVI_M_IMMDATA) != 0) { 2332 Index16 = VmReadIndex16 (VmPtr, 2); 2333 Size = 4; 2334 } else { 2335 Index16 = 0; 2336 Size = 2; 2337 } 2338 // 2339 // Extract the immediate data and convert to a 64-bit index. 2340 // 2341 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { 2342 ImmedIndex16 = VmReadIndex16 (VmPtr, Size); 2343 ImmedIndex64 = (INT64) ImmedIndex16; 2344 Size += 2; 2345 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { 2346 ImmedIndex32 = VmReadIndex32 (VmPtr, Size); 2347 ImmedIndex64 = (INT64) ImmedIndex32; 2348 Size += 4; 2349 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { 2350 ImmedIndex64 = VmReadIndex64 (VmPtr, Size); 2351 Size += 8; 2352 } else { 2353 // 2354 // Invalid encoding 2355 // 2356 EbcDebugSignalException ( 2357 EXCEPT_EBC_INSTRUCTION_ENCODING, 2358 EXCEPTION_FLAG_FATAL, 2359 VmPtr 2360 ); 2361 return EFI_UNSUPPORTED; 2362 } 2363 // 2364 // Now write back the result 2365 // 2366 if (!OPERAND1_INDIRECT (Operands)) { 2367 // 2368 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which 2369 // is illegal 2370 // 2371 if ((Operands & MOVI_M_IMMDATA) != 0) { 2372 EbcDebugSignalException ( 2373 EXCEPT_EBC_INSTRUCTION_ENCODING, 2374 EXCEPTION_FLAG_FATAL, 2375 VmPtr 2376 ); 2377 return EFI_UNSUPPORTED; 2378 } 2379 2380 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmedIndex64; 2381 } else { 2382 // 2383 // Get the address 2384 // 2385 Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; 2386 VmWriteMemN (VmPtr, (UINTN) Op1, (UINTN)(INTN) ImmedIndex64); 2387 } 2388 // 2389 // Advance the instruction pointer 2390 // 2391 VmPtr->Ip += Size; 2392 return EFI_SUCCESS; 2393 } 2394 2395 2396 /** 2397 Execute the EBC MOVREL instruction. 2398 Dest <- Ip + ImmData 2399 2400 Instruction syntax: 2401 2402 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64 2403 2404 @param VmPtr A pointer to a VM context. 2405 2406 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 2407 @retval EFI_SUCCESS The instruction is executed successfully. 2408 2409 **/ 2410 EFI_STATUS 2411 ExecuteMOVREL ( 2412 IN VM_CONTEXT *VmPtr 2413 ) 2414 { 2415 UINT8 Opcode; 2416 UINT8 Operands; 2417 UINT8 Size; 2418 INT16 Index16; 2419 INT64 ImmData64; 2420 UINT64 Op1; 2421 UINT64 Op2; 2422 2423 // 2424 // Get the opcode and operands byte so we can get R1 and R2 2425 // 2426 Opcode = GETOPCODE (VmPtr); 2427 Operands = GETOPERANDS (VmPtr); 2428 2429 // 2430 // Get the Operand 1 index (16-bit) if present 2431 // 2432 if ((Operands & MOVI_M_IMMDATA) != 0) { 2433 Index16 = VmReadIndex16 (VmPtr, 2); 2434 Size = 4; 2435 } else { 2436 Index16 = 0; 2437 Size = 2; 2438 } 2439 // 2440 // Get the immediate data. 2441 // 2442 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { 2443 ImmData64 = (INT64) VmReadImmed16 (VmPtr, Size); 2444 Size += 2; 2445 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { 2446 ImmData64 = (INT64) VmReadImmed32 (VmPtr, Size); 2447 Size += 4; 2448 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { 2449 ImmData64 = VmReadImmed64 (VmPtr, Size); 2450 Size += 8; 2451 } else { 2452 // 2453 // Invalid encoding 2454 // 2455 EbcDebugSignalException ( 2456 EXCEPT_EBC_INSTRUCTION_ENCODING, 2457 EXCEPTION_FLAG_FATAL, 2458 VmPtr 2459 ); 2460 return EFI_UNSUPPORTED; 2461 } 2462 // 2463 // Compute the value and write back the result 2464 // 2465 Op2 = (UINT64) ((INT64) ((UINT64) (UINTN) VmPtr->Ip) + (INT64) ImmData64 + Size); 2466 if (!OPERAND1_INDIRECT (Operands)) { 2467 // 2468 // Check for illegal combination of operand1 direct with immediate data 2469 // 2470 if ((Operands & MOVI_M_IMMDATA) != 0) { 2471 EbcDebugSignalException ( 2472 EXCEPT_EBC_INSTRUCTION_ENCODING, 2473 EXCEPTION_FLAG_FATAL, 2474 VmPtr 2475 ); 2476 return EFI_UNSUPPORTED; 2477 } 2478 2479 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (VM_REGISTER) Op2; 2480 } else { 2481 // 2482 // Get the address = [Rx] + Index16 2483 // Write back the result. Always a natural size write, since 2484 // we're talking addresses here. 2485 // 2486 Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; 2487 VmWriteMemN (VmPtr, (UINTN) Op1, (UINTN) Op2); 2488 } 2489 // 2490 // Advance the instruction pointer 2491 // 2492 VmPtr->Ip += Size; 2493 return EFI_SUCCESS; 2494 } 2495 2496 2497 /** 2498 Execute the EBC MOVsnw instruction. This instruction loads a signed 2499 natural value from memory or register to another memory or register. On 2500 32-bit machines, the value gets sign-extended to 64 bits if the destination 2501 is a register. 2502 2503 Instruction syntax: 2504 2505 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16} 2506 2507 0:7 1=>operand1 index present 2508 0:6 1=>operand2 index present 2509 2510 @param VmPtr A pointer to a VM context. 2511 2512 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 2513 @retval EFI_SUCCESS The instruction is executed successfully. 2514 2515 **/ 2516 EFI_STATUS 2517 ExecuteMOVsnw ( 2518 IN VM_CONTEXT *VmPtr 2519 ) 2520 { 2521 UINT8 Opcode; 2522 UINT8 Operands; 2523 UINT8 Size; 2524 INT16 Op1Index; 2525 INT16 Op2Index; 2526 UINT64 Op2; 2527 2528 // 2529 // Get the opcode and operand bytes 2530 // 2531 Opcode = GETOPCODE (VmPtr); 2532 Operands = GETOPERANDS (VmPtr); 2533 2534 Op1Index = Op2Index = 0; 2535 2536 // 2537 // Get the indexes if present. 2538 // 2539 Size = 2; 2540 if ((Opcode & OPCODE_M_IMMED_OP1) !=0) { 2541 if (OPERAND1_INDIRECT (Operands)) { 2542 Op1Index = VmReadIndex16 (VmPtr, 2); 2543 } else { 2544 // 2545 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2 2546 // 2547 EbcDebugSignalException ( 2548 EXCEPT_EBC_INSTRUCTION_ENCODING, 2549 EXCEPTION_FLAG_FATAL, 2550 VmPtr 2551 ); 2552 return EFI_UNSUPPORTED; 2553 } 2554 2555 Size += sizeof (UINT16); 2556 } 2557 2558 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 2559 if (OPERAND2_INDIRECT (Operands)) { 2560 Op2Index = VmReadIndex16 (VmPtr, Size); 2561 } else { 2562 Op2Index = VmReadImmed16 (VmPtr, Size); 2563 } 2564 2565 Size += sizeof (UINT16); 2566 } 2567 // 2568 // Get the data from the source. 2569 // 2570 Op2 = (UINT64)(INT64)(INTN)(VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Op2Index); 2571 if (OPERAND2_INDIRECT (Operands)) { 2572 Op2 = (UINT64)(INT64)(INTN)VmReadMemN (VmPtr, (UINTN) Op2); 2573 } 2574 // 2575 // Now write back the result. 2576 // 2577 if (!OPERAND1_INDIRECT (Operands)) { 2578 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; 2579 } else { 2580 VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2); 2581 } 2582 // 2583 // Advance the instruction pointer 2584 // 2585 VmPtr->Ip += Size; 2586 return EFI_SUCCESS; 2587 } 2588 2589 2590 /** 2591 Execute the EBC MOVsnw instruction. This instruction loads a signed 2592 natural value from memory or register to another memory or register. On 2593 32-bit machines, the value gets sign-extended to 64 bits if the destination 2594 is a register. 2595 2596 Instruction syntax: 2597 2598 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32} 2599 2600 0:7 1=>operand1 index present 2601 0:6 1=>operand2 index present 2602 2603 @param VmPtr A pointer to a VM context. 2604 2605 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 2606 @retval EFI_SUCCESS The instruction is executed successfully. 2607 2608 **/ 2609 EFI_STATUS 2610 ExecuteMOVsnd ( 2611 IN VM_CONTEXT *VmPtr 2612 ) 2613 { 2614 UINT8 Opcode; 2615 UINT8 Operands; 2616 UINT8 Size; 2617 INT32 Op1Index; 2618 INT32 Op2Index; 2619 UINT64 Op2; 2620 2621 // 2622 // Get the opcode and operand bytes 2623 // 2624 Opcode = GETOPCODE (VmPtr); 2625 Operands = GETOPERANDS (VmPtr); 2626 2627 Op1Index = Op2Index = 0; 2628 2629 // 2630 // Get the indexes if present. 2631 // 2632 Size = 2; 2633 if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { 2634 if (OPERAND1_INDIRECT (Operands)) { 2635 Op1Index = VmReadIndex32 (VmPtr, 2); 2636 } else { 2637 // 2638 // Illegal form operand1 direct with index: MOVsnd R1 Index16,.. 2639 // 2640 EbcDebugSignalException ( 2641 EXCEPT_EBC_INSTRUCTION_ENCODING, 2642 EXCEPTION_FLAG_FATAL, 2643 VmPtr 2644 ); 2645 return EFI_UNSUPPORTED; 2646 } 2647 2648 Size += sizeof (UINT32); 2649 } 2650 2651 if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { 2652 if (OPERAND2_INDIRECT (Operands)) { 2653 Op2Index = VmReadIndex32 (VmPtr, Size); 2654 } else { 2655 Op2Index = VmReadImmed32 (VmPtr, Size); 2656 } 2657 2658 Size += sizeof (UINT32); 2659 } 2660 // 2661 // Get the data from the source. 2662 // 2663 Op2 = (UINT64)(INT64)(INTN)(INT64)(VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Op2Index); 2664 if (OPERAND2_INDIRECT (Operands)) { 2665 Op2 = (UINT64)(INT64)(INTN)(INT64)VmReadMemN (VmPtr, (UINTN) Op2); 2666 } 2667 // 2668 // Now write back the result. 2669 // 2670 if (!OPERAND1_INDIRECT (Operands)) { 2671 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; 2672 } else { 2673 VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2); 2674 } 2675 // 2676 // Advance the instruction pointer 2677 // 2678 VmPtr->Ip += Size; 2679 return EFI_SUCCESS; 2680 } 2681 2682 2683 /** 2684 Execute the EBC PUSHn instruction 2685 2686 Instruction syntax: 2687 PUSHn {@}R1 {Index16|Immed16} 2688 2689 @param VmPtr A pointer to a VM context. 2690 2691 @retval EFI_SUCCESS The instruction is executed successfully. 2692 2693 **/ 2694 EFI_STATUS 2695 ExecutePUSHn ( 2696 IN VM_CONTEXT *VmPtr 2697 ) 2698 { 2699 UINT8 Opcode; 2700 UINT8 Operands; 2701 INT16 Index16; 2702 UINTN DataN; 2703 2704 // 2705 // Get opcode and operands 2706 // 2707 Opcode = GETOPCODE (VmPtr); 2708 Operands = GETOPERANDS (VmPtr); 2709 2710 // 2711 // Get index if present 2712 // 2713 if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { 2714 if (OPERAND1_INDIRECT (Operands)) { 2715 Index16 = VmReadIndex16 (VmPtr, 2); 2716 } else { 2717 Index16 = VmReadImmed16 (VmPtr, 2); 2718 } 2719 2720 VmPtr->Ip += 4; 2721 } else { 2722 Index16 = 0; 2723 VmPtr->Ip += 2; 2724 } 2725 // 2726 // Get the data to push 2727 // 2728 if (OPERAND1_INDIRECT (Operands)) { 2729 DataN = VmReadMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); 2730 } else { 2731 DataN = (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16); 2732 } 2733 // 2734 // Adjust the stack down. 2735 // 2736 VmPtr->Gpr[0] -= sizeof (UINTN); 2737 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], DataN); 2738 return EFI_SUCCESS; 2739 } 2740 2741 2742 /** 2743 Execute the EBC PUSH instruction. 2744 2745 Instruction syntax: 2746 PUSH[32|64] {@}R1 {Index16|Immed16} 2747 2748 @param VmPtr A pointer to a VM context. 2749 2750 @retval EFI_SUCCESS The instruction is executed successfully. 2751 2752 **/ 2753 EFI_STATUS 2754 ExecutePUSH ( 2755 IN VM_CONTEXT *VmPtr 2756 ) 2757 { 2758 UINT8 Opcode; 2759 UINT8 Operands; 2760 UINT32 Data32; 2761 UINT64 Data64; 2762 INT16 Index16; 2763 2764 // 2765 // Get opcode and operands 2766 // 2767 Opcode = GETOPCODE (VmPtr); 2768 Operands = GETOPERANDS (VmPtr); 2769 // 2770 // Get immediate index if present, then advance the IP. 2771 // 2772 if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { 2773 if (OPERAND1_INDIRECT (Operands)) { 2774 Index16 = VmReadIndex16 (VmPtr, 2); 2775 } else { 2776 Index16 = VmReadImmed16 (VmPtr, 2); 2777 } 2778 2779 VmPtr->Ip += 4; 2780 } else { 2781 Index16 = 0; 2782 VmPtr->Ip += 2; 2783 } 2784 // 2785 // Get the data to push 2786 // 2787 if ((Opcode & PUSHPOP_M_64) != 0) { 2788 if (OPERAND1_INDIRECT (Operands)) { 2789 Data64 = VmReadMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); 2790 } else { 2791 Data64 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; 2792 } 2793 // 2794 // Adjust the stack down, then write back the data 2795 // 2796 VmPtr->Gpr[0] -= sizeof (UINT64); 2797 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], Data64); 2798 } else { 2799 // 2800 // 32-bit data 2801 // 2802 if (OPERAND1_INDIRECT (Operands)) { 2803 Data32 = VmReadMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); 2804 } else { 2805 Data32 = (UINT32) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; 2806 } 2807 // 2808 // Adjust the stack down and write the data 2809 // 2810 VmPtr->Gpr[0] -= sizeof (UINT32); 2811 VmWriteMem32 (VmPtr, (UINTN) VmPtr->Gpr[0], Data32); 2812 } 2813 2814 return EFI_SUCCESS; 2815 } 2816 2817 2818 /** 2819 Execute the EBC POPn instruction. 2820 2821 Instruction syntax: 2822 POPn {@}R1 {Index16|Immed16} 2823 2824 @param VmPtr A pointer to a VM context. 2825 2826 @retval EFI_SUCCESS The instruction is executed successfully. 2827 2828 **/ 2829 EFI_STATUS 2830 ExecutePOPn ( 2831 IN VM_CONTEXT *VmPtr 2832 ) 2833 { 2834 UINT8 Opcode; 2835 UINT8 Operands; 2836 INT16 Index16; 2837 UINTN DataN; 2838 2839 // 2840 // Get opcode and operands 2841 // 2842 Opcode = GETOPCODE (VmPtr); 2843 Operands = GETOPERANDS (VmPtr); 2844 // 2845 // Get immediate data if present, and advance the IP 2846 // 2847 if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { 2848 if (OPERAND1_INDIRECT (Operands)) { 2849 Index16 = VmReadIndex16 (VmPtr, 2); 2850 } else { 2851 Index16 = VmReadImmed16 (VmPtr, 2); 2852 } 2853 2854 VmPtr->Ip += 4; 2855 } else { 2856 Index16 = 0; 2857 VmPtr->Ip += 2; 2858 } 2859 // 2860 // Read the data off the stack, then adjust the stack pointer 2861 // 2862 DataN = VmReadMemN (VmPtr, (UINTN) VmPtr->Gpr[0]); 2863 VmPtr->Gpr[0] += sizeof (UINTN); 2864 // 2865 // Do the write-back 2866 // 2867 if (OPERAND1_INDIRECT (Operands)) { 2868 VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), DataN); 2869 } else { 2870 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) (UINT64) ((UINTN) DataN + Index16); 2871 } 2872 2873 return EFI_SUCCESS; 2874 } 2875 2876 2877 /** 2878 Execute the EBC POP instruction. 2879 2880 Instruction syntax: 2881 POPn {@}R1 {Index16|Immed16} 2882 2883 @param VmPtr A pointer to a VM context. 2884 2885 @retval EFI_SUCCESS The instruction is executed successfully. 2886 2887 **/ 2888 EFI_STATUS 2889 ExecutePOP ( 2890 IN VM_CONTEXT *VmPtr 2891 ) 2892 { 2893 UINT8 Opcode; 2894 UINT8 Operands; 2895 INT16 Index16; 2896 INT32 Data32; 2897 UINT64 Data64; 2898 2899 // 2900 // Get opcode and operands 2901 // 2902 Opcode = GETOPCODE (VmPtr); 2903 Operands = GETOPERANDS (VmPtr); 2904 // 2905 // Get immediate data if present, and advance the IP. 2906 // 2907 if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { 2908 if (OPERAND1_INDIRECT (Operands)) { 2909 Index16 = VmReadIndex16 (VmPtr, 2); 2910 } else { 2911 Index16 = VmReadImmed16 (VmPtr, 2); 2912 } 2913 2914 VmPtr->Ip += 4; 2915 } else { 2916 Index16 = 0; 2917 VmPtr->Ip += 2; 2918 } 2919 // 2920 // Get the data off the stack, then write it to the appropriate location 2921 // 2922 if ((Opcode & PUSHPOP_M_64) != 0) { 2923 // 2924 // Read the data off the stack, then adjust the stack pointer 2925 // 2926 Data64 = VmReadMem64 (VmPtr, (UINTN) VmPtr->Gpr[0]); 2927 VmPtr->Gpr[0] += sizeof (UINT64); 2928 // 2929 // Do the write-back 2930 // 2931 if (OPERAND1_INDIRECT (Operands)) { 2932 VmWriteMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), Data64); 2933 } else { 2934 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Data64 + Index16; 2935 } 2936 } else { 2937 // 2938 // 32-bit pop. Read it off the stack and adjust the stack pointer 2939 // 2940 Data32 = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[0]); 2941 VmPtr->Gpr[0] += sizeof (UINT32); 2942 // 2943 // Do the write-back 2944 // 2945 if (OPERAND1_INDIRECT (Operands)) { 2946 VmWriteMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), Data32); 2947 } else { 2948 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) Data32 + Index16; 2949 } 2950 } 2951 2952 return EFI_SUCCESS; 2953 } 2954 2955 2956 /** 2957 Implements the EBC CALL instruction. 2958 2959 Instruction format: 2960 CALL64 Immed64 2961 CALL32 {@}R1 {Immed32|Index32} 2962 CALLEX64 Immed64 2963 CALLEX16 {@}R1 {Immed32} 2964 2965 If Rx == R0, then it's a PC relative call to PC = PC + imm32. 2966 2967 @param VmPtr A pointer to a VM context. 2968 2969 @retval EFI_SUCCESS The instruction is executed successfully. 2970 2971 **/ 2972 EFI_STATUS 2973 ExecuteCALL ( 2974 IN VM_CONTEXT *VmPtr 2975 ) 2976 { 2977 UINT8 Opcode; 2978 UINT8 Operands; 2979 INT32 Immed32; 2980 UINT8 Size; 2981 INT64 Immed64; 2982 VOID *FramePtr; 2983 2984 // 2985 // Get opcode and operands 2986 // 2987 Opcode = GETOPCODE (VmPtr); 2988 Operands = GETOPERANDS (VmPtr); 2989 2990 if ((Operands & OPERAND_M_NATIVE_CALL) != 0) { 2991 EbcDebuggerHookCALLEXStart (VmPtr); 2992 } else { 2993 EbcDebuggerHookCALLStart (VmPtr); 2994 } 2995 2996 // 2997 // Assign these as well to avoid compiler warnings 2998 // 2999 Immed64 = 0; 3000 Immed32 = 0; 3001 3002 FramePtr = VmPtr->FramePtr; 3003 // 3004 // Determine the instruction size, and get immediate data if present 3005 // 3006 if ((Opcode & OPCODE_M_IMMDATA) != 0) { 3007 if ((Opcode & OPCODE_M_IMMDATA64) != 0) { 3008 Immed64 = VmReadImmed64 (VmPtr, 2); 3009 Size = 10; 3010 } else { 3011 // 3012 // If register operand is indirect, then the immediate data is an index 3013 // 3014 if (OPERAND1_INDIRECT (Operands)) { 3015 Immed32 = VmReadIndex32 (VmPtr, 2); 3016 } else { 3017 Immed32 = VmReadImmed32 (VmPtr, 2); 3018 } 3019 3020 Size = 6; 3021 } 3022 } else { 3023 Size = 2; 3024 } 3025 // 3026 // If it's a call to EBC, adjust the stack pointer down 16 bytes and 3027 // put our return address and frame pointer on the VM stack. 3028 // 3029 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { 3030 VmPtr->Gpr[0] -= 8; 3031 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr); 3032 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0]; 3033 VmPtr->Gpr[0] -= 8; 3034 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size)); 3035 } 3036 // 3037 // If 64-bit data, then absolute jump only 3038 // 3039 if ((Opcode & OPCODE_M_IMMDATA64) != 0) { 3040 // 3041 // Native or EBC call? 3042 // 3043 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { 3044 VmPtr->Ip = (VMIP) (UINTN) Immed64; 3045 } else { 3046 // 3047 // Call external function, get the return value, and advance the IP 3048 // 3049 EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->Gpr[0], FramePtr, Size); 3050 } 3051 } else { 3052 // 3053 // Get the register data. If operand1 == 0, then ignore register and 3054 // take immediate data as relative or absolute address. 3055 // Compiler should take care of upper bits if 32-bit machine. 3056 // 3057 if (OPERAND1_REGNUM (Operands) != 0) { 3058 Immed64 = (UINT64) (UINTN) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; 3059 } 3060 // 3061 // Get final address 3062 // 3063 if (OPERAND1_INDIRECT (Operands)) { 3064 Immed64 = (INT64) (UINT64) (UINTN) VmReadMemN (VmPtr, (UINTN) (Immed64 + Immed32)); 3065 } else { 3066 Immed64 += Immed32; 3067 } 3068 // 3069 // Now determine if external call, and then if relative or absolute 3070 // 3071 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { 3072 // 3073 // EBC call. Relative or absolute? If relative, then it's relative to the 3074 // start of the next instruction. 3075 // 3076 if ((Operands & OPERAND_M_RELATIVE_ADDR) != 0) { 3077 VmPtr->Ip += Immed64 + Size; 3078 } else { 3079 VmPtr->Ip = (VMIP) (UINTN) Immed64; 3080 } 3081 } else { 3082 // 3083 // Native call. Relative or absolute? 3084 // 3085 if ((Operands & OPERAND_M_RELATIVE_ADDR) != 0) { 3086 EbcLLCALLEX (VmPtr, (UINTN) (Immed64 + VmPtr->Ip + Size), (UINTN) VmPtr->Gpr[0], FramePtr, Size); 3087 } else { 3088 if ((VmPtr->StopFlags & STOPFLAG_BREAK_ON_CALLEX) != 0) { 3089 CpuBreakpoint (); 3090 } 3091 3092 EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->Gpr[0], FramePtr, Size); 3093 } 3094 } 3095 } 3096 3097 if ((Operands & OPERAND_M_NATIVE_CALL) != 0) { 3098 EbcDebuggerHookCALLEXEnd (VmPtr); 3099 } else { 3100 EbcDebuggerHookCALLEnd (VmPtr); 3101 } 3102 3103 return EFI_SUCCESS; 3104 } 3105 3106 3107 /** 3108 Execute the EBC RET instruction. 3109 3110 Instruction syntax: 3111 RET 3112 3113 @param VmPtr A pointer to a VM context. 3114 3115 @retval EFI_SUCCESS The instruction is executed successfully. 3116 3117 **/ 3118 EFI_STATUS 3119 ExecuteRET ( 3120 IN VM_CONTEXT *VmPtr 3121 ) 3122 { 3123 3124 EbcDebuggerHookRETStart (VmPtr); 3125 3126 // 3127 // If we're at the top of the stack, then simply set the done 3128 // flag and return 3129 // 3130 if (VmPtr->StackRetAddr == (UINT64) VmPtr->Gpr[0]) { 3131 VmPtr->StopFlags |= STOPFLAG_APP_DONE; 3132 } else { 3133 // 3134 // Pull the return address off the VM app's stack and set the IP 3135 // to it 3136 // 3137 if (!IS_ALIGNED ((UINTN) VmPtr->Gpr[0], sizeof (UINT16))) { 3138 EbcDebugSignalException ( 3139 EXCEPT_EBC_ALIGNMENT_CHECK, 3140 EXCEPTION_FLAG_FATAL, 3141 VmPtr 3142 ); 3143 } 3144 // 3145 // Restore the IP and frame pointer from the stack 3146 // 3147 VmPtr->Ip = (VMIP) (UINTN) VmReadMem64 (VmPtr, (UINTN) VmPtr->Gpr[0]); 3148 VmPtr->Gpr[0] += 8; 3149 VmPtr->FramePtr = (VOID *) VmReadMemN (VmPtr, (UINTN) VmPtr->Gpr[0]); 3150 VmPtr->Gpr[0] += 8; 3151 } 3152 3153 3154 EbcDebuggerHookRETEnd (VmPtr); 3155 3156 return EFI_SUCCESS; 3157 } 3158 3159 3160 /** 3161 Execute the EBC CMP instruction. 3162 3163 Instruction syntax: 3164 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16} 3165 3166 @param VmPtr A pointer to a VM context. 3167 3168 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 3169 @retval EFI_SUCCESS The instruction is executed successfully. 3170 3171 **/ 3172 EFI_STATUS 3173 ExecuteCMP ( 3174 IN VM_CONTEXT *VmPtr 3175 ) 3176 { 3177 UINT8 Opcode; 3178 UINT8 Operands; 3179 UINT8 Size; 3180 INT16 Index16; 3181 UINT32 Flag; 3182 INT64 Op2; 3183 INT64 Op1; 3184 3185 // 3186 // Get opcode and operands 3187 // 3188 Opcode = GETOPCODE (VmPtr); 3189 Operands = GETOPERANDS (VmPtr); 3190 // 3191 // Get the register data we're going to compare to 3192 // 3193 Op1 = VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; 3194 // 3195 // Get immediate data 3196 // 3197 if ((Opcode & OPCODE_M_IMMDATA) != 0) { 3198 if (OPERAND2_INDIRECT (Operands)) { 3199 Index16 = VmReadIndex16 (VmPtr, 2); 3200 } else { 3201 Index16 = VmReadImmed16 (VmPtr, 2); 3202 } 3203 3204 Size = 4; 3205 } else { 3206 Index16 = 0; 3207 Size = 2; 3208 } 3209 // 3210 // Now get Op2 3211 // 3212 if (OPERAND2_INDIRECT (Operands)) { 3213 if ((Opcode & OPCODE_M_64BIT) != 0) { 3214 Op2 = (INT64) VmReadMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16)); 3215 } else { 3216 // 3217 // 32-bit operations. 0-extend the values for all cases. 3218 // 3219 Op2 = (INT64) (UINT64) ((UINT32) VmReadMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16))); 3220 } 3221 } else { 3222 Op2 = VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16; 3223 } 3224 // 3225 // Now do the compare 3226 // 3227 Flag = 0; 3228 if ((Opcode & OPCODE_M_64BIT) != 0) { 3229 // 3230 // 64-bit compares 3231 // 3232 switch (Opcode & OPCODE_M_OPCODE) { 3233 case OPCODE_CMPEQ: 3234 if (Op1 == Op2) { 3235 Flag = 1; 3236 } 3237 break; 3238 3239 case OPCODE_CMPLTE: 3240 if (Op1 <= Op2) { 3241 Flag = 1; 3242 } 3243 break; 3244 3245 case OPCODE_CMPGTE: 3246 if (Op1 >= Op2) { 3247 Flag = 1; 3248 } 3249 break; 3250 3251 case OPCODE_CMPULTE: 3252 if ((UINT64) Op1 <= (UINT64) Op2) { 3253 Flag = 1; 3254 } 3255 break; 3256 3257 case OPCODE_CMPUGTE: 3258 if ((UINT64) Op1 >= (UINT64) Op2) { 3259 Flag = 1; 3260 } 3261 break; 3262 3263 default: 3264 ASSERT (0); 3265 } 3266 } else { 3267 // 3268 // 32-bit compares 3269 // 3270 switch (Opcode & OPCODE_M_OPCODE) { 3271 case OPCODE_CMPEQ: 3272 if ((INT32) Op1 == (INT32) Op2) { 3273 Flag = 1; 3274 } 3275 break; 3276 3277 case OPCODE_CMPLTE: 3278 if ((INT32) Op1 <= (INT32) Op2) { 3279 Flag = 1; 3280 } 3281 break; 3282 3283 case OPCODE_CMPGTE: 3284 if ((INT32) Op1 >= (INT32) Op2) { 3285 Flag = 1; 3286 } 3287 break; 3288 3289 case OPCODE_CMPULTE: 3290 if ((UINT32) Op1 <= (UINT32) Op2) { 3291 Flag = 1; 3292 } 3293 break; 3294 3295 case OPCODE_CMPUGTE: 3296 if ((UINT32) Op1 >= (UINT32) Op2) { 3297 Flag = 1; 3298 } 3299 break; 3300 3301 default: 3302 ASSERT (0); 3303 } 3304 } 3305 // 3306 // Now set the flag accordingly for the comparison 3307 // 3308 if (Flag != 0) { 3309 VMFLAG_SET (VmPtr, VMFLAGS_CC); 3310 } else { 3311 VMFLAG_CLEAR (VmPtr, (UINT64)VMFLAGS_CC); 3312 } 3313 // 3314 // Advance the IP 3315 // 3316 VmPtr->Ip += Size; 3317 return EFI_SUCCESS; 3318 } 3319 3320 3321 /** 3322 Execute the EBC CMPI instruction 3323 3324 Instruction syntax: 3325 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32 3326 3327 @param VmPtr A pointer to a VM context. 3328 3329 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 3330 @retval EFI_SUCCESS The instruction is executed successfully. 3331 3332 **/ 3333 EFI_STATUS 3334 ExecuteCMPI ( 3335 IN VM_CONTEXT *VmPtr 3336 ) 3337 { 3338 UINT8 Opcode; 3339 UINT8 Operands; 3340 UINT8 Size; 3341 INT64 Op1; 3342 INT64 Op2; 3343 INT16 Index16; 3344 UINT32 Flag; 3345 3346 // 3347 // Get opcode and operands 3348 // 3349 Opcode = GETOPCODE (VmPtr); 3350 Operands = GETOPERANDS (VmPtr); 3351 3352 // 3353 // Get operand1 index if present 3354 // 3355 Size = 2; 3356 if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { 3357 Index16 = VmReadIndex16 (VmPtr, 2); 3358 Size += 2; 3359 } else { 3360 Index16 = 0; 3361 } 3362 // 3363 // Get operand1 data we're going to compare to 3364 // 3365 Op1 = (INT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; 3366 if (OPERAND1_INDIRECT (Operands)) { 3367 // 3368 // Indirect operand1. Fetch 32 or 64-bit value based on compare size. 3369 // 3370 if ((Opcode & OPCODE_M_CMPI64) != 0) { 3371 Op1 = (INT64) VmReadMem64 (VmPtr, (UINTN) Op1 + Index16); 3372 } else { 3373 Op1 = (INT64) VmReadMem32 (VmPtr, (UINTN) Op1 + Index16); 3374 } 3375 } else { 3376 // 3377 // Better not have been an index with direct. That is, CMPI R1 Index,... 3378 // is illegal. 3379 // 3380 if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { 3381 EbcDebugSignalException ( 3382 EXCEPT_EBC_INSTRUCTION_ENCODING, 3383 EXCEPTION_FLAG_ERROR, 3384 VmPtr 3385 ); 3386 VmPtr->Ip += Size; 3387 return EFI_UNSUPPORTED; 3388 } 3389 } 3390 // 3391 // Get immediate data -- 16- or 32-bit sign extended 3392 // 3393 if ((Opcode & OPCODE_M_CMPI32_DATA) != 0) { 3394 Op2 = (INT64) VmReadImmed32 (VmPtr, Size); 3395 Size += 4; 3396 } else { 3397 // 3398 // 16-bit immediate data. Sign extend always. 3399 // 3400 Op2 = (INT64) ((INT16) VmReadImmed16 (VmPtr, Size)); 3401 Size += 2; 3402 } 3403 // 3404 // Now do the compare 3405 // 3406 Flag = 0; 3407 if ((Opcode & OPCODE_M_CMPI64) != 0) { 3408 // 3409 // 64 bit comparison 3410 // 3411 switch (Opcode & OPCODE_M_OPCODE) { 3412 case OPCODE_CMPIEQ: 3413 if (Op1 == (INT64) Op2) { 3414 Flag = 1; 3415 } 3416 break; 3417 3418 case OPCODE_CMPILTE: 3419 if (Op1 <= (INT64) Op2) { 3420 Flag = 1; 3421 } 3422 break; 3423 3424 case OPCODE_CMPIGTE: 3425 if (Op1 >= (INT64) Op2) { 3426 Flag = 1; 3427 } 3428 break; 3429 3430 case OPCODE_CMPIULTE: 3431 if ((UINT64) Op1 <= (UINT64) ((UINT32) Op2)) { 3432 Flag = 1; 3433 } 3434 break; 3435 3436 case OPCODE_CMPIUGTE: 3437 if ((UINT64) Op1 >= (UINT64) ((UINT32) Op2)) { 3438 Flag = 1; 3439 } 3440 break; 3441 3442 default: 3443 ASSERT (0); 3444 } 3445 } else { 3446 // 3447 // 32-bit comparisons 3448 // 3449 switch (Opcode & OPCODE_M_OPCODE) { 3450 case OPCODE_CMPIEQ: 3451 if ((INT32) Op1 == Op2) { 3452 Flag = 1; 3453 } 3454 break; 3455 3456 case OPCODE_CMPILTE: 3457 if ((INT32) Op1 <= Op2) { 3458 Flag = 1; 3459 } 3460 break; 3461 3462 case OPCODE_CMPIGTE: 3463 if ((INT32) Op1 >= Op2) { 3464 Flag = 1; 3465 } 3466 break; 3467 3468 case OPCODE_CMPIULTE: 3469 if ((UINT32) Op1 <= (UINT32) Op2) { 3470 Flag = 1; 3471 } 3472 break; 3473 3474 case OPCODE_CMPIUGTE: 3475 if ((UINT32) Op1 >= (UINT32) Op2) { 3476 Flag = 1; 3477 } 3478 break; 3479 3480 default: 3481 ASSERT (0); 3482 } 3483 } 3484 // 3485 // Now set the flag accordingly for the comparison 3486 // 3487 if (Flag != 0) { 3488 VMFLAG_SET (VmPtr, VMFLAGS_CC); 3489 } else { 3490 VMFLAG_CLEAR (VmPtr, (UINT64)VMFLAGS_CC); 3491 } 3492 // 3493 // Advance the IP 3494 // 3495 VmPtr->Ip += Size; 3496 return EFI_SUCCESS; 3497 } 3498 3499 3500 /** 3501 Execute the EBC NOT instruction.s 3502 3503 Instruction syntax: 3504 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16} 3505 3506 @param VmPtr A pointer to a VM context. 3507 @param Op1 Operand 1 from the instruction 3508 @param Op2 Operand 2 from the instruction 3509 3510 @return ~Op2 3511 3512 **/ 3513 UINT64 3514 ExecuteNOT ( 3515 IN VM_CONTEXT *VmPtr, 3516 IN UINT64 Op1, 3517 IN UINT64 Op2 3518 ) 3519 { 3520 return ~Op2; 3521 } 3522 3523 3524 /** 3525 Execute the EBC NEG instruction. 3526 3527 Instruction syntax: 3528 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16} 3529 3530 @param VmPtr A pointer to a VM context. 3531 @param Op1 Operand 1 from the instruction 3532 @param Op2 Operand 2 from the instruction 3533 3534 @return Op2 * -1 3535 3536 **/ 3537 UINT64 3538 ExecuteNEG ( 3539 IN VM_CONTEXT *VmPtr, 3540 IN UINT64 Op1, 3541 IN UINT64 Op2 3542 ) 3543 { 3544 return ~Op2 + 1; 3545 } 3546 3547 3548 /** 3549 Execute the EBC ADD instruction. 3550 3551 Instruction syntax: 3552 ADD[32|64] {@}R1, {@}R2 {Index16} 3553 3554 @param VmPtr A pointer to a VM context. 3555 @param Op1 Operand 1 from the instruction 3556 @param Op2 Operand 2 from the instruction 3557 3558 @return Op1 + Op2 3559 3560 **/ 3561 UINT64 3562 ExecuteADD ( 3563 IN VM_CONTEXT *VmPtr, 3564 IN UINT64 Op1, 3565 IN UINT64 Op2 3566 ) 3567 { 3568 return Op1 + Op2; 3569 } 3570 3571 3572 /** 3573 Execute the EBC SUB instruction. 3574 3575 Instruction syntax: 3576 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} 3577 3578 @param VmPtr A pointer to a VM context. 3579 @param Op1 Operand 1 from the instruction 3580 @param Op2 Operand 2 from the instruction 3581 3582 @return Op1 - Op2 3583 3584 **/ 3585 UINT64 3586 ExecuteSUB ( 3587 IN VM_CONTEXT *VmPtr, 3588 IN UINT64 Op1, 3589 IN UINT64 Op2 3590 ) 3591 { 3592 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3593 return (UINT64) ((INT64) ((INT64) Op1 - (INT64) Op2)); 3594 } else { 3595 return (UINT64) ((INT64) ((INT32) Op1 - (INT32) Op2)); 3596 } 3597 } 3598 3599 3600 /** 3601 Execute the EBC MUL instruction. 3602 3603 Instruction syntax: 3604 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} 3605 3606 @param VmPtr A pointer to a VM context. 3607 @param Op1 Operand 1 from the instruction 3608 @param Op2 Operand 2 from the instruction 3609 3610 @return Op1 * Op2 3611 3612 **/ 3613 UINT64 3614 ExecuteMUL ( 3615 IN VM_CONTEXT *VmPtr, 3616 IN UINT64 Op1, 3617 IN UINT64 Op2 3618 ) 3619 { 3620 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3621 return MultS64x64 ((INT64)Op1, (INT64)Op2); 3622 } else { 3623 return (UINT64) ((INT64) ((INT32) Op1 * (INT32) Op2)); 3624 } 3625 } 3626 3627 3628 /** 3629 Execute the EBC MULU instruction 3630 3631 Instruction syntax: 3632 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16} 3633 3634 @param VmPtr A pointer to a VM context. 3635 @param Op1 Operand 1 from the instruction 3636 @param Op2 Operand 2 from the instruction 3637 3638 @return (unsigned)Op1 * (unsigned)Op2 3639 3640 **/ 3641 UINT64 3642 ExecuteMULU ( 3643 IN VM_CONTEXT *VmPtr, 3644 IN UINT64 Op1, 3645 IN UINT64 Op2 3646 ) 3647 { 3648 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3649 return MultU64x64 (Op1, Op2); 3650 } else { 3651 return (UINT64) ((UINT32) Op1 * (UINT32) Op2); 3652 } 3653 } 3654 3655 3656 /** 3657 Execute the EBC DIV instruction. 3658 3659 Instruction syntax: 3660 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16} 3661 3662 @param VmPtr A pointer to a VM context. 3663 @param Op1 Operand 1 from the instruction 3664 @param Op2 Operand 2 from the instruction 3665 3666 @return Op1 / Op2 3667 3668 **/ 3669 UINT64 3670 ExecuteDIV ( 3671 IN VM_CONTEXT *VmPtr, 3672 IN UINT64 Op1, 3673 IN UINT64 Op2 3674 ) 3675 { 3676 INT64 Remainder; 3677 3678 // 3679 // Check for divide-by-0 3680 // 3681 if (Op2 == 0) { 3682 EbcDebugSignalException ( 3683 EXCEPT_EBC_DIVIDE_ERROR, 3684 EXCEPTION_FLAG_FATAL, 3685 VmPtr 3686 ); 3687 3688 return 0; 3689 } else { 3690 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3691 return (UINT64) (DivS64x64Remainder (Op1, Op2, &Remainder)); 3692 } else { 3693 return (UINT64) ((INT64) ((INT32) Op1 / (INT32) Op2)); 3694 } 3695 } 3696 } 3697 3698 3699 /** 3700 Execute the EBC DIVU instruction 3701 3702 Instruction syntax: 3703 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16} 3704 3705 @param VmPtr A pointer to a VM context. 3706 @param Op1 Operand 1 from the instruction 3707 @param Op2 Operand 2 from the instruction 3708 3709 @return (unsigned)Op1 / (unsigned)Op2 3710 3711 **/ 3712 UINT64 3713 ExecuteDIVU ( 3714 IN VM_CONTEXT *VmPtr, 3715 IN UINT64 Op1, 3716 IN UINT64 Op2 3717 ) 3718 { 3719 UINT64 Remainder; 3720 3721 // 3722 // Check for divide-by-0 3723 // 3724 if (Op2 == 0) { 3725 EbcDebugSignalException ( 3726 EXCEPT_EBC_DIVIDE_ERROR, 3727 EXCEPTION_FLAG_FATAL, 3728 VmPtr 3729 ); 3730 return 0; 3731 } else { 3732 // 3733 // Get the destination register 3734 // 3735 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3736 return (UINT64) (DivU64x64Remainder (Op1, Op2, &Remainder)); 3737 } else { 3738 return (UINT64) ((UINT32) Op1 / (UINT32) Op2); 3739 } 3740 } 3741 } 3742 3743 3744 /** 3745 Execute the EBC MOD instruction. 3746 3747 Instruction syntax: 3748 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16} 3749 3750 @param VmPtr A pointer to a VM context. 3751 @param Op1 Operand 1 from the instruction 3752 @param Op2 Operand 2 from the instruction 3753 3754 @return Op1 MODULUS Op2 3755 3756 **/ 3757 UINT64 3758 ExecuteMOD ( 3759 IN VM_CONTEXT *VmPtr, 3760 IN UINT64 Op1, 3761 IN UINT64 Op2 3762 ) 3763 { 3764 INT64 Remainder; 3765 3766 // 3767 // Check for divide-by-0 3768 // 3769 if (Op2 == 0) { 3770 EbcDebugSignalException ( 3771 EXCEPT_EBC_DIVIDE_ERROR, 3772 EXCEPTION_FLAG_FATAL, 3773 VmPtr 3774 ); 3775 return 0; 3776 } else { 3777 DivS64x64Remainder ((INT64)Op1, (INT64)Op2, &Remainder); 3778 return Remainder; 3779 } 3780 } 3781 3782 3783 /** 3784 Execute the EBC MODU instruction. 3785 3786 Instruction syntax: 3787 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16} 3788 3789 @param VmPtr A pointer to a VM context. 3790 @param Op1 Operand 1 from the instruction 3791 @param Op2 Operand 2 from the instruction 3792 3793 @return Op1 UNSIGNED_MODULUS Op2 3794 3795 **/ 3796 UINT64 3797 ExecuteMODU ( 3798 IN VM_CONTEXT *VmPtr, 3799 IN UINT64 Op1, 3800 IN UINT64 Op2 3801 ) 3802 { 3803 UINT64 Remainder; 3804 3805 // 3806 // Check for divide-by-0 3807 // 3808 if (Op2 == 0) { 3809 EbcDebugSignalException ( 3810 EXCEPT_EBC_DIVIDE_ERROR, 3811 EXCEPTION_FLAG_FATAL, 3812 VmPtr 3813 ); 3814 return 0; 3815 } else { 3816 DivU64x64Remainder (Op1, Op2, &Remainder); 3817 return Remainder; 3818 } 3819 } 3820 3821 3822 /** 3823 Execute the EBC AND instruction. 3824 3825 Instruction syntax: 3826 AND[32|64] {@}R1, {@}R2 {Index16|Immed16} 3827 3828 @param VmPtr A pointer to a VM context. 3829 @param Op1 Operand 1 from the instruction 3830 @param Op2 Operand 2 from the instruction 3831 3832 @return Op1 AND Op2 3833 3834 **/ 3835 UINT64 3836 ExecuteAND ( 3837 IN VM_CONTEXT *VmPtr, 3838 IN UINT64 Op1, 3839 IN UINT64 Op2 3840 ) 3841 { 3842 return Op1 & Op2; 3843 } 3844 3845 3846 /** 3847 Execute the EBC OR instruction. 3848 3849 Instruction syntax: 3850 OR[32|64] {@}R1, {@}R2 {Index16|Immed16} 3851 3852 @param VmPtr A pointer to a VM context. 3853 @param Op1 Operand 1 from the instruction 3854 @param Op2 Operand 2 from the instruction 3855 3856 @return Op1 OR Op2 3857 3858 **/ 3859 UINT64 3860 ExecuteOR ( 3861 IN VM_CONTEXT *VmPtr, 3862 IN UINT64 Op1, 3863 IN UINT64 Op2 3864 ) 3865 { 3866 return Op1 | Op2; 3867 } 3868 3869 3870 /** 3871 Execute the EBC XOR instruction. 3872 3873 Instruction syntax: 3874 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16} 3875 3876 @param VmPtr A pointer to a VM context. 3877 @param Op1 Operand 1 from the instruction 3878 @param Op2 Operand 2 from the instruction 3879 3880 @return Op1 XOR Op2 3881 3882 **/ 3883 UINT64 3884 ExecuteXOR ( 3885 IN VM_CONTEXT *VmPtr, 3886 IN UINT64 Op1, 3887 IN UINT64 Op2 3888 ) 3889 { 3890 return Op1 ^ Op2; 3891 } 3892 3893 3894 /** 3895 Execute the EBC SHL shift left instruction. 3896 3897 Instruction syntax: 3898 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16} 3899 3900 @param VmPtr A pointer to a VM context. 3901 @param Op1 Operand 1 from the instruction 3902 @param Op2 Operand 2 from the instruction 3903 3904 @return Op1 << Op2 3905 3906 **/ 3907 UINT64 3908 ExecuteSHL ( 3909 IN VM_CONTEXT *VmPtr, 3910 IN UINT64 Op1, 3911 IN UINT64 Op2 3912 ) 3913 { 3914 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3915 return LShiftU64 (Op1, (UINTN)Op2); 3916 } else { 3917 return (UINT64) ((UINT32) ((UINT32) Op1 << (UINT32) Op2)); 3918 } 3919 } 3920 3921 3922 /** 3923 Execute the EBC SHR instruction. 3924 3925 Instruction syntax: 3926 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16} 3927 3928 @param VmPtr A pointer to a VM context. 3929 @param Op1 Operand 1 from the instruction 3930 @param Op2 Operand 2 from the instruction 3931 3932 @return Op1 >> Op2 (unsigned operands) 3933 3934 **/ 3935 UINT64 3936 ExecuteSHR ( 3937 IN VM_CONTEXT *VmPtr, 3938 IN UINT64 Op1, 3939 IN UINT64 Op2 3940 ) 3941 { 3942 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3943 return RShiftU64 (Op1, (UINTN)Op2); 3944 } else { 3945 return (UINT64) ((UINT32) Op1 >> (UINT32) Op2); 3946 } 3947 } 3948 3949 3950 /** 3951 Execute the EBC ASHR instruction. 3952 3953 Instruction syntax: 3954 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16} 3955 3956 @param VmPtr A pointer to a VM context. 3957 @param Op1 Operand 1 from the instruction 3958 @param Op2 Operand 2 from the instruction 3959 3960 @return Op1 >> Op2 (signed) 3961 3962 **/ 3963 UINT64 3964 ExecuteASHR ( 3965 IN VM_CONTEXT *VmPtr, 3966 IN UINT64 Op1, 3967 IN UINT64 Op2 3968 ) 3969 { 3970 if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { 3971 return ARShiftU64 (Op1, (UINTN)Op2); 3972 } else { 3973 return (UINT64) ((INT64) ((INT32) Op1 >> (UINT32) Op2)); 3974 } 3975 } 3976 3977 3978 /** 3979 Execute the EBC EXTNDB instruction to sign-extend a byte value. 3980 3981 Instruction syntax: 3982 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16} 3983 3984 @param VmPtr A pointer to a VM context. 3985 @param Op1 Operand 1 from the instruction 3986 @param Op2 Operand 2 from the instruction 3987 3988 @return (INT64)(INT8)Op2 3989 3990 **/ 3991 UINT64 3992 ExecuteEXTNDB ( 3993 IN VM_CONTEXT *VmPtr, 3994 IN UINT64 Op1, 3995 IN UINT64 Op2 3996 ) 3997 { 3998 INT8 Data8; 3999 INT64 Data64; 4000 // 4001 // Convert to byte, then return as 64-bit signed value to let compiler 4002 // sign-extend the value 4003 // 4004 Data8 = (INT8) Op2; 4005 Data64 = (INT64) Data8; 4006 4007 return (UINT64) Data64; 4008 } 4009 4010 4011 /** 4012 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value. 4013 4014 Instruction syntax: 4015 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16} 4016 4017 @param VmPtr A pointer to a VM context. 4018 @param Op1 Operand 1 from the instruction 4019 @param Op2 Operand 2 from the instruction 4020 4021 @return (INT64)(INT16)Op2 4022 4023 **/ 4024 UINT64 4025 ExecuteEXTNDW ( 4026 IN VM_CONTEXT *VmPtr, 4027 IN UINT64 Op1, 4028 IN UINT64 Op2 4029 ) 4030 { 4031 INT16 Data16; 4032 INT64 Data64; 4033 // 4034 // Convert to word, then return as 64-bit signed value to let compiler 4035 // sign-extend the value 4036 // 4037 Data16 = (INT16) Op2; 4038 Data64 = (INT64) Data16; 4039 4040 return (UINT64) Data64; 4041 } 4042 // 4043 // Execute the EBC EXTNDD instruction. 4044 // 4045 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16] 4046 // EXTNDD Dest, Source 4047 // 4048 // Operation: Dest <- SignExtended((DWORD)Source)) 4049 // 4050 4051 /** 4052 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value. 4053 4054 Instruction syntax: 4055 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16} 4056 4057 @param VmPtr A pointer to a VM context. 4058 @param Op1 Operand 1 from the instruction 4059 @param Op2 Operand 2 from the instruction 4060 4061 @return (INT64)(INT32)Op2 4062 4063 **/ 4064 UINT64 4065 ExecuteEXTNDD ( 4066 IN VM_CONTEXT *VmPtr, 4067 IN UINT64 Op1, 4068 IN UINT64 Op2 4069 ) 4070 { 4071 INT32 Data32; 4072 INT64 Data64; 4073 // 4074 // Convert to 32-bit value, then return as 64-bit signed value to let compiler 4075 // sign-extend the value 4076 // 4077 Data32 = (INT32) Op2; 4078 Data64 = (INT64) Data32; 4079 4080 return (UINT64) Data64; 4081 } 4082 4083 4084 /** 4085 Execute all the EBC signed data manipulation instructions. 4086 Since the EBC data manipulation instructions all have the same basic form, 4087 they can share the code that does the fetch of operands and the write-back 4088 of the result. This function performs the fetch of the operands (even if 4089 both are not needed to be fetched, like NOT instruction), dispatches to the 4090 appropriate subfunction, then writes back the returned result. 4091 4092 Format: 4093 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 4094 4095 @param VmPtr A pointer to VM context. 4096 4097 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 4098 @retval EFI_SUCCESS The instruction is executed successfully. 4099 4100 **/ 4101 EFI_STATUS 4102 ExecuteSignedDataManip ( 4103 IN VM_CONTEXT *VmPtr 4104 ) 4105 { 4106 // 4107 // Just call the data manipulation function with a flag indicating this 4108 // is a signed operation. 4109 // 4110 return ExecuteDataManip (VmPtr, TRUE); 4111 } 4112 4113 4114 /** 4115 Execute all the EBC unsigned data manipulation instructions. 4116 Since the EBC data manipulation instructions all have the same basic form, 4117 they can share the code that does the fetch of operands and the write-back 4118 of the result. This function performs the fetch of the operands (even if 4119 both are not needed to be fetched, like NOT instruction), dispatches to the 4120 appropriate subfunction, then writes back the returned result. 4121 4122 Format: 4123 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 4124 4125 @param VmPtr A pointer to VM context. 4126 4127 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 4128 @retval EFI_SUCCESS The instruction is executed successfully. 4129 4130 **/ 4131 EFI_STATUS 4132 ExecuteUnsignedDataManip ( 4133 IN VM_CONTEXT *VmPtr 4134 ) 4135 { 4136 // 4137 // Just call the data manipulation function with a flag indicating this 4138 // is not a signed operation. 4139 // 4140 return ExecuteDataManip (VmPtr, FALSE); 4141 } 4142 4143 4144 /** 4145 Execute all the EBC data manipulation instructions. 4146 Since the EBC data manipulation instructions all have the same basic form, 4147 they can share the code that does the fetch of operands and the write-back 4148 of the result. This function performs the fetch of the operands (even if 4149 both are not needed to be fetched, like NOT instruction), dispatches to the 4150 appropriate subfunction, then writes back the returned result. 4151 4152 Format: 4153 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} 4154 4155 @param VmPtr A pointer to VM context. 4156 @param IsSignedOp Indicates whether the operand is signed or not. 4157 4158 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 4159 @retval EFI_SUCCESS The instruction is executed successfully. 4160 4161 **/ 4162 EFI_STATUS 4163 ExecuteDataManip ( 4164 IN VM_CONTEXT *VmPtr, 4165 IN BOOLEAN IsSignedOp 4166 ) 4167 { 4168 UINT8 Opcode; 4169 INT16 Index16; 4170 UINT8 Operands; 4171 UINT8 Size; 4172 UINT64 Op1; 4173 UINT64 Op2; 4174 INTN DataManipDispatchTableIndex; 4175 4176 // 4177 // Get opcode and operands 4178 // 4179 Opcode = GETOPCODE (VmPtr); 4180 Operands = GETOPERANDS (VmPtr); 4181 4182 // 4183 // Determine if we have immediate data by the opcode 4184 // 4185 if ((Opcode & DATAMANIP_M_IMMDATA) != 0) { 4186 // 4187 // Index16 if Ry is indirect, or Immed16 if Ry direct. 4188 // 4189 if (OPERAND2_INDIRECT (Operands)) { 4190 Index16 = VmReadIndex16 (VmPtr, 2); 4191 } else { 4192 Index16 = VmReadImmed16 (VmPtr, 2); 4193 } 4194 4195 Size = 4; 4196 } else { 4197 Index16 = 0; 4198 Size = 2; 4199 } 4200 // 4201 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16} 4202 // 4203 Op2 = (UINT64) VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16; 4204 if (OPERAND2_INDIRECT (Operands)) { 4205 // 4206 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data 4207 // 4208 if ((Opcode & DATAMANIP_M_64) != 0) { 4209 Op2 = VmReadMem64 (VmPtr, (UINTN) Op2); 4210 } else { 4211 // 4212 // Read as signed value where appropriate. 4213 // 4214 if (IsSignedOp) { 4215 Op2 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op2)); 4216 } else { 4217 Op2 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op2); 4218 } 4219 } 4220 } else { 4221 if ((Opcode & DATAMANIP_M_64) == 0) { 4222 if (IsSignedOp) { 4223 Op2 = (UINT64) (INT64) ((INT32) Op2); 4224 } else { 4225 Op2 = (UINT64) ((UINT32) Op2); 4226 } 4227 } 4228 } 4229 // 4230 // Get operand1 (destination and sometimes also an actual operand) 4231 // of form {@}R1 4232 // 4233 Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; 4234 if (OPERAND1_INDIRECT (Operands)) { 4235 if ((Opcode & DATAMANIP_M_64) != 0) { 4236 Op1 = VmReadMem64 (VmPtr, (UINTN) Op1); 4237 } else { 4238 if (IsSignedOp) { 4239 Op1 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op1)); 4240 } else { 4241 Op1 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op1); 4242 } 4243 } 4244 } else { 4245 if ((Opcode & DATAMANIP_M_64) == 0) { 4246 if (IsSignedOp) { 4247 Op1 = (UINT64) (INT64) ((INT32) Op1); 4248 } else { 4249 Op1 = (UINT64) ((UINT32) Op1); 4250 } 4251 } 4252 } 4253 // 4254 // Dispatch to the computation function 4255 // 4256 DataManipDispatchTableIndex = (Opcode & OPCODE_M_OPCODE) - OPCODE_NOT; 4257 if ((DataManipDispatchTableIndex < 0) || 4258 (DataManipDispatchTableIndex >= ARRAY_SIZE (mDataManipDispatchTable))) { 4259 EbcDebugSignalException ( 4260 EXCEPT_EBC_INVALID_OPCODE, 4261 EXCEPTION_FLAG_ERROR, 4262 VmPtr 4263 ); 4264 // 4265 // Advance and return 4266 // 4267 VmPtr->Ip += Size; 4268 return EFI_UNSUPPORTED; 4269 } else { 4270 Op2 = mDataManipDispatchTable[DataManipDispatchTableIndex](VmPtr, Op1, Op2); 4271 } 4272 // 4273 // Write back the result. 4274 // 4275 if (OPERAND1_INDIRECT (Operands)) { 4276 Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; 4277 if ((Opcode & DATAMANIP_M_64) != 0) { 4278 VmWriteMem64 (VmPtr, (UINTN) Op1, Op2); 4279 } else { 4280 VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) Op2); 4281 } 4282 } else { 4283 // 4284 // Storage back to a register. Write back, clearing upper bits (as per 4285 // the specification) if 32-bit operation. 4286 // 4287 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; 4288 if ((Opcode & DATAMANIP_M_64) == 0) { 4289 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] &= 0xFFFFFFFF; 4290 } 4291 } 4292 // 4293 // Advance the instruction pointer 4294 // 4295 VmPtr->Ip += Size; 4296 return EFI_SUCCESS; 4297 } 4298 4299 4300 /** 4301 Execute the EBC LOADSP instruction. 4302 4303 Instruction syntax: 4304 LOADSP SP1, R2 4305 4306 @param VmPtr A pointer to a VM context. 4307 4308 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 4309 @retval EFI_SUCCESS The instruction is executed successfully. 4310 4311 **/ 4312 EFI_STATUS 4313 ExecuteLOADSP ( 4314 IN VM_CONTEXT *VmPtr 4315 ) 4316 { 4317 UINT8 Operands; 4318 4319 // 4320 // Get the operands 4321 // 4322 Operands = GETOPERANDS (VmPtr); 4323 4324 // 4325 // Do the operation 4326 // 4327 switch (OPERAND1_REGNUM (Operands)) { 4328 // 4329 // Set flags 4330 // 4331 case 0: 4332 // 4333 // Spec states that this instruction will not modify reserved bits in 4334 // the flags register. 4335 // 4336 VmPtr->Flags = (VmPtr->Flags &~VMFLAGS_ALL_VALID) | (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] & VMFLAGS_ALL_VALID); 4337 break; 4338 4339 default: 4340 EbcDebugSignalException ( 4341 EXCEPT_EBC_INSTRUCTION_ENCODING, 4342 EXCEPTION_FLAG_WARNING, 4343 VmPtr 4344 ); 4345 VmPtr->Ip += 2; 4346 return EFI_UNSUPPORTED; 4347 } 4348 4349 VmPtr->Ip += 2; 4350 return EFI_SUCCESS; 4351 } 4352 4353 4354 /** 4355 Execute the EBC STORESP instruction. 4356 4357 Instruction syntax: 4358 STORESP Rx, FLAGS|IP 4359 4360 @param VmPtr A pointer to a VM context. 4361 4362 @retval EFI_UNSUPPORTED The opcodes/operands is not supported. 4363 @retval EFI_SUCCESS The instruction is executed successfully. 4364 4365 **/ 4366 EFI_STATUS 4367 ExecuteSTORESP ( 4368 IN VM_CONTEXT *VmPtr 4369 ) 4370 { 4371 UINT8 Operands; 4372 4373 // 4374 // Get the operands 4375 // 4376 Operands = GETOPERANDS (VmPtr); 4377 4378 // 4379 // Do the operation 4380 // 4381 switch (OPERAND2_REGNUM (Operands)) { 4382 // 4383 // Get flags 4384 // 4385 case 0: 4386 // 4387 // Retrieve the value in the flags register, then clear reserved bits 4388 // 4389 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (UINT64) (VmPtr->Flags & VMFLAGS_ALL_VALID); 4390 break; 4391 4392 // 4393 // Get IP -- address of following instruction 4394 // 4395 case 1: 4396 VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (UINT64) (UINTN) VmPtr->Ip + 2; 4397 break; 4398 4399 default: 4400 EbcDebugSignalException ( 4401 EXCEPT_EBC_INSTRUCTION_ENCODING, 4402 EXCEPTION_FLAG_WARNING, 4403 VmPtr 4404 ); 4405 VmPtr->Ip += 2; 4406 return EFI_UNSUPPORTED; 4407 break; 4408 } 4409 4410 VmPtr->Ip += 2; 4411 return EFI_SUCCESS; 4412 } 4413 4414 4415 /** 4416 Decode a 16-bit index to determine the offset. Given an index value: 4417 4418 b15 - sign bit 4419 b14:12 - number of bits in this index assigned to natural units (=a) 4420 ba:11 - constant units = ConstUnits 4421 b0:a - natural units = NaturalUnits 4422 4423 Given this info, the offset can be computed by: 4424 offset = sign_bit * (ConstUnits + NaturalUnits * sizeof(UINTN)) 4425 4426 Max offset is achieved with index = 0x7FFF giving an offset of 4427 0x27B (32-bit machine) or 0x477 (64-bit machine). 4428 Min offset is achieved with index = 4429 4430 @param VmPtr A pointer to VM context. 4431 @param CodeOffset Offset from IP of the location of the 16-bit index 4432 to decode. 4433 4434 @return The decoded offset. 4435 4436 **/ 4437 INT16 4438 VmReadIndex16 ( 4439 IN VM_CONTEXT *VmPtr, 4440 IN UINT32 CodeOffset 4441 ) 4442 { 4443 UINT16 Index; 4444 INT16 Offset; 4445 INT16 ConstUnits; 4446 INT16 NaturalUnits; 4447 INT16 NBits; 4448 INT16 Mask; 4449 4450 // 4451 // First read the index from the code stream 4452 // 4453 Index = VmReadCode16 (VmPtr, CodeOffset); 4454 4455 // 4456 // Get the mask for NaturalUnits. First get the number of bits from the index. 4457 // 4458 NBits = (INT16) ((Index & 0x7000) >> 12); 4459 4460 // 4461 // Scale it for 16-bit indexes 4462 // 4463 NBits *= 2; 4464 4465 // 4466 // Now using the number of bits, create a mask. 4467 // 4468 Mask = (INT16) ((INT16)~0 << NBits); 4469 4470 // 4471 // Now using the mask, extract NaturalUnits from the lower bits of the index. 4472 // 4473 NaturalUnits = (INT16) (Index &~Mask); 4474 4475 // 4476 // Now compute ConstUnits 4477 // 4478 ConstUnits = (INT16) (((Index &~0xF000) & Mask) >> NBits); 4479 4480 Offset = (INT16) (NaturalUnits * sizeof (UINTN) + ConstUnits); 4481 4482 // 4483 // Now set the sign 4484 // 4485 if ((Index & 0x8000) != 0) { 4486 // 4487 // Do it the hard way to work around a bogus compiler warning 4488 // 4489 // Offset = -1 * Offset; 4490 // 4491 Offset = (INT16) ((INT32) Offset * -1); 4492 } 4493 4494 return Offset; 4495 } 4496 4497 4498 /** 4499 Decode a 32-bit index to determine the offset. 4500 4501 @param VmPtr A pointer to VM context. 4502 @param CodeOffset Offset from IP of the location of the 32-bit index 4503 to decode. 4504 4505 @return Converted index per EBC VM specification. 4506 4507 **/ 4508 INT32 4509 VmReadIndex32 ( 4510 IN VM_CONTEXT *VmPtr, 4511 IN UINT32 CodeOffset 4512 ) 4513 { 4514 UINT32 Index; 4515 INT32 Offset; 4516 INT32 ConstUnits; 4517 INT32 NaturalUnits; 4518 INT32 NBits; 4519 INT32 Mask; 4520 4521 Index = VmReadImmed32 (VmPtr, CodeOffset); 4522 4523 // 4524 // Get the mask for NaturalUnits. First get the number of bits from the index. 4525 // 4526 NBits = (Index & 0x70000000) >> 28; 4527 4528 // 4529 // Scale it for 32-bit indexes 4530 // 4531 NBits *= 4; 4532 4533 // 4534 // Now using the number of bits, create a mask. 4535 // 4536 Mask = (INT32)~0 << NBits; 4537 4538 // 4539 // Now using the mask, extract NaturalUnits from the lower bits of the index. 4540 // 4541 NaturalUnits = Index &~Mask; 4542 4543 // 4544 // Now compute ConstUnits 4545 // 4546 ConstUnits = ((Index &~0xF0000000) & Mask) >> NBits; 4547 4548 Offset = NaturalUnits * sizeof (UINTN) + ConstUnits; 4549 4550 // 4551 // Now set the sign 4552 // 4553 if ((Index & 0x80000000) != 0) { 4554 Offset = Offset * -1; 4555 } 4556 4557 return Offset; 4558 } 4559 4560 4561 /** 4562 Decode a 64-bit index to determine the offset. 4563 4564 @param VmPtr A pointer to VM context.s 4565 @param CodeOffset Offset from IP of the location of the 64-bit index 4566 to decode. 4567 4568 @return Converted index per EBC VM specification 4569 4570 **/ 4571 INT64 4572 VmReadIndex64 ( 4573 IN VM_CONTEXT *VmPtr, 4574 IN UINT32 CodeOffset 4575 ) 4576 { 4577 UINT64 Index; 4578 INT64 Offset; 4579 INT64 ConstUnits; 4580 INT64 NaturalUnits; 4581 INT64 NBits; 4582 INT64 Mask; 4583 4584 Index = VmReadCode64 (VmPtr, CodeOffset); 4585 4586 // 4587 // Get the mask for NaturalUnits. First get the number of bits from the index. 4588 // 4589 NBits = RShiftU64 ((Index & 0x7000000000000000ULL), 60); 4590 4591 // 4592 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3) 4593 // 4594 NBits = LShiftU64 ((UINT64)NBits, 3); 4595 4596 // 4597 // Now using the number of bits, create a mask. 4598 // 4599 Mask = (LShiftU64 ((UINT64)~0, (UINTN)NBits)); 4600 4601 // 4602 // Now using the mask, extract NaturalUnits from the lower bits of the index. 4603 // 4604 NaturalUnits = Index &~Mask; 4605 4606 // 4607 // Now compute ConstUnits 4608 // 4609 ConstUnits = ARShiftU64 (((Index &~0xF000000000000000ULL) & Mask), (UINTN)NBits); 4610 4611 Offset = MultU64x64 ((UINT64) NaturalUnits, sizeof (UINTN)) + ConstUnits; 4612 4613 // 4614 // Now set the sign 4615 // 4616 if ((Index & 0x8000000000000000ULL) != 0) { 4617 Offset = MultS64x64 (Offset, -1); 4618 } 4619 4620 return Offset; 4621 } 4622 4623 4624 /** 4625 Writes 8-bit data to memory address. 4626 4627 This routine is called by the EBC data 4628 movement instructions that write to memory. Since these writes 4629 may be to the stack, which looks like (high address on top) this, 4630 4631 [EBC entry point arguments] 4632 [VM stack] 4633 [EBC stack] 4634 4635 we need to detect all attempts to write to the EBC entry point argument 4636 stack area and adjust the address (which will initially point into the 4637 VM stack) to point into the EBC entry point arguments. 4638 4639 @param VmPtr A pointer to a VM context. 4640 @param Addr Address to write to. 4641 @param Data Value to write to Addr. 4642 4643 @retval EFI_SUCCESS The instruction is executed successfully. 4644 @retval Other Some error occurs when writing data to the address. 4645 4646 **/ 4647 EFI_STATUS 4648 VmWriteMem8 ( 4649 IN VM_CONTEXT *VmPtr, 4650 IN UINTN Addr, 4651 IN UINT8 Data 4652 ) 4653 { 4654 // 4655 // Convert the address if it's in the stack gap 4656 // 4657 Addr = ConvertStackAddr (VmPtr, Addr); 4658 *(UINT8 *) Addr = Data; 4659 return EFI_SUCCESS; 4660 } 4661 4662 /** 4663 Writes 16-bit data to memory address. 4664 4665 This routine is called by the EBC data 4666 movement instructions that write to memory. Since these writes 4667 may be to the stack, which looks like (high address on top) this, 4668 4669 [EBC entry point arguments] 4670 [VM stack] 4671 [EBC stack] 4672 4673 we need to detect all attempts to write to the EBC entry point argument 4674 stack area and adjust the address (which will initially point into the 4675 VM stack) to point into the EBC entry point arguments. 4676 4677 @param VmPtr A pointer to a VM context. 4678 @param Addr Address to write to. 4679 @param Data Value to write to Addr. 4680 4681 @retval EFI_SUCCESS The instruction is executed successfully. 4682 @retval Other Some error occurs when writing data to the address. 4683 4684 **/ 4685 EFI_STATUS 4686 VmWriteMem16 ( 4687 IN VM_CONTEXT *VmPtr, 4688 IN UINTN Addr, 4689 IN UINT16 Data 4690 ) 4691 { 4692 EFI_STATUS Status; 4693 4694 // 4695 // Convert the address if it's in the stack gap 4696 // 4697 Addr = ConvertStackAddr (VmPtr, Addr); 4698 4699 // 4700 // Do a simple write if aligned 4701 // 4702 if (IS_ALIGNED (Addr, sizeof (UINT16))) { 4703 *(UINT16 *) Addr = Data; 4704 } else { 4705 // 4706 // Write as two bytes 4707 // 4708 MemoryFence (); 4709 if ((Status = VmWriteMem8 (VmPtr, Addr, (UINT8) Data)) != EFI_SUCCESS) { 4710 return Status; 4711 } 4712 4713 MemoryFence (); 4714 if ((Status = VmWriteMem8 (VmPtr, Addr + 1, (UINT8) (Data >> 8))) != EFI_SUCCESS) { 4715 return Status; 4716 } 4717 4718 MemoryFence (); 4719 } 4720 4721 return EFI_SUCCESS; 4722 } 4723 4724 4725 /** 4726 Writes 32-bit data to memory address. 4727 4728 This routine is called by the EBC data 4729 movement instructions that write to memory. Since these writes 4730 may be to the stack, which looks like (high address on top) this, 4731 4732 [EBC entry point arguments] 4733 [VM stack] 4734 [EBC stack] 4735 4736 we need to detect all attempts to write to the EBC entry point argument 4737 stack area and adjust the address (which will initially point into the 4738 VM stack) to point into the EBC entry point arguments. 4739 4740 @param VmPtr A pointer to a VM context. 4741 @param Addr Address to write to. 4742 @param Data Value to write to Addr. 4743 4744 @retval EFI_SUCCESS The instruction is executed successfully. 4745 @retval Other Some error occurs when writing data to the address. 4746 4747 **/ 4748 EFI_STATUS 4749 VmWriteMem32 ( 4750 IN VM_CONTEXT *VmPtr, 4751 IN UINTN Addr, 4752 IN UINT32 Data 4753 ) 4754 { 4755 EFI_STATUS Status; 4756 4757 // 4758 // Convert the address if it's in the stack gap 4759 // 4760 Addr = ConvertStackAddr (VmPtr, Addr); 4761 4762 // 4763 // Do a simple write if aligned 4764 // 4765 if (IS_ALIGNED (Addr, sizeof (UINT32))) { 4766 *(UINT32 *) Addr = Data; 4767 } else { 4768 // 4769 // Write as two words 4770 // 4771 MemoryFence (); 4772 if ((Status = VmWriteMem16 (VmPtr, Addr, (UINT16) Data)) != EFI_SUCCESS) { 4773 return Status; 4774 } 4775 4776 MemoryFence (); 4777 if ((Status = VmWriteMem16 (VmPtr, Addr + sizeof (UINT16), (UINT16) (Data >> 16))) != EFI_SUCCESS) { 4778 return Status; 4779 } 4780 4781 MemoryFence (); 4782 } 4783 4784 return EFI_SUCCESS; 4785 } 4786 4787 4788 /** 4789 Writes 64-bit data to memory address. 4790 4791 This routine is called by the EBC data 4792 movement instructions that write to memory. Since these writes 4793 may be to the stack, which looks like (high address on top) this, 4794 4795 [EBC entry point arguments] 4796 [VM stack] 4797 [EBC stack] 4798 4799 we need to detect all attempts to write to the EBC entry point argument 4800 stack area and adjust the address (which will initially point into the 4801 VM stack) to point into the EBC entry point arguments. 4802 4803 @param VmPtr A pointer to a VM context. 4804 @param Addr Address to write to. 4805 @param Data Value to write to Addr. 4806 4807 @retval EFI_SUCCESS The instruction is executed successfully. 4808 @retval Other Some error occurs when writing data to the address. 4809 4810 **/ 4811 EFI_STATUS 4812 VmWriteMem64 ( 4813 IN VM_CONTEXT *VmPtr, 4814 IN UINTN Addr, 4815 IN UINT64 Data 4816 ) 4817 { 4818 EFI_STATUS Status; 4819 4820 // 4821 // Convert the address if it's in the stack gap 4822 // 4823 Addr = ConvertStackAddr (VmPtr, Addr); 4824 4825 // 4826 // Do a simple write if aligned 4827 // 4828 if (IS_ALIGNED (Addr, sizeof (UINT64))) { 4829 *(UINT64 *) Addr = Data; 4830 } else { 4831 // 4832 // Write as two 32-bit words 4833 // 4834 MemoryFence (); 4835 if ((Status = VmWriteMem32 (VmPtr, Addr, (UINT32) Data)) != EFI_SUCCESS) { 4836 return Status; 4837 } 4838 4839 MemoryFence (); 4840 if ((Status = VmWriteMem32 (VmPtr, Addr + sizeof (UINT32), (UINT32) RShiftU64(Data, 32))) != EFI_SUCCESS) { 4841 return Status; 4842 } 4843 4844 MemoryFence (); 4845 } 4846 4847 return EFI_SUCCESS; 4848 } 4849 4850 4851 /** 4852 Writes UINTN data to memory address. 4853 4854 This routine is called by the EBC data 4855 movement instructions that write to memory. Since these writes 4856 may be to the stack, which looks like (high address on top) this, 4857 4858 [EBC entry point arguments] 4859 [VM stack] 4860 [EBC stack] 4861 4862 we need to detect all attempts to write to the EBC entry point argument 4863 stack area and adjust the address (which will initially point into the 4864 VM stack) to point into the EBC entry point arguments. 4865 4866 @param VmPtr A pointer to a VM context. 4867 @param Addr Address to write to. 4868 @param Data Value to write to Addr. 4869 4870 @retval EFI_SUCCESS The instruction is executed successfully. 4871 @retval Other Some error occurs when writing data to the address. 4872 4873 **/ 4874 EFI_STATUS 4875 VmWriteMemN ( 4876 IN VM_CONTEXT *VmPtr, 4877 IN UINTN Addr, 4878 IN UINTN Data 4879 ) 4880 { 4881 EFI_STATUS Status; 4882 UINTN Index; 4883 4884 Status = EFI_SUCCESS; 4885 4886 // 4887 // Convert the address if it's in the stack gap 4888 // 4889 Addr = ConvertStackAddr (VmPtr, Addr); 4890 4891 // 4892 // Do a simple write if aligned 4893 // 4894 if (IS_ALIGNED (Addr, sizeof (UINTN))) { 4895 *(UINTN *) Addr = Data; 4896 } else { 4897 for (Index = 0; Index < sizeof (UINTN) / sizeof (UINT32); Index++) { 4898 MemoryFence (); 4899 Status = VmWriteMem32 (VmPtr, Addr + Index * sizeof (UINT32), (UINT32) Data); 4900 MemoryFence (); 4901 Data = (UINTN) RShiftU64 ((UINT64)Data, 32); 4902 } 4903 } 4904 4905 return Status; 4906 } 4907 4908 4909 /** 4910 Reads 8-bit immediate value at the offset. 4911 4912 This routine is called by the EBC execute 4913 functions to read EBC immediate values from the code stream. 4914 Since we can't assume alignment, each tries to read in the biggest 4915 chunks size available, but will revert to smaller reads if necessary. 4916 4917 @param VmPtr A pointer to a VM context. 4918 @param Offset offset from IP of the code bytes to read. 4919 4920 @return Signed data of the requested size from the specified address. 4921 4922 **/ 4923 INT8 4924 VmReadImmed8 ( 4925 IN VM_CONTEXT *VmPtr, 4926 IN UINT32 Offset 4927 ) 4928 { 4929 // 4930 // Simply return the data in flat memory space 4931 // 4932 return * (INT8 *) (VmPtr->Ip + Offset); 4933 } 4934 4935 /** 4936 Reads 16-bit immediate value at the offset. 4937 4938 This routine is called by the EBC execute 4939 functions to read EBC immediate values from the code stream. 4940 Since we can't assume alignment, each tries to read in the biggest 4941 chunks size available, but will revert to smaller reads if necessary. 4942 4943 @param VmPtr A pointer to a VM context. 4944 @param Offset offset from IP of the code bytes to read. 4945 4946 @return Signed data of the requested size from the specified address. 4947 4948 **/ 4949 INT16 4950 VmReadImmed16 ( 4951 IN VM_CONTEXT *VmPtr, 4952 IN UINT32 Offset 4953 ) 4954 { 4955 // 4956 // Read direct if aligned 4957 // 4958 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (INT16))) { 4959 return * (INT16 *) (VmPtr->Ip + Offset); 4960 } else { 4961 // 4962 // All code word reads should be aligned 4963 // 4964 EbcDebugSignalException ( 4965 EXCEPT_EBC_ALIGNMENT_CHECK, 4966 EXCEPTION_FLAG_WARNING, 4967 VmPtr 4968 ); 4969 } 4970 // 4971 // Return unaligned data 4972 // 4973 return (INT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8)); 4974 } 4975 4976 4977 /** 4978 Reads 32-bit immediate value at the offset. 4979 4980 This routine is called by the EBC execute 4981 functions to read EBC immediate values from the code stream. 4982 Since we can't assume alignment, each tries to read in the biggest 4983 chunks size available, but will revert to smaller reads if necessary. 4984 4985 @param VmPtr A pointer to a VM context. 4986 @param Offset offset from IP of the code bytes to read. 4987 4988 @return Signed data of the requested size from the specified address. 4989 4990 **/ 4991 INT32 4992 VmReadImmed32 ( 4993 IN VM_CONTEXT *VmPtr, 4994 IN UINT32 Offset 4995 ) 4996 { 4997 UINT32 Data; 4998 4999 // 5000 // Read direct if aligned 5001 // 5002 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) { 5003 return * (INT32 *) (VmPtr->Ip + Offset); 5004 } 5005 // 5006 // Return unaligned data 5007 // 5008 Data = (UINT32) VmReadCode16 (VmPtr, Offset); 5009 Data |= (UINT32)(VmReadCode16 (VmPtr, Offset + 2) << 16); 5010 return Data; 5011 } 5012 5013 5014 /** 5015 Reads 64-bit immediate value at the offset. 5016 5017 This routine is called by the EBC execute 5018 functions to read EBC immediate values from the code stream. 5019 Since we can't assume alignment, each tries to read in the biggest 5020 chunks size available, but will revert to smaller reads if necessary. 5021 5022 @param VmPtr A pointer to a VM context. 5023 @param Offset offset from IP of the code bytes to read. 5024 5025 @return Signed data of the requested size from the specified address. 5026 5027 **/ 5028 INT64 5029 VmReadImmed64 ( 5030 IN VM_CONTEXT *VmPtr, 5031 IN UINT32 Offset 5032 ) 5033 { 5034 UINT64 Data64; 5035 UINT32 Data32; 5036 UINT8 *Ptr; 5037 5038 // 5039 // Read direct if aligned 5040 // 5041 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) { 5042 return * (UINT64 *) (VmPtr->Ip + Offset); 5043 } 5044 // 5045 // Return unaligned data. 5046 // 5047 Ptr = (UINT8 *) &Data64; 5048 Data32 = VmReadCode32 (VmPtr, Offset); 5049 *(UINT32 *) Ptr = Data32; 5050 Ptr += sizeof (Data32); 5051 Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32)); 5052 *(UINT32 *) Ptr = Data32; 5053 return Data64; 5054 } 5055 5056 5057 /** 5058 Reads 16-bit unsigned data from the code stream. 5059 5060 This routine provides the ability to read raw unsigned data from the code 5061 stream. 5062 5063 @param VmPtr A pointer to VM context 5064 @param Offset Offset from current IP to the raw data to read. 5065 5066 @return The raw unsigned 16-bit value from the code stream. 5067 5068 **/ 5069 UINT16 5070 VmReadCode16 ( 5071 IN VM_CONTEXT *VmPtr, 5072 IN UINT32 Offset 5073 ) 5074 { 5075 // 5076 // Read direct if aligned 5077 // 5078 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT16))) { 5079 return * (UINT16 *) (VmPtr->Ip + Offset); 5080 } else { 5081 // 5082 // All code word reads should be aligned 5083 // 5084 EbcDebugSignalException ( 5085 EXCEPT_EBC_ALIGNMENT_CHECK, 5086 EXCEPTION_FLAG_WARNING, 5087 VmPtr 5088 ); 5089 } 5090 // 5091 // Return unaligned data 5092 // 5093 return (UINT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8)); 5094 } 5095 5096 5097 /** 5098 Reads 32-bit unsigned data from the code stream. 5099 5100 This routine provides the ability to read raw unsigned data from the code 5101 stream. 5102 5103 @param VmPtr A pointer to VM context 5104 @param Offset Offset from current IP to the raw data to read. 5105 5106 @return The raw unsigned 32-bit value from the code stream. 5107 5108 **/ 5109 UINT32 5110 VmReadCode32 ( 5111 IN VM_CONTEXT *VmPtr, 5112 IN UINT32 Offset 5113 ) 5114 { 5115 UINT32 Data; 5116 // 5117 // Read direct if aligned 5118 // 5119 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) { 5120 return * (UINT32 *) (VmPtr->Ip + Offset); 5121 } 5122 // 5123 // Return unaligned data 5124 // 5125 Data = (UINT32) VmReadCode16 (VmPtr, Offset); 5126 Data |= (VmReadCode16 (VmPtr, Offset + 2) << 16); 5127 return Data; 5128 } 5129 5130 5131 /** 5132 Reads 64-bit unsigned data from the code stream. 5133 5134 This routine provides the ability to read raw unsigned data from the code 5135 stream. 5136 5137 @param VmPtr A pointer to VM context 5138 @param Offset Offset from current IP to the raw data to read. 5139 5140 @return The raw unsigned 64-bit value from the code stream. 5141 5142 **/ 5143 UINT64 5144 VmReadCode64 ( 5145 IN VM_CONTEXT *VmPtr, 5146 IN UINT32 Offset 5147 ) 5148 { 5149 UINT64 Data64; 5150 UINT32 Data32; 5151 UINT8 *Ptr; 5152 5153 // 5154 // Read direct if aligned 5155 // 5156 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) { 5157 return * (UINT64 *) (VmPtr->Ip + Offset); 5158 } 5159 // 5160 // Return unaligned data. 5161 // 5162 Ptr = (UINT8 *) &Data64; 5163 Data32 = VmReadCode32 (VmPtr, Offset); 5164 *(UINT32 *) Ptr = Data32; 5165 Ptr += sizeof (Data32); 5166 Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32)); 5167 *(UINT32 *) Ptr = Data32; 5168 return Data64; 5169 } 5170 5171 5172 /** 5173 Reads 8-bit data form the memory address. 5174 5175 @param VmPtr A pointer to VM context. 5176 @param Addr The memory address. 5177 5178 @return The 8-bit value from the memory address. 5179 5180 **/ 5181 UINT8 5182 VmReadMem8 ( 5183 IN VM_CONTEXT *VmPtr, 5184 IN UINTN Addr 5185 ) 5186 { 5187 // 5188 // Convert the address if it's in the stack gap 5189 // 5190 Addr = ConvertStackAddr (VmPtr, Addr); 5191 // 5192 // Simply return the data in flat memory space 5193 // 5194 return * (UINT8 *) Addr; 5195 } 5196 5197 /** 5198 Reads 16-bit data form the memory address. 5199 5200 @param VmPtr A pointer to VM context. 5201 @param Addr The memory address. 5202 5203 @return The 16-bit value from the memory address. 5204 5205 **/ 5206 UINT16 5207 VmReadMem16 ( 5208 IN VM_CONTEXT *VmPtr, 5209 IN UINTN Addr 5210 ) 5211 { 5212 // 5213 // Convert the address if it's in the stack gap 5214 // 5215 Addr = ConvertStackAddr (VmPtr, Addr); 5216 // 5217 // Read direct if aligned 5218 // 5219 if (IS_ALIGNED (Addr, sizeof (UINT16))) { 5220 return * (UINT16 *) Addr; 5221 } 5222 // 5223 // Return unaligned data 5224 // 5225 return (UINT16) (*(UINT8 *) Addr + (*(UINT8 *) (Addr + 1) << 8)); 5226 } 5227 5228 /** 5229 Reads 32-bit data form the memory address. 5230 5231 @param VmPtr A pointer to VM context. 5232 @param Addr The memory address. 5233 5234 @return The 32-bit value from the memory address. 5235 5236 **/ 5237 UINT32 5238 VmReadMem32 ( 5239 IN VM_CONTEXT *VmPtr, 5240 IN UINTN Addr 5241 ) 5242 { 5243 UINT32 Data; 5244 5245 // 5246 // Convert the address if it's in the stack gap 5247 // 5248 Addr = ConvertStackAddr (VmPtr, Addr); 5249 // 5250 // Read direct if aligned 5251 // 5252 if (IS_ALIGNED (Addr, sizeof (UINT32))) { 5253 return * (UINT32 *) Addr; 5254 } 5255 // 5256 // Return unaligned data 5257 // 5258 Data = (UINT32) VmReadMem16 (VmPtr, Addr); 5259 Data |= (VmReadMem16 (VmPtr, Addr + 2) << 16); 5260 return Data; 5261 } 5262 5263 /** 5264 Reads 64-bit data form the memory address. 5265 5266 @param VmPtr A pointer to VM context. 5267 @param Addr The memory address. 5268 5269 @return The 64-bit value from the memory address. 5270 5271 **/ 5272 UINT64 5273 VmReadMem64 ( 5274 IN VM_CONTEXT *VmPtr, 5275 IN UINTN Addr 5276 ) 5277 { 5278 UINT64 Data; 5279 UINT32 Data32; 5280 5281 // 5282 // Convert the address if it's in the stack gap 5283 // 5284 Addr = ConvertStackAddr (VmPtr, Addr); 5285 5286 // 5287 // Read direct if aligned 5288 // 5289 if (IS_ALIGNED (Addr, sizeof (UINT64))) { 5290 return * (UINT64 *) Addr; 5291 } 5292 // 5293 // Return unaligned data. Assume little endian. 5294 // 5295 Data32 = VmReadMem32 (VmPtr, Addr); 5296 Data = (UINT64) VmReadMem32 (VmPtr, Addr + sizeof (UINT32)); 5297 Data = LShiftU64 (Data, 32) | Data32; 5298 return Data; 5299 } 5300 5301 5302 /** 5303 Given an address that EBC is going to read from or write to, return 5304 an appropriate address that accounts for a gap in the stack. 5305 The stack for this application looks like this (high addr on top) 5306 [EBC entry point arguments] 5307 [VM stack] 5308 [EBC stack] 5309 The EBC assumes that its arguments are at the top of its stack, which 5310 is where the VM stack is really. Therefore if the EBC does memory 5311 accesses into the VM stack area, then we need to convert the address 5312 to point to the EBC entry point arguments area. Do this here. 5313 5314 @param VmPtr A Pointer to VM context. 5315 @param Addr Address of interest 5316 5317 @return The unchanged address if it's not in the VM stack region. Otherwise, 5318 adjust for the stack gap and return the modified address. 5319 5320 **/ 5321 UINTN 5322 ConvertStackAddr ( 5323 IN VM_CONTEXT *VmPtr, 5324 IN UINTN Addr 5325 ) 5326 { 5327 ASSERT(((Addr < VmPtr->LowStackTop) || (Addr > VmPtr->HighStackBottom))); 5328 return Addr; 5329 } 5330 5331 5332 /** 5333 Read a natural value from memory. May or may not be aligned. 5334 5335 @param VmPtr current VM context 5336 @param Addr the address to read from 5337 5338 @return The natural value at address Addr. 5339 5340 **/ 5341 UINTN 5342 VmReadMemN ( 5343 IN VM_CONTEXT *VmPtr, 5344 IN UINTN Addr 5345 ) 5346 { 5347 UINTN Data; 5348 volatile UINT32 Size; 5349 UINT8 *FromPtr; 5350 UINT8 *ToPtr; 5351 // 5352 // Convert the address if it's in the stack gap 5353 // 5354 Addr = ConvertStackAddr (VmPtr, Addr); 5355 // 5356 // Read direct if aligned 5357 // 5358 if (IS_ALIGNED (Addr, sizeof (UINTN))) { 5359 return * (UINTN *) Addr; 5360 } 5361 // 5362 // Return unaligned data 5363 // 5364 Data = 0; 5365 FromPtr = (UINT8 *) Addr; 5366 ToPtr = (UINT8 *) &Data; 5367 5368 for (Size = 0; Size < sizeof (Data); Size++) { 5369 *ToPtr = *FromPtr; 5370 ToPtr++; 5371 FromPtr++; 5372 } 5373 5374 return Data; 5375 } 5376 5377 /** 5378 Returns the version of the EBC virtual machine. 5379 5380 @return The 64-bit version of EBC virtual machine. 5381 5382 **/ 5383 UINT64 5384 GetVmVersion ( 5385 VOID 5386 ) 5387 { 5388 return (UINT64) (((VM_MAJOR_VERSION & 0xFFFF) << 16) | ((VM_MINOR_VERSION & 0xFFFF))); 5389 } 5390