Hi, Unlike most architectures which only handle memory faults/privilege violations via __ex_table, the MIPS port allows to handle bus error exceptions via __dbe_table. Unfortunately, the latter table is not exported to modules, so if a modularized driver needs to probe the address space for a device it's out of luck. The following patch implements the kernel part of __dbe_table support. A separate patch is needed for modutils. A side effect of the patch is a fix to unhandled bus error exceptions which happen in the kernel mode. Currently they cause the faulting code to be reexecuted which results in a hang in an infinite loop. With the patch applied, the kernel's response is an "Oops" similar to the one printed when a memory fault happens. I hope it's fine to apply. Maciej -- + Maciej W. Rozycki, Technical University of Gdansk, Poland + +--------------------------------------------------------------+ + e-mail: macro@ds2.pg.gda.pl, PGP key available + patch-mips-2.4.5-20010704-dbe-0 diff -up --recursive --new-file linux-mips-2.4.5-20010704.macro/arch/mips/kernel/traps.c linux-mips-2.4.5-20010704/arch/mips/kernel/traps.c --- linux-mips-2.4.5-20010704.macro/arch/mips/kernel/traps.c Fri Jun 15 04:27:07 2001 +++ linux-mips-2.4.5-20010704/arch/mips/kernel/traps.c Sun Aug 12 17:34:55 2001 @@ -14,6 +14,7 @@ #include <linux/config.h> #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> @@ -254,23 +255,59 @@ search_one_table(const struct exception_ return (first == last && first->insn == value) ? first->nextinsn : 0; } -#define search_dbe_table(addr) \ - search_one_table(__start___dbe_table, __stop___dbe_table - 1, (addr)) +extern spinlock_t modlist_lock; + +static unsigned long +search_dbe_table(unsigned long addr) +{ + unsigned long ret = 0; + +#ifndef CONFIG_MODULES + /* There is only the kernel to search. */ + ret = search_one_table(__start___dbe_table, __stop___dbe_table-1, addr); + return ret; +#else + unsigned long flags; + + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->dbe_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + continue; + ret = search_one_table(mp->dbe_table_start, + mp->dbe_table_end - 1, addr); + if (ret) + break; + } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; +#endif +} static void default_be_board_handler(struct pt_regs *regs) { unsigned long new_epc; - unsigned long fixup = search_dbe_table(regs->cp0_epc); + unsigned long fixup; - if (fixup) { - new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); - regs->cp0_epc = new_epc; - return; + if (!user_mode(regs)) { + fixup = search_dbe_table(regs->cp0_epc); + if (fixup) { + new_epc = fixup_exception(dpf_reg, fixup, + regs->cp0_epc); + regs->cp0_epc = new_epc; + return; + } } /* * Assume it would be too dangerous to continue ... */ + printk(KERN_ALERT "%s bus error, epc == %08lx, ra == %08lx\n", + (regs->cp0_cause & 4) ? "Data" : "Instruction", + regs->cp0_epc, regs->regs[31]); + die_if_kernel("Oops", regs); force_sig(SIGBUS, current); } diff -up --recursive --new-file linux-mips-2.4.5-20010704.macro/include/linux/module.h linux-mips-2.4.5-20010704/include/linux/module.h --- linux-mips-2.4.5-20010704.macro/include/linux/module.h Mon Jul 16 02:13:58 2001 +++ linux-mips-2.4.5-20010704/include/linux/module.h Sun Aug 12 11:22:09 2001 @@ -75,6 +75,10 @@ struct module void (*cleanup)(void); const struct exception_table_entry *ex_table_start; const struct exception_table_entry *ex_table_end; +#ifdef __mips__ + const struct exception_table_entry *dbe_table_start; + const struct exception_table_entry *dbe_table_end; +#endif #ifdef __alpha__ unsigned long gp; #endif diff -up --recursive --new-file linux-mips-2.4.5-20010704.macro/kernel/module.c linux-mips-2.4.5-20010704/kernel/module.c --- linux-mips-2.4.5-20010704.macro/kernel/module.c Thu Jun 14 04:28:48 2001 +++ linux-mips-2.4.5-20010704/kernel/module.c Sun Aug 12 11:24:53 2001 @@ -36,6 +36,11 @@ extern struct module_symbol __stop___ksy extern const struct exception_table_entry __start___ex_table[]; extern const struct exception_table_entry __stop___ex_table[]; +#ifdef __mips__ +extern const struct exception_table_entry __start___dbe_table[]; +extern const struct exception_table_entry __stop___dbe_table[]; +#endif + extern const char __start___kallsyms[] __attribute__ ((weak)); extern const char __stop___kallsyms[] __attribute__ ((weak)); @@ -48,6 +53,10 @@ static struct module kernel_module = syms: __start___ksymtab, ex_table_start: __start___ex_table, ex_table_end: __stop___ex_table, +#ifdef __mips__ + dbe_table_start: __start___dbe_table, + dbe_table_end: __stop___dbe_table, +#endif kallsyms_start: __start___kallsyms, kallsyms_end: __stop___kallsyms, }; @@ -436,6 +445,19 @@ sys_init_module(const char *name_user, s printk(KERN_ERR "init_module: mod->ex_table_* invalid.\n"); goto err2; } +#ifdef __mips__ + if (mod->dbe_table_start > mod->dbe_table_end + || (mod->dbe_table_start && + !((unsigned long)mod->dbe_table_start >= ((unsigned long)mod + mod->size_of_struct) + && ((unsigned long)mod->dbe_table_end + < (unsigned long)mod + mod->size))) + || (((unsigned long)mod->dbe_table_start + - (unsigned long)mod->dbe_table_end) + % sizeof(struct exception_table_entry))) { + printk(KERN_ERR "init_module: mod->dbe_table_* invalid.\n"); + goto err2; + } +#endif if (mod->flags & ~MOD_AUTOCLEAN) { printk(KERN_ERR "init_module: mod->flags invalid.\n"); goto err2;