Path: blob/master/arch/powerpc/platforms/pseries/ras.c
10818 views
/*1* Copyright (C) 2001 Dave Engebretsen IBM Corporation2*3* This program is free software; you can redistribute it and/or modify4* it under the terms of the GNU General Public License as published by5* the Free Software Foundation; either version 2 of the License, or6* (at your option) any later version.7*8* This program is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11* GNU General Public License for more details.12*13* You should have received a copy of the GNU General Public License14* along with this program; if not, write to the Free Software15* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA16*/1718/* Change Activity:19* 2001/09/21 : engebret : Created with minimal EPOW and HW exception support.20* End Change Activity21*/2223#include <linux/errno.h>24#include <linux/threads.h>25#include <linux/kernel_stat.h>26#include <linux/signal.h>27#include <linux/sched.h>28#include <linux/ioport.h>29#include <linux/interrupt.h>30#include <linux/timex.h>31#include <linux/init.h>32#include <linux/delay.h>33#include <linux/irq.h>34#include <linux/random.h>35#include <linux/sysrq.h>36#include <linux/bitops.h>3738#include <asm/uaccess.h>39#include <asm/system.h>40#include <asm/io.h>41#include <asm/pgtable.h>42#include <asm/irq.h>43#include <asm/cache.h>44#include <asm/prom.h>45#include <asm/ptrace.h>46#include <asm/machdep.h>47#include <asm/rtas.h>48#include <asm/udbg.h>49#include <asm/firmware.h>5051#include "pseries.h"5253static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];54static DEFINE_SPINLOCK(ras_log_buf_lock);5556static char global_mce_data_buf[RTAS_ERROR_LOG_MAX];57static DEFINE_PER_CPU(__u64, mce_data_buf);5859static int ras_get_sensor_state_token;60static int ras_check_exception_token;6162#define EPOW_SENSOR_TOKEN 963#define EPOW_SENSOR_INDEX 06465static irqreturn_t ras_epow_interrupt(int irq, void *dev_id);66static irqreturn_t ras_error_interrupt(int irq, void *dev_id);676869/*70* Initialize handlers for the set of interrupts caused by hardware errors71* and power system events.72*/73static int __init init_ras_IRQ(void)74{75struct device_node *np;7677ras_get_sensor_state_token = rtas_token("get-sensor-state");78ras_check_exception_token = rtas_token("check-exception");7980/* Internal Errors */81np = of_find_node_by_path("/event-sources/internal-errors");82if (np != NULL) {83request_event_sources_irqs(np, ras_error_interrupt,84"RAS_ERROR");85of_node_put(np);86}8788/* EPOW Events */89np = of_find_node_by_path("/event-sources/epow-events");90if (np != NULL) {91request_event_sources_irqs(np, ras_epow_interrupt, "RAS_EPOW");92of_node_put(np);93}9495return 0;96}97__initcall(init_ras_IRQ);9899/*100* Handle power subsystem events (EPOW).101*102* Presently we just log the event has occurred. This should be fixed103* to examine the type of power failure and take appropriate action where104* the time horizon permits something useful to be done.105*/106static irqreturn_t ras_epow_interrupt(int irq, void *dev_id)107{108int status = 0xdeadbeef;109int state = 0;110int critical;111112status = rtas_call(ras_get_sensor_state_token, 2, 2, &state,113EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX);114115if (state > 3)116critical = 1; /* Time Critical */117else118critical = 0;119120spin_lock(&ras_log_buf_lock);121122status = rtas_call(ras_check_exception_token, 6, 1, NULL,123RTAS_VECTOR_EXTERNAL_INTERRUPT,124virq_to_hw(irq),125RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS,126critical, __pa(&ras_log_buf),127rtas_get_error_log_max());128129udbg_printf("EPOW <0x%lx 0x%x 0x%x>\n",130*((unsigned long *)&ras_log_buf), status, state);131printk(KERN_WARNING "EPOW <0x%lx 0x%x 0x%x>\n",132*((unsigned long *)&ras_log_buf), status, state);133134/* format and print the extended information */135log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, 0);136137spin_unlock(&ras_log_buf_lock);138return IRQ_HANDLED;139}140141/*142* Handle hardware error interrupts.143*144* RTAS check-exception is called to collect data on the exception. If145* the error is deemed recoverable, we log a warning and return.146* For nonrecoverable errors, an error is logged and we stop all processing147* as quickly as possible in order to prevent propagation of the failure.148*/149static irqreturn_t ras_error_interrupt(int irq, void *dev_id)150{151struct rtas_error_log *rtas_elog;152int status = 0xdeadbeef;153int fatal;154155spin_lock(&ras_log_buf_lock);156157status = rtas_call(ras_check_exception_token, 6, 1, NULL,158RTAS_VECTOR_EXTERNAL_INTERRUPT,159virq_to_hw(irq),160RTAS_INTERNAL_ERROR, 1 /*Time Critical */,161__pa(&ras_log_buf),162rtas_get_error_log_max());163164rtas_elog = (struct rtas_error_log *)ras_log_buf;165166if ((status == 0) && (rtas_elog->severity >= RTAS_SEVERITY_ERROR_SYNC))167fatal = 1;168else169fatal = 0;170171/* format and print the extended information */172log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, fatal);173174if (fatal) {175udbg_printf("Fatal HW Error <0x%lx 0x%x>\n",176*((unsigned long *)&ras_log_buf), status);177printk(KERN_EMERG "Error: Fatal hardware error <0x%lx 0x%x>\n",178*((unsigned long *)&ras_log_buf), status);179180#ifndef DEBUG_RTAS_POWER_OFF181/* Don't actually power off when debugging so we can test182* without actually failing while injecting errors.183* Error data will not be logged to syslog.184*/185ppc_md.power_off();186#endif187} else {188udbg_printf("Recoverable HW Error <0x%lx 0x%x>\n",189*((unsigned long *)&ras_log_buf), status);190printk(KERN_WARNING191"Warning: Recoverable hardware error <0x%lx 0x%x>\n",192*((unsigned long *)&ras_log_buf), status);193}194195spin_unlock(&ras_log_buf_lock);196return IRQ_HANDLED;197}198199/*200* Some versions of FWNMI place the buffer inside the 4kB page starting at201* 0x7000. Other versions place it inside the rtas buffer. We check both.202*/203#define VALID_FWNMI_BUFFER(A) \204((((A) >= 0x7000) && ((A) < 0x7ff0)) || \205(((A) >= rtas.base) && ((A) < (rtas.base + rtas.size - 16))))206207/*208* Get the error information for errors coming through the209* FWNMI vectors. The pt_regs' r3 will be updated to reflect210* the actual r3 if possible, and a ptr to the error log entry211* will be returned if found.212*213* If the RTAS error is not of the extended type, then we put it in a per214* cpu 64bit buffer. If it is the extended type we use global_mce_data_buf.215*216* The global_mce_data_buf does not have any locks or protection around it,217* if a second machine check comes in, or a system reset is done218* before we have logged the error, then we will get corruption in the219* error log. This is preferable over holding off on calling220* ibm,nmi-interlock which would result in us checkstopping if a221* second machine check did come in.222*/223static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)224{225unsigned long *savep;226struct rtas_error_log *h, *errhdr = NULL;227228if (!VALID_FWNMI_BUFFER(regs->gpr[3])) {229printk(KERN_ERR "FWNMI: corrupt r3 0x%016lx\n", regs->gpr[3]);230return NULL;231}232233savep = __va(regs->gpr[3]);234regs->gpr[3] = savep[0]; /* restore original r3 */235236/* If it isn't an extended log we can use the per cpu 64bit buffer */237h = (struct rtas_error_log *)&savep[1];238if (!h->extended) {239memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64));240errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf);241} else {242int len;243244len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX);245memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX);246memcpy(global_mce_data_buf, h, len);247errhdr = (struct rtas_error_log *)global_mce_data_buf;248}249250return errhdr;251}252253/* Call this when done with the data returned by FWNMI_get_errinfo.254* It will release the saved data area for other CPUs in the255* partition to receive FWNMI errors.256*/257static void fwnmi_release_errinfo(void)258{259int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);260if (ret != 0)261printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret);262}263264int pSeries_system_reset_exception(struct pt_regs *regs)265{266if (fwnmi_active) {267struct rtas_error_log *errhdr = fwnmi_get_errinfo(regs);268if (errhdr) {269/* XXX Should look at FWNMI information */270}271fwnmi_release_errinfo();272}273return 0; /* need to perform reset */274}275276/*277* See if we can recover from a machine check exception.278* This is only called on power4 (or above) and only via279* the Firmware Non-Maskable Interrupts (fwnmi) handler280* which provides the error analysis for us.281*282* Return 1 if corrected (or delivered a signal).283* Return 0 if there is nothing we can do.284*/285static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)286{287int recovered = 0;288289if (!(regs->msr & MSR_RI)) {290/* If MSR_RI isn't set, we cannot recover */291recovered = 0;292293} else if (err->disposition == RTAS_DISP_FULLY_RECOVERED) {294/* Platform corrected itself */295recovered = 1;296297} else if (err->disposition == RTAS_DISP_LIMITED_RECOVERY) {298/* Platform corrected itself but could be degraded */299printk(KERN_ERR "MCE: limited recovery, system may "300"be degraded\n");301recovered = 1;302303} else if (user_mode(regs) && !is_global_init(current) &&304err->severity == RTAS_SEVERITY_ERROR_SYNC) {305306/*307* If we received a synchronous error when in userspace308* kill the task. Firmware may report details of the fail309* asynchronously, so we can't rely on the target and type310* fields being valid here.311*/312printk(KERN_ERR "MCE: uncorrectable error, killing task "313"%s:%d\n", current->comm, current->pid);314315_exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);316recovered = 1;317}318319log_error((char *)err, ERR_TYPE_RTAS_LOG, 0);320321return recovered;322}323324/*325* Handle a machine check.326*327* Note that on Power 4 and beyond Firmware Non-Maskable Interrupts (fwnmi)328* should be present. If so the handler which called us tells us if the329* error was recovered (never true if RI=0).330*331* On hardware prior to Power 4 these exceptions were asynchronous which332* means we can't tell exactly where it occurred and so we can't recover.333*/334int pSeries_machine_check_exception(struct pt_regs *regs)335{336struct rtas_error_log *errp;337338if (fwnmi_active) {339errp = fwnmi_get_errinfo(regs);340fwnmi_release_errinfo();341if (errp && recover_mce(regs, errp))342return 1;343}344345return 0;346}347348349