linux/arch/powerpc/platforms/pseries/ras.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2001 Dave Engebretsen IBM Corporation
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  17 */
  18
  19/* Change Activity:
  20 * 2001/09/21 : engebret : Created with minimal EPOW and HW exception support.
  21 * End Change Activity
  22 */
  23
  24#include <linux/errno.h>
  25#include <linux/threads.h>
  26#include <linux/kernel_stat.h>
  27#include <linux/signal.h>
  28#include <linux/sched.h>
  29#include <linux/ioport.h>
  30#include <linux/interrupt.h>
  31#include <linux/timex.h>
  32#include <linux/init.h>
  33#include <linux/delay.h>
  34#include <linux/irq.h>
  35#include <linux/random.h>
  36#include <linux/sysrq.h>
  37#include <linux/bitops.h>
  38
  39#include <asm/uaccess.h>
  40#include <asm/system.h>
  41#include <asm/io.h>
  42#include <asm/pgtable.h>
  43#include <asm/irq.h>
  44#include <asm/cache.h>
  45#include <asm/prom.h>
  46#include <asm/ptrace.h>
  47#include <asm/machdep.h>
  48#include <asm/rtas.h>
  49#include <asm/udbg.h>
  50#include <asm/firmware.h>
  51
  52#include "pseries.h"
  53
  54static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
  55static DEFINE_SPINLOCK(ras_log_buf_lock);
  56
  57static char global_mce_data_buf[RTAS_ERROR_LOG_MAX];
  58static DEFINE_PER_CPU(__u64, mce_data_buf);
  59
  60static int ras_get_sensor_state_token;
  61static int ras_check_exception_token;
  62
  63#define EPOW_SENSOR_TOKEN       9
  64#define EPOW_SENSOR_INDEX       0
  65
  66static irqreturn_t ras_epow_interrupt(int irq, void *dev_id);
  67static irqreturn_t ras_error_interrupt(int irq, void *dev_id);
  68
  69
  70/*
  71 * Initialize handlers for the set of interrupts caused by hardware errors
  72 * and power system events.
  73 */
  74static int __init init_ras_IRQ(void)
  75{
  76        struct device_node *np;
  77
  78        ras_get_sensor_state_token = rtas_token("get-sensor-state");
  79        ras_check_exception_token = rtas_token("check-exception");
  80
  81        /* Internal Errors */
  82        np = of_find_node_by_path("/event-sources/internal-errors");
  83        if (np != NULL) {
  84                request_event_sources_irqs(np, ras_error_interrupt,
  85                                           "RAS_ERROR");
  86                of_node_put(np);
  87        }
  88
  89        /* EPOW Events */
  90        np = of_find_node_by_path("/event-sources/epow-events");
  91        if (np != NULL) {
  92                request_event_sources_irqs(np, ras_epow_interrupt, "RAS_EPOW");
  93                of_node_put(np);
  94        }
  95
  96        return 0;
  97}
  98__initcall(init_ras_IRQ);
  99
 100/*
 101 * Handle power subsystem events (EPOW).
 102 *
 103 * Presently we just log the event has occurred.  This should be fixed
 104 * to examine the type of power failure and take appropriate action where
 105 * the time horizon permits something useful to be done.
 106 */
 107static irqreturn_t ras_epow_interrupt(int irq, void *dev_id)
 108{
 109        int status = 0xdeadbeef;
 110        int state = 0;
 111        int critical;
 112
 113        status = rtas_call(ras_get_sensor_state_token, 2, 2, &state,
 114                           EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX);
 115
 116        if (state > 3)
 117                critical = 1;  /* Time Critical */
 118        else
 119                critical = 0;
 120
 121        spin_lock(&ras_log_buf_lock);
 122
 123        status = rtas_call(ras_check_exception_token, 6, 1, NULL,
 124                           RTAS_VECTOR_EXTERNAL_INTERRUPT,
 125                           virq_to_hw(irq),
 126                           RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS,
 127                           critical, __pa(&ras_log_buf),
 128                                rtas_get_error_log_max());
 129
 130        udbg_printf("EPOW <0x%lx 0x%x 0x%x>\n",
 131                    *((unsigned long *)&ras_log_buf), status, state);
 132        printk(KERN_WARNING "EPOW <0x%lx 0x%x 0x%x>\n",
 133               *((unsigned long *)&ras_log_buf), status, state);
 134
 135        /* format and print the extended information */
 136        log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, 0);
 137
 138        spin_unlock(&ras_log_buf_lock);
 139        return IRQ_HANDLED;
 140}
 141
 142/*
 143 * Handle hardware error interrupts.
 144 *
 145 * RTAS check-exception is called to collect data on the exception.  If
 146 * the error is deemed recoverable, we log a warning and return.
 147 * For nonrecoverable errors, an error is logged and we stop all processing
 148 * as quickly as possible in order to prevent propagation of the failure.
 149 */
 150static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
 151{
 152        struct rtas_error_log *rtas_elog;
 153        int status = 0xdeadbeef;
 154        int fatal;
 155
 156        spin_lock(&ras_log_buf_lock);
 157
 158        status = rtas_call(ras_check_exception_token, 6, 1, NULL,
 159                           RTAS_VECTOR_EXTERNAL_INTERRUPT,
 160                           virq_to_hw(irq),
 161                           RTAS_INTERNAL_ERROR, 1 /*Time Critical */,
 162                           __pa(&ras_log_buf),
 163                                rtas_get_error_log_max());
 164
 165        rtas_elog = (struct rtas_error_log *)ras_log_buf;
 166
 167        if ((status == 0) && (rtas_elog->severity >= RTAS_SEVERITY_ERROR_SYNC))
 168                fatal = 1;
 169        else
 170                fatal = 0;
 171
 172        /* format and print the extended information */
 173        log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, fatal);
 174
 175        if (fatal) {
 176                udbg_printf("Fatal HW Error <0x%lx 0x%x>\n",
 177                            *((unsigned long *)&ras_log_buf), status);
 178                printk(KERN_EMERG "Error: Fatal hardware error <0x%lx 0x%x>\n",
 179                       *((unsigned long *)&ras_log_buf), status);
 180
 181#ifndef DEBUG_RTAS_POWER_OFF
 182                /* Don't actually power off when debugging so we can test
 183                 * without actually failing while injecting errors.
 184                 * Error data will not be logged to syslog.
 185                 */
 186                ppc_md.power_off();
 187#endif
 188        } else {
 189                udbg_printf("Recoverable HW Error <0x%lx 0x%x>\n",
 190                            *((unsigned long *)&ras_log_buf), status);
 191                printk(KERN_WARNING
 192                       "Warning: Recoverable hardware error <0x%lx 0x%x>\n",
 193                       *((unsigned long *)&ras_log_buf), status);
 194        }
 195
 196        spin_unlock(&ras_log_buf_lock);
 197        return IRQ_HANDLED;
 198}
 199
 200/*
 201 * Some versions of FWNMI place the buffer inside the 4kB page starting at
 202 * 0x7000. Other versions place it inside the rtas buffer. We check both.
 203 */
 204#define VALID_FWNMI_BUFFER(A) \
 205        ((((A) >= 0x7000) && ((A) < 0x7ff0)) || \
 206        (((A) >= rtas.base) && ((A) < (rtas.base + rtas.size - 16))))
 207
 208/*
 209 * Get the error information for errors coming through the
 210 * FWNMI vectors.  The pt_regs' r3 will be updated to reflect
 211 * the actual r3 if possible, and a ptr to the error log entry
 212 * will be returned if found.
 213 *
 214 * If the RTAS error is not of the extended type, then we put it in a per
 215 * cpu 64bit buffer. If it is the extended type we use global_mce_data_buf.
 216 *
 217 * The global_mce_data_buf does not have any locks or protection around it,
 218 * if a second machine check comes in, or a system reset is done
 219 * before we have logged the error, then we will get corruption in the
 220 * error log.  This is preferable over holding off on calling
 221 * ibm,nmi-interlock which would result in us checkstopping if a
 222 * second machine check did come in.
 223 */
 224static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
 225{
 226        unsigned long *savep;
 227        struct rtas_error_log *h, *errhdr = NULL;
 228
 229        if (!VALID_FWNMI_BUFFER(regs->gpr[3])) {
 230                printk(KERN_ERR "FWNMI: corrupt r3 0x%016lx\n", regs->gpr[3]);
 231                return NULL;
 232        }
 233
 234        savep = __va(regs->gpr[3]);
 235        regs->gpr[3] = savep[0];        /* restore original r3 */
 236
 237        /* If it isn't an extended log we can use the per cpu 64bit buffer */
 238        h = (struct rtas_error_log *)&savep[1];
 239        if (!h->extended) {
 240                memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64));
 241                errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf);
 242        } else {
 243                int len;
 244
 245                len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX);
 246                memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
 247                memcpy(global_mce_data_buf, h, len);
 248                errhdr = (struct rtas_error_log *)global_mce_data_buf;
 249        }
 250
 251        return errhdr;
 252}
 253
 254/* Call this when done with the data returned by FWNMI_get_errinfo.
 255 * It will release the saved data area for other CPUs in the
 256 * partition to receive FWNMI errors.
 257 */
 258static void fwnmi_release_errinfo(void)
 259{
 260        int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
 261        if (ret != 0)
 262                printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret);
 263}
 264
 265int pSeries_system_reset_exception(struct pt_regs *regs)
 266{
 267        if (fwnmi_active) {
 268                struct rtas_error_log *errhdr = fwnmi_get_errinfo(regs);
 269                if (errhdr) {
 270                        /* XXX Should look at FWNMI information */
 271                }
 272                fwnmi_release_errinfo();
 273        }
 274        return 0; /* need to perform reset */
 275}
 276
 277/*
 278 * See if we can recover from a machine check exception.
 279 * This is only called on power4 (or above) and only via
 280 * the Firmware Non-Maskable Interrupts (fwnmi) handler
 281 * which provides the error analysis for us.
 282 *
 283 * Return 1 if corrected (or delivered a signal).
 284 * Return 0 if there is nothing we can do.
 285 */
 286static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)
 287{
 288        int recovered = 0;
 289
 290        if (!(regs->msr & MSR_RI)) {
 291                /* If MSR_RI isn't set, we cannot recover */
 292                recovered = 0;
 293
 294        } else if (err->disposition == RTAS_DISP_FULLY_RECOVERED) {
 295                /* Platform corrected itself */
 296                recovered = 1;
 297
 298        } else if (err->disposition == RTAS_DISP_LIMITED_RECOVERY) {
 299                /* Platform corrected itself but could be degraded */
 300                printk(KERN_ERR "MCE: limited recovery, system may "
 301                       "be degraded\n");
 302                recovered = 1;
 303
 304        } else if (user_mode(regs) && !is_global_init(current) &&
 305                   err->severity == RTAS_SEVERITY_ERROR_SYNC) {
 306
 307                /*
 308                 * If we received a synchronous error when in userspace
 309                 * kill the task. Firmware may report details of the fail
 310                 * asynchronously, so we can't rely on the target and type
 311                 * fields being valid here.
 312                 */
 313                printk(KERN_ERR "MCE: uncorrectable error, killing task "
 314                       "%s:%d\n", current->comm, current->pid);
 315
 316                _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);
 317                recovered = 1;
 318        }
 319
 320        log_error((char *)err, ERR_TYPE_RTAS_LOG, 0);
 321
 322        return recovered;
 323}
 324
 325/*
 326 * Handle a machine check.
 327 *
 328 * Note that on Power 4 and beyond Firmware Non-Maskable Interrupts (fwnmi)
 329 * should be present.  If so the handler which called us tells us if the
 330 * error was recovered (never true if RI=0).
 331 *
 332 * On hardware prior to Power 4 these exceptions were asynchronous which
 333 * means we can't tell exactly where it occurred and so we can't recover.
 334 */
 335int pSeries_machine_check_exception(struct pt_regs *regs)
 336{
 337        struct rtas_error_log *errp;
 338
 339        if (fwnmi_active) {
 340                errp = fwnmi_get_errinfo(regs);
 341                fwnmi_release_errinfo();
 342                if (errp && recover_mce(regs, errp))
 343                        return 1;
 344        }
 345
 346        return 0;
 347}
 348