xref: /openbmc/linux/arch/riscv/kernel/traps.c (revision 9a6e7af02f7fcc8c68e9d6ce167ee6ab0052cb60)
176d2a049SPalmer Dabbelt /*
276d2a049SPalmer Dabbelt  * Copyright (C) 2012 Regents of the University of California
376d2a049SPalmer Dabbelt  *
476d2a049SPalmer Dabbelt  *   This program is free software; you can redistribute it and/or
576d2a049SPalmer Dabbelt  *   modify it under the terms of the GNU General Public License
676d2a049SPalmer Dabbelt  *   as published by the Free Software Foundation, version 2.
776d2a049SPalmer Dabbelt  *
876d2a049SPalmer Dabbelt  *   This program is distributed in the hope that it will be useful,
976d2a049SPalmer Dabbelt  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1076d2a049SPalmer Dabbelt  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1176d2a049SPalmer Dabbelt  *   GNU General Public License for more details.
1276d2a049SPalmer Dabbelt  */
1376d2a049SPalmer Dabbelt 
1476d2a049SPalmer Dabbelt #include <linux/kernel.h>
1576d2a049SPalmer Dabbelt #include <linux/init.h>
1676d2a049SPalmer Dabbelt #include <linux/sched.h>
1776d2a049SPalmer Dabbelt #include <linux/sched/debug.h>
1876d2a049SPalmer Dabbelt #include <linux/sched/signal.h>
1976d2a049SPalmer Dabbelt #include <linux/signal.h>
2076d2a049SPalmer Dabbelt #include <linux/kdebug.h>
2176d2a049SPalmer Dabbelt #include <linux/uaccess.h>
2276d2a049SPalmer Dabbelt #include <linux/mm.h>
2376d2a049SPalmer Dabbelt #include <linux/module.h>
2476d2a049SPalmer Dabbelt #include <linux/irq.h>
2576d2a049SPalmer Dabbelt 
2676d2a049SPalmer Dabbelt #include <asm/processor.h>
2776d2a049SPalmer Dabbelt #include <asm/ptrace.h>
2876d2a049SPalmer Dabbelt #include <asm/csr.h>
2976d2a049SPalmer Dabbelt 
3076d2a049SPalmer Dabbelt int show_unhandled_signals = 1;
3176d2a049SPalmer Dabbelt 
3276d2a049SPalmer Dabbelt extern asmlinkage void handle_exception(void);
3376d2a049SPalmer Dabbelt 
3476d2a049SPalmer Dabbelt static DEFINE_SPINLOCK(die_lock);
3576d2a049SPalmer Dabbelt 
3676d2a049SPalmer Dabbelt void die(struct pt_regs *regs, const char *str)
3776d2a049SPalmer Dabbelt {
3876d2a049SPalmer Dabbelt 	static int die_counter;
3976d2a049SPalmer Dabbelt 	int ret;
4076d2a049SPalmer Dabbelt 
4176d2a049SPalmer Dabbelt 	oops_enter();
4276d2a049SPalmer Dabbelt 
4376d2a049SPalmer Dabbelt 	spin_lock_irq(&die_lock);
4476d2a049SPalmer Dabbelt 	console_verbose();
4576d2a049SPalmer Dabbelt 	bust_spinlocks(1);
4676d2a049SPalmer Dabbelt 
4776d2a049SPalmer Dabbelt 	pr_emerg("%s [#%d]\n", str, ++die_counter);
4876d2a049SPalmer Dabbelt 	print_modules();
4976d2a049SPalmer Dabbelt 	show_regs(regs);
5076d2a049SPalmer Dabbelt 
5176d2a049SPalmer Dabbelt 	ret = notify_die(DIE_OOPS, str, regs, 0, regs->scause, SIGSEGV);
5276d2a049SPalmer Dabbelt 
5376d2a049SPalmer Dabbelt 	bust_spinlocks(0);
5476d2a049SPalmer Dabbelt 	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
5576d2a049SPalmer Dabbelt 	spin_unlock_irq(&die_lock);
5676d2a049SPalmer Dabbelt 	oops_exit();
5776d2a049SPalmer Dabbelt 
5876d2a049SPalmer Dabbelt 	if (in_interrupt())
5976d2a049SPalmer Dabbelt 		panic("Fatal exception in interrupt");
6076d2a049SPalmer Dabbelt 	if (panic_on_oops)
6176d2a049SPalmer Dabbelt 		panic("Fatal exception");
6276d2a049SPalmer Dabbelt 	if (ret != NOTIFY_STOP)
6376d2a049SPalmer Dabbelt 		do_exit(SIGSEGV);
6476d2a049SPalmer Dabbelt }
6576d2a049SPalmer Dabbelt 
6676d2a049SPalmer Dabbelt void do_trap(struct pt_regs *regs, int signo, int code,
6776d2a049SPalmer Dabbelt 	unsigned long addr, struct task_struct *tsk)
6876d2a049SPalmer Dabbelt {
6976d2a049SPalmer Dabbelt 	if (show_unhandled_signals && unhandled_signal(tsk, signo)
7076d2a049SPalmer Dabbelt 	    && printk_ratelimit()) {
7176d2a049SPalmer Dabbelt 		pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT,
7276d2a049SPalmer Dabbelt 			tsk->comm, task_pid_nr(tsk), signo, code, addr);
736ab77af4SChristoph Hellwig 		print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
7476d2a049SPalmer Dabbelt 		pr_cont("\n");
7576d2a049SPalmer Dabbelt 		show_regs(regs);
7676d2a049SPalmer Dabbelt 	}
7776d2a049SPalmer Dabbelt 
787ff3a762SEric W. Biederman 	force_sig_fault(signo, code, (void __user *)addr, tsk);
7976d2a049SPalmer Dabbelt }
8076d2a049SPalmer Dabbelt 
8176d2a049SPalmer Dabbelt static void do_trap_error(struct pt_regs *regs, int signo, int code,
8276d2a049SPalmer Dabbelt 	unsigned long addr, const char *str)
8376d2a049SPalmer Dabbelt {
8476d2a049SPalmer Dabbelt 	if (user_mode(regs)) {
8576d2a049SPalmer Dabbelt 		do_trap(regs, signo, code, addr, current);
8676d2a049SPalmer Dabbelt 	} else {
8776d2a049SPalmer Dabbelt 		if (!fixup_exception(regs))
8876d2a049SPalmer Dabbelt 			die(regs, str);
8976d2a049SPalmer Dabbelt 	}
9076d2a049SPalmer Dabbelt }
9176d2a049SPalmer Dabbelt 
9276d2a049SPalmer Dabbelt #define DO_ERROR_INFO(name, signo, code, str)				\
9376d2a049SPalmer Dabbelt asmlinkage void name(struct pt_regs *regs)				\
9476d2a049SPalmer Dabbelt {									\
9576d2a049SPalmer Dabbelt 	do_trap_error(regs, signo, code, regs->sepc, "Oops - " str);	\
9676d2a049SPalmer Dabbelt }
9776d2a049SPalmer Dabbelt 
9876d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_unknown,
9976d2a049SPalmer Dabbelt 	SIGILL, ILL_ILLTRP, "unknown exception");
10076d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_insn_misaligned,
10176d2a049SPalmer Dabbelt 	SIGBUS, BUS_ADRALN, "instruction address misaligned");
10276d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_insn_fault,
10376d2a049SPalmer Dabbelt 	SIGSEGV, SEGV_ACCERR, "instruction access fault");
10476d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_insn_illegal,
10576d2a049SPalmer Dabbelt 	SIGILL, ILL_ILLOPC, "illegal instruction");
10676d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_load_misaligned,
10776d2a049SPalmer Dabbelt 	SIGBUS, BUS_ADRALN, "load address misaligned");
10876d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_load_fault,
10976d2a049SPalmer Dabbelt 	SIGSEGV, SEGV_ACCERR, "load access fault");
11076d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_store_misaligned,
11176d2a049SPalmer Dabbelt 	SIGBUS, BUS_ADRALN, "store (or AMO) address misaligned");
11276d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_store_fault,
11376d2a049SPalmer Dabbelt 	SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
11476d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_ecall_u,
11576d2a049SPalmer Dabbelt 	SIGILL, ILL_ILLTRP, "environment call from U-mode");
11676d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_ecall_s,
11776d2a049SPalmer Dabbelt 	SIGILL, ILL_ILLTRP, "environment call from S-mode");
11876d2a049SPalmer Dabbelt DO_ERROR_INFO(do_trap_ecall_m,
11976d2a049SPalmer Dabbelt 	SIGILL, ILL_ILLTRP, "environment call from M-mode");
12076d2a049SPalmer Dabbelt 
121ee72e0e7SVincent Chen #ifdef CONFIG_GENERIC_BUG
122ee72e0e7SVincent Chen static inline unsigned long get_break_insn_length(unsigned long pc)
123ee72e0e7SVincent Chen {
124ee72e0e7SVincent Chen 	bug_insn_t insn;
125ee72e0e7SVincent Chen 
126ee72e0e7SVincent Chen 	if (probe_kernel_address((bug_insn_t *)pc, insn))
127ee72e0e7SVincent Chen 		return 0;
128ee72e0e7SVincent Chen 	return (((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) ? 4UL : 2UL);
129ee72e0e7SVincent Chen }
130ee72e0e7SVincent Chen #endif /* CONFIG_GENERIC_BUG */
131ee72e0e7SVincent Chen 
13276d2a049SPalmer Dabbelt asmlinkage void do_trap_break(struct pt_regs *regs)
13376d2a049SPalmer Dabbelt {
13476d2a049SPalmer Dabbelt #ifdef CONFIG_GENERIC_BUG
13576d2a049SPalmer Dabbelt 	if (!user_mode(regs)) {
13676d2a049SPalmer Dabbelt 		enum bug_trap_type type;
13776d2a049SPalmer Dabbelt 
13876d2a049SPalmer Dabbelt 		type = report_bug(regs->sepc, regs);
13976d2a049SPalmer Dabbelt 		switch (type) {
14076d2a049SPalmer Dabbelt 		case BUG_TRAP_TYPE_NONE:
14176d2a049SPalmer Dabbelt 			break;
14276d2a049SPalmer Dabbelt 		case BUG_TRAP_TYPE_WARN:
143ee72e0e7SVincent Chen 			regs->sepc += get_break_insn_length(regs->sepc);
144ee72e0e7SVincent Chen 			break;
14576d2a049SPalmer Dabbelt 		case BUG_TRAP_TYPE_BUG:
14676d2a049SPalmer Dabbelt 			die(regs, "Kernel BUG");
14776d2a049SPalmer Dabbelt 		}
14876d2a049SPalmer Dabbelt 	}
14976d2a049SPalmer Dabbelt #endif /* CONFIG_GENERIC_BUG */
15076d2a049SPalmer Dabbelt 
1517ff3a762SEric W. Biederman 	force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)(regs->sepc), current);
15276d2a049SPalmer Dabbelt }
15376d2a049SPalmer Dabbelt 
15476d2a049SPalmer Dabbelt #ifdef CONFIG_GENERIC_BUG
15576d2a049SPalmer Dabbelt int is_valid_bugaddr(unsigned long pc)
15676d2a049SPalmer Dabbelt {
15776d2a049SPalmer Dabbelt 	bug_insn_t insn;
15876d2a049SPalmer Dabbelt 
159*9a6e7af0SVincent Chen 	if (pc < VMALLOC_START)
16076d2a049SPalmer Dabbelt 		return 0;
1619bf97390SLuc Van Oostenryck 	if (probe_kernel_address((bug_insn_t *)pc, insn))
16276d2a049SPalmer Dabbelt 		return 0;
163ee72e0e7SVincent Chen 	if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
164ee72e0e7SVincent Chen 		return (insn == __BUG_INSN_32);
165ee72e0e7SVincent Chen 	else
166ee72e0e7SVincent Chen 		return ((insn & __COMPRESSED_INSN_MASK) == __BUG_INSN_16);
16776d2a049SPalmer Dabbelt }
16876d2a049SPalmer Dabbelt #endif /* CONFIG_GENERIC_BUG */
16976d2a049SPalmer Dabbelt 
17076d2a049SPalmer Dabbelt void __init trap_init(void)
17176d2a049SPalmer Dabbelt {
17276d2a049SPalmer Dabbelt 	/*
17376d2a049SPalmer Dabbelt 	 * Set sup0 scratch register to 0, indicating to exception vector
17476d2a049SPalmer Dabbelt 	 * that we are presently executing in the kernel
17576d2a049SPalmer Dabbelt 	 */
176a3182c91SAnup Patel 	csr_write(CSR_SSCRATCH, 0);
17776d2a049SPalmer Dabbelt 	/* Set the exception vector address */
178a3182c91SAnup Patel 	csr_write(CSR_STVEC, &handle_exception);
17976d2a049SPalmer Dabbelt 	/* Enable all interrupts */
180a3182c91SAnup Patel 	csr_write(CSR_SIE, -1);
18176d2a049SPalmer Dabbelt }
182