| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This file is subject to the terms and conditions of the GNU General Public | 
					
						
							|  |  |  |  * License.  See the file "COPYING" in the main directory of this archive | 
					
						
							|  |  |  |  * for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle | 
					
						
							|  |  |  |  * Copyright (C) 2001 MIPS Technologies, Inc. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/branch.h>
 | 
					
						
							|  |  |  | #include <asm/cpu.h>
 | 
					
						
							|  |  |  | #include <asm/cpu-features.h>
 | 
					
						
							| 
									
										
										
										
											2005-05-09 13:16:07 +00:00
										 |  |  | #include <asm/fpu.h>
 | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  | #include <asm/fpu_emulator.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/inst.h>
 | 
					
						
							|  |  |  | #include <asm/ptrace.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2013-03-25 13:45:19 -05:00
										 |  |  |  * Calculate and return exception PC in case of branch delay slot | 
					
						
							|  |  |  |  * for microMIPS and MIPS16e. It does not clear the ISA mode bit. | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  |  */ | 
					
						
							|  |  |  | int __isa_exception_epc(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned short inst; | 
					
						
							| 
									
										
										
										
											2013-03-25 13:45:19 -05:00
										 |  |  | 	long epc = regs->cp0_epc; | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Calculate exception PC in branch delay slot. */ | 
					
						
							|  |  |  | 	if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { | 
					
						
							|  |  |  | 		/* This should never happen because delay slot was checked. */ | 
					
						
							|  |  |  | 		force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 		return epc; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-03-25 13:45:19 -05:00
										 |  |  | 	if (cpu_has_mips16) { | 
					
						
							|  |  |  | 		if (((union mips16e_instruction)inst).ri.opcode | 
					
						
							|  |  |  | 				== MIPS16e_jal_op) | 
					
						
							|  |  |  | 			epc += 4; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			epc += 2; | 
					
						
							|  |  |  | 	} else if (mm_insn_16bit(inst)) | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  | 		epc += 2; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		epc += 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return epc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Compute return address and emulate branch in microMIPS mode after an | 
					
						
							|  |  |  |  * exception only. It does not handle compact branches/jumps and cannot | 
					
						
							|  |  |  |  * be used in interrupt context. (Compact branches/jumps do not cause | 
					
						
							|  |  |  |  * exceptions.) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int __microMIPS_compute_return_epc(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 __user *pc16; | 
					
						
							|  |  |  | 	u16 halfword; | 
					
						
							|  |  |  | 	unsigned int word; | 
					
						
							|  |  |  | 	unsigned long contpc; | 
					
						
							|  |  |  | 	struct mm_decoded_insn mminsn = { 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mminsn.micro_mips_mode = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* This load never faults. */ | 
					
						
							|  |  |  | 	pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc); | 
					
						
							|  |  |  | 	__get_user(halfword, pc16); | 
					
						
							|  |  |  | 	pc16++; | 
					
						
							|  |  |  | 	contpc = regs->cp0_epc + 2; | 
					
						
							|  |  |  | 	word = ((unsigned int)halfword << 16); | 
					
						
							|  |  |  | 	mminsn.pc_inc = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!mm_insn_16bit(halfword)) { | 
					
						
							|  |  |  | 		__get_user(halfword, pc16); | 
					
						
							|  |  |  | 		pc16++; | 
					
						
							|  |  |  | 		contpc = regs->cp0_epc + 4; | 
					
						
							|  |  |  | 		mminsn.pc_inc = 4; | 
					
						
							|  |  |  | 		word |= halfword; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mminsn.insn = word; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (get_user(halfword, pc16)) | 
					
						
							|  |  |  | 		goto sigsegv; | 
					
						
							|  |  |  | 	mminsn.next_pc_inc = 2; | 
					
						
							|  |  |  | 	word = ((unsigned int)halfword << 16); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!mm_insn_16bit(halfword)) { | 
					
						
							|  |  |  | 		pc16++; | 
					
						
							|  |  |  | 		if (get_user(halfword, pc16)) | 
					
						
							|  |  |  | 			goto sigsegv; | 
					
						
							|  |  |  | 		mminsn.next_pc_inc = 4; | 
					
						
							|  |  |  | 		word |= halfword; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mminsn.next_insn = word; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mm_isBranchInstr(regs, mminsn, &contpc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regs->cp0_epc = contpc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sigsegv: | 
					
						
							|  |  |  | 	force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 	return -EFAULT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-25 13:45:19 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Compute return address and emulate branch in MIPS16e mode after an | 
					
						
							|  |  |  |  * exception only. It does not handle compact branches/jumps and cannot | 
					
						
							|  |  |  |  * be used in interrupt context. (Compact branches/jumps do not cause | 
					
						
							|  |  |  |  * exceptions.) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int __MIPS16e_compute_return_epc(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 __user *addr; | 
					
						
							|  |  |  | 	union mips16e_instruction inst; | 
					
						
							|  |  |  | 	u16 inst2; | 
					
						
							|  |  |  | 	u32 fullinst; | 
					
						
							|  |  |  | 	long epc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	epc = regs->cp0_epc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read the instruction. */ | 
					
						
							|  |  |  | 	addr = (u16 __user *)msk_isa16_mode(epc); | 
					
						
							|  |  |  | 	if (__get_user(inst.full, addr)) { | 
					
						
							|  |  |  | 		force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (inst.ri.opcode) { | 
					
						
							|  |  |  | 	case MIPS16e_extend_op: | 
					
						
							|  |  |  | 		regs->cp0_epc += 4; | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 *  JAL and JALX in MIPS16e mode | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 	case MIPS16e_jal_op: | 
					
						
							|  |  |  | 		addr += 1; | 
					
						
							|  |  |  | 		if (__get_user(inst2, addr)) { | 
					
						
							|  |  |  | 			force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fullinst = ((unsigned)inst.full << 16) | inst2; | 
					
						
							|  |  |  | 		regs->regs[31] = epc + 6; | 
					
						
							|  |  |  | 		epc += 4; | 
					
						
							|  |  |  | 		epc >>= 28; | 
					
						
							|  |  |  | 		epc <<= 28; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * JAL:5 X:1 TARGET[20-16]:5 TARGET[25:21]:5 TARGET[15:0]:16 | 
					
						
							|  |  |  | 		 * | 
					
						
							|  |  |  | 		 * ......TARGET[15:0].................TARGET[20:16]........... | 
					
						
							|  |  |  | 		 * ......TARGET[25:21] | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		epc |= | 
					
						
							|  |  |  | 		    ((fullinst & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) | | 
					
						
							|  |  |  | 		    ((fullinst & 0x1f0000) << 7); | 
					
						
							|  |  |  | 		if (!inst.jal.x) | 
					
						
							|  |  |  | 			set_isa16_mode(epc);	/* Set ISA mode bit. */ | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 *  J(AL)R(C) | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 	case MIPS16e_rr_op: | 
					
						
							|  |  |  | 		if (inst.rr.func == MIPS16e_jr_func) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (inst.rr.ra) | 
					
						
							|  |  |  | 				regs->cp0_epc = regs->regs[31]; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				regs->cp0_epc = | 
					
						
							|  |  |  | 				    regs->regs[reg16to32[inst.rr.rx]]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (inst.rr.l) { | 
					
						
							|  |  |  | 				if (inst.rr.nd) | 
					
						
							|  |  |  | 					regs->regs[31] = epc + 2; | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					regs->regs[31] = epc + 4; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * All other cases have no branch delay slot and are 16-bits. | 
					
						
							|  |  |  | 	 * Branches do not cause an exception. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	regs->cp0_epc += 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | /**
 | 
					
						
							|  |  |  |  * __compute_return_epc_for_insn - Computes the return address and do emulate | 
					
						
							|  |  |  |  *				    branch simulation, if required. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @regs:	Pointer to pt_regs | 
					
						
							|  |  |  |  * @insn:	branch instruction to decode | 
					
						
							|  |  |  |  * @returns:	-EFAULT on error and forces SIGBUS, and on success | 
					
						
							|  |  |  |  *		returns 0 or BRANCH_LIKELY_TAKEN as appropriate after | 
					
						
							|  |  |  |  *		evaluating the branch. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | int __compute_return_epc_for_insn(struct pt_regs *regs, | 
					
						
							|  |  |  | 				   union mips_instruction insn) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-07-13 23:02:42 +09:00
										 |  |  | 	unsigned int bit, fcr31, dspcontrol; | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 	long epc = regs->cp0_epc; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (insn.i_format.opcode) { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * jr and jalr are in r_format format. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	case spec_op: | 
					
						
							|  |  |  | 		switch (insn.r_format.func) { | 
					
						
							|  |  |  | 		case jalr_op: | 
					
						
							|  |  |  | 			regs->regs[insn.r_format.rd] = epc + 8; | 
					
						
							|  |  |  | 			/* Fall through */ | 
					
						
							|  |  |  | 		case jr_op: | 
					
						
							|  |  |  | 			regs->cp0_epc = regs->regs[insn.r_format.rs]; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * This group contains: | 
					
						
							|  |  |  | 	 * bltz_op, bgez_op, bltzl_op, bgezl_op, | 
					
						
							|  |  |  | 	 * bltzal_op, bgezal_op, bltzall_op, bgezall_op. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	case bcond_op: | 
					
						
							|  |  |  | 		switch (insn.i_format.rt) { | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | 		case bltz_op: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		case bltzl_op: | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if ((long)regs->regs[insn.i_format.rs] < 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == bltzl_op) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case bgez_op: | 
					
						
							|  |  |  | 		case bgezl_op: | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if ((long)regs->regs[insn.i_format.rs] >= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == bgezl_op) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case bltzal_op: | 
					
						
							|  |  |  | 		case bltzall_op: | 
					
						
							|  |  |  | 			regs->regs[31] = epc + 8; | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if ((long)regs->regs[insn.i_format.rs] < 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == bltzall_op) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case bgezal_op: | 
					
						
							|  |  |  | 		case bgezall_op: | 
					
						
							|  |  |  | 			regs->regs[31] = epc + 8; | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if ((long)regs->regs[insn.i_format.rs] >= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == bgezall_op) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-31 11:49:19 +00:00
										 |  |  | 		case bposge32_op: | 
					
						
							|  |  |  | 			if (!cpu_has_dsp) | 
					
						
							|  |  |  | 				goto sigill; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			dspcontrol = rddsp(0x01); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (dspcontrol >= 32) { | 
					
						
							|  |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * These are unconditional and in j_format. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	case jal_op: | 
					
						
							|  |  |  | 		regs->regs[31] = regs->cp0_epc + 8; | 
					
						
							|  |  |  | 	case j_op: | 
					
						
							|  |  |  | 		epc += 4; | 
					
						
							|  |  |  | 		epc >>= 28; | 
					
						
							|  |  |  | 		epc <<= 28; | 
					
						
							|  |  |  | 		epc |= (insn.j_format.target << 2); | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							| 
									
										
										
										
											2013-03-25 13:08:40 -05:00
										 |  |  | 		if (insn.i_format.opcode == jalx_op) | 
					
						
							|  |  |  | 			set_isa16_mode(regs->cp0_epc); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * These are conditional and in i_format. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	case beq_op: | 
					
						
							|  |  |  | 	case beql_op: | 
					
						
							|  |  |  | 		if (regs->regs[insn.i_format.rs] == | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 		    regs->regs[insn.i_format.rt]) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (insn.i_format.rt == beql_op) | 
					
						
							|  |  |  | 				ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case bne_op: | 
					
						
							|  |  |  | 	case bnel_op: | 
					
						
							|  |  |  | 		if (regs->regs[insn.i_format.rs] != | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 		    regs->regs[insn.i_format.rt]) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (insn.i_format.rt == bnel_op) | 
					
						
							|  |  |  | 				ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case blez_op: /* not really i_format */ | 
					
						
							|  |  |  | 	case blezl_op: | 
					
						
							|  |  |  | 		/* rt field assumed to be zero */ | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 		if ((long)regs->regs[insn.i_format.rs] <= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (insn.i_format.rt == bnel_op) | 
					
						
							|  |  |  | 				ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case bgtz_op: | 
					
						
							|  |  |  | 	case bgtzl_op: | 
					
						
							|  |  |  | 		/* rt field assumed to be zero */ | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 		if ((long)regs->regs[insn.i_format.rs] > 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (insn.i_format.rt == bnel_op) | 
					
						
							|  |  |  | 				ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * And now the FPA/cp1 branch instructions. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	case cop1_op: | 
					
						
							| 
									
										
										
										
											2005-05-09 13:16:07 +00:00
										 |  |  | 		preempt_disable(); | 
					
						
							|  |  |  | 		if (is_fpu_owner()) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); | 
					
						
							| 
									
										
										
										
											2005-05-09 13:16:07 +00:00
										 |  |  | 		else | 
					
						
							| 
									
										
										
										
											2006-05-16 01:26:03 +09:00
										 |  |  | 			fcr31 = current->thread.fpu.fcr31; | 
					
						
							| 
									
										
										
										
											2005-05-09 13:16:07 +00:00
										 |  |  | 		preempt_enable(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		bit = (insn.i_format.rt >> 2); | 
					
						
							|  |  |  | 		bit += (bit != 0); | 
					
						
							|  |  |  | 		bit += 23; | 
					
						
							| 
									
										
										
										
											2006-04-26 21:33:03 +01:00
										 |  |  | 		switch (insn.i_format.rt & 3) { | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | 		case 0: /* bc1f */ | 
					
						
							|  |  |  | 		case 2: /* bc1fl */ | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (~fcr31 & (1 << bit)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == 2) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | 		case 1: /* bc1t */ | 
					
						
							|  |  |  | 		case 3: /* bc1tl */ | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 			if (fcr31 & (1 << bit)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 				if (insn.i_format.rt == 3) | 
					
						
							|  |  |  | 					ret = BRANCH_LIKELY_TAKEN; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				epc += 8; | 
					
						
							|  |  |  | 			regs->cp0_epc = epc; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2008-12-11 15:33:34 -08:00
										 |  |  | #ifdef CONFIG_CPU_CAVIUM_OCTEON
 | 
					
						
							|  |  |  | 	case lwc2_op: /* This is bbit0 on Octeon */ | 
					
						
							|  |  |  | 		if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) | 
					
						
							|  |  |  | 		     == 0) | 
					
						
							|  |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case ldc2_op: /* This is bbit032 on Octeon */ | 
					
						
							|  |  |  | 		if ((regs->regs[insn.i_format.rs] & | 
					
						
							|  |  |  | 		    (1ull<<(insn.i_format.rt+32))) == 0) | 
					
						
							|  |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case swc2_op: /* This is bbit1 on Octeon */ | 
					
						
							|  |  |  | 		if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) | 
					
						
							|  |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case sdc2_op: /* This is bbit132 on Octeon */ | 
					
						
							|  |  |  | 		if (regs->regs[insn.i_format.rs] & | 
					
						
							|  |  |  | 		    (1ull<<(insn.i_format.rt+32))) | 
					
						
							|  |  |  | 			epc = epc + 4 + (insn.i_format.simmediate << 2); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			epc += 8; | 
					
						
							|  |  |  | 		regs->cp0_epc = epc; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | sigill: | 
					
						
							|  |  |  | 	printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | 	return -EFAULT; | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); | 
					
						
							| 
									
										
										
										
											2005-05-31 11:49:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-08 17:07:11 +05:30
										 |  |  | int __compute_return_epc(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int __user *addr; | 
					
						
							|  |  |  | 	long epc; | 
					
						
							|  |  |  | 	union mips_instruction insn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	epc = regs->cp0_epc; | 
					
						
							|  |  |  | 	if (epc & 3) | 
					
						
							|  |  |  | 		goto unaligned; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Read the instruction | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addr = (unsigned int __user *) epc; | 
					
						
							|  |  |  | 	if (__get_user(insn.word, addr)) { | 
					
						
							|  |  |  | 		force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return __compute_return_epc_for_insn(regs, insn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unaligned: | 
					
						
							|  |  |  | 	printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); | 
					
						
							| 
									
										
										
										
											2005-05-31 11:49:19 +00:00
										 |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | 	return -EFAULT; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |