linux/arch/microblaze/kernel/exceptions.c
<<
>>
Prefs
   1/*
   2 * HW exception handling
   3 *
   4 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
   5 * Copyright (C) 2008 PetaLogix
   6 *
   7 * This file is subject to the terms and conditions of the GNU General
   8 * Public License.  See the file COPYING in the main directory of this
   9 * archive for more details.
  10 */
  11
  12/*
  13 * This file handles the architecture-dependent parts of hardware exceptions
  14 */
  15
  16#include <linux/export.h>
  17#include <linux/kernel.h>
  18#include <linux/signal.h>
  19#include <linux/sched.h>
  20#include <linux/kallsyms.h>
  21
  22#include <asm/exceptions.h>
  23#include <asm/entry.h>          /* For KM CPU var */
  24#include <linux/uaccess.h>
  25#include <linux/errno.h>
  26#include <linux/ptrace.h>
  27#include <asm/current.h>
  28#include <asm/cacheflush.h>
  29
  30#define MICROBLAZE_ILL_OPCODE_EXCEPTION 0x02
  31#define MICROBLAZE_IBUS_EXCEPTION       0x03
  32#define MICROBLAZE_DBUS_EXCEPTION       0x04
  33#define MICROBLAZE_DIV_ZERO_EXCEPTION   0x05
  34#define MICROBLAZE_FPU_EXCEPTION        0x06
  35#define MICROBLAZE_PRIVILEGED_EXCEPTION 0x07
  36
  37static DEFINE_SPINLOCK(die_lock);
  38
  39void die(const char *str, struct pt_regs *fp, long err)
  40{
  41        console_verbose();
  42        spin_lock_irq(&die_lock);
  43        pr_warn("Oops: %s, sig: %ld\n", str, err);
  44        show_regs(fp);
  45        spin_unlock_irq(&die_lock);
  46        /* do_exit() should take care of panic'ing from an interrupt
  47         * context so we don't handle it here
  48         */
  49        do_exit(err);
  50}
  51
  52/* for user application debugging */
  53asmlinkage void sw_exception(struct pt_regs *regs)
  54{
  55        _exception(SIGTRAP, regs, TRAP_BRKPT, regs->r16);
  56        flush_dcache_range(regs->r16, regs->r16 + 0x4);
  57        flush_icache_range(regs->r16, regs->r16 + 0x4);
  58}
  59
  60void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
  61{
  62        siginfo_t info;
  63
  64        if (kernel_mode(regs))
  65                die("Exception in kernel mode", regs, signr);
  66
  67        info.si_signo = signr;
  68        info.si_errno = 0;
  69        info.si_code = code;
  70        info.si_addr = (void __user *) addr;
  71        force_sig_info(signr, &info, current);
  72}
  73
  74asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
  75                                                        int fsr, int addr)
  76{
  77#ifdef CONFIG_MMU
  78        addr = regs->pc;
  79#endif
  80
  81#if 0
  82        pr_warn("Exception %02x in %s mode, FSR=%08x PC=%08x ESR=%08x\n",
  83                        type, user_mode(regs) ? "user" : "kernel", fsr,
  84                        (unsigned int) regs->pc, (unsigned int) regs->esr);
  85#endif
  86
  87        switch (type & 0x1F) {
  88        case MICROBLAZE_ILL_OPCODE_EXCEPTION:
  89                if (user_mode(regs)) {
  90                        pr_debug("Illegal opcode exception in user mode\n");
  91                        _exception(SIGILL, regs, ILL_ILLOPC, addr);
  92                        return;
  93                }
  94                pr_warn("Illegal opcode exception in kernel mode.\n");
  95                die("opcode exception", regs, SIGBUS);
  96                break;
  97        case MICROBLAZE_IBUS_EXCEPTION:
  98                if (user_mode(regs)) {
  99                        pr_debug("Instruction bus error exception in user mode\n");
 100                        _exception(SIGBUS, regs, BUS_ADRERR, addr);
 101                        return;
 102                }
 103                pr_warn("Instruction bus error exception in kernel mode.\n");
 104                die("bus exception", regs, SIGBUS);
 105                break;
 106        case MICROBLAZE_DBUS_EXCEPTION:
 107                if (user_mode(regs)) {
 108                        pr_debug("Data bus error exception in user mode\n");
 109                        _exception(SIGBUS, regs, BUS_ADRERR, addr);
 110                        return;
 111                }
 112                pr_warn("Data bus error exception in kernel mode.\n");
 113                die("bus exception", regs, SIGBUS);
 114                break;
 115        case MICROBLAZE_DIV_ZERO_EXCEPTION:
 116                if (user_mode(regs)) {
 117                        pr_debug("Divide by zero exception in user mode\n");
 118                        _exception(SIGFPE, regs, FPE_INTDIV, addr);
 119                        return;
 120                }
 121                pr_warn("Divide by zero exception in kernel mode.\n");
 122                die("Divide by zero exception", regs, SIGBUS);
 123                break;
 124        case MICROBLAZE_FPU_EXCEPTION:
 125                pr_debug("FPU exception\n");
 126                /* IEEE FP exception */
 127                /* I removed fsr variable and use code var for storing fsr */
 128                if (fsr & FSR_IO)
 129                        fsr = FPE_FLTINV;
 130                else if (fsr & FSR_OF)
 131                        fsr = FPE_FLTOVF;
 132                else if (fsr & FSR_UF)
 133                        fsr = FPE_FLTUND;
 134                else if (fsr & FSR_DZ)
 135                        fsr = FPE_FLTDIV;
 136                else if (fsr & FSR_DO)
 137                        fsr = FPE_FLTRES;
 138                _exception(SIGFPE, regs, fsr, addr);
 139                break;
 140
 141#ifdef CONFIG_MMU
 142        case MICROBLAZE_PRIVILEGED_EXCEPTION:
 143                pr_debug("Privileged exception\n");
 144                _exception(SIGILL, regs, ILL_PRVOPC, addr);
 145                break;
 146#endif
 147        default:
 148        /* FIXME what to do in unexpected exception */
 149                pr_warn("Unexpected exception %02x PC=%08x in %s mode\n",
 150                        type, (unsigned int) addr,
 151                        kernel_mode(regs) ? "kernel" : "user");
 152        }
 153        return;
 154}
 155