summaryrefslogtreecommitdiff
path: root/arch/tile/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/tile/kernel/traps.c')
-rw-r--r--arch/tile/kernel/traps.c130
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();
}