#include <linux/kexec.h>
#include <linux/backlight.h>
#include <linux/bug.h>
+#include <linux/kdebug.h>
-#include <asm/kdebug.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#endif
#include <asm/kexec.h>
-#ifdef CONFIG_DEBUGGER
+#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
int (*__debugger)(struct pt_regs *regs);
int (*__debugger_ipi)(struct pt_regs *regs);
int (*__debugger_bpt)(struct pt_regs *regs);
EXPORT_SYMBOL(__debugger_fault_handler);
#endif
-ATOMIC_NOTIFIER_HEAD(powerpc_die_chain);
-
-int register_die_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&powerpc_die_chain, nb);
-}
-EXPORT_SYMBOL(register_die_notifier);
-
-int unregister_die_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&powerpc_die_chain, nb);
-}
-EXPORT_SYMBOL(unregister_die_notifier);
-
/*
* Trap & Exception support
*/
-static DEFINE_SPINLOCK(die_lock);
+#ifdef CONFIG_PMAC_BACKLIGHT
+static void pmac_backlight_unblank(void)
+{
+ mutex_lock(&pmac_backlight_mutex);
+ if (pmac_backlight) {
+ struct backlight_properties *props;
+
+ props = &pmac_backlight->props;
+ props->brightness = props->max_brightness;
+ props->power = FB_BLANK_UNBLANK;
+ backlight_update_status(pmac_backlight);
+ }
+ mutex_unlock(&pmac_backlight_mutex);
+}
+#else
+static inline void pmac_backlight_unblank(void) { }
+#endif
int die(const char *str, struct pt_regs *regs, long err)
{
+ static struct {
+ spinlock_t lock;
+ u32 lock_owner;
+ int lock_owner_depth;
+ } die = {
+ .lock = __SPIN_LOCK_UNLOCKED(die.lock),
+ .lock_owner = -1,
+ .lock_owner_depth = 0
+ };
static int die_counter;
+ unsigned long flags;
if (debugger(regs))
return 1;
- console_verbose();
- spin_lock_irq(&die_lock);
- bust_spinlocks(1);
-#ifdef CONFIG_PMAC_BACKLIGHT
- mutex_lock(&pmac_backlight_mutex);
- if (machine_is(powermac) && pmac_backlight) {
- struct backlight_properties *props;
+ oops_enter();
- down(&pmac_backlight->sem);
- props = pmac_backlight->props;
- props->brightness = props->max_brightness;
- props->power = FB_BLANK_UNBLANK;
- props->update_status(pmac_backlight);
- up(&pmac_backlight->sem);
+ if (die.lock_owner != raw_smp_processor_id()) {
+ console_verbose();
+ spin_lock_irqsave(&die.lock, flags);
+ die.lock_owner = smp_processor_id();
+ die.lock_owner_depth = 0;
+ bust_spinlocks(1);
+ if (machine_is(powermac))
+ pmac_backlight_unblank();
+ } else {
+ local_save_flags(flags);
}
- mutex_unlock(&pmac_backlight_mutex);
-#endif
- printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
+
+ if (++die.lock_owner_depth < 3) {
+ printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
#ifdef CONFIG_PREEMPT
- printk("PREEMPT ");
+ printk("PREEMPT ");
#endif
#ifdef CONFIG_SMP
- printk("SMP NR_CPUS=%d ", NR_CPUS);
+ printk("SMP NR_CPUS=%d ", NR_CPUS);
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
- printk("DEBUG_PAGEALLOC ");
+ printk("DEBUG_PAGEALLOC ");
#endif
#ifdef CONFIG_NUMA
- printk("NUMA ");
+ printk("NUMA ");
#endif
- printk("%s\n", ppc_md.name ? "" : ppc_md.name);
+ printk("%s\n", ppc_md.name ? ppc_md.name : "");
+
+ print_modules();
+ show_regs(regs);
+ } else {
+ printk("Recursive die() failure, output suppressed\n");
+ }
- print_modules();
- show_regs(regs);
bust_spinlocks(0);
- spin_unlock_irq(&die_lock);
+ die.lock_owner = -1;
+ add_taint(TAINT_DIE);
+ spin_unlock_irqrestore(&die.lock, flags);
if (kexec_should_crash(current) ||
kexec_sr_activated(smp_processor_id()))
if (panic_on_oops)
panic("Fatal exception");
+ oops_exit();
do_exit(err);
return 0;
void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{
siginfo_t info;
+ const char fmt32[] = KERN_INFO "%s[%d]: unhandled signal %d " \
+ "at %08lx nip %08lx lr %08lx code %x\n";
+ const char fmt64[] = KERN_INFO "%s[%d]: unhandled signal %d " \
+ "at %016lx nip %016lx lr %016lx code %x\n";
if (!user_mode(regs)) {
if (die("Exception in kernel mode", regs, signr))
return;
- }
+ } else if (show_unhandled_signals &&
+ unhandled_signal(current, signr) &&
+ printk_ratelimit()) {
+ printk(regs->msr & MSR_SF ? fmt64 : fmt32,
+ current->comm, current->pid, signr,
+ addr, regs->nip, regs->link, code);
+ }
memset(&info, 0, sizeof(info));
info.si_signo = signr;
* generate the same exception over and over again and we get
* nowhere. Better to kill it and let the kernel panic.
*/
- if (is_init(current)) {
+ if (is_global_init(current)) {
__sighandler_t handler;
spin_lock_irq(¤t->sighand->siglock);
#ifndef CONFIG_FSL_BOOKE
#define get_mc_reason(regs) ((regs)->dsisr)
#else
-#define get_mc_reason(regs) (mfspr(SPRN_MCSR))
+#define get_mc_reason(regs) (mfspr(SPRN_MCSR) & MCSR_MASK)
#endif
#define REASON_FP ESR_FP
#define REASON_ILLEGAL (ESR_PIL | ESR_PUO)
#define clear_single_step(regs) ((regs)->msr &= ~MSR_SE)
#endif
-/*
- * This is "fall-back" implementation for configurations
- * which don't provide platform-specific machine check info
- */
-void __attribute__ ((weak))
-platform_machine_check(struct pt_regs *regs)
-{
-}
-
-void machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
{
- int recover = 0;
unsigned long reason = get_mc_reason(regs);
- /* See if any machine dependent calls */
- if (ppc_md.machine_check_exception)
- recover = ppc_md.machine_check_exception(regs);
-
- if (recover)
- return;
-
- if (user_mode(regs)) {
- regs->msr |= MSR_RI;
- _exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
- return;
- }
-
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
- /* the qspan pci read routines can cause machine checks -- Cort */
- bad_page_fault(regs, regs->dar, SIGBUS);
- return;
-#endif
-
- if (debugger_fault_handler(regs)) {
- regs->msr |= MSR_RI;
- return;
- }
-
- if (check_io_access(regs))
- return;
-
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
if (reason & ESR_IMCP) {
printk("Instruction");
mtspr(SPRN_ESR, reason & ~ESR_IMCP);
} else
printk("Data");
printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+ return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+ unsigned long reason = get_mc_reason(regs);
+
printk("Machine check in kernel mode.\n");
if (reason & ESR_IMCP){
printk("Instruction Synchronous Machine Check exception\n");
/* Clear MCSR */
mtspr(SPRN_MCSR, mcsr);
}
-#elif defined (CONFIG_E500)
+ return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+ unsigned long reason = get_mc_reason(regs);
+
printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason);
printk("Data Cache Push Parity Error\n");
if (reason & MCSR_DCPERR)
printk("Data Cache Parity Error\n");
- if (reason & MCSR_GL_CI)
- printk("Guarded Load or Cache-Inhibited stwcx.\n");
if (reason & MCSR_BUS_IAERR)
printk("Bus - Instruction Address Error\n");
if (reason & MCSR_BUS_RAERR)
printk("Bus - Instruction Parity Error\n");
if (reason & MCSR_BUS_RPERR)
printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+ return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+ unsigned long reason = get_mc_reason(regs);
+
printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason);
printk("Bus - Read Bus Error on data load\n");
if (reason & MCSR_BUS_WRERR)
printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+ return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+ unsigned long reason = get_mc_reason(regs);
+
printk("Machine check in kernel mode.\n");
printk("Caused by (from SRR1=%lx): ", reason);
switch (reason & 0x601F0000) {
default:
printk("Unknown values in msr\n");
}
-#endif /* CONFIG_4xx */
+ return 0;
+}
+#endif /* everything else */
- /*
- * Optional platform-provided routine to print out
- * additional info, e.g. bus error registers.
+void machine_check_exception(struct pt_regs *regs)
+{
+ int recover = 0;
+
+ /* See if any machine dependent calls. In theory, we would want
+ * to call the CPU first, and call the ppc_md. one if the CPU
+ * one returns a positive number. However there is existing code
+ * that assumes the board gets a first chance, so let's keep it
+ * that way for now and fix things later. --BenH.
*/
- platform_machine_check(regs);
+ if (ppc_md.machine_check_exception)
+ recover = ppc_md.machine_check_exception(regs);
+ else if (cur_cpu_spec->machine_check)
+ recover = cur_cpu_spec->machine_check(regs);
+
+ if (recover > 0)
+ return;
+
+ if (user_mode(regs)) {
+ regs->msr |= MSR_RI;
+ _exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
+ return;
+ }
+
+#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
+ /* the qspan pci read routines can cause machine checks -- Cort
+ *
+ * yuck !!! that totally needs to go away ! There are better ways
+ * to deal with that than having a wart in the mcheck handler.
+ * -- BenH
+ */
+ bad_page_fault(regs, regs->dar, SIGBUS);
+ return;
+#endif
+
+ if (debugger_fault_handler(regs)) {
+ regs->msr |= MSR_RI;
+ return;
+ }
+
+ if (check_io_access(regs))
+ return;
if (debugger_fault_handler(regs))
return;
#define INST_POPCNTB 0x7c0000f4
#define INST_POPCNTB_MASK 0xfc0007fe
+#define INST_ISEL 0x7c00001e
+#define INST_ISEL_MASK 0xfc00003e
+
static int emulate_string_inst(struct pt_regs *regs, u32 instword)
{
u8 rT = (instword >> 21) & 0x1f;
return 0;
}
+static int emulate_isel(struct pt_regs *regs, u32 instword)
+{
+ u8 rT = (instword >> 21) & 0x1f;
+ u8 rA = (instword >> 16) & 0x1f;
+ u8 rB = (instword >> 11) & 0x1f;
+ u8 BC = (instword >> 6) & 0x1f;
+ u8 bit;
+ unsigned long tmp;
+
+ tmp = (rA == 0) ? 0 : regs->gpr[rA];
+ bit = (regs->ccr >> (31 - BC)) & 0x1;
+
+ regs->gpr[rT] = bit ? tmp : regs->gpr[rB];
+
+ return 0;
+}
+
static int emulate_instruction(struct pt_regs *regs)
{
u32 instword;
return emulate_popcntb_inst(regs, instword);
}
+ /* Emulate isel (Integer Select) instruction */
+ if ((instword & INST_ISEL_MASK) == INST_ISEL) {
+ return emulate_isel(regs, instword);
+ }
+
return -EINVAL;
}
return;
if (!(regs->msr & MSR_PR) && /* not user-mode */
- report_bug(regs->nip) == BUG_TRAP_TYPE_WARN) {
+ report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
regs->nip += 4;
return;
}
void trace_syscall(struct pt_regs *regs)
{
printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld %s\n",
- current, current->pid, regs->nip, regs->link, regs->gpr[0],
+ current, task_pid_nr(current), regs->nip, regs->link, regs->gpr[0],
regs->ccr&0x10000000?"Error=":"", regs->gpr[3], print_tainted());
}
{
extern int do_mathemu(struct pt_regs *);
extern int Soft_emulate_8xx(struct pt_regs *);
+#if defined(CONFIG_MATH_EMULATION) || defined(CONFIG_8XX_MINIMAL_FPEMU)
int errcode;
+#endif
CHECK_FULL_REGS(regs);
return;
}
-#else
+#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
errcode = Soft_emulate_8xx(regs);
switch (errcode) {
case 0:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
}
+#else
+ _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
#endif
}
#endif /* CONFIG_8xx */