2019-05-29 17:18:00 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-11 04:00:26 +03:00
/*
* Copyright ( C ) 2012 Regents of the University of California
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/sched/debug.h>
# include <linux/sched/signal.h>
# include <linux/signal.h>
# include <linux/kdebug.h>
# include <linux/uaccess.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/irq.h>
# include <asm/processor.h>
# include <asm/ptrace.h>
# include <asm/csr.h>
int show_unhandled_signals = 1 ;
extern asmlinkage void handle_exception ( void ) ;
static DEFINE_SPINLOCK ( die_lock ) ;
void die ( struct pt_regs * regs , const char * str )
{
static int die_counter ;
int ret ;
oops_enter ( ) ;
spin_lock_irq ( & die_lock ) ;
console_verbose ( ) ;
bust_spinlocks ( 1 ) ;
pr_emerg ( " %s [#%d] \n " , str , + + die_counter ) ;
print_modules ( ) ;
show_regs ( regs ) ;
ret = notify_die ( DIE_OOPS , str , regs , 0 , regs - > scause , SIGSEGV ) ;
bust_spinlocks ( 0 ) ;
add_taint ( TAINT_DIE , LOCKDEP_NOW_UNRELIABLE ) ;
spin_unlock_irq ( & die_lock ) ;
oops_exit ( ) ;
if ( in_interrupt ( ) )
panic ( " Fatal exception in interrupt " ) ;
if ( panic_on_oops )
panic ( " Fatal exception " ) ;
if ( ret ! = NOTIFY_STOP )
do_exit ( SIGSEGV ) ;
}
2019-02-06 04:10:48 +03:00
void do_trap ( struct pt_regs * regs , int signo , int code , unsigned long addr )
2017-07-11 04:00:26 +03:00
{
2019-02-06 04:10:48 +03:00
struct task_struct * tsk = current ;
2017-07-11 04:00:26 +03:00
if ( show_unhandled_signals & & unhandled_signal ( tsk , signo )
& & printk_ratelimit ( ) ) {
pr_info ( " %s[%d]: unhandled signal %d code 0x%x at 0x " REG_FMT ,
tsk - > comm , task_pid_nr ( tsk ) , signo , code , addr ) ;
2019-04-15 12:14:40 +03:00
print_vma_addr ( KERN_CONT " in " , instruction_pointer ( regs ) ) ;
2017-07-11 04:00:26 +03:00
pr_cont ( " \n " ) ;
show_regs ( regs ) ;
}
2019-05-23 19:04:24 +03:00
force_sig_fault ( signo , code , ( void __user * ) addr ) ;
2017-07-11 04:00:26 +03:00
}
static void do_trap_error ( struct pt_regs * regs , int signo , int code ,
unsigned long addr , const char * str )
{
if ( user_mode ( regs ) ) {
2019-02-06 04:10:48 +03:00
do_trap ( regs , signo , code , addr ) ;
2017-07-11 04:00:26 +03:00
} else {
if ( ! fixup_exception ( regs ) )
die ( regs , str ) ;
}
}
# define DO_ERROR_INFO(name, signo, code, str) \
asmlinkage void name ( struct pt_regs * regs ) \
{ \
do_trap_error ( regs , signo , code , regs - > sepc , " Oops - " str ) ; \
}
DO_ERROR_INFO ( do_trap_unknown ,
SIGILL , ILL_ILLTRP , " unknown exception " ) ;
DO_ERROR_INFO ( do_trap_insn_misaligned ,
SIGBUS , BUS_ADRALN , " instruction address misaligned " ) ;
DO_ERROR_INFO ( do_trap_insn_fault ,
SIGSEGV , SEGV_ACCERR , " instruction access fault " ) ;
DO_ERROR_INFO ( do_trap_insn_illegal ,
SIGILL , ILL_ILLOPC , " illegal instruction " ) ;
DO_ERROR_INFO ( do_trap_load_misaligned ,
SIGBUS , BUS_ADRALN , " load address misaligned " ) ;
DO_ERROR_INFO ( do_trap_load_fault ,
SIGSEGV , SEGV_ACCERR , " load access fault " ) ;
DO_ERROR_INFO ( do_trap_store_misaligned ,
SIGBUS , BUS_ADRALN , " store (or AMO) address misaligned " ) ;
DO_ERROR_INFO ( do_trap_store_fault ,
SIGSEGV , SEGV_ACCERR , " store (or AMO) access fault " ) ;
DO_ERROR_INFO ( do_trap_ecall_u ,
SIGILL , ILL_ILLTRP , " environment call from U-mode " ) ;
DO_ERROR_INFO ( do_trap_ecall_s ,
SIGILL , ILL_ILLTRP , " environment call from S-mode " ) ;
DO_ERROR_INFO ( do_trap_ecall_m ,
SIGILL , ILL_ILLTRP , " environment call from M-mode " ) ;
2019-03-05 06:23:34 +03:00
# ifdef CONFIG_GENERIC_BUG
static inline unsigned long get_break_insn_length ( unsigned long pc )
{
bug_insn_t insn ;
if ( probe_kernel_address ( ( bug_insn_t * ) pc , insn ) )
return 0 ;
return ( ( ( insn & __INSN_LENGTH_MASK ) = = __INSN_LENGTH_32 ) ? 4UL : 2UL ) ;
}
# endif /* CONFIG_GENERIC_BUG */
2017-07-11 04:00:26 +03:00
asmlinkage void do_trap_break ( struct pt_regs * regs )
{
# ifdef CONFIG_GENERIC_BUG
if ( ! user_mode ( regs ) ) {
enum bug_trap_type type ;
type = report_bug ( regs - > sepc , regs ) ;
switch ( type ) {
case BUG_TRAP_TYPE_NONE :
break ;
case BUG_TRAP_TYPE_WARN :
2019-03-05 06:23:34 +03:00
regs - > sepc + = get_break_insn_length ( regs - > sepc ) ;
break ;
2017-07-11 04:00:26 +03:00
case BUG_TRAP_TYPE_BUG :
die ( regs , " Kernel BUG " ) ;
}
}
# endif /* CONFIG_GENERIC_BUG */
2019-05-23 19:04:24 +03:00
force_sig_fault ( SIGTRAP , TRAP_BRKPT , ( void __user * ) ( regs - > sepc ) ) ;
2017-07-11 04:00:26 +03:00
}
# ifdef CONFIG_GENERIC_BUG
int is_valid_bugaddr ( unsigned long pc )
{
bug_insn_t insn ;
2019-03-05 06:23:35 +03:00
if ( pc < VMALLOC_START )
2017-07-11 04:00:26 +03:00
return 0 ;
2018-06-01 18:21:22 +03:00
if ( probe_kernel_address ( ( bug_insn_t * ) pc , insn ) )
2017-07-11 04:00:26 +03:00
return 0 ;
2019-03-05 06:23:34 +03:00
if ( ( insn & __INSN_LENGTH_MASK ) = = __INSN_LENGTH_32 )
return ( insn = = __BUG_INSN_32 ) ;
else
return ( ( insn & __COMPRESSED_INSN_MASK ) = = __BUG_INSN_16 ) ;
2017-07-11 04:00:26 +03:00
}
# endif /* CONFIG_GENERIC_BUG */
void __init trap_init ( void )
{
/*
* Set sup0 scratch register to 0 , indicating to exception vector
* that we are presently executing in the kernel
*/
2019-04-25 11:38:41 +03:00
csr_write ( CSR_SSCRATCH , 0 ) ;
2017-07-11 04:00:26 +03:00
/* Set the exception vector address */
2019-04-25 11:38:41 +03:00
csr_write ( CSR_STVEC , & handle_exception ) ;
2017-07-11 04:00:26 +03:00
/* Enable all interrupts */
2019-04-25 11:38:41 +03:00
csr_write ( CSR_SIE , - 1 ) ;
2017-07-11 04:00:26 +03:00
}