diff options
author | Markos Chandras <markos.chandras@imgtec.com> | 2014-11-26 05:10:18 -0500 |
---|---|---|
committer | Markos Chandras <markos.chandras@imgtec.com> | 2015-02-17 10:37:32 -0500 |
commit | c8a34581ec09a5ee11dd833d6c5cf41fdbef706f (patch) | |
tree | ae78d1cc82562dfa92bcad4bcc7848343af21dc7 /arch/mips | |
parent | 319824eabc3f1c1aab67f408d66f384fbb996ee2 (diff) |
MIPS: Emulate the BC1{EQ,NE}Z FPU instructions
MIPS R6 introduced the following two branch instructions for COP1:
BC1EQZ: Branch if Cop1 (FPR) Register Bit 0 is Equal to Zero
BC1NEZ: Branch if Cop1 (FPR) Register Bit 0 is Not Equal to Zero
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/include/uapi/asm/inst.h | 3 | ||||
-rw-r--r-- | arch/mips/kernel/branch.c | 101 | ||||
-rw-r--r-- | arch/mips/math-emu/cp1emu.c | 27 |
3 files changed, 101 insertions, 30 deletions
diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index 5c9e14a903af..19d3bc1e6510 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h | |||
@@ -115,7 +115,8 @@ enum cop_op { | |||
115 | mfhc_op = 0x03, mtc_op = 0x04, | 115 | mfhc_op = 0x03, mtc_op = 0x04, |
116 | dmtc_op = 0x05, ctc_op = 0x06, | 116 | dmtc_op = 0x05, ctc_op = 0x06, |
117 | mthc0_op = 0x06, mthc_op = 0x07, | 117 | mthc0_op = 0x06, mthc_op = 0x07, |
118 | bc_op = 0x08, cop_op = 0x10, | 118 | bc_op = 0x08, bc1eqz_op = 0x09, |
119 | bc1nez_op = 0x0d, cop_op = 0x10, | ||
119 | copm_op = 0x18 | 120 | copm_op = 0x18 |
120 | }; | 121 | }; |
121 | 122 | ||
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 5121adaa34bd..f9cb13c56595 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c | |||
@@ -403,7 +403,7 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs) | |||
403 | int __compute_return_epc_for_insn(struct pt_regs *regs, | 403 | int __compute_return_epc_for_insn(struct pt_regs *regs, |
404 | union mips_instruction insn) | 404 | union mips_instruction insn) |
405 | { | 405 | { |
406 | unsigned int bit, fcr31, dspcontrol; | 406 | unsigned int bit, fcr31, dspcontrol, reg; |
407 | long epc = regs->cp0_epc; | 407 | long epc = regs->cp0_epc; |
408 | int ret = 0; | 408 | int ret = 0; |
409 | 409 | ||
@@ -618,40 +618,83 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, | |||
618 | * And now the FPA/cp1 branch instructions. | 618 | * And now the FPA/cp1 branch instructions. |
619 | */ | 619 | */ |
620 | case cop1_op: | 620 | case cop1_op: |
621 | preempt_disable(); | 621 | if (cpu_has_mips_r6 && |
622 | if (is_fpu_owner()) | 622 | ((insn.i_format.rs == bc1eqz_op) || |
623 | fcr31 = read_32bit_cp1_register(CP1_STATUS); | 623 | (insn.i_format.rs == bc1nez_op))) { |
624 | else | 624 | if (!used_math()) { /* First time FPU user */ |
625 | fcr31 = current->thread.fpu.fcr31; | 625 | ret = init_fpu(); |
626 | preempt_enable(); | 626 | if (ret && NO_R6EMU) { |
627 | 627 | ret = -ret; | |
628 | bit = (insn.i_format.rt >> 2); | 628 | break; |
629 | bit += (bit != 0); | 629 | } |
630 | bit += 23; | 630 | ret = 0; |
631 | switch (insn.i_format.rt & 3) { | 631 | set_used_math(); |
632 | case 0: /* bc1f */ | 632 | } |
633 | case 2: /* bc1fl */ | 633 | lose_fpu(1); /* Save FPU state for the emulator. */ |
634 | if (~fcr31 & (1 << bit)) { | 634 | reg = insn.i_format.rt; |
635 | epc = epc + 4 + (insn.i_format.simmediate << 2); | 635 | bit = 0; |
636 | if (insn.i_format.rt == 2) | 636 | switch (insn.i_format.rs) { |
637 | ret = BRANCH_LIKELY_TAKEN; | 637 | case bc1eqz_op: |
638 | } else | 638 | /* Test bit 0 */ |
639 | if (get_fpr32(¤t->thread.fpu.fpr[reg], 0) | ||
640 | & 0x1) | ||
641 | bit = 1; | ||
642 | break; | ||
643 | case bc1nez_op: | ||
644 | /* Test bit 0 */ | ||
645 | if (!(get_fpr32(¤t->thread.fpu.fpr[reg], 0) | ||
646 | & 0x1)) | ||
647 | bit = 1; | ||
648 | break; | ||
649 | } | ||
650 | own_fpu(1); | ||
651 | if (bit) | ||
652 | epc = epc + 4 + | ||
653 | (insn.i_format.simmediate << 2); | ||
654 | else | ||
639 | epc += 8; | 655 | epc += 8; |
640 | regs->cp0_epc = epc; | 656 | regs->cp0_epc = epc; |
657 | |||
641 | break; | 658 | break; |
659 | } else { | ||
642 | 660 | ||
643 | case 1: /* bc1t */ | 661 | preempt_disable(); |
644 | case 3: /* bc1tl */ | 662 | if (is_fpu_owner()) |
645 | if (fcr31 & (1 << bit)) { | 663 | fcr31 = read_32bit_cp1_register(CP1_STATUS); |
646 | epc = epc + 4 + (insn.i_format.simmediate << 2); | 664 | else |
647 | if (insn.i_format.rt == 3) | 665 | fcr31 = current->thread.fpu.fcr31; |
648 | ret = BRANCH_LIKELY_TAKEN; | 666 | preempt_enable(); |
649 | } else | 667 | |
650 | epc += 8; | 668 | bit = (insn.i_format.rt >> 2); |
651 | regs->cp0_epc = epc; | 669 | bit += (bit != 0); |
670 | bit += 23; | ||
671 | switch (insn.i_format.rt & 3) { | ||
672 | case 0: /* bc1f */ | ||
673 | case 2: /* bc1fl */ | ||
674 | if (~fcr31 & (1 << bit)) { | ||
675 | epc = epc + 4 + | ||
676 | (insn.i_format.simmediate << 2); | ||
677 | if (insn.i_format.rt == 2) | ||
678 | ret = BRANCH_LIKELY_TAKEN; | ||
679 | } else | ||
680 | epc += 8; | ||
681 | regs->cp0_epc = epc; | ||
682 | break; | ||
683 | |||
684 | case 1: /* bc1t */ | ||
685 | case 3: /* bc1tl */ | ||
686 | if (fcr31 & (1 << bit)) { | ||
687 | epc = epc + 4 + | ||
688 | (insn.i_format.simmediate << 2); | ||
689 | if (insn.i_format.rt == 3) | ||
690 | ret = BRANCH_LIKELY_TAKEN; | ||
691 | } else | ||
692 | epc += 8; | ||
693 | regs->cp0_epc = epc; | ||
694 | break; | ||
695 | } | ||
652 | break; | 696 | break; |
653 | } | 697 | } |
654 | break; | ||
655 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | 698 | #ifdef CONFIG_CPU_CAVIUM_OCTEON |
656 | case lwc2_op: /* This is bbit0 on Octeon */ | 699 | case lwc2_op: /* This is bbit0 on Octeon */ |
657 | if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) | 700 | if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) |
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 7bbaefe0434d..798204e492fc 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c | |||
@@ -602,6 +602,33 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | |||
602 | #endif | 602 | #endif |
603 | case cop0_op: | 603 | case cop0_op: |
604 | case cop1_op: | 604 | case cop1_op: |
605 | /* Need to check for R6 bc1nez and bc1eqz branches */ | ||
606 | if (cpu_has_mips_r6 && | ||
607 | ((insn.i_format.rs == bc1eqz_op) || | ||
608 | (insn.i_format.rs == bc1nez_op))) { | ||
609 | bit = 0; | ||
610 | switch (insn.i_format.rs) { | ||
611 | case bc1eqz_op: | ||
612 | if (get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1) | ||
613 | bit = 1; | ||
614 | break; | ||
615 | case bc1nez_op: | ||
616 | if (!(get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1)) | ||
617 | bit = 1; | ||
618 | break; | ||
619 | } | ||
620 | if (bit) | ||
621 | *contpc = regs->cp0_epc + | ||
622 | dec_insn.pc_inc + | ||
623 | (insn.i_format.simmediate << 2); | ||
624 | else | ||
625 | *contpc = regs->cp0_epc + | ||
626 | dec_insn.pc_inc + | ||
627 | dec_insn.next_pc_inc; | ||
628 | |||
629 | return 1; | ||
630 | } | ||
631 | /* R2/R6 compatible cop1 instruction. Fall through */ | ||
605 | case cop2_op: | 632 | case cop2_op: |
606 | case cop1x_op: | 633 | case cop1x_op: |
607 | if (insn.i_format.rs == bc_op) { | 634 | if (insn.i_format.rs == bc_op) { |