965 lines · 23.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/arch/arm/kernel/traps.c * *  Copyright (C) 1995-2009 Russell King *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds * *  'traps.c' handles hardware exceptions after we have saved some state in *  'linux/arch/arm/lib/traps.S'.  Mostly a debugging aid, but will probably *  kill the offending process. */#include <linux/signal.h>#include <linux/personality.h>#include <linux/kallsyms.h>#include <linux/spinlock.h>#include <linux/uaccess.h>#include <linux/hardirq.h>#include <linux/kdebug.h>#include <linux/kprobes.h>#include <linux/module.h>#include <linux/kexec.h>#include <linux/bug.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/sched/signal.h>#include <linux/sched/debug.h>#include <linux/sched/task_stack.h>#include <linux/irq.h>#include <linux/vmalloc.h> #include <linux/atomic.h>#include <asm/cacheflush.h>#include <asm/exception.h>#include <asm/spectre.h>#include <asm/unistd.h>#include <asm/traps.h>#include <asm/ptrace.h>#include <asm/unwind.h>#include <asm/tls.h>#include <asm/stacktrace.h>#include <asm/system_misc.h>#include <asm/opcodes.h>  static const char *handler[]= {	"prefetch abort",	"data abort",	"address exception",	"interrupt",	"undefined instruction",}; void *vectors_page; #ifdef CONFIG_DEBUG_USERunsigned int user_debug; static int __init user_debug_setup(char *str){	get_option(&str, &user_debug);	return 1;}__setup("user_debug=", user_debug_setup);#endif void dump_backtrace_entry(unsigned long where, unsigned long from,			  unsigned long frame, const char *loglvl){	unsigned long end = frame + 4 + sizeof(struct pt_regs); 	if (IS_ENABLED(CONFIG_UNWINDER_FRAME_POINTER) &&	    IS_ENABLED(CONFIG_CC_IS_GCC) &&	    end > ALIGN(frame, THREAD_SIZE)) {		/*		 * If we are walking past the end of the stack, it may be due		 * to the fact that we are on an IRQ or overflow stack. In this		 * case, we can load the address of the other stack from the		 * frame record.		 */		frame = ((unsigned long *)frame)[-2] - 4;		end = frame + 4 + sizeof(struct pt_regs);	} #ifndef CONFIG_KALLSYMS	printk("%sFunction entered at [<%08lx>] from [<%08lx>]\n",		loglvl, where, from);#elif defined CONFIG_BACKTRACE_VERBOSE	printk("%s[<%08lx>] (%ps) from [<%08lx>] (%pS)\n",		loglvl, where, (void *)where, from, (void *)from);#else	printk("%s %ps from %pS\n", loglvl, (void *)where, (void *)from);#endif 	if (in_entry_text(from) && end <= ALIGN(frame, THREAD_SIZE))		dump_mem(loglvl, "Exception stack", frame + 4, end);} void dump_backtrace_stm(u32 *stack, u32 instruction, const char *loglvl){	char str[80], *p;	unsigned int x;	int reg; 	for (reg = 10, x = 0, p = str; reg >= 0; reg--) {		if (instruction & BIT(reg)) {			p += sprintf(p, " r%d:%08x", reg, *stack--);			if (++x == 6) {				x = 0;				p = str;				printk("%s%s\n", loglvl, str);			}		}	}	if (p != str)		printk("%s%s\n", loglvl, str);} #ifndef CONFIG_ARM_UNWIND/* * Stack pointers should always be within the kernels view of * physical memory.  If it is not there, then we can't dump * out any information relating to the stack. */static int verify_stack(unsigned long sp){	if (sp < PAGE_OFFSET ||	    (!IS_ENABLED(CONFIG_VMAP_STACK) &&	     sp > (unsigned long)high_memory && high_memory != NULL))		return -EFAULT; 	return 0;}#endif /* * Dump out the contents of some memory nicely... */void dump_mem(const char *lvl, const char *str, unsigned long bottom,	      unsigned long top){	unsigned long first;	int i; 	printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top); 	for (first = bottom & ~31; first < top; first += 32) {		unsigned long p;		char str[sizeof(" 12345678") * 8 + 1]; 		memset(str, ' ', sizeof(str));		str[sizeof(str) - 1] = '\0'; 		for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {			if (p >= bottom && p < top) {				unsigned long val;				if (!get_kernel_nofault(val, (unsigned long *)p))					sprintf(str + i * 9, " %08lx", val);				else					sprintf(str + i * 9, " ????????");			}		}		printk("%s%04lx:%s\n", lvl, first & 0xffff, str);	}} static void dump_instr(const char *lvl, struct pt_regs *regs){	unsigned long addr = instruction_pointer(regs);	const int thumb = thumb_mode(regs);	const int width = thumb ? 4 : 8;	char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;	int i; 	/*	 * Note that we now dump the code first, just in case the backtrace	 * kills us.	 */ 	for (i = -4; i < 1 + !!thumb; i++) {		unsigned int val, bad; 		if (thumb) {			u16 tmp; 			if (user_mode(regs))				bad = get_user(tmp, &((u16 __user *)addr)[i]);			else				bad = get_kernel_nofault(tmp, &((u16 *)addr)[i]); 			val = __mem_to_opcode_thumb16(tmp);		} else {			if (user_mode(regs))				bad = get_user(val, &((u32 __user *)addr)[i]);			else				bad = get_kernel_nofault(val, &((u32 *)addr)[i]); 			val = __mem_to_opcode_arm(val);		} 		if (!bad)			p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",					width, val);		else {			p += sprintf(p, "bad PC value");			break;		}	}	printk("%sCode: %s\n", lvl, str);} #ifdef CONFIG_ARM_UNWINDvoid dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,		    const char *loglvl){	unwind_backtrace(regs, tsk, loglvl);}#elsevoid dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,		    const char *loglvl){	unsigned int fp, mode;	int ok = 1; 	printk("%sCall trace: ", loglvl); 	if (!tsk)		tsk = current; 	if (regs) {		fp = frame_pointer(regs);		mode = processor_mode(regs);	} else if (tsk != current) {		fp = thread_saved_fp(tsk);		mode = 0x10;	} else {		asm("mov %0, fp" : "=r" (fp) : : "cc");		mode = 0x10;	} 	if (!fp) {		pr_cont("no frame pointer");		ok = 0;	} else if (verify_stack(fp)) {		pr_cont("invalid frame pointer 0x%08x", fp);		ok = 0;	} else if (fp < (unsigned long)end_of_stack(tsk))		pr_cont("frame pointer underflow");	pr_cont("\n"); 	if (ok)		c_backtrace(fp, mode, loglvl);}#endif void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl){	dump_backtrace(NULL, tsk, loglvl);	barrier();} #ifdef CONFIG_SMP#define S_SMP " SMP"#else#define S_SMP ""#endif#ifdef CONFIG_THUMB2_KERNEL#define S_ISA " THUMB2"#else#define S_ISA " ARM"#endif static int __die(const char *str, int err, struct pt_regs *regs){	struct task_struct *tsk = current;	static int die_counter;	int ret; 	pr_emerg("Internal error: %s: %x [#%d]" S_SMP S_ISA "\n",		 str, err, ++die_counter); 	/* trap and error numbers are mostly meaningless on ARM */	ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV);	if (ret == NOTIFY_STOP)		return 1; 	print_modules();	__show_regs(regs);	__show_regs_alloc_free(regs);	pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",		 TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk)); 	if (!user_mode(regs) || in_interrupt()) {		dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp,			 ALIGN(regs->ARM_sp - THREAD_SIZE, THREAD_ALIGN)			 + THREAD_SIZE);		dump_backtrace(regs, tsk, KERN_EMERG);		dump_instr(KERN_EMERG, regs);	} 	return 0;} static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;static int die_owner = -1;static unsigned int die_nest_count; static unsigned long oops_begin(void){	int cpu;	unsigned long flags; 	oops_enter(); 	/* racy, but better than risking deadlock. */	raw_local_irq_save(flags);	cpu = smp_processor_id();	if (!arch_spin_trylock(&die_lock)) {		if (cpu == die_owner)			/* nested oops. should stop eventually */;		else			arch_spin_lock(&die_lock);	}	die_nest_count++;	die_owner = cpu;	console_verbose();	bust_spinlocks(1);	return flags;} static void oops_end(unsigned long flags, struct pt_regs *regs, int signr){	if (regs && kexec_should_crash(current))		crash_kexec(regs); 	bust_spinlocks(0);	die_owner = -1;	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);	die_nest_count--;	if (!die_nest_count)		/* Nest count reaches zero, release the lock. */		arch_spin_unlock(&die_lock);	raw_local_irq_restore(flags);	oops_exit(); 	if (in_interrupt())		panic("Fatal exception in interrupt");	if (panic_on_oops)		panic("Fatal exception");	if (signr)		make_task_dead(signr);} /* * This function is protected against re-entrancy. */void die(const char *str, struct pt_regs *regs, int err){	enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE;	unsigned long flags = oops_begin();	int sig = SIGSEGV; 	if (!user_mode(regs))		bug_type = report_bug(regs->ARM_pc, regs);	if (bug_type != BUG_TRAP_TYPE_NONE)		str = "Oops - BUG"; 	if (__die(str, err, regs))		sig = 0; 	oops_end(flags, regs, sig);} void arm_notify_die(const char *str, struct pt_regs *regs,		int signo, int si_code, void __user *addr,		unsigned long err, unsigned long trap){	if (user_mode(regs)) {		current->thread.error_code = err;		current->thread.trap_no = trap; 		force_sig_fault(signo, si_code, addr);	} else {		die(str, regs, err);	}} #ifdef CONFIG_GENERIC_BUG int is_valid_bugaddr(unsigned long pc){#ifdef CONFIG_THUMB2_KERNEL	u16 bkpt;	u16 insn = __opcode_to_mem_thumb16(BUG_INSTR_VALUE);#else	u32 bkpt;	u32 insn = __opcode_to_mem_arm(BUG_INSTR_VALUE);#endif 	if (get_kernel_nofault(bkpt, (void *)pc))		return 0; 	return bkpt == insn;} #endif static LIST_HEAD(undef_hook);static DEFINE_RAW_SPINLOCK(undef_lock); void register_undef_hook(struct undef_hook *hook){	unsigned long flags; 	raw_spin_lock_irqsave(&undef_lock, flags);	list_add(&hook->node, &undef_hook);	raw_spin_unlock_irqrestore(&undef_lock, flags);} void unregister_undef_hook(struct undef_hook *hook){	unsigned long flags; 	raw_spin_lock_irqsave(&undef_lock, flags);	list_del(&hook->node);	raw_spin_unlock_irqrestore(&undef_lock, flags);} static nokprobe_inlineint call_undef_hook(struct pt_regs *regs, unsigned int instr){	struct undef_hook *hook;	unsigned long flags;	int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL; 	raw_spin_lock_irqsave(&undef_lock, flags);	list_for_each_entry(hook, &undef_hook, node)		if ((instr & hook->instr_mask) == hook->instr_val &&		    (regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)			fn = hook->fn;	raw_spin_unlock_irqrestore(&undef_lock, flags); 	return fn ? fn(regs, instr) : 1;} asmlinkage void do_undefinstr(struct pt_regs *regs){	unsigned int instr;	void __user *pc; 	pc = (void __user *)instruction_pointer(regs); 	if (processor_mode(regs) == SVC_MODE) {#ifdef CONFIG_THUMB2_KERNEL		if (thumb_mode(regs)) {			instr = __mem_to_opcode_thumb16(((u16 *)pc)[0]);			if (is_wide_instruction(instr)) {				u16 inst2;				inst2 = __mem_to_opcode_thumb16(((u16 *)pc)[1]);				instr = __opcode_thumb32_compose(instr, inst2);			}		} else#endif			instr = __mem_to_opcode_arm(*(u32 *) pc);	} else if (thumb_mode(regs)) {		if (get_user(instr, (u16 __user *)pc))			goto die_sig;		instr = __mem_to_opcode_thumb16(instr);		if (is_wide_instruction(instr)) {			unsigned int instr2;			if (get_user(instr2, (u16 __user *)pc+1))				goto die_sig;			instr2 = __mem_to_opcode_thumb16(instr2);			instr = __opcode_thumb32_compose(instr, instr2);		}	} else {		if (get_user(instr, (u32 __user *)pc))			goto die_sig;		instr = __mem_to_opcode_arm(instr);	} 	if (call_undef_hook(regs, instr) == 0)		return; die_sig:#ifdef CONFIG_DEBUG_USER	if (user_debug & UDBG_UNDEFINED) {		pr_info("%s (%d): undefined instruction: pc=%px\n",			current->comm, task_pid_nr(current), pc);		__show_regs(regs);		dump_instr(KERN_INFO, regs);	}#endif	arm_notify_die("Oops - undefined instruction", regs,		       SIGILL, ILL_ILLOPC, pc, 0, 6);}NOKPROBE_SYMBOL(do_undefinstr) /* * Handle FIQ similarly to NMI on x86 systems. *

Lines 1–500 of 965.