diff options
Diffstat (limited to 'arch/tile/kernel/traps.c')
| -rw-r--r-- | arch/tile/kernel/traps.c | 140 | 
1 files changed, 111 insertions, 29 deletions
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 5474fc2e77e..f3ceb6308e4 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c @@ -15,21 +15,22 @@  #include <linux/sched.h>  #include <linux/kernel.h>  #include <linux/kprobes.h> +#include <linux/kdebug.h>  #include <linux/module.h>  #include <linux/reboot.h>  #include <linux/uaccess.h>  #include <linux/ptrace.h> -#include <asm/opcode-tile.h> -#include <asm/opcode_constants.h>  #include <asm/stack.h>  #include <asm/traps.h> +#include <asm/setup.h>  #include <arch/interrupts.h>  #include <arch/spr_def.h> +#include <arch/opcode.h>  void __init trap_init(void)  { -	/* Nothing needed here since we link code at .intrpt1 */ +	/* Nothing needed here since we link code at .intrpt */  }  int unaligned_fixup = 1; @@ -41,10 +42,9 @@ static int __init setup_unaligned_fixup(char *str)  	 * will still parse the instruction, then fire a SIGBUS with  	 * the correct address from inside the single_step code.  	 */ -	long val; -	if (strict_strtol(str, 0, &val) != 0) +	if (kstrtoint(str, 0, &unaligned_fixup) != 0)  		return 0; -	unaligned_fixup = val; +  	pr_info("Fixups for unaligned data accesses are %s\n",  	       unaligned_fixup >= 0 ?  	       (unaligned_fixup ? "enabled" : "disabled") : @@ -100,13 +100,7 @@ static int retry_gpv(unsigned int gpv_reason)  #endif /* CHIP_HAS_TILE_DMA() */ -#ifdef __tilegx__ -#define bundle_bits tilegx_bundle_bits -#else -#define bundle_bits tile_bundle_bits -#endif - -extern bundle_bits bpt_code; +extern tile_bundle_bits bpt_code;  asm(".pushsection .rodata.bpt_code,\"a\";"      ".align 8;" @@ -114,7 +108,7 @@ asm(".pushsection .rodata.bpt_code,\"a\";"      ".size bpt_code,.-bpt_code;"      ".popsection"); -static int special_ill(bundle_bits bundle, int *sigp, int *codep) +static int special_ill(tile_bundle_bits bundle, int *sigp, int *codep)  {  	int sig, code, maxcode; @@ -135,7 +129,7 @@ static int special_ill(bundle_bits bundle, int *sigp, int *codep)  	if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1)  		return 0;  #else -	if (bundle & TILE_BUNDLE_Y_ENCODING_MASK) +	if (bundle & TILEPRO_BUNDLE_Y_ENCODING_MASK)  		return 0;  	if (get_Opcode_X1(bundle) != SHUN_0_OPCODE_X1)  		return 0; @@ -195,34 +189,119 @@ static int special_ill(bundle_bits bundle, int *sigp, int *codep)  	return 1;  } +static const char *const int_name[] = { +	[INT_MEM_ERROR] = "Memory error", +	[INT_ILL] = "Illegal instruction", +	[INT_GPV] = "General protection violation", +	[INT_UDN_ACCESS] = "UDN access", +	[INT_IDN_ACCESS] = "IDN access", +#if CHIP_HAS_SN() +	[INT_SN_ACCESS] = "SN access", +#endif +	[INT_SWINT_3] = "Software interrupt 3", +	[INT_SWINT_2] = "Software interrupt 2", +	[INT_SWINT_0] = "Software interrupt 0", +	[INT_UNALIGN_DATA] = "Unaligned data", +	[INT_DOUBLE_FAULT] = "Double fault", +#ifdef __tilegx__ +	[INT_ILL_TRANS] = "Illegal virtual address", +#endif +}; + +static int do_bpt(struct pt_regs *regs) +{ +	unsigned long bundle, bcode, bpt; + +	bundle = *(unsigned long *)instruction_pointer(regs); + +	/* +	 * bpt shoule be { bpt; nop }, which is 0x286a44ae51485000ULL. +	 * we encode the unused least significant bits for other purpose. +	 */ +	bpt = bundle & ~((1ULL << 12) - 1); +	if (bpt != TILE_BPT_BUNDLE) +		return 0; + +	bcode = bundle & ((1ULL << 12) - 1); +	/* +	 * notify the kprobe handlers, if instruction is likely to +	 * pertain to them. +	 */ +	switch (bcode) { +	/* breakpoint_insn */ +	case 0: +		notify_die(DIE_BREAK, "debug", regs, bundle, +			INT_ILL, SIGTRAP); +		break; +	/* compiled_bpt */ +	case DIE_COMPILED_BPT: +		notify_die(DIE_COMPILED_BPT, "debug", regs, bundle, +			INT_ILL, SIGTRAP); +		break; +	/* breakpoint2_insn */ +	case DIE_SSTEPBP: +		notify_die(DIE_SSTEPBP, "single_step", regs, bundle, +			INT_ILL, SIGTRAP); +		break; +	default: +		return 0; +	} + +	return 1; +} +  void __kprobes do_trap(struct pt_regs *regs, int fault_num,  		       unsigned long reason)  {  	siginfo_t info = { 0 };  	int signo, code; -	unsigned long address; -	bundle_bits instr; +	unsigned long address = 0; +	tile_bundle_bits instr; +	int is_kernel = !user_mode(regs); + +	/* Handle breakpoints, etc. */ +	if (is_kernel && fault_num == INT_ILL && do_bpt(regs)) +		return; -	/* Re-enable interrupts. */ -	local_irq_enable(); +	/* Re-enable interrupts, if they were previously enabled. */ +	if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) +		local_irq_enable();  	/*  	 * If it hits in kernel mode and we can't fix it up, just exit the  	 * current process and hope for the best.  	 */ -	if (!user_mode(regs)) { -		if (fixup_exception(regs))  /* only UNALIGN_DATA in practice */ +	if (is_kernel) { +		const char *name; +		char buf[100]; +		if (fixup_exception(regs))  /* ILL_TRANS or UNALIGN_DATA */  			return; -		pr_alert("Kernel took bad trap %d at PC %#lx\n", -		       fault_num, regs->pc); +		if (fault_num >= 0 && +		    fault_num < sizeof(int_name)/sizeof(int_name[0]) && +		    int_name[fault_num] != NULL) +			name = int_name[fault_num]; +		else +			name = "Unknown interrupt";  		if (fault_num == INT_GPV) -			pr_alert("GPV_REASON is %#lx\n", reason); +			snprintf(buf, sizeof(buf), "; GPV_REASON %#lx", reason); +#ifdef __tilegx__ +		else if (fault_num == INT_ILL_TRANS) +			snprintf(buf, sizeof(buf), "; address %#lx", reason); +#endif +		else +			buf[0] = '\0'; +		pr_alert("Kernel took bad trap %d (%s) at PC %#lx%s\n", +			 fault_num, name, regs->pc, buf);  		show_regs(regs);  		do_exit(SIGKILL);  /* FIXME: implement i386 die() */  		return;  	}  	switch (fault_num) { +	case INT_MEM_ERROR: +		signo = SIGBUS; +		code = BUS_OBJERR; +		break;  	case INT_ILL:  		if (copy_from_user(&instr, (void __user *)regs->pc,  				   sizeof(instr))) { @@ -289,14 +368,15 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num,  		address = regs->pc;  		break;  #ifdef __tilegx__ -	case INT_ILL_TRANS: +	case INT_ILL_TRANS: { +		/* Avoid a hardware erratum with the return address stack. */ +		fill_ra_stack(); +  		signo = SIGSEGV; +		address = reason;  		code = SEGV_MAPERR; -		if (reason & SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK) -			address = regs->pc; -		else -			address = 0;  /* FIXME: GX: single-step for address */  		break; +	}  #endif  	default:  		panic("Unexpected do_trap interrupt number %d", fault_num); @@ -308,6 +388,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num,  	info.si_addr = (void __user *)address;  	if (signo == SIGILL)  		info.si_trapno = fault_num; +	if (signo != SIGTRAP) +		trace_unhandled_signal("trap", regs, address, signo);  	force_sig_info(signo, &info, current);  }  | 
