Thomas BogendoerferMIPS: Fix new sparse warnings7cba4125 years agoHistory
1555 lines · 33.1 KB · C
RawDownload
/* * Handle unaligned accesses by emulation. * * 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, 1998, 1999, 2002 by Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. * Copyright (C) 2014 Imagination Technologies Ltd. * * This file contains exception handler for address error exception with the * special capability to execute faulting instructions in software.  The * handler does not try to handle the case when the program counter points * to an address not aligned to a word boundary. * * Putting data to unaligned addresses is a bad practice even on Intel where * only the performance is affected.  Much worse is that such code is non- * portable.  Due to several programs that die on MIPS due to alignment * problems I decided to implement this handler anyway though I originally * didn't intend to do this at all for user code. * * For now I enable fixing of address errors by default to make life easier. * I however intend to disable this somewhen in the future when the alignment * problems with user programs have been fixed.	 For programmers this is the * right way to go. * * Fixing address errors is a per process option.  The option is inherited * across fork(2) and execve(2) calls.	If you really want to use the * option in your user programs - I discourage the use of the software * emulation strongly - use the following code in your userland stuff: * * #include <sys/sysmips.h> * * ... * sysmips(MIPS_FIXADE, x); * ... * * The argument x is 0 for disabling software emulation, enabled otherwise. * * Below a little program to play around with this feature. * * #include <stdio.h> * #include <sys/sysmips.h> * * struct foo { *	   unsigned char bar[8]; * }; * * main(int argc, char *argv[]) * { *	   struct foo x = {0, 1, 2, 3, 4, 5, 6, 7}; *	   unsigned int *p = (unsigned int *) (x.bar + 3); *	   int i; * *	   if (argc > 1) *		   sysmips(MIPS_FIXADE, atoi(argv[1])); * *	   printf("*p = %08lx\n", *p); * *	   *p = 0xdeadface; * *	   for(i = 0; i <= 7; i++) *	   printf("%02x ", x.bar[i]); *	   printf("\n"); * } * * Coprocessor loads are not supported; I think this case is unimportant * in the practice. * * TODO: Handle ndc (attempted store to doubleword in uncached memory) *	 exception for the R6000. *	 A store crossing a page boundary might be executed only partially. *	 Undo the partial store in this case. */#include <linux/context_tracking.h>#include <linux/mm.h>#include <linux/signal.h>#include <linux/smp.h>#include <linux/sched.h>#include <linux/debugfs.h>#include <linux/perf_event.h> #include <asm/asm.h>#include <asm/branch.h>#include <asm/byteorder.h>#include <asm/cop2.h>#include <asm/debug.h>#include <asm/fpu.h>#include <asm/fpu_emulator.h>#include <asm/inst.h>#include <asm/unaligned-emul.h>#include <asm/mmu_context.h>#include <linux/uaccess.h> #include "access-helper.h" enum {	UNALIGNED_ACTION_QUIET,	UNALIGNED_ACTION_SIGNAL,	UNALIGNED_ACTION_SHOW,};#ifdef CONFIG_DEBUG_FSstatic u32 unaligned_instructions;static u32 unaligned_action;#else#define unaligned_action UNALIGNED_ACTION_QUIET#endifextern void show_registers(struct pt_regs *regs); static void emulate_load_store_insn(struct pt_regs *regs,	void __user *addr, unsigned int *pc){	unsigned long origpc, orig31, value;	union mips_instruction insn;	unsigned int res;	bool user = user_mode(regs); 	origpc = (unsigned long)pc;	orig31 = regs->regs[31]; 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); 	/*	 * This load never faults.	 */	__get_inst32(&insn.word, pc, user); 	switch (insn.i_format.opcode) {		/*		 * These are instructions that a compiler doesn't generate.  We		 * can assume therefore that the code is MIPS-aware and		 * really buggy.  Emulating these instructions would break the		 * semantics anyway.		 */	case ll_op:	case lld_op:	case sc_op:	case scd_op: 		/*		 * For these instructions the only way to create an address		 * error is an attempted access to kernel/supervisor address		 * space.		 */	case ldl_op:	case ldr_op:	case lwl_op:	case lwr_op:	case sdl_op:	case sdr_op:	case swl_op:	case swr_op:	case lb_op:	case lbu_op:	case sb_op:		goto sigbus; 		/*		 * The remaining opcodes are the ones that are really of		 * interest.		 */	case spec3_op:		if (insn.dsp_format.func == lx_op) {			switch (insn.dsp_format.op) {			case lwx_op:				if (user && !access_ok(addr, 4))					goto sigbus;				LoadW(addr, value, res);				if (res)					goto fault;				compute_return_epc(regs);				regs->regs[insn.dsp_format.rd] = value;				break;			case lhx_op:				if (user && !access_ok(addr, 2))					goto sigbus;				LoadHW(addr, value, res);				if (res)					goto fault;				compute_return_epc(regs);				regs->regs[insn.dsp_format.rd] = value;				break;			default:				goto sigill;			}		}#ifdef CONFIG_EVA		else {			/*			 * we can land here only from kernel accessing user			 * memory, so we need to "switch" the address limit to			 * user space, so that address check can work properly.			 */			switch (insn.spec3_format.func) {			case lhe_op:				if (!access_ok(addr, 2))					goto sigbus;				LoadHWE(addr, value, res);				if (res)					goto fault;				compute_return_epc(regs);				regs->regs[insn.spec3_format.rt] = value;				break;			case lwe_op:				if (!access_ok(addr, 4))					goto sigbus;				LoadWE(addr, value, res);				if (res)					goto fault;				compute_return_epc(regs);				regs->regs[insn.spec3_format.rt] = value;				break;			case lhue_op:				if (!access_ok(addr, 2))					goto sigbus;				LoadHWUE(addr, value, res);				if (res)					goto fault;				compute_return_epc(regs);				regs->regs[insn.spec3_format.rt] = value;				break;			case she_op:				if (!access_ok(addr, 2))					goto sigbus;				compute_return_epc(regs);				value = regs->regs[insn.spec3_format.rt];				StoreHWE(addr, value, res);				if (res)					goto fault;				break;			case swe_op:				if (!access_ok(addr, 4))					goto sigbus;				compute_return_epc(regs);				value = regs->regs[insn.spec3_format.rt];				StoreWE(addr, value, res);				if (res)					goto fault;				break;			default:				goto sigill;			}		}#endif		break;	case lh_op:		if (user && !access_ok(addr, 2))			goto sigbus; 		if (IS_ENABLED(CONFIG_EVA) && user)			LoadHWE(addr, value, res);		else			LoadHW(addr, value, res); 		if (res)			goto fault;		compute_return_epc(regs);		regs->regs[insn.i_format.rt] = value;		break; 	case lw_op:		if (user && !access_ok(addr, 4))			goto sigbus; 		if (IS_ENABLED(CONFIG_EVA) && user)			LoadWE(addr, value, res);		else			LoadW(addr, value, res); 		if (res)			goto fault;		compute_return_epc(regs);		regs->regs[insn.i_format.rt] = value;		break; 	case lhu_op:		if (user && !access_ok(addr, 2))			goto sigbus; 		if (IS_ENABLED(CONFIG_EVA) && user)			LoadHWUE(addr, value, res);		else			LoadHWU(addr, value, res); 		if (res)			goto fault;		compute_return_epc(regs);		regs->regs[insn.i_format.rt] = value;		break; 	case lwu_op:#ifdef CONFIG_64BIT		/*		 * A 32-bit kernel might be running on a 64-bit processor.  But		 * if we're on a 32-bit processor and an i-cache incoherency		 * or race makes us see a 64-bit instruction here the sdl/sdr		 * would blow up, so for now we don't handle unaligned 64-bit		 * instructions on 32-bit kernels.		 */		if (user && !access_ok(addr, 4))			goto sigbus; 		LoadWU(addr, value, res);		if (res)			goto fault;		compute_return_epc(regs);		regs->regs[insn.i_format.rt] = value;		break;#endif /* CONFIG_64BIT */ 		/* Cannot handle 64-bit instructions in 32-bit kernel */		goto sigill; 	case ld_op:#ifdef CONFIG_64BIT		/*		 * A 32-bit kernel might be running on a 64-bit processor.  But		 * if we're on a 32-bit processor and an i-cache incoherency		 * or race makes us see a 64-bit instruction here the sdl/sdr		 * would blow up, so for now we don't handle unaligned 64-bit		 * instructions on 32-bit kernels.		 */		if (user && !access_ok(addr, 8))			goto sigbus; 		LoadDW(addr, value, res);		if (res)			goto fault;		compute_return_epc(regs);		regs->regs[insn.i_format.rt] = value;		break;#endif /* CONFIG_64BIT */ 		/* Cannot handle 64-bit instructions in 32-bit kernel */		goto sigill; 	case sh_op:		if (user && !access_ok(addr, 2))			goto sigbus; 		compute_return_epc(regs);		value = regs->regs[insn.i_format.rt]; 		if (IS_ENABLED(CONFIG_EVA) && user)			StoreHWE(addr, value, res);		else			StoreHW(addr, value, res); 		if (res)			goto fault;		break; 	case sw_op:		if (user && !access_ok(addr, 4))			goto sigbus; 		compute_return_epc(regs);		value = regs->regs[insn.i_format.rt]; 		if (IS_ENABLED(CONFIG_EVA) && user)			StoreWE(addr, value, res);		else			StoreW(addr, value, res); 		if (res)			goto fault;		break; 	case sd_op:#ifdef CONFIG_64BIT		/*		 * A 32-bit kernel might be running on a 64-bit processor.  But		 * if we're on a 32-bit processor and an i-cache incoherency		 * or race makes us see a 64-bit instruction here the sdl/sdr		 * would blow up, so for now we don't handle unaligned 64-bit		 * instructions on 32-bit kernels.		 */		if (user && !access_ok(addr, 8))			goto sigbus; 		compute_return_epc(regs);		value = regs->regs[insn.i_format.rt];		StoreDW(addr, value, res);		if (res)			goto fault;		break;#endif /* CONFIG_64BIT */ 		/* Cannot handle 64-bit instructions in 32-bit kernel */		goto sigill; #ifdef CONFIG_MIPS_FP_SUPPORT 	case lwc1_op:	case ldc1_op:	case swc1_op:	case sdc1_op:	case cop1x_op: {		void __user *fault_addr = NULL; 		die_if_kernel("Unaligned FP access in kernel code", regs);		BUG_ON(!used_math()); 		res = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,					       &fault_addr);		own_fpu(1);	/* Restore FPU state. */ 		/* Signal if something went wrong. */		process_fpemu_return(res, fault_addr, 0); 		if (res == 0)			break;		return;	}#endif /* CONFIG_MIPS_FP_SUPPORT */ #ifdef CONFIG_CPU_HAS_MSA 	case msa_op: {		unsigned int wd, preempted;		enum msa_2b_fmt df;		union fpureg *fpr; 		if (!cpu_has_msa)			goto sigill; 		/*		 * If we've reached this point then userland should have taken		 * the MSA disabled exception & initialised vector context at		 * some point in the past.		 */		BUG_ON(!thread_msa_context_live()); 		df = insn.msa_mi10_format.df;		wd = insn.msa_mi10_format.wd;		fpr = &current->thread.fpu.fpr[wd]; 		switch (insn.msa_mi10_format.func) {		case msa_ld_op:			if (!access_ok(addr, sizeof(*fpr)))				goto sigbus; 			do {				/*				 * If we have live MSA context keep track of				 * whether we get preempted in order to avoid				 * the register context we load being clobbered				 * by the live context as it's saved during				 * preemption. If we don't have live context				 * then it can't be saved to clobber the value				 * we load.				 */				preempted = test_thread_flag(TIF_USEDMSA); 				res = __copy_from_user_inatomic(fpr, addr,								sizeof(*fpr));				if (res)					goto fault; 				/*				 * Update the hardware register if it is in use				 * by the task in this quantum, in order to				 * avoid having to save & restore the whole				 * vector context.				 */				preempt_disable();				if (test_thread_flag(TIF_USEDMSA)) {					write_msa_wr(wd, fpr, df);					preempted = 0;				}				preempt_enable();			} while (preempted);			break; 		case msa_st_op:			if (!access_ok(addr, sizeof(*fpr)))				goto sigbus; 			/*			 * Update from the hardware register if it is in use by			 * the task in this quantum, in order to avoid having to			 * save & restore the whole vector context.			 */			preempt_disable();			if (test_thread_flag(TIF_USEDMSA))				read_msa_wr(wd, fpr, df);			preempt_enable(); 			res = __copy_to_user_inatomic(addr, fpr, sizeof(*fpr));			if (res)				goto fault;			break; 		default:			goto sigbus;		} 		compute_return_epc(regs);		break;

Lines 1–500 of 1555.