arch/tile: Miscellaneous cleanup changes.
[pandora-kernel.git] / arch / tile / kernel / traps.c
index 12cb10f..3870abb 100644 (file)
@@ -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();
 }