Commit 8e7844eb authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: clean up trap handling

This patch is from Jake Moilanen <moilanen@austin.ibm.com>.

Clean-up of traps.c.  Moved the machine dependent calls to a ppc_md call,
and moved the pSeries specific code to ras.c.

I also changed the naming convention to more closely follow the Linux
standards.
Signed-off-by: default avatarJake Moilanen <moilanen@austin.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 96f4e868
...@@ -738,7 +738,7 @@ __end_stab: ...@@ -738,7 +738,7 @@ __end_stab:
/*** Common interrupt handlers ***/ /*** Common interrupt handlers ***/
STD_EXCEPTION_COMMON(0x100, SystemReset, .SystemResetException) STD_EXCEPTION_COMMON(0x100, SystemReset, .system_reset_exception)
/* /*
* Machine check is different because we use a different * Machine check is different because we use a different
...@@ -751,20 +751,20 @@ MachineCheck_common: ...@@ -751,20 +751,20 @@ MachineCheck_common:
DISABLE_INTS DISABLE_INTS
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
bl .MachineCheckException bl .machine_check_exception
b .ret_from_except b .ret_from_except
STD_EXCEPTION_COMMON_LITE(0x900, Decrementer, .timer_interrupt) STD_EXCEPTION_COMMON_LITE(0x900, Decrementer, .timer_interrupt)
STD_EXCEPTION_COMMON(0xa00, Trap_0a, .UnknownException) STD_EXCEPTION_COMMON(0xa00, Trap_0a, .unknown_exception)
STD_EXCEPTION_COMMON(0xb00, Trap_0b, .UnknownException) STD_EXCEPTION_COMMON(0xb00, Trap_0b, .unknown_exception)
STD_EXCEPTION_COMMON(0xd00, SingleStep, .SingleStepException) STD_EXCEPTION_COMMON(0xd00, SingleStep, .single_step_exception)
STD_EXCEPTION_COMMON(0xe00, Trap_0e, .UnknownException) STD_EXCEPTION_COMMON(0xe00, Trap_0e, .unknown_exception)
STD_EXCEPTION_COMMON(0xf00, PerformanceMonitor, .PerformanceMonitorException) STD_EXCEPTION_COMMON(0xf00, PerformanceMonitor, .performance_monitor_exception)
STD_EXCEPTION_COMMON(0x1300, InstructionBreakpoint, .InstructionBreakpointException) STD_EXCEPTION_COMMON(0x1300, InstructionBreakpoint, .instruction_breakpoint_exception)
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
STD_EXCEPTION_COMMON(0x1700, AltivecAssist, .AltivecAssistException) STD_EXCEPTION_COMMON(0x1700, AltivecAssist, .altivec_assist_exception)
#else #else
STD_EXCEPTION_COMMON(0x1700, AltivecAssist, .UnknownException) STD_EXCEPTION_COMMON(0x1700, AltivecAssist, .unknown_exception)
#endif #endif
/* /*
...@@ -901,7 +901,7 @@ Alignment_common: ...@@ -901,7 +901,7 @@ Alignment_common:
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
ENABLE_INTS ENABLE_INTS
bl .AlignmentException bl .alignment_exception
b .ret_from_except b .ret_from_except
.align 7 .align 7
...@@ -911,7 +911,7 @@ ProgramCheck_common: ...@@ -911,7 +911,7 @@ ProgramCheck_common:
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
ENABLE_INTS ENABLE_INTS
bl .ProgramCheckException bl .program_check_exception
b .ret_from_except b .ret_from_except
.align 7 .align 7
...@@ -922,7 +922,7 @@ FPUnavailable_common: ...@@ -922,7 +922,7 @@ FPUnavailable_common:
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
ENABLE_INTS ENABLE_INTS
bl .KernelFPUnavailableException bl .kernel_fp_unavailable_exception
BUG_OPCODE BUG_OPCODE
.align 7 .align 7
...@@ -935,7 +935,7 @@ AltivecUnavailable_common: ...@@ -935,7 +935,7 @@ AltivecUnavailable_common:
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
ENABLE_INTS ENABLE_INTS
bl .AltivecUnavailableException bl .altivec_unavailable_exception
b .ret_from_except b .ret_from_except
/* /*
......
...@@ -80,7 +80,8 @@ extern void pSeries_get_boot_time(struct rtc_time *rtc_time); ...@@ -80,7 +80,8 @@ extern void pSeries_get_boot_time(struct rtc_time *rtc_time);
extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); extern void pSeries_get_rtc_time(struct rtc_time *rtc_time);
extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); extern int pSeries_set_rtc_time(struct rtc_time *rtc_time);
extern void find_udbg_vterm(void); extern void find_udbg_vterm(void);
extern void SystemReset_FWNMI(void), MachineCheck_FWNMI(void); /* from head.S */ extern void system_reset_fwnmi(void); /* from head.S */
extern void machine_check_fwnmi(void); /* from head.S */
extern void generic_find_legacy_serial_ports(u64 *physport, extern void generic_find_legacy_serial_ports(u64 *physport,
unsigned int *default_speed); unsigned int *default_speed);
...@@ -93,6 +94,9 @@ extern unsigned long loops_per_jiffy; ...@@ -93,6 +94,9 @@ extern unsigned long loops_per_jiffy;
extern unsigned long ppc_proc_freq; extern unsigned long ppc_proc_freq;
extern unsigned long ppc_tb_freq; extern unsigned long ppc_tb_freq;
extern void pSeries_system_reset_exception(struct pt_regs *regs);
extern int pSeries_machine_check_exception(struct pt_regs *regs);
static volatile void __iomem * chrp_int_ack_special; static volatile void __iomem * chrp_int_ack_special;
struct mpic *pSeries_mpic; struct mpic *pSeries_mpic;
...@@ -119,8 +123,8 @@ static void __init fwnmi_init(void) ...@@ -119,8 +123,8 @@ static void __init fwnmi_init(void)
if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE) if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE)
return; return;
ret = rtas_call(ibm_nmi_register, 2, 1, NULL, ret = rtas_call(ibm_nmi_register, 2, 1, NULL,
__pa((unsigned long)SystemReset_FWNMI), __pa((unsigned long)system_reset_fwnmi),
__pa((unsigned long)MachineCheck_FWNMI)); __pa((unsigned long)machine_check_fwnmi));
if (ret == 0) if (ret == 0)
fwnmi_active = 1; fwnmi_active = 1;
} }
...@@ -612,4 +616,6 @@ struct machdep_calls __initdata pSeries_md = { ...@@ -612,4 +616,6 @@ struct machdep_calls __initdata pSeries_md = {
.calibrate_decr = pSeries_calibrate_decr, .calibrate_decr = pSeries_calibrate_decr,
.progress = pSeries_progress, .progress = pSeries_progress,
.check_legacy_ioport = pSeries_check_legacy_ioport, .check_legacy_ioport = pSeries_check_legacy_ioport,
.system_reset_exception = pSeries_system_reset_exception,
.machine_check_exception = pSeries_machine_check_exception,
}; };
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX]; static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
static spinlock_t ras_log_buf_lock = SPIN_LOCK_UNLOCKED; static spinlock_t ras_log_buf_lock = SPIN_LOCK_UNLOCKED;
/* This is true if we are using the firmware NMI handler (typically LPAR) */
extern int fwnmi_active;
static int ras_get_sensor_state_token; static int ras_get_sensor_state_token;
static int ras_check_exception_token; static int ras_check_exception_token;
...@@ -234,3 +237,104 @@ ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -234,3 +237,104 @@ ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs)
spin_unlock(&ras_log_buf_lock); spin_unlock(&ras_log_buf_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/* Get the error information for errors coming through the
* FWNMI vectors. The pt_regs' r3 will be updated to reflect
* the actual r3 if possible, and a ptr to the error log entry
* will be returned if found.
*/
static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
{
unsigned long errdata = regs->gpr[3];
struct rtas_error_log *errhdr = NULL;
unsigned long *savep;
if ((errdata >= 0x7000 && errdata < 0x7fff0) ||
(errdata >= rtas.base && errdata < rtas.base + rtas.size - 16)) {
savep = __va(errdata);
regs->gpr[3] = savep[0]; /* restore original r3 */
errhdr = (struct rtas_error_log *)(savep + 1);
} else {
printk("FWNMI: corrupt r3\n");
}
return errhdr;
}
/* Call this when done with the data returned by FWNMI_get_errinfo.
* It will release the saved data area for other CPUs in the
* partition to receive FWNMI errors.
*/
static void fwnmi_release_errinfo(void)
{
int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
if (ret != 0)
printk("FWNMI: nmi-interlock failed: %d\n", ret);
}
void pSeries_system_reset_exception(struct pt_regs *regs)
{
if (fwnmi_active) {
struct rtas_error_log *errhdr = fwnmi_get_errinfo(regs);
if (errhdr) {
/* XXX Should look at FWNMI information */
}
fwnmi_release_errinfo();
}
}
/*
* See if we can recover from a machine check exception.
* This is only called on power4 (or above) and only via
* the Firmware Non-Maskable Interrupts (fwnmi) handler
* which provides the error analysis for us.
*
* Return 1 if corrected (or delivered a signal).
* Return 0 if there is nothing we can do.
*/
static int recover_mce(struct pt_regs *regs, struct rtas_error_log err)
{
if (err.disposition == RTAS_DISP_FULLY_RECOVERED) {
/* Platform corrected itself */
return 1;
} else if ((regs->msr & MSR_RI) &&
user_mode(regs) &&
err.severity == RTAS_SEVERITY_ERROR_SYNC &&
err.disposition == RTAS_DISP_NOT_RECOVERED &&
err.target == RTAS_TARGET_MEMORY &&
err.type == RTAS_TYPE_ECC_UNCORR &&
!(current->pid == 0 || current->pid == 1)) {
/* Kill off a user process with an ECC error */
printk(KERN_ERR "MCE: uncorrectable ecc error for pid %d\n",
current->pid);
/* XXX something better for ECC error? */
_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
return 1;
}
return 0;
}
/*
* Handle a machine check.
*
* Note that on Power 4 and beyond Firmware Non-Maskable Interrupts (fwnmi)
* should be present. If so the handler which called us tells us if the
* error was recovered (never true if RI=0).
*
* On hardware prior to Power 4 these exceptions were asynchronous which
* means we can't tell exactly where it occurred and so we can't recover.
*/
int pSeries_machine_check_exception(struct pt_regs *regs)
{
struct rtas_error_log err, *errp;
if (fwnmi_active) {
errp = fwnmi_get_errinfo(regs);
if (errp)
err = *errp;
fwnmi_release_errinfo(); /* frees errp */
if (errp && recover_mce(regs, err))
return 1;
}
return 0;
}
...@@ -39,11 +39,7 @@ ...@@ -39,11 +39,7 @@
#include <asm/ppcdebug.h> #include <asm/ppcdebug.h>
#include <asm/rtas.h> #include <asm/rtas.h>
#include <asm/systemcfg.h> #include <asm/systemcfg.h>
#include <asm/machdep.h>
#ifdef CONFIG_PPC_PSERIES
/* This is true if we are using the firmware NMI handler (typically LPAR) */
extern int fwnmi_active;
#endif
#ifdef CONFIG_DEBUGGER #ifdef CONFIG_DEBUGGER
int (*__debugger)(struct pt_regs *regs); int (*__debugger)(struct pt_regs *regs);
...@@ -149,8 +145,7 @@ int die(const char *str, struct pt_regs *regs, long err) ...@@ -149,8 +145,7 @@ int die(const char *str, struct pt_regs *regs, long err)
return 0; return 0;
} }
static void void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
_exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{ {
siginfo_t info; siginfo_t info;
...@@ -166,53 +161,11 @@ _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) ...@@ -166,53 +161,11 @@ _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
force_sig_info(signr, &info, current); force_sig_info(signr, &info, current);
} }
#ifdef CONFIG_PPC_PSERIES void system_reset_exception(struct pt_regs *regs)
/* Get the error information for errors coming through the
* FWNMI vectors. The pt_regs' r3 will be updated to reflect
* the actual r3 if possible, and a ptr to the error log entry
* will be returned if found.
*/
static struct rtas_error_log *FWNMI_get_errinfo(struct pt_regs *regs)
{ {
unsigned long errdata = regs->gpr[3]; /* See if any machine dependent calls */
struct rtas_error_log *errhdr = NULL; if (ppc_md.system_reset_exception)
unsigned long *savep; ppc_md.system_reset_exception(regs);
if ((errdata >= 0x7000 && errdata < 0x7fff0) ||
(errdata >= rtas.base && errdata < rtas.base + rtas.size - 16)) {
savep = __va(errdata);
regs->gpr[3] = savep[0]; /* restore original r3 */
errhdr = (struct rtas_error_log *)(savep + 1);
} else {
printk("FWNMI: corrupt r3\n");
}
return errhdr;
}
/* Call this when done with the data returned by FWNMI_get_errinfo.
* It will release the saved data area for other CPUs in the
* partition to receive FWNMI errors.
*/
static void FWNMI_release_errinfo(void)
{
int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
if (ret != 0)
printk("FWNMI: nmi-interlock failed: %d\n", ret);
}
#endif
void
SystemResetException(struct pt_regs *regs)
{
#ifdef CONFIG_PPC_PSERIES
if (fwnmi_active) {
struct rtas_error_log *errhdr = FWNMI_get_errinfo(regs);
if (errhdr) {
/* XXX Should look at FWNMI information */
}
FWNMI_release_errinfo();
}
#endif
die("System Reset", regs, 0); die("System Reset", regs, 0);
...@@ -223,64 +176,16 @@ SystemResetException(struct pt_regs *regs) ...@@ -223,64 +176,16 @@ SystemResetException(struct pt_regs *regs)
/* What should we do here? We could issue a shutdown or hard reset. */ /* What should we do here? We could issue a shutdown or hard reset. */
} }
#ifdef CONFIG_PPC_PSERIES void machine_check_exception(struct pt_regs *regs)
/*
* See if we can recover from a machine check exception.
* This is only called on power4 (or above) and only via
* the Firmware Non-Maskable Interrupts (fwnmi) handler
* which provides the error analysis for us.
*
* Return 1 if corrected (or delivered a signal).
* Return 0 if there is nothing we can do.
*/
static int recover_mce(struct pt_regs *regs, struct rtas_error_log err)
{ {
if (err.disposition == RTAS_DISP_FULLY_RECOVERED) { int recover = 0;
/* Platform corrected itself */
return 1;
} else if ((regs->msr & MSR_RI) &&
user_mode(regs) &&
err.severity == RTAS_SEVERITY_ERROR_SYNC &&
err.disposition == RTAS_DISP_NOT_RECOVERED &&
err.target == RTAS_TARGET_MEMORY &&
err.type == RTAS_TYPE_ECC_UNCORR &&
!(current->pid == 0 || current->pid == 1)) {
/* Kill off a user process with an ECC error */
printk(KERN_ERR "MCE: uncorrectable ecc error for pid %d\n",
current->pid);
/* XXX something better for ECC error? */
_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
return 1;
}
return 0;
}
#endif
/* /* See if any machine dependent calls */
* Handle a machine check. if (ppc_md.machine_check_exception)
* recover = ppc_md.machine_check_exception(regs);
* Note that on Power 4 and beyond Firmware Non-Maskable Interrupts (fwnmi)
* should be present. If so the handler which called us tells us if the if (recover)
* error was recovered (never true if RI=0).
*
* On hardware prior to Power 4 these exceptions were asynchronous which
* means we can't tell exactly where it occurred and so we can't recover.
*/
void
MachineCheckException(struct pt_regs *regs)
{
#ifdef CONFIG_PPC_PSERIES
struct rtas_error_log err, *errp;
if (fwnmi_active) {
errp = FWNMI_get_errinfo(regs);
if (errp)
err = *errp;
FWNMI_release_errinfo(); /* frees errp */
if (errp && recover_mce(regs, err))
return; return;
}
#endif
if (debugger_fault_handler(regs)) if (debugger_fault_handler(regs))
return; return;
...@@ -291,8 +196,7 @@ MachineCheckException(struct pt_regs *regs) ...@@ -291,8 +196,7 @@ MachineCheckException(struct pt_regs *regs)
panic("Unrecoverable Machine check"); panic("Unrecoverable Machine check");
} }
void void unknown_exception(struct pt_regs *regs)
UnknownException(struct pt_regs *regs)
{ {
printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
regs->nip, regs->msr, regs->trap); regs->nip, regs->msr, regs->trap);
...@@ -300,8 +204,7 @@ UnknownException(struct pt_regs *regs) ...@@ -300,8 +204,7 @@ UnknownException(struct pt_regs *regs)
_exception(SIGTRAP, regs, 0, 0); _exception(SIGTRAP, regs, 0, 0);
} }
void void instruction_breakpoint_exception(struct pt_regs *regs)
InstructionBreakpointException(struct pt_regs *regs)
{ {
if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5, if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
5, SIGTRAP) == NOTIFY_STOP) 5, SIGTRAP) == NOTIFY_STOP)
...@@ -311,8 +214,7 @@ InstructionBreakpointException(struct pt_regs *regs) ...@@ -311,8 +214,7 @@ InstructionBreakpointException(struct pt_regs *regs)
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
} }
void void single_step_exception(struct pt_regs *regs)
SingleStepException(struct pt_regs *regs)
{ {
regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */
...@@ -334,7 +236,7 @@ SingleStepException(struct pt_regs *regs) ...@@ -334,7 +236,7 @@ SingleStepException(struct pt_regs *regs)
static inline void emulate_single_step(struct pt_regs *regs) static inline void emulate_single_step(struct pt_regs *regs)
{ {
if (regs->msr & MSR_SE) if (regs->msr & MSR_SE)
SingleStepException(regs); single_step_exception(regs);
} }
static void parse_fpe(struct pt_regs *regs) static void parse_fpe(struct pt_regs *regs)
...@@ -478,8 +380,7 @@ check_bug_trap(struct pt_regs *regs) ...@@ -478,8 +380,7 @@ check_bug_trap(struct pt_regs *regs)
return 0; return 0;
} }
void void program_check_exception(struct pt_regs *regs)
ProgramCheckException(struct pt_regs *regs)
{ {
if (debugger_fault_handler(regs)) if (debugger_fault_handler(regs))
return; return;
...@@ -526,14 +427,14 @@ ProgramCheckException(struct pt_regs *regs) ...@@ -526,14 +427,14 @@ ProgramCheckException(struct pt_regs *regs)
} }
} }
void KernelFPUnavailableException(struct pt_regs *regs) void kernel_fp_unavailable_exception(struct pt_regs *regs)
{ {
printk(KERN_EMERG "Unrecoverable FP Unavailable Exception " printk(KERN_EMERG "Unrecoverable FP Unavailable Exception "
"%lx at %lx\n", regs->trap, regs->nip); "%lx at %lx\n", regs->trap, regs->nip);
die("Unrecoverable FP Unavailable Exception", regs, SIGABRT); die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
} }
void AltivecUnavailableException(struct pt_regs *regs) void altivec_unavailable_exception(struct pt_regs *regs)
{ {
#ifndef CONFIG_ALTIVEC #ifndef CONFIG_ALTIVEC
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -561,14 +462,12 @@ void (*perf_irq)(struct pt_regs *) = dummy_perf; ...@@ -561,14 +462,12 @@ void (*perf_irq)(struct pt_regs *) = dummy_perf;
EXPORT_SYMBOL(perf_irq); EXPORT_SYMBOL(perf_irq);
void void performance_monitor_exception(struct pt_regs *regs)
PerformanceMonitorException(struct pt_regs *regs)
{ {
perf_irq(regs); perf_irq(regs);
} }
void void alignment_exception(struct pt_regs *regs)
AlignmentException(struct pt_regs *regs)
{ {
int fixed; int fixed;
...@@ -596,8 +495,7 @@ AlignmentException(struct pt_regs *regs) ...@@ -596,8 +495,7 @@ AlignmentException(struct pt_regs *regs)
} }
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
void void altivec_assist_exception(struct pt_regs *regs)
AltivecAssistException(struct pt_regs *regs)
{ {
int err; int err;
siginfo_t info; siginfo_t info;
......
...@@ -110,6 +110,10 @@ struct machdep_calls { ...@@ -110,6 +110,10 @@ struct machdep_calls {
ssize_t (*nvram_size)(void); ssize_t (*nvram_size)(void);
int (*nvram_sync)(void); int (*nvram_sync)(void);
/* Exception handlers */
void (*system_reset_exception)(struct pt_regs *regs);
int (*machine_check_exception)(struct pt_regs *regs);
/* Motherboard/chipset features. This is a kind of general purpose /* Motherboard/chipset features. This is a kind of general purpose
* hook used to control some machine specific features (like reset * hook used to control some machine specific features (like reset
* lines, chip power control, etc...). * lines, chip power control, etc...).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment