2533 lines · 61.6 KB · C
RawDownload
/* * 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. * * Copyright (C) 1994 - 1999, 2000, 01, 06 Ralf Baechle * Copyright (C) 1995, 1996 Paul M. Antoine * Copyright (C) 1998 Ulf Carlsson * Copyright (C) 1999 Silicon Graphics, Inc. * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2002, 2003, 2004, 2005, 2007  Maciej W. Rozycki * Copyright (C) 2000, 2001, 2012 MIPS Technologies, Inc.  All rights reserved. * Copyright (C) 2014, Imagination Technologies Ltd. */#include <linux/bitops.h>#include <linux/bug.h>#include <linux/compiler.h>#include <linux/context_tracking.h>#include <linux/cpu_pm.h>#include <linux/kexec.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/extable.h>#include <linux/mm.h>#include <linux/sched/mm.h>#include <linux/sched/debug.h>#include <linux/smp.h>#include <linux/spinlock.h>#include <linux/kallsyms.h>#include <linux/memblock.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/kgdb.h>#include <linux/kdebug.h>#include <linux/kprobes.h>#include <linux/notifier.h>#include <linux/kdb.h>#include <linux/irq.h>#include <linux/perf_event.h>#include <linux/string_choices.h> #include <asm/addrspace.h>#include <asm/bootinfo.h>#include <asm/branch.h>#include <asm/break.h>#include <asm/cop2.h>#include <asm/cpu.h>#include <asm/cpu-type.h>#include <asm/dsp.h>#include <asm/fpu.h>#include <asm/fpu_emulator.h>#include <asm/idle.h>#include <asm/isa-rev.h>#include <asm/mips-cps.h>#include <asm/mips-r2-to-r6-emul.h>#include <asm/mipsregs.h>#include <asm/mipsmtregs.h>#include <asm/module.h>#include <asm/msa.h>#include <asm/ptrace.h>#include <asm/regdef.h>#include <asm/sections.h>#include <asm/siginfo.h>#include <asm/tlbdebug.h>#include <asm/traps.h>#include <linux/uaccess.h>#include <asm/watch.h>#include <asm/mmu_context.h>#include <asm/types.h>#include <asm/stacktrace.h>#include <asm/tlbex.h>#include <asm/uasm.h> #include <asm/mach-loongson64/cpucfg-emul.h> #include "access-helper.h" extern void check_wait(void);extern asmlinkage void skipover_handle_int(void);extern asmlinkage void handle_int(void);extern asmlinkage void handle_adel(void);extern asmlinkage void handle_ades(void);extern asmlinkage void handle_ibe(void);extern asmlinkage void handle_dbe(void);extern asmlinkage void handle_sys(void);extern asmlinkage void handle_bp(void);extern asmlinkage void handle_ri(void);extern asmlinkage void handle_ri_rdhwr_tlbp(void);extern asmlinkage void handle_ri_rdhwr(void);extern asmlinkage void handle_cpu(void);extern asmlinkage void handle_ov(void);extern asmlinkage void handle_tr(void);extern asmlinkage void handle_msa_fpe(void);extern asmlinkage void handle_fpe(void);extern asmlinkage void handle_ftlb(void);extern asmlinkage void handle_gsexc(void);extern asmlinkage void handle_msa(void);extern asmlinkage void handle_mdmx(void);extern asmlinkage void handle_watch(void);extern asmlinkage void handle_mt(void);extern asmlinkage void handle_dsp(void);extern asmlinkage void handle_mcheck(void);extern asmlinkage void handle_reserved(void);extern void tlb_do_page_fault_0(void); void (*board_be_init)(void);static int (*board_be_handler)(struct pt_regs *regs, int is_fixup);void (*board_nmi_handler_setup)(void);void (*board_ejtag_handler_setup)(void);void (*board_bind_eic_interrupt)(int irq, int regset);void (*board_ebase_setup)(void);void(*board_cache_error_setup)(void); void mips_set_be_handler(int (*handler)(struct pt_regs *regs, int is_fixup)){	board_be_handler = handler;}EXPORT_SYMBOL_GPL(mips_set_be_handler); static void show_raw_backtrace(unsigned long reg29, const char *loglvl,			       bool user){	unsigned long *sp = (unsigned long *)(reg29 & ~3);	unsigned long addr; 	printk("%sCall Trace:", loglvl);#ifdef CONFIG_KALLSYMS	printk("%s\n", loglvl);#endif	while (!kstack_end(sp)) {		if (__get_addr(&addr, sp++, user)) {			printk("%s (Bad stack address)", loglvl);			break;		}		if (__kernel_text_address(addr))			print_ip_sym(loglvl, addr);	}	printk("%s\n", loglvl);} #ifdef CONFIG_KALLSYMSint raw_show_trace;static int __init set_raw_show_trace(char *str){	raw_show_trace = 1;	return 1;}__setup("raw_show_trace", set_raw_show_trace);#endif static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,			   const char *loglvl, bool user){	unsigned long sp = regs->regs[29];	unsigned long ra = regs->regs[31];	unsigned long pc = regs->cp0_epc; 	if (!task)		task = current; 	if (raw_show_trace || user_mode(regs) || !__kernel_text_address(pc)) {		show_raw_backtrace(sp, loglvl, user);		return;	}	printk("%sCall Trace:\n", loglvl);	do {		print_ip_sym(loglvl, pc);		pc = unwind_stack(task, &sp, pc, &ra);	} while (pc);	pr_cont("\n");} /* * This routine abuses get_user()/put_user() to reference pointers * with at least a bit of error checking ... */static void show_stacktrace(struct task_struct *task,	const struct pt_regs *regs, const char *loglvl, bool user){	const int field = 2 * sizeof(unsigned long);	unsigned long stackdata;	int i;	unsigned long *sp = (unsigned long *)regs->regs[29]; 	printk("%sStack :", loglvl);	i = 0;	while ((unsigned long) sp & (PAGE_SIZE - 1)) {		if (i && ((i % (64 / field)) == 0)) {			pr_cont("\n");			printk("%s       ", loglvl);		}		if (i > 39) {			pr_cont(" ...");			break;		} 		if (__get_addr(&stackdata, sp++, user)) {			pr_cont(" (Bad stack address)");			break;		} 		pr_cont(" %0*lx", field, stackdata);		i++;	}	pr_cont("\n");	show_backtrace(task, regs, loglvl, user);} void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl){	struct pt_regs regs; 	regs.cp0_status = KSU_KERNEL;	if (sp) {		regs.regs[29] = (unsigned long)sp;		regs.regs[31] = 0;		regs.cp0_epc = 0;	} else {		if (task && task != current) {			regs.regs[29] = task->thread.reg29;			regs.regs[31] = 0;			regs.cp0_epc = task->thread.reg31;		} else {			prepare_frametrace(&regs);		}	}	show_stacktrace(task, &regs, loglvl, false);} static void show_code(void *pc, bool user){	long i;	unsigned short *pc16 = NULL; 	printk("Code:"); 	if ((unsigned long)pc & 1)		pc16 = (u16 *)((unsigned long)pc & ~1); 	for(i = -3 ; i < 6 ; i++) {		if (pc16) {			u16 insn16; 			if (__get_inst16(&insn16, pc16 + i, user))				goto bad_address; 			pr_cont("%c%04x%c", (i?' ':'<'), insn16, (i?' ':'>'));		} else {			u32 insn32; 			if (__get_inst32(&insn32, (u32 *)pc + i, user))				goto bad_address; 			pr_cont("%c%08x%c", (i?' ':'<'), insn32, (i?' ':'>'));		}	}	pr_cont("\n");	return; bad_address:	pr_cont(" (Bad address in epc)\n\n");} static void __show_regs(const struct pt_regs *regs){	const int field = 2 * sizeof(unsigned long);	unsigned int cause = regs->cp0_cause;	unsigned int exccode;	int i; 	show_regs_print_info(KERN_DEFAULT); 	/*	 * Saved main processor registers	 */	for (i = 0; i < 32; ) {		if ((i % 4) == 0)			printk("$%2d   :", i);		if (i == 0)			pr_cont(" %0*lx", field, 0UL);		else if (i == 26 || i == 27)			pr_cont(" %*s", field, "");		else			pr_cont(" %0*lx", field, regs->regs[i]); 		i++;		if ((i % 4) == 0)			pr_cont("\n");	} #ifdef CONFIG_CPU_HAS_SMARTMIPS	printk("Acx    : %0*lx\n", field, regs->acx);#endif	if (MIPS_ISA_REV < 6) {		printk("Hi    : %0*lx\n", field, regs->hi);		printk("Lo    : %0*lx\n", field, regs->lo);	} 	/*	 * Saved cp0 registers	 */	printk("epc   : %0*lx %pS\n", field, regs->cp0_epc,	       (void *) regs->cp0_epc);	printk("ra    : %0*lx %pS\n", field, regs->regs[31],	       (void *) regs->regs[31]); 	printk("Status: %08x	", (uint32_t) regs->cp0_status); 	if (cpu_has_3kex) {		if (regs->cp0_status & ST0_KUO)			pr_cont("KUo ");		if (regs->cp0_status & ST0_IEO)			pr_cont("IEo ");		if (regs->cp0_status & ST0_KUP)			pr_cont("KUp ");		if (regs->cp0_status & ST0_IEP)			pr_cont("IEp ");		if (regs->cp0_status & ST0_KUC)			pr_cont("KUc ");		if (regs->cp0_status & ST0_IEC)			pr_cont("IEc ");	} else if (cpu_has_4kex) {		if (regs->cp0_status & ST0_KX)			pr_cont("KX ");		if (regs->cp0_status & ST0_SX)			pr_cont("SX ");		if (regs->cp0_status & ST0_UX)			pr_cont("UX ");		switch (regs->cp0_status & ST0_KSU) {		case KSU_USER:			pr_cont("USER ");			break;		case KSU_SUPERVISOR:			pr_cont("SUPERVISOR ");			break;		case KSU_KERNEL:			pr_cont("KERNEL ");			break;		default:			pr_cont("BAD_MODE ");			break;		}		if (regs->cp0_status & ST0_ERL)			pr_cont("ERL ");		if (regs->cp0_status & ST0_EXL)			pr_cont("EXL ");		if (regs->cp0_status & ST0_IE)			pr_cont("IE ");	}	pr_cont("\n"); 	exccode = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE;	printk("Cause : %08x (ExcCode %02x)\n", cause, exccode); 	if (1 <= exccode && exccode <= 5)		printk("BadVA : %0*lx\n", field, regs->cp0_badvaddr); 	printk("PrId  : %08x (%s)\n", read_c0_prid(),	       cpu_name_string());} /* * FIXME: really the generic show_regs should take a const pointer argument. */void show_regs(struct pt_regs *regs){	__show_regs(regs);	dump_stack();} void show_registers(struct pt_regs *regs){	const int field = 2 * sizeof(unsigned long); 	__show_regs(regs);	print_modules();	printk("Process %s (pid: %d, threadinfo=%p, task=%p, tls=%0*lx)\n",	       current->comm, current->pid, current_thread_info(), current,	      field, current_thread_info()->tp_value);	if (cpu_has_userlocal) {		unsigned long tls; 		tls = read_c0_userlocal();		if (tls != current_thread_info()->tp_value)			printk("*HwTLS: %0*lx\n", field, tls);	} 	show_stacktrace(current, regs, KERN_DEFAULT, user_mode(regs));	show_code((void *)regs->cp0_epc, user_mode(regs));	printk("\n");} static DEFINE_RAW_SPINLOCK(die_lock); void __noreturn die(const char *str, struct pt_regs *regs){	static int die_counter;	int sig = SIGSEGV; 	oops_enter(); 	if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_nr,		       SIGSEGV) == NOTIFY_STOP)		sig = 0; 	console_verbose();	raw_spin_lock_irq(&die_lock);	bust_spinlocks(1); 	printk("%s[#%d]:\n", str, ++die_counter);	show_registers(regs);	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);	raw_spin_unlock_irq(&die_lock); 	oops_exit(); 	if (in_interrupt())		panic("Fatal exception in interrupt"); 	if (panic_on_oops)		panic("Fatal exception"); 	if (regs && kexec_should_crash(current))		crash_kexec(regs); 	make_task_dead(sig);} extern struct exception_table_entry __start___dbe_table[];extern struct exception_table_entry __stop___dbe_table[]; __asm__("	.section	__dbe_table, \"a\"\n""	.previous			\n"); /* Given an address, look for it in the exception tables. */static const struct exception_table_entry *search_dbe_tables(unsigned long addr){	const struct exception_table_entry *e; 	e = search_extable(__start___dbe_table,			   __stop___dbe_table - __start___dbe_table, addr);	if (!e)		e = search_module_dbetables(addr);	return e;} asmlinkage void do_be(struct pt_regs *regs){	const int field = 2 * sizeof(unsigned long);	const struct exception_table_entry *fixup = NULL;	int data = regs->cp0_cause & 4;	int action = MIPS_BE_FATAL;	enum ctx_state prev_state; 	prev_state = exception_enter();	/* XXX For now.	 Fixme, this searches the wrong table ...  */	if (data && !user_mode(regs))		fixup = search_dbe_tables(exception_epc(regs)); 	if (fixup)		action = MIPS_BE_FIXUP; 	if (board_be_handler)		action = board_be_handler(regs, fixup != NULL);	else		mips_cm_error_report(); 	switch (action) {	case MIPS_BE_DISCARD:		goto out;	case MIPS_BE_FIXUP:		if (fixup) {			regs->cp0_epc = fixup->nextinsn;			goto out;		}		break;	default:		break;	} 	/*	 * Assume it would be too dangerous to continue ...	 */	printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",	       data ? "Data" : "Instruction",	       field, regs->cp0_epc, field, regs->regs[31]);	if (notify_die(DIE_OOPS, "bus error", regs, 0, current->thread.trap_nr,		       SIGBUS) == NOTIFY_STOP)		goto out; 	die_if_kernel("Oops", regs);	force_sig(SIGBUS); out:	exception_exit(prev_state);} /*

Lines 1–500 of 2533.