/*
* 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) 2000, 01 MIPS Technologies, Inc.
* Copyright (C) 2002, 2003, 2004, 2005 Maciej W. Rozycki
*/
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/bootmem.h>
#include <asm/bootinfo.h>
#include <asm/branch.h>
#include <asm/break.h>
#include <asm/cpu.h>
#include <asm/dsp.h>
#include <asm/fpu.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/module.h>
#include <asm/pgtable.h>
#include <asm/ptrace.h>
#include <asm/sections.h>
#include <asm/system.h>
#include <asm/tlbdebug.h>
#include <asm/traps.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/watch.h>
#include <asm/types.h>
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
extern asmlinkage void handle_tlbl(void);
extern asmlinkage void handle_tlbs(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_cpu(void);
extern asmlinkage void handle_ov(void);
extern asmlinkage void handle_tr(void);
extern asmlinkage void handle_fpe(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 int fpu_emulator_cop1Handler(struct pt_regs *xcp,
struct mips_fpu_struct *ctx);
void (*board_be_init)(void);
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);
/*
* These constant is for searching for possible module text segments.
* MODULE_RANGE is a guess of how much space is likely to be vmalloced.
*/
#define MODULE_RANGE (8*1024*1024)
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
*/
void show_stack(struct task_struct *task, unsigned long *sp)
{
const int field = 2 * sizeof(unsigned long);
long stackdata;
int i;
if (!sp) {
if (task && task != current)
sp = (unsigned long *) task->thread.reg29;
else
sp = (unsigned long *) &sp;
}
printk("Stack :");
i = 0;
while ((unsigned long) sp & (PAGE_SIZE - 1)) {
if (i && ((i % (64 / field)) == 0))
printk("\n ");
if (i > 39) {
printk(" ...");
break;
}
if (__get_user(stackdata, sp++)) {
printk(" (Bad stack address)");
break;
}
printk(" %0*lx", field, stackdata);
i++;
}
printk("\n");
}
void show_trace(struct task_struct *task, unsigned long *stack)
{
const int field = 2 * sizeof(unsigned long);
unsigned long addr;
if (!stack) {
if (task && task != current)
stack = (unsigned long *) task->thread.reg29;
else
stack = (unsigned long *) &stack;
}
printk("Call Trace:");
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
while (!kstack_end(stack)) {
addr = *stack++;
if (__kernel_text_address(addr)) {
printk(" [<%0*lx>] ", field, addr);
print_symbol("%s\n", addr);
}
}
printk("\n");
}
/*
* The architecture-independent dump_stack generator
*/
void dump_stack(void)
{
unsigned long stack;
show_trace(current, &stack);
}
EXPORT_SYMBOL(dump_stack);
void show_code(unsigned int *pc)
{
long i;
printk("\nCode:");
for(i = -3 ; i < 6 ; i++) {
unsigned int insn;
if (__get_user(insn, pc + i)) {
printk(" (Bad address in epc)\n");
break;
}
printk("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>'));
}
}
void show_regs(struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
unsigned int cause = regs->cp0_cause;
int i;
printk("Cpu %d\n", smp_processor_id());
/*
* Saved main processor registers
*/
for (i = 0; i < 32; ) {
if ((i % 4) == 0)
printk("$%2d :", i);
if (i == 0)
printk(" %0*lx", field, 0UL);
else if (i == 26 || i == 27)
printk(" %*s", field, "");
else
printk(" %0*lx", field, regs->regs[i]);
i++;
if ((i % 4) == 0)
printk("\n");
}
printk("Hi : %0*lx\n", field, regs->hi);
printk("Lo : %0*lx\n", field, regs->lo);
/*
* Saved cp0 registers
*/
printk("epc : %0*lx ", field, regs->cp0_epc);
print_symbol("%s ", regs->cp0_epc);
printk(" %s\n", print_tainted());
printk("ra : %0*lx ", field, regs->regs[31]);
print_symbol("%s\n", regs->regs[31]);
printk("Status: %08x ", (uint32_t) regs->cp0_status);
if (current_cpu_data.isa_level == MIPS_CPU_ISA_I) {
if (regs->cp0_status & ST0_KUO)
printk("KUo ");
if (regs->cp0_status & ST0_IEO)
printk("IEo ");
if (regs->cp0_status & ST0_KUP)
printk("KUp ");
if (regs->cp0_status & ST0_IEP)
printk("IEp ");
if (regs->cp0_status & ST0_KUC)
printk("KUc ");
if (regs->cp0_status & ST0_IEC)
printk("IEc ");
} else {
if (regs->cp0_status & ST0_KX)
printk("KX ");
if (regs->cp0_status & ST0_SX)
printk("SX ");
if (regs->cp0_status & ST0_UX)
printk("UX ");
switch (regs->cp0_status & ST0_KSU) {
case KSU_USER:
printk("USER ");
break;
case KSU_SUPERVISOR:
printk("SUPERVISOR ");
break;
case KSU_KERNEL:
printk("KERNEL ");
break;
default:
printk("BAD_MODE ");
break;
}
if (regs->cp0_status & ST0_ERL)
printk("ERL ");
if (regs->cp0_status & ST0_EXL)
printk("EXL ");
if (regs->cp0_status & ST0_IE)
printk("IE ");
}
printk("\n");
printk("Cause : %08x\n", cause);
cause = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE;
if (1 <= cause && cause <= 5)
printk("BadVA : %0*lx\n", field, regs->cp0_badvaddr);
printk("PrId : %08x\n", read_c0_prid());
}
void show_registers(struct pt_regs *regs)
{
show_regs(regs);
print_modules();
printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
current->comm, current->pid, current_thread_info(), current);
show_stack(current, (long *) regs->regs[29]);
show_trace(current, (long *) regs->regs[29]);
show_code((unsigned int *) regs->cp0_epc);
printk("\n");
}
static DEFINE_SPINLOCK(die_lock);
NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs)
{
static int die_counter;
#ifdef CONFIG_MIPS_MT_SMTC
unsigned long dvpret = dvpe();
#endif /* CONFIG_MIPS_MT_SMTC */
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
#ifdef CONFIG_MIPS_MT_SMTC
mips_mt_regdump(dvpret);
#endif /* CONFIG_MIPS_MT_SMTC */
printk("%s[#%d]:\n", str, ++die_counter);
show_registers(regs);
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
}
extern const struct exception_table_entry __start___dbe_table[];
extern const struct exception_table_e
|