diff options
Diffstat (limited to 'arch/tile/kernel/traps.c')
-rw-r--r-- | arch/tile/kernel/traps.c | 130 |
1 files changed, 105 insertions, 25 deletions
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 12cb10f3852..3870abbeeaa 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c @@ -20,6 +20,9 @@ #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 <arch/interrupts.h> #include <arch/spr_def.h> @@ -42,7 +45,7 @@ static int __init setup_unaligned_fixup(char *str) if (strict_strtol(str, 0, &val) != 0) return 0; unaligned_fixup = val; - printk("Fixups for unaligned data accesses are %s\n", + pr_info("Fixups for unaligned data accesses are %s\n", unaligned_fixup >= 0 ? (unaligned_fixup ? "enabled" : "disabled") : "completely disabled"); @@ -56,7 +59,7 @@ static int dma_disabled; static int __init nodma(char *str) { - printk("User-space DMA is disabled\n"); + pr_info("User-space DMA is disabled\n"); dma_disabled = 1; return 1; } @@ -97,20 +100,106 @@ static int retry_gpv(unsigned int gpv_reason) #endif /* CHIP_HAS_TILE_DMA() */ -/* Defined inside do_trap(), below. */ #ifdef __tilegx__ -extern tilegx_bundle_bits bpt_code; +#define bundle_bits tilegx_bundle_bits #else -extern tile_bundle_bits bpt_code; +#define bundle_bits tile_bundle_bits #endif +extern bundle_bits bpt_code; + +asm(".pushsection .rodata.bpt_code,\"a\";" + ".align 8;" + "bpt_code: bpt;" + ".size bpt_code,.-bpt_code;" + ".popsection"); + +static int special_ill(bundle_bits bundle, int *sigp, int *codep) +{ + int sig, code, maxcode; + + if (bundle == bpt_code) { + *sigp = SIGTRAP; + *codep = TRAP_BRKPT; + return 1; + } + + /* If it's a "raise" bundle, then "ill" must be in pipe X1. */ +#ifdef __tilegx__ + if ((bundle & TILEGX_BUNDLE_MODE_MASK) != 0) + return 0; + if (get_Opcode_X1(bundle) != UNARY_OPCODE_X1) + return 0; + if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1) + return 0; +#else + if (bundle & TILE_BUNDLE_Y_ENCODING_MASK) + return 0; + if (get_Opcode_X1(bundle) != SHUN_0_OPCODE_X1) + return 0; + if (get_UnShOpcodeExtension_X1(bundle) != UN_0_SHUN_0_OPCODE_X1) + return 0; + if (get_UnOpcodeExtension_X1(bundle) != ILL_UN_0_SHUN_0_OPCODE_X1) + return 0; +#endif + + /* Check that the magic distinguishers are set to mean "raise". */ + if (get_Dest_X1(bundle) != 29 || get_SrcA_X1(bundle) != 37) + return 0; + + /* There must be an "addli zero, zero, VAL" in X0. */ + if (get_Opcode_X0(bundle) != ADDLI_OPCODE_X0) + return 0; + if (get_Dest_X0(bundle) != TREG_ZERO) + return 0; + if (get_SrcA_X0(bundle) != TREG_ZERO) + return 0; + + /* + * Validate the proposed signal number and si_code value. + * Note that we embed these in the static instruction itself + * so that we perturb the register state as little as possible + * at the time of the actual fault; it's unlikely you'd ever + * need to dynamically choose which kind of fault to raise + * from user space. + */ + sig = get_Imm16_X0(bundle) & 0x3f; + switch (sig) { + case SIGILL: + maxcode = NSIGILL; + break; + case SIGFPE: + maxcode = NSIGFPE; + break; + case SIGSEGV: + maxcode = NSIGSEGV; + break; + case SIGBUS: + maxcode = NSIGBUS; + break; + case SIGTRAP: + maxcode = NSIGTRAP; + break; + default: + return 0; + } + code = (get_Imm16_X0(bundle) >> 6) & 0xf; + if (code <= 0 || code > maxcode) + return 0; + + /* Make it the requested signal. */ + *sigp = sig; + *codep = code | __SI_FAULT; + 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; - __typeof__(bpt_code) instr; + bundle_bits instr; /* Re-enable interrupts. */ local_irq_enable(); @@ -122,10 +211,10 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, if (!user_mode(regs)) { if (fixup_exception(regs)) /* only UNALIGN_DATA in practice */ return; - printk(KERN_ALERT "Kernel took bad trap %d at PC %#lx\n", + pr_alert("Kernel took bad trap %d at PC %#lx\n", fault_num, regs->pc); if (fault_num == INT_GPV) - printk(KERN_ALERT "GPV_REASON is %#lx\n", reason); + pr_alert("GPV_REASON is %#lx\n", reason); show_regs(regs); do_exit(SIGKILL); /* FIXME: implement i386 die() */ return; @@ -133,22 +222,14 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, switch (fault_num) { case INT_ILL: - asm(".pushsection .rodata.bpt_code,\"a\";" - ".align 8;" - "bpt_code: bpt;" - ".size bpt_code,.-bpt_code;" - ".popsection"); - - if (copy_from_user(&instr, (void *)regs->pc, sizeof(instr))) { - printk(KERN_ERR "Unreadable instruction for INT_ILL:" + if (copy_from_user(&instr, (void __user *)regs->pc, + sizeof(instr))) { + pr_err("Unreadable instruction for INT_ILL:" " %#lx\n", regs->pc); do_exit(SIGKILL); return; } - if (instr == bpt_code) { - signo = SIGTRAP; - code = TRAP_BRKPT; - } else { + if (!special_ill(instr, &signo, &code)) { signo = SIGILL; code = ILL_ILLOPC; } @@ -181,7 +262,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, if (unaligned_fixup >= 0) { struct single_step_state *state = current_thread_info()->step_state; - if (!state || (void *)(regs->pc) != state->buffer) { + if (!state || + (void __user *)(regs->pc) != state->buffer) { single_step_once(regs); return; } @@ -221,17 +303,15 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, info.si_signo = signo; info.si_code = code; - info.si_addr = (void *)address; + info.si_addr = (void __user *)address; if (signo == SIGILL) info.si_trapno = fault_num; force_sig_info(signo, &info, current); } -extern void _dump_stack(int dummy, ulong pc, ulong lr, ulong sp, ulong r52); - void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) { _dump_stack(dummy, pc, lr, sp, r52); - printk("Double fault: exiting\n"); + pr_emerg("Double fault: exiting\n"); machine_halt(); } |