aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2014-11-26 05:10:18 -0500
committerMarkos Chandras <markos.chandras@imgtec.com>2015-02-17 10:37:32 -0500
commitc8a34581ec09a5ee11dd833d6c5cf41fdbef706f (patch)
treeae78d1cc82562dfa92bcad4bcc7848343af21dc7 /arch/mips
parent319824eabc3f1c1aab67f408d66f384fbb996ee2 (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.h3
-rw-r--r--arch/mips/kernel/branch.c101
-rw-r--r--arch/mips/math-emu/cp1emu.c27
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)
403int __compute_return_epc_for_insn(struct pt_regs *regs, 403int __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(&current->thread.fpu.fpr[reg], 0)
640 & 0x1)
641 bit = 1;
642 break;
643 case bc1nez_op:
644 /* Test bit 0 */
645 if (!(get_fpr32(&current->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(&current->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1)
613 bit = 1;
614 break;
615 case bc1nez_op:
616 if (!(get_fpr32(&current->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) {