exceptions.c 4.2 KB
Newer Older
M
Michal Simek 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * HW exception handling
 *
 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
 * Copyright (C) 2008 PetaLogix
 *
 * This file is subject to the terms and conditions of the GNU General
 * Public License.  See the file COPYING in the main directory of this
 * archive for more details.
 */

/*
 * This file handles the architecture-dependent parts of hardware exceptions
 */

#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kallsyms.h>
#include <linux/module.h>

#include <asm/exceptions.h>
#include <asm/entry.h>		/* For KM CPU var */
24 25 26
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
M
Michal Simek 已提交
27 28 29 30 31 32 33
#include <asm/current.h>

#define MICROBLAZE_ILL_OPCODE_EXCEPTION	0x02
#define MICROBLAZE_IBUS_EXCEPTION	0x03
#define MICROBLAZE_DBUS_EXCEPTION	0x04
#define MICROBLAZE_DIV_ZERO_EXCEPTION	0x05
#define MICROBLAZE_FPU_EXCEPTION	0x06
34
#define MICROBLAZE_PRIVILEGED_EXCEPTION	0x07
M
Michal Simek 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

static DEFINE_SPINLOCK(die_lock);

void die(const char *str, struct pt_regs *fp, long err)
{
	console_verbose();
	spin_lock_irq(&die_lock);
	printk(KERN_WARNING "Oops: %s, sig: %ld\n", str, err);
	show_regs(fp);
	spin_unlock_irq(&die_lock);
	/* do_exit() should take care of panic'ing from an interrupt
	 * context so we don't handle it here
	 */
	do_exit(err);
}

void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{
	siginfo_t info;

	if (kernel_mode(regs)) {
		debugger(regs);
		die("Exception in kernel mode", regs, signr);
	}
	info.si_signo = signr;
	info.si_errno = 0;
	info.si_code = code;
	info.si_addr = (void __user *) addr;
	force_sig_info(signr, &info, current);
}

asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
							int fsr, int addr)
{
69 70 71 72 73
#ifdef CONFIG_MMU
	int code;
	addr = regs->pc;
#endif

M
Michal Simek 已提交
74 75 76 77 78 79 80 81
#if 0
	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:
82 83 84 85 86 87 88
		if (user_mode(regs)) {
			printk(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);
M
Michal Simek 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
		break;
	case MICROBLAZE_IBUS_EXCEPTION:
		if (user_mode(regs)) {
			printk(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");
		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");
			_exception(SIGBUS, regs, BUS_ADRERR, addr);
			return;
		}
		printk(KERN_WARNING "Data bus error exception in kernel mode.\n");
		die("bus exception", regs, SIGBUS);
		break;
	case MICROBLAZE_DIV_ZERO_EXCEPTION:
109 110 111 112 113 114 115
		if (user_mode(regs)) {
			printk(KERN_WARNING "Divide by zero exception in user mode\n");
			_exception(SIGILL, regs, ILL_ILLOPC, addr);
			return;
		}
		printk(KERN_WARNING "Divide by zero exception in kernel mode.\n");
		die("Divide by exception", regs, SIGBUS);
M
Michal Simek 已提交
116 117
		break;
	case MICROBLAZE_FPU_EXCEPTION:
118
		printk(KERN_WARNING "FPU exception\n");
M
Michal Simek 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
		/* IEEE FP exception */
		/* I removed fsr variable and use code var for storing fsr */
		if (fsr & FSR_IO)
			fsr = FPE_FLTINV;
		else if (fsr & FSR_OF)
			fsr = FPE_FLTOVF;
		else if (fsr & FSR_UF)
			fsr = FPE_FLTUND;
		else if (fsr & FSR_DZ)
			fsr = FPE_FLTDIV;
		else if (fsr & FSR_DO)
			fsr = FPE_FLTRES;
		_exception(SIGFPE, regs, fsr, addr);
		break;

134 135 136 137 138 139 140 141 142 143 144 145
#ifdef CONFIG_MMU
	case MICROBLAZE_PRIVILEGED_EXCEPTION:
		printk(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
M
Michal Simek 已提交
146
	default:
147
	/* FIXME what to do in unexpected exception */
M
Michal Simek 已提交
148 149 150 151 152 153
		printk(KERN_WARNING "Unexpected exception %02x "
			"PC=%08x in %s mode\n", type, (unsigned int) addr,
			kernel_mode(regs) ? "kernel" : "user");
	}
	return;
}