diff options
Diffstat (limited to 'arch/mips/math-emu/cp1emu.c')
| -rw-r--r-- | arch/mips/math-emu/cp1emu.c | 169 |
1 files changed, 158 insertions, 11 deletions
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 9dfcd7fc1bc3..b30bf65c7d7d 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c | |||
| @@ -48,6 +48,7 @@ | |||
| 48 | #include <asm/processor.h> | 48 | #include <asm/processor.h> |
| 49 | #include <asm/fpu_emulator.h> | 49 | #include <asm/fpu_emulator.h> |
| 50 | #include <asm/fpu.h> | 50 | #include <asm/fpu.h> |
| 51 | #include <asm/mips-r2-to-r6-emul.h> | ||
| 51 | 52 | ||
| 52 | #include "ieee754.h" | 53 | #include "ieee754.h" |
| 53 | 54 | ||
| @@ -68,7 +69,7 @@ static int fpux_emu(struct pt_regs *, | |||
| 68 | #define modeindex(v) ((v) & FPU_CSR_RM) | 69 | #define modeindex(v) ((v) & FPU_CSR_RM) |
| 69 | 70 | ||
| 70 | /* convert condition code register number to csr bit */ | 71 | /* convert condition code register number to csr bit */ |
| 71 | static const unsigned int fpucondbit[8] = { | 72 | const unsigned int fpucondbit[8] = { |
| 72 | FPU_CSR_COND0, | 73 | FPU_CSR_COND0, |
| 73 | FPU_CSR_COND1, | 74 | FPU_CSR_COND1, |
| 74 | FPU_CSR_COND2, | 75 | FPU_CSR_COND2, |
| @@ -448,6 +449,9 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 448 | dec_insn.next_pc_inc; | 449 | dec_insn.next_pc_inc; |
| 449 | /* Fall through */ | 450 | /* Fall through */ |
| 450 | case jr_op: | 451 | case jr_op: |
| 452 | /* For R6, JR already emulated in jalr_op */ | ||
| 453 | if (NO_R6EMU && insn.r_format.opcode == jr_op) | ||
| 454 | break; | ||
| 451 | *contpc = regs->regs[insn.r_format.rs]; | 455 | *contpc = regs->regs[insn.r_format.rs]; |
| 452 | return 1; | 456 | return 1; |
| 453 | } | 457 | } |
| @@ -456,12 +460,18 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 456 | switch (insn.i_format.rt) { | 460 | switch (insn.i_format.rt) { |
| 457 | case bltzal_op: | 461 | case bltzal_op: |
| 458 | case bltzall_op: | 462 | case bltzall_op: |
| 463 | if (NO_R6EMU && (insn.i_format.rs || | ||
| 464 | insn.i_format.rt == bltzall_op)) | ||
| 465 | break; | ||
| 466 | |||
| 459 | regs->regs[31] = regs->cp0_epc + | 467 | regs->regs[31] = regs->cp0_epc + |
| 460 | dec_insn.pc_inc + | 468 | dec_insn.pc_inc + |
| 461 | dec_insn.next_pc_inc; | 469 | dec_insn.next_pc_inc; |
| 462 | /* Fall through */ | 470 | /* Fall through */ |
| 463 | case bltz_op: | ||
| 464 | case bltzl_op: | 471 | case bltzl_op: |
| 472 | if (NO_R6EMU) | ||
| 473 | break; | ||
| 474 | case bltz_op: | ||
| 465 | if ((long)regs->regs[insn.i_format.rs] < 0) | 475 | if ((long)regs->regs[insn.i_format.rs] < 0) |
| 466 | *contpc = regs->cp0_epc + | 476 | *contpc = regs->cp0_epc + |
| 467 | dec_insn.pc_inc + | 477 | dec_insn.pc_inc + |
| @@ -473,12 +483,18 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 473 | return 1; | 483 | return 1; |
| 474 | case bgezal_op: | 484 | case bgezal_op: |
| 475 | case bgezall_op: | 485 | case bgezall_op: |
| 486 | if (NO_R6EMU && (insn.i_format.rs || | ||
| 487 | insn.i_format.rt == bgezall_op)) | ||
| 488 | break; | ||
| 489 | |||
| 476 | regs->regs[31] = regs->cp0_epc + | 490 | regs->regs[31] = regs->cp0_epc + |
| 477 | dec_insn.pc_inc + | 491 | dec_insn.pc_inc + |
| 478 | dec_insn.next_pc_inc; | 492 | dec_insn.next_pc_inc; |
| 479 | /* Fall through */ | 493 | /* Fall through */ |
| 480 | case bgez_op: | ||
| 481 | case bgezl_op: | 494 | case bgezl_op: |
| 495 | if (NO_R6EMU) | ||
| 496 | break; | ||
| 497 | case bgez_op: | ||
| 482 | if ((long)regs->regs[insn.i_format.rs] >= 0) | 498 | if ((long)regs->regs[insn.i_format.rs] >= 0) |
| 483 | *contpc = regs->cp0_epc + | 499 | *contpc = regs->cp0_epc + |
| 484 | dec_insn.pc_inc + | 500 | dec_insn.pc_inc + |
| @@ -505,8 +521,10 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 505 | /* Set microMIPS mode bit: XOR for jalx. */ | 521 | /* Set microMIPS mode bit: XOR for jalx. */ |
| 506 | *contpc ^= bit; | 522 | *contpc ^= bit; |
| 507 | return 1; | 523 | return 1; |
| 508 | case beq_op: | ||
| 509 | case beql_op: | 524 | case beql_op: |
| 525 | if (NO_R6EMU) | ||
| 526 | break; | ||
| 527 | case beq_op: | ||
| 510 | if (regs->regs[insn.i_format.rs] == | 528 | if (regs->regs[insn.i_format.rs] == |
| 511 | regs->regs[insn.i_format.rt]) | 529 | regs->regs[insn.i_format.rt]) |
| 512 | *contpc = regs->cp0_epc + | 530 | *contpc = regs->cp0_epc + |
| @@ -517,8 +535,10 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 517 | dec_insn.pc_inc + | 535 | dec_insn.pc_inc + |
| 518 | dec_insn.next_pc_inc; | 536 | dec_insn.next_pc_inc; |
| 519 | return 1; | 537 | return 1; |
| 520 | case bne_op: | ||
| 521 | case bnel_op: | 538 | case bnel_op: |
| 539 | if (NO_R6EMU) | ||
| 540 | break; | ||
| 541 | case bne_op: | ||
| 522 | if (regs->regs[insn.i_format.rs] != | 542 | if (regs->regs[insn.i_format.rs] != |
| 523 | regs->regs[insn.i_format.rt]) | 543 | regs->regs[insn.i_format.rt]) |
| 524 | *contpc = regs->cp0_epc + | 544 | *contpc = regs->cp0_epc + |
| @@ -529,8 +549,34 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 529 | dec_insn.pc_inc + | 549 | dec_insn.pc_inc + |
| 530 | dec_insn.next_pc_inc; | 550 | dec_insn.next_pc_inc; |
| 531 | return 1; | 551 | return 1; |
| 532 | case blez_op: | ||
| 533 | case blezl_op: | 552 | case blezl_op: |
| 553 | if (NO_R6EMU) | ||
| 554 | break; | ||
| 555 | case blez_op: | ||
| 556 | |||
| 557 | /* | ||
| 558 | * Compact branches for R6 for the | ||
| 559 | * blez and blezl opcodes. | ||
| 560 | * BLEZ | rs = 0 | rt != 0 == BLEZALC | ||
| 561 | * BLEZ | rs = rt != 0 == BGEZALC | ||
| 562 | * BLEZ | rs != 0 | rt != 0 == BGEUC | ||
| 563 | * BLEZL | rs = 0 | rt != 0 == BLEZC | ||
| 564 | * BLEZL | rs = rt != 0 == BGEZC | ||
| 565 | * BLEZL | rs != 0 | rt != 0 == BGEC | ||
| 566 | * | ||
| 567 | * For real BLEZ{,L}, rt is always 0. | ||
| 568 | */ | ||
| 569 | if (cpu_has_mips_r6 && insn.i_format.rt) { | ||
| 570 | if ((insn.i_format.opcode == blez_op) && | ||
| 571 | ((!insn.i_format.rs && insn.i_format.rt) || | ||
| 572 | (insn.i_format.rs == insn.i_format.rt))) | ||
| 573 | regs->regs[31] = regs->cp0_epc + | ||
| 574 | dec_insn.pc_inc; | ||
| 575 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 576 | dec_insn.next_pc_inc; | ||
| 577 | |||
| 578 | return 1; | ||
| 579 | } | ||
| 534 | if ((long)regs->regs[insn.i_format.rs] <= 0) | 580 | if ((long)regs->regs[insn.i_format.rs] <= 0) |
| 535 | *contpc = regs->cp0_epc + | 581 | *contpc = regs->cp0_epc + |
| 536 | dec_insn.pc_inc + | 582 | dec_insn.pc_inc + |
| @@ -540,8 +586,35 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 540 | dec_insn.pc_inc + | 586 | dec_insn.pc_inc + |
| 541 | dec_insn.next_pc_inc; | 587 | dec_insn.next_pc_inc; |
| 542 | return 1; | 588 | return 1; |
| 543 | case bgtz_op: | ||
| 544 | case bgtzl_op: | 589 | case bgtzl_op: |
| 590 | if (NO_R6EMU) | ||
| 591 | break; | ||
| 592 | case bgtz_op: | ||
| 593 | /* | ||
| 594 | * Compact branches for R6 for the | ||
| 595 | * bgtz and bgtzl opcodes. | ||
| 596 | * BGTZ | rs = 0 | rt != 0 == BGTZALC | ||
| 597 | * BGTZ | rs = rt != 0 == BLTZALC | ||
| 598 | * BGTZ | rs != 0 | rt != 0 == BLTUC | ||
| 599 | * BGTZL | rs = 0 | rt != 0 == BGTZC | ||
| 600 | * BGTZL | rs = rt != 0 == BLTZC | ||
| 601 | * BGTZL | rs != 0 | rt != 0 == BLTC | ||
| 602 | * | ||
| 603 | * *ZALC varint for BGTZ &&& rt != 0 | ||
| 604 | * For real GTZ{,L}, rt is always 0. | ||
| 605 | */ | ||
| 606 | if (cpu_has_mips_r6 && insn.i_format.rt) { | ||
| 607 | if ((insn.i_format.opcode == blez_op) && | ||
| 608 | ((!insn.i_format.rs && insn.i_format.rt) || | ||
| 609 | (insn.i_format.rs == insn.i_format.rt))) | ||
| 610 | regs->regs[31] = regs->cp0_epc + | ||
| 611 | dec_insn.pc_inc; | ||
| 612 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 613 | dec_insn.next_pc_inc; | ||
| 614 | |||
| 615 | return 1; | ||
| 616 | } | ||
| 617 | |||
| 545 | if ((long)regs->regs[insn.i_format.rs] > 0) | 618 | if ((long)regs->regs[insn.i_format.rs] > 0) |
| 546 | *contpc = regs->cp0_epc + | 619 | *contpc = regs->cp0_epc + |
| 547 | dec_insn.pc_inc + | 620 | dec_insn.pc_inc + |
| @@ -551,6 +624,16 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 551 | dec_insn.pc_inc + | 624 | dec_insn.pc_inc + |
| 552 | dec_insn.next_pc_inc; | 625 | dec_insn.next_pc_inc; |
| 553 | return 1; | 626 | return 1; |
| 627 | case cbcond0_op: | ||
| 628 | case cbcond1_op: | ||
| 629 | if (!cpu_has_mips_r6) | ||
| 630 | break; | ||
| 631 | if (insn.i_format.rt && !insn.i_format.rs) | ||
| 632 | regs->regs[31] = regs->cp0_epc + 4; | ||
| 633 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 634 | dec_insn.next_pc_inc; | ||
| 635 | |||
| 636 | return 1; | ||
| 554 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | 637 | #ifdef CONFIG_CPU_CAVIUM_OCTEON |
| 555 | case lwc2_op: /* This is bbit0 on Octeon */ | 638 | case lwc2_op: /* This is bbit0 on Octeon */ |
| 556 | if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) | 639 | if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) |
| @@ -576,9 +659,73 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
| 576 | else | 659 | else |
| 577 | *contpc = regs->cp0_epc + 8; | 660 | *contpc = regs->cp0_epc + 8; |
| 578 | return 1; | 661 | return 1; |
| 662 | #else | ||
| 663 | case bc6_op: | ||
| 664 | /* | ||
| 665 | * Only valid for MIPS R6 but we can still end up | ||
| 666 | * here from a broken userland so just tell emulator | ||
| 667 | * this is not a branch and let it break later on. | ||
| 668 | */ | ||
| 669 | if (!cpu_has_mips_r6) | ||
| 670 | break; | ||
| 671 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 672 | dec_insn.next_pc_inc; | ||
| 673 | |||
| 674 | return 1; | ||
| 675 | case balc6_op: | ||
| 676 | if (!cpu_has_mips_r6) | ||
| 677 | break; | ||
| 678 | regs->regs[31] = regs->cp0_epc + 4; | ||
| 679 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 680 | dec_insn.next_pc_inc; | ||
| 681 | |||
| 682 | return 1; | ||
| 683 | case beqzcjic_op: | ||
| 684 | if (!cpu_has_mips_r6) | ||
| 685 | break; | ||
| 686 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 687 | dec_insn.next_pc_inc; | ||
| 688 | |||
| 689 | return 1; | ||
| 690 | case bnezcjialc_op: | ||
| 691 | if (!cpu_has_mips_r6) | ||
| 692 | break; | ||
| 693 | if (!insn.i_format.rs) | ||
| 694 | regs->regs[31] = regs->cp0_epc + 4; | ||
| 695 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
| 696 | dec_insn.next_pc_inc; | ||
| 697 | |||
| 698 | return 1; | ||
| 579 | #endif | 699 | #endif |
| 580 | case cop0_op: | 700 | case cop0_op: |
| 581 | case cop1_op: | 701 | case cop1_op: |
| 702 | /* Need to check for R6 bc1nez and bc1eqz branches */ | ||
| 703 | if (cpu_has_mips_r6 && | ||
| 704 | ((insn.i_format.rs == bc1eqz_op) || | ||
| 705 | (insn.i_format.rs == bc1nez_op))) { | ||
| 706 | bit = 0; | ||
| 707 | switch (insn.i_format.rs) { | ||
| 708 | case bc1eqz_op: | ||
| 709 | if (get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1) | ||
| 710 | bit = 1; | ||
| 711 | break; | ||
| 712 | case bc1nez_op: | ||
| 713 | if (!(get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1)) | ||
| 714 | bit = 1; | ||
| 715 | break; | ||
| 716 | } | ||
| 717 | if (bit) | ||
| 718 | *contpc = regs->cp0_epc + | ||
| 719 | dec_insn.pc_inc + | ||
| 720 | (insn.i_format.simmediate << 2); | ||
| 721 | else | ||
| 722 | *contpc = regs->cp0_epc + | ||
| 723 | dec_insn.pc_inc + | ||
| 724 | dec_insn.next_pc_inc; | ||
| 725 | |||
| 726 | return 1; | ||
| 727 | } | ||
| 728 | /* R2/R6 compatible cop1 instruction. Fall through */ | ||
| 582 | case cop2_op: | 729 | case cop2_op: |
| 583 | case cop1x_op: | 730 | case cop1x_op: |
| 584 | if (insn.i_format.rs == bc_op) { | 731 | if (insn.i_format.rs == bc_op) { |
| @@ -1414,14 +1561,14 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
| 1414 | * achieve full IEEE-754 accuracy - however this emulator does. | 1561 | * achieve full IEEE-754 accuracy - however this emulator does. |
| 1415 | */ | 1562 | */ |
| 1416 | case frsqrt_op: | 1563 | case frsqrt_op: |
| 1417 | if (!cpu_has_mips_4_5_r2) | 1564 | if (!cpu_has_mips_4_5_r2_r6) |
| 1418 | return SIGILL; | 1565 | return SIGILL; |
| 1419 | 1566 | ||
| 1420 | handler.u = fpemu_sp_rsqrt; | 1567 | handler.u = fpemu_sp_rsqrt; |
| 1421 | goto scopuop; | 1568 | goto scopuop; |
| 1422 | 1569 | ||
| 1423 | case frecip_op: | 1570 | case frecip_op: |
| 1424 | if (!cpu_has_mips_4_5_r2) | 1571 | if (!cpu_has_mips_4_5_r2_r6) |
| 1425 | return SIGILL; | 1572 | return SIGILL; |
| 1426 | 1573 | ||
| 1427 | handler.u = fpemu_sp_recip; | 1574 | handler.u = fpemu_sp_recip; |
| @@ -1616,13 +1763,13 @@ copcsr: | |||
| 1616 | * achieve full IEEE-754 accuracy - however this emulator does. | 1763 | * achieve full IEEE-754 accuracy - however this emulator does. |
| 1617 | */ | 1764 | */ |
| 1618 | case frsqrt_op: | 1765 | case frsqrt_op: |
| 1619 | if (!cpu_has_mips_4_5_r2) | 1766 | if (!cpu_has_mips_4_5_r2_r6) |
| 1620 | return SIGILL; | 1767 | return SIGILL; |
| 1621 | 1768 | ||
| 1622 | handler.u = fpemu_dp_rsqrt; | 1769 | handler.u = fpemu_dp_rsqrt; |
| 1623 | goto dcopuop; | 1770 | goto dcopuop; |
| 1624 | case frecip_op: | 1771 | case frecip_op: |
| 1625 | if (!cpu_has_mips_4_5_r2) | 1772 | if (!cpu_has_mips_4_5_r2_r6) |
| 1626 | return SIGILL; | 1773 | return SIGILL; |
| 1627 | 1774 | ||
| 1628 | handler.u = fpemu_dp_recip; | 1775 | handler.u = fpemu_dp_recip; |
