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>
This commit is contained in:
		
					parent
					
						
							
								319824eabc
							
						
					
				
			
			
				commit
				
					
						c8a34581ec
					
				
			
		
					 3 changed files with 101 additions and 30 deletions
				
			
		| 
						 | 
					@ -115,7 +115,8 @@ enum cop_op {
 | 
				
			||||||
	mfhc_op       = 0x03, mtc_op	    = 0x04,
 | 
						mfhc_op       = 0x03, mtc_op	    = 0x04,
 | 
				
			||||||
	dmtc_op	      = 0x05, ctc_op	    = 0x06,
 | 
						dmtc_op	      = 0x05, ctc_op	    = 0x06,
 | 
				
			||||||
	mthc0_op      = 0x06, mthc_op	    = 0x07,
 | 
						mthc0_op      = 0x06, mthc_op	    = 0x07,
 | 
				
			||||||
	bc_op	      = 0x08, cop_op	    = 0x10,
 | 
						bc_op	      = 0x08, bc1eqz_op     = 0x09,
 | 
				
			||||||
 | 
						bc1nez_op     = 0x0d, cop_op	    = 0x10,
 | 
				
			||||||
	copm_op	      = 0x18
 | 
						copm_op	      = 0x18
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -403,7 +403,7 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs)
 | 
				
			||||||
int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
					int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
				
			||||||
				   union mips_instruction insn)
 | 
									   union mips_instruction insn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int bit, fcr31, dspcontrol;
 | 
						unsigned int bit, fcr31, dspcontrol, reg;
 | 
				
			||||||
	long epc = regs->cp0_epc;
 | 
						long epc = regs->cp0_epc;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -618,6 +618,46 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
				
			||||||
	 * And now the FPA/cp1 branch instructions.
 | 
						 * And now the FPA/cp1 branch instructions.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	case cop1_op:
 | 
						case cop1_op:
 | 
				
			||||||
 | 
							if (cpu_has_mips_r6 &&
 | 
				
			||||||
 | 
							    ((insn.i_format.rs == bc1eqz_op) ||
 | 
				
			||||||
 | 
							     (insn.i_format.rs == bc1nez_op))) {
 | 
				
			||||||
 | 
								if (!used_math()) { /* First time FPU user */
 | 
				
			||||||
 | 
									ret = init_fpu();
 | 
				
			||||||
 | 
									if (ret && NO_R6EMU) {
 | 
				
			||||||
 | 
										ret = -ret;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ret = 0;
 | 
				
			||||||
 | 
									set_used_math();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								lose_fpu(1);    /* Save FPU state for the emulator. */
 | 
				
			||||||
 | 
								reg = insn.i_format.rt;
 | 
				
			||||||
 | 
								bit = 0;
 | 
				
			||||||
 | 
								switch (insn.i_format.rs) {
 | 
				
			||||||
 | 
								case bc1eqz_op:
 | 
				
			||||||
 | 
									/* Test bit 0 */
 | 
				
			||||||
 | 
									if (get_fpr32(¤t->thread.fpu.fpr[reg], 0)
 | 
				
			||||||
 | 
									    & 0x1)
 | 
				
			||||||
 | 
										bit = 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case bc1nez_op:
 | 
				
			||||||
 | 
									/* Test bit 0 */
 | 
				
			||||||
 | 
									if (!(get_fpr32(¤t->thread.fpu.fpr[reg], 0)
 | 
				
			||||||
 | 
									      & 0x1))
 | 
				
			||||||
 | 
										bit = 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								own_fpu(1);
 | 
				
			||||||
 | 
								if (bit)
 | 
				
			||||||
 | 
									epc = epc + 4 +
 | 
				
			||||||
 | 
										(insn.i_format.simmediate << 2);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									epc += 8;
 | 
				
			||||||
 | 
								regs->cp0_epc = epc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			preempt_disable();
 | 
								preempt_disable();
 | 
				
			||||||
			if (is_fpu_owner())
 | 
								if (is_fpu_owner())
 | 
				
			||||||
			        fcr31 = read_32bit_cp1_register(CP1_STATUS);
 | 
								        fcr31 = read_32bit_cp1_register(CP1_STATUS);
 | 
				
			||||||
| 
						 | 
					@ -632,7 +672,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
				
			||||||
			case 0: /* bc1f */
 | 
								case 0: /* bc1f */
 | 
				
			||||||
			case 2: /* bc1fl */
 | 
								case 2: /* bc1fl */
 | 
				
			||||||
				if (~fcr31 & (1 << bit)) {
 | 
									if (~fcr31 & (1 << bit)) {
 | 
				
			||||||
				epc = epc + 4 + (insn.i_format.simmediate << 2);
 | 
										epc = epc + 4 +
 | 
				
			||||||
 | 
											(insn.i_format.simmediate << 2);
 | 
				
			||||||
					if (insn.i_format.rt == 2)
 | 
										if (insn.i_format.rt == 2)
 | 
				
			||||||
						ret = BRANCH_LIKELY_TAKEN;
 | 
											ret = BRANCH_LIKELY_TAKEN;
 | 
				
			||||||
				} else
 | 
									} else
 | 
				
			||||||
| 
						 | 
					@ -643,7 +684,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
				
			||||||
			case 1: /* bc1t */
 | 
								case 1: /* bc1t */
 | 
				
			||||||
			case 3: /* bc1tl */
 | 
								case 3: /* bc1tl */
 | 
				
			||||||
				if (fcr31 & (1 << bit)) {
 | 
									if (fcr31 & (1 << bit)) {
 | 
				
			||||||
				epc = epc + 4 + (insn.i_format.simmediate << 2);
 | 
										epc = epc + 4 +
 | 
				
			||||||
 | 
											(insn.i_format.simmediate << 2);
 | 
				
			||||||
					if (insn.i_format.rt == 3)
 | 
										if (insn.i_format.rt == 3)
 | 
				
			||||||
						ret = BRANCH_LIKELY_TAKEN;
 | 
											ret = BRANCH_LIKELY_TAKEN;
 | 
				
			||||||
				} else
 | 
									} else
 | 
				
			||||||
| 
						 | 
					@ -652,6 +694,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
#ifdef CONFIG_CPU_CAVIUM_OCTEON
 | 
					#ifdef CONFIG_CPU_CAVIUM_OCTEON
 | 
				
			||||||
	case lwc2_op: /* This is bbit0 on Octeon */
 | 
						case lwc2_op: /* This is bbit0 on Octeon */
 | 
				
			||||||
		if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
 | 
							if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -602,6 +602,33 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	case cop0_op:
 | 
						case cop0_op:
 | 
				
			||||||
	case cop1_op:
 | 
						case cop1_op:
 | 
				
			||||||
 | 
							/* Need to check for R6 bc1nez and bc1eqz branches */
 | 
				
			||||||
 | 
							if (cpu_has_mips_r6 &&
 | 
				
			||||||
 | 
							    ((insn.i_format.rs == bc1eqz_op) ||
 | 
				
			||||||
 | 
							     (insn.i_format.rs == bc1nez_op))) {
 | 
				
			||||||
 | 
								bit = 0;
 | 
				
			||||||
 | 
								switch (insn.i_format.rs) {
 | 
				
			||||||
 | 
								case bc1eqz_op:
 | 
				
			||||||
 | 
									if (get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1)
 | 
				
			||||||
 | 
									    bit = 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case bc1nez_op:
 | 
				
			||||||
 | 
									if (!(get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1))
 | 
				
			||||||
 | 
									    bit = 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (bit)
 | 
				
			||||||
 | 
									*contpc = regs->cp0_epc +
 | 
				
			||||||
 | 
										dec_insn.pc_inc +
 | 
				
			||||||
 | 
										(insn.i_format.simmediate << 2);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									*contpc = regs->cp0_epc +
 | 
				
			||||||
 | 
										dec_insn.pc_inc +
 | 
				
			||||||
 | 
										dec_insn.next_pc_inc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* R2/R6 compatible cop1 instruction. Fall through */
 | 
				
			||||||
	case cop2_op:
 | 
						case cop2_op:
 | 
				
			||||||
	case cop1x_op:
 | 
						case cop1x_op:
 | 
				
			||||||
		if (insn.i_format.rs == bc_op) {
 | 
							if (insn.i_format.rs == bc_op) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue