Skip to content

Commit c8a3458

Browse files
author
Markos Chandras
committed
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>
1 parent 319824e commit c8a3458

File tree

3 files changed

+101
-30
lines changed

3 files changed

+101
-30
lines changed

arch/mips/include/uapi/asm/inst.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ enum cop_op {
115115
mfhc_op = 0x03, mtc_op = 0x04,
116116
dmtc_op = 0x05, ctc_op = 0x06,
117117
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,
119120
copm_op = 0x18
120121
};
121122

arch/mips/kernel/branch.c

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs)
403403
int __compute_return_epc_for_insn(struct pt_regs *regs,
404404
union mips_instruction insn)
405405
{
406-
unsigned int bit, fcr31, dspcontrol;
406+
unsigned int bit, fcr31, dspcontrol, reg;
407407
long epc = regs->cp0_epc;
408408
int ret = 0;
409409

@@ -618,40 +618,83 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
618618
* And now the FPA/cp1 branch instructions.
619619
*/
620620
case cop1_op:
621-
preempt_disable();
622-
if (is_fpu_owner())
623-
fcr31 = read_32bit_cp1_register(CP1_STATUS);
624-
else
625-
fcr31 = current->thread.fpu.fcr31;
626-
preempt_enable();
627-
628-
bit = (insn.i_format.rt >> 2);
629-
bit += (bit != 0);
630-
bit += 23;
631-
switch (insn.i_format.rt & 3) {
632-
case 0: /* bc1f */
633-
case 2: /* bc1fl */
634-
if (~fcr31 & (1 << bit)) {
635-
epc = epc + 4 + (insn.i_format.simmediate << 2);
636-
if (insn.i_format.rt == 2)
637-
ret = BRANCH_LIKELY_TAKEN;
638-
} else
621+
if (cpu_has_mips_r6 &&
622+
((insn.i_format.rs == bc1eqz_op) ||
623+
(insn.i_format.rs == bc1nez_op))) {
624+
if (!used_math()) { /* First time FPU user */
625+
ret = init_fpu();
626+
if (ret && NO_R6EMU) {
627+
ret = -ret;
628+
break;
629+
}
630+
ret = 0;
631+
set_used_math();
632+
}
633+
lose_fpu(1); /* Save FPU state for the emulator. */
634+
reg = insn.i_format.rt;
635+
bit = 0;
636+
switch (insn.i_format.rs) {
637+
case bc1eqz_op:
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
639655
epc += 8;
640656
regs->cp0_epc = epc;
657+
641658
break;
659+
} else {
642660

643-
case 1: /* bc1t */
644-
case 3: /* bc1tl */
645-
if (fcr31 & (1 << bit)) {
646-
epc = epc + 4 + (insn.i_format.simmediate << 2);
647-
if (insn.i_format.rt == 3)
648-
ret = BRANCH_LIKELY_TAKEN;
649-
} else
650-
epc += 8;
651-
regs->cp0_epc = epc;
661+
preempt_disable();
662+
if (is_fpu_owner())
663+
fcr31 = read_32bit_cp1_register(CP1_STATUS);
664+
else
665+
fcr31 = current->thread.fpu.fcr31;
666+
preempt_enable();
667+
668+
bit = (insn.i_format.rt >> 2);
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+
}
652696
break;
653697
}
654-
break;
655698
#ifdef CONFIG_CPU_CAVIUM_OCTEON
656699
case lwc2_op: /* This is bbit0 on Octeon */
657700
if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))

arch/mips/math-emu/cp1emu.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,33 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
602602
#endif
603603
case cop0_op:
604604
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 */
605632
case cop2_op:
606633
case cop1x_op:
607634
if (insn.i_format.rs == bc_op) {

0 commit comments

Comments
 (0)