Pull acpica into release branch
[pandora-kernel.git] / arch / ia64 / kernel / mca_drv.c
index 3492e32..ca6666b 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com)
  * Copyright (C) 2005 Silicon Graphics, Inc
  * Copyright (C) 2005 Keith Owens <kaos@sgi.com>
+ * Copyright (C) 2006 Russ Anderson <rja@sgi.com>
  */
 #include <linux/config.h>
 #include <linux/types.h>
@@ -61,6 +62,11 @@ typedef enum {
        ISOLATE_NONE
 } isolate_status_t;
 
+typedef enum {
+       MCA_NOT_RECOVERED = 0,
+       MCA_RECOVERED     = 1
+} recovery_status_t;
+
 /*
  *  This pool keeps pointers to the section part of SAL error record
  */
@@ -70,6 +76,18 @@ static struct {
        int          max_idx; /* Maximum index of section pointer list pool */
 } slidx_pool;
 
+static int
+fatal_mca(const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vprintk(fmt, args);
+       va_end(args);
+
+       return MCA_NOT_RECOVERED;
+}
+
 /**
  * mca_page_isolate - isolate a poisoned page in order not to use it later
  * @paddr:     poisoned memory location
@@ -121,10 +139,12 @@ mca_page_isolate(unsigned long paddr)
  */
 
 void
-mca_handler_bh(unsigned long paddr)
+mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr)
 {
-       printk(KERN_DEBUG "OS_MCA: process [pid: %d](%s) encounters MCA.\n",
-               current->pid, current->comm);
+       printk(KERN_ERR "OS_MCA: process [cpu %d, pid: %d, uid: %d, "
+               "iip: %p, psr: 0x%lx,paddr: 0x%lx](%s) encounters MCA.\n",
+               raw_smp_processor_id(), current->pid, current->uid,
+               iip, ipsr, paddr, current->comm);
 
        spin_lock(&mca_bh_lock);
        switch (mca_page_isolate(paddr)) {
@@ -132,7 +152,7 @@ mca_handler_bh(unsigned long paddr)
                printk(KERN_DEBUG "Page isolation: ( %lx ) success.\n", paddr);
                break;
        case ISOLATE_NG:
-               printk(KERN_DEBUG "Page isolation: ( %lx ) failure.\n", paddr);
+               printk(KERN_CRIT "Page isolation: ( %lx ) failure.\n", paddr);
                break;
        default:
                break;
@@ -421,7 +441,7 @@ recover_from_read_error(slidx_table_t *slidx,
 
        /* Is target address valid? */
        if (!pbci->tv)
-               return 0;
+               return fatal_mca(KERN_ALERT "MCA: target address not valid\n");
 
        /*
         * cpu read or memory-mapped io read
@@ -437,22 +457,30 @@ recover_from_read_error(slidx_table_t *slidx,
         *    the process not have any locks of kernel.
         */
 
+       /* Is minstate valid? */
+       if (!peidx_bottom(peidx) || !(peidx_bottom(peidx)->valid.minstate))
+               return fatal_mca(KERN_ALERT "MCA: minstate not valid\n");
        psr1 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_ipsr);
+       psr2 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_xpsr);
 
        /*
         *  Check the privilege level of interrupted context.
         *   If it is user-mode, then terminate affected process.
         */
-       if (psr1->cpl != 0) {
+
+       pmsa = sos->pal_min_state;
+       if (psr1->cpl != 0 ||
+          ((psr2->cpl != 0) && mca_recover_range(pmsa->pmsa_iip))) {
                smei = peidx_bus_check(peidx, 0);
                if (smei->valid.target_identifier) {
                        /*
                         *  setup for resume to bottom half of MCA,
                         * "mca_handler_bhhook"
                         */
-                       pmsa = sos->pal_min_state;
-                       /* pass to bhhook as 1st argument (gr8) */
+                       /* pass to bhhook as argument (gr8, ...) */
                        pmsa->pmsa_gr[8-1] = smei->target_identifier;
+                       pmsa->pmsa_gr[9-1] = pmsa->pmsa_iip;
+                       pmsa->pmsa_gr[10-1] = pmsa->pmsa_ipsr;
                        /* set interrupted return address (but no use) */
                        pmsa->pmsa_br0 = pmsa->pmsa_iip;
                        /* change resume address to bottom half */
@@ -462,14 +490,16 @@ recover_from_read_error(slidx_table_t *slidx,
                        psr2 = (struct ia64_psr *)&pmsa->pmsa_ipsr;
                        psr2->cpl = 0;
                        psr2->ri  = 0;
+                       psr2->bn  = 1;
                        psr2->i  = 0;
 
-                       return 1;
+                       return MCA_RECOVERED;
                }
 
        }
 
-       return 0;
+       return fatal_mca(KERN_ALERT "MCA: kernel context not recovered,"
+                         " iip 0x%lx\n", pmsa->pmsa_iip);
 }
 
 /**
@@ -555,32 +585,39 @@ recover_from_processor_error(int platform, slidx_table_t *slidx,
         * The machine check is corrected.
         */
        if (psp->cm == 1)
-               return 1;
+               return MCA_RECOVERED;
 
        /*
         * The error was not contained.  Software must be reset.
         */
        if (psp->us || psp->ci == 0)
-               return 0;
+               return fatal_mca(KERN_ALERT "MCA: error not contained\n");
 
        /*
-        * If there is no bus error, record is weird but we need not to recover.
+        * The cache check and bus check bits have four possible states
+        *   cc bc
+        *    0  0      Weird record, not recovered
+        *    1  0      Cache error, not recovered
+        *    0  1      I/O error, attempt recovery
+        *    1  1      Memory error, attempt recovery
         */
        if (psp->bc == 0 || pbci == NULL)
-               return 1;
+               return fatal_mca(KERN_ALERT "MCA: No bus check\n");
 
        /*
         * Sorry, we cannot handle so many.
         */
        if (peidx_bus_check_num(peidx) > 1)
-               return 0;
+               return fatal_mca(KERN_ALERT "MCA: Too many bus checks\n");
        /*
         * Well, here is only one bus error.
         */
-       if (pbci->ib || pbci->cc)
-               return 0;
+       if (pbci->ib)
+               return fatal_mca(KERN_ALERT "MCA: Internal Bus error\n");
+       if (pbci->cc)
+               return fatal_mca(KERN_ALERT "MCA: Cache-cache error\n");
        if (pbci->eb && pbci->bsi > 0)
-               return 0;
+               return fatal_mca(KERN_ALERT "MCA: External bus check fatal status\n");
 
        /*
         * This is a local MCA and estimated as recoverble external bus error.
@@ -592,7 +629,7 @@ recover_from_processor_error(int platform, slidx_table_t *slidx,
        /*
         * On account of strange SAL error record, we cannot recover.
         */
-       return 0;
+       return fatal_mca(KERN_ALERT "MCA: Strange SAL record\n");
 }
 
 /**
@@ -621,12 +658,10 @@ mca_try_to_recover(void *rec, struct ia64_sal_os_state *sos)
 
         /* Now, OS can recover when there is one processor error section */
        if (n_proc_err > 1)
-               return 0;
-       else if (n_proc_err == 0) {
+               return fatal_mca(KERN_ALERT "MCA: Too Many Errors\n");
+       else if (n_proc_err == 0)
                /* Weird SAL record ... We need not to recover */
-
-               return 1;
-       }
+               return fatal_mca(KERN_ALERT "MCA: Weird SAL record\n");
 
        /* Make index of processor error section */
        mca_make_peidx((sal_log_processor_info_t*)
@@ -637,7 +672,7 @@ mca_try_to_recover(void *rec, struct ia64_sal_os_state *sos)
 
        /* Check whether MCA is global or not */
        if (is_mca_global(&peidx, &pbci, sos))
-               return 0;
+               return fatal_mca(KERN_ALERT "MCA: global MCA\n");
        
        /* Try to recover a processor error */
        return recover_from_processor_error(platform_err, &slidx, &peidx,