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