Merge branch 'writeback' of git://git.kernel.dk/linux-2.6-block
[pandora-kernel.git] / arch / microblaze / kernel / exceptions.c
index 4a8a406..d9f70f8 100644 (file)
@@ -21,9 +21,9 @@
 
 #include <asm/exceptions.h>
 #include <asm/entry.h>         /* For KM CPU var */
-#include <asm/uaccess.h>
-#include <asm/errno.h>
-#include <asm/ptrace.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
 #include <asm/current.h>
 
 #define MICROBLAZE_ILL_OPCODE_EXCEPTION        0x02
@@ -31,7 +31,7 @@
 #define MICROBLAZE_DBUS_EXCEPTION      0x04
 #define MICROBLAZE_DIV_ZERO_EXCEPTION  0x05
 #define MICROBLAZE_FPU_EXCEPTION       0x06
-#define MICROBLAZE_PRIVILEG_EXCEPTION  0x07
+#define MICROBLAZE_PRIVILEGED_EXCEPTION        0x07
 
 static DEFINE_SPINLOCK(die_lock);
 
@@ -66,40 +66,65 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
                                                        int fsr, int addr)
 {
+#ifdef CONFIG_MMU
+       int code;
+       addr = regs->pc;
+#endif
+
 #if 0
-       printk(KERN_WARNING "Exception %02x in %s mode, FSR=%08x PC=%08x ESR=%08x\n",
+       printk(KERN_WARNING "Exception %02x in %s mode, FSR=%08x PC=%08x " \
+                                                       "ESR=%08x\n",
                        type, user_mode(regs) ? "user" : "kernel", fsr,
                        (unsigned int) regs->pc, (unsigned int) regs->esr);
 #endif
 
        switch (type & 0x1F) {
        case MICROBLAZE_ILL_OPCODE_EXCEPTION:
-               _exception(SIGILL, regs, ILL_ILLOPC, addr);
+               if (user_mode(regs)) {
+                       pr_debug(KERN_WARNING "Illegal opcode exception " \
+                                                       "in user mode.\n");
+                       _exception(SIGILL, regs, ILL_ILLOPC, addr);
+                       return;
+               }
+               printk(KERN_WARNING "Illegal opcode exception " \
+                                                       "in kernel mode.\n");
+               die("opcode exception", regs, SIGBUS);
                break;
        case MICROBLAZE_IBUS_EXCEPTION:
                if (user_mode(regs)) {
-                       printk(KERN_WARNING "Instruction bus error exception in user mode.\n");
+                       pr_debug(KERN_WARNING "Instruction bus error " \
+                                               "exception in user mode.\n");
                        _exception(SIGBUS, regs, BUS_ADRERR, addr);
                        return;
                }
-               printk(KERN_WARNING "Instruction bus error exception in kernel mode.\n");
+               printk(KERN_WARNING "Instruction bus error exception " \
+                                                       "in kernel mode.\n");
                die("bus exception", regs, SIGBUS);
                break;
        case MICROBLAZE_DBUS_EXCEPTION:
                if (user_mode(regs)) {
-                       printk(KERN_WARNING "Data bus error exception in user mode.\n");
+                       pr_debug(KERN_WARNING "Data bus error exception " \
+                                                       "in user mode.\n");
                        _exception(SIGBUS, regs, BUS_ADRERR, addr);
                        return;
                }
-               printk(KERN_WARNING "Data bus error exception in kernel mode.\n");
+               printk(KERN_WARNING "Data bus error exception " \
+                                                       "in kernel mode.\n");
                die("bus exception", regs, SIGBUS);
                break;
        case MICROBLAZE_DIV_ZERO_EXCEPTION:
-               printk(KERN_WARNING "Divide by zero exception\n");
-               _exception(SIGILL, regs, ILL_ILLOPC, addr);
+               if (user_mode(regs)) {
+                       pr_debug(KERN_WARNING "Divide by zero exception " \
+                                                       "in user mode\n");
+                       _exception(SIGILL, regs, FPE_INTDIV, addr);
+                       return;
+               }
+               printk(KERN_WARNING "Divide by zero exception " \
+                                                       "in kernel mode.\n");
+               die("Divide by exception", regs, SIGBUS);
                break;
-
        case MICROBLAZE_FPU_EXCEPTION:
+               pr_debug(KERN_WARNING "FPU exception\n");
                /* IEEE FP exception */
                /* I removed fsr variable and use code var for storing fsr */
                if (fsr & FSR_IO)
@@ -115,7 +140,20 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
                _exception(SIGFPE, regs, fsr, addr);
                break;
 
+#ifdef CONFIG_MMU
+       case MICROBLAZE_PRIVILEGED_EXCEPTION:
+               pr_debug(KERN_WARNING "Privileged exception\n");
+               /* "brk r0,r0" - used as debug breakpoint */
+               if (get_user(code, (unsigned long *)regs->pc) == 0
+                       && code == 0x980c0000) {
+                       _exception(SIGTRAP, regs, TRAP_BRKPT, addr);
+               } else {
+                       _exception(SIGILL, regs, ILL_PRVOPC, addr);
+               }
+               break;
+#endif
        default:
+       /* FIXME what to do in unexpected exception */
                printk(KERN_WARNING "Unexpected exception %02x "
                        "PC=%08x in %s mode\n", type, (unsigned int) addr,
                        kernel_mode(regs) ? "kernel" : "user");