Add ia64 patch.
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / kernel / mca.c
index 5808798..b5d0fcd 100644 (file)
@@ -3,6 +3,9 @@
  * Purpose:    Generic MCA handling layer
  *
  * Updated for latest kernel
+ * Copyright (C) 2003 Hewlett-Packard Co
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ *
  * Copyright (C) 2002 Dell Computer Corporation
  * Copyright (C) Matt Domsch (Matt_Domsch@dell.com)
  *
@@ -18,6 +21,7 @@
  * Copyright (C) 1999 Silicon Graphics, Inc.
  * Copyright (C) Vijay Chander(vijay@engr.sgi.com)
  *
+ * 03/04/15 D. Mosberger Added INIT backtrace support.
  * 02/03/25 M. Domsch  GUID cleanups
  *
  * 02/01/04 J. Hall    Aligned MCA stack to 16 bytes, added platform vs. CPU
@@ -39,6 +43,7 @@
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/kallsyms.h>
 #include <linux/smp_lock.h>
 #include <linux/bootmem.h>
 #include <linux/acpi.h>
@@ -47,6 +52,7 @@
 #include <linux/kernel.h>
 #include <linux/smp.h>
 
+#include <asm/delay.h>
 #include <asm/machvec.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
@@ -139,7 +145,7 @@ ia64_mca_log_sal_error_record(int sal_info_type, int called_from_init)
 
        /* Get the MCA error record */
        if (!ia64_log_get(sal_info_type, (prfunc_t)printk))
-               return platform_err;                 // no record retrieved
+               return platform_err;            /* no record retrieved */
 
        /* TODO:
         * 1. analyze error logs to determine recoverability
@@ -166,7 +172,7 @@ mca_handler_platform (void)
 
 }
 
-void
+irqreturn_t
 ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs)
 {
        IA64_MCA_DEBUG("ia64_mca_cpe_int_handler: received interrupt. CPU:%d vector = %#x\n",
@@ -174,20 +180,186 @@ ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs)
 
        /* Get the CMC error record and log it */
        ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE, 0);
+       return IRQ_HANDLED;
+}
+
+static void
+show_min_state (pal_min_state_area_t *minstate)
+{
+       u64 iip = minstate->pmsa_iip + ((struct ia64_psr *)(&minstate->pmsa_ipsr))->ri;
+       u64 xip = minstate->pmsa_xip + ((struct ia64_psr *)(&minstate->pmsa_xpsr))->ri;
+
+       printk("NaT bits\t%016lx\n", minstate->pmsa_nat_bits);
+       printk("pr\t\t%016lx\n", minstate->pmsa_pr);
+       printk("b0\t\t%016lx ", minstate->pmsa_br0); print_symbol("%s\n", minstate->pmsa_br0);
+       printk("ar.rsc\t\t%016lx\n", minstate->pmsa_rsc);
+       printk("cr.iip\t\t%016lx ", iip); print_symbol("%s\n", iip);
+       printk("cr.ipsr\t\t%016lx\n", minstate->pmsa_ipsr);
+       printk("cr.ifs\t\t%016lx\n", minstate->pmsa_ifs);
+       printk("xip\t\t%016lx ", xip); print_symbol("%s\n", xip);
+       printk("xpsr\t\t%016lx\n", minstate->pmsa_xpsr);
+       printk("xfs\t\t%016lx\n", minstate->pmsa_xfs);
+       printk("b1\t\t%016lx ", minstate->pmsa_br1);
+       print_symbol("%s\n", minstate->pmsa_br1);
+
+       printk("\nstatic registers r0-r15:\n");
+       printk(" r0- 3 %016lx %016lx %016lx %016lx\n",
+              0UL, minstate->pmsa_gr[0], minstate->pmsa_gr[1], minstate->pmsa_gr[2]);
+       printk(" r4- 7 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_gr[3], minstate->pmsa_gr[4],
+              minstate->pmsa_gr[5], minstate->pmsa_gr[6]);
+       printk(" r8-11 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_gr[7], minstate->pmsa_gr[8],
+              minstate->pmsa_gr[9], minstate->pmsa_gr[10]);
+       printk("r12-15 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_gr[11], minstate->pmsa_gr[12],
+              minstate->pmsa_gr[13], minstate->pmsa_gr[14]);
+
+       printk("\nbank 0:\n");
+       printk("r16-19 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank0_gr[0], minstate->pmsa_bank0_gr[1],
+              minstate->pmsa_bank0_gr[2], minstate->pmsa_bank0_gr[3]);
+       printk("r20-23 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank0_gr[4], minstate->pmsa_bank0_gr[5],
+              minstate->pmsa_bank0_gr[6], minstate->pmsa_bank0_gr[7]);
+       printk("r24-27 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank0_gr[8], minstate->pmsa_bank0_gr[9],
+              minstate->pmsa_bank0_gr[10], minstate->pmsa_bank0_gr[11]);
+       printk("r28-31 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank0_gr[12], minstate->pmsa_bank0_gr[13],
+              minstate->pmsa_bank0_gr[14], minstate->pmsa_bank0_gr[15]);
+
+       printk("\nbank 1:\n");
+       printk("r16-19 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank1_gr[0], minstate->pmsa_bank1_gr[1],
+              minstate->pmsa_bank1_gr[2], minstate->pmsa_bank1_gr[3]);
+       printk("r20-23 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank1_gr[4], minstate->pmsa_bank1_gr[5],
+              minstate->pmsa_bank1_gr[6], minstate->pmsa_bank1_gr[7]);
+       printk("r24-27 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank1_gr[8], minstate->pmsa_bank1_gr[9],
+              minstate->pmsa_bank1_gr[10], minstate->pmsa_bank1_gr[11]);
+       printk("r28-31 %016lx %016lx %016lx %016lx\n",
+              minstate->pmsa_bank1_gr[12], minstate->pmsa_bank1_gr[13],
+              minstate->pmsa_bank1_gr[14], minstate->pmsa_bank1_gr[15]);
+}
+
+static void
+fetch_min_state (pal_min_state_area_t *ms, struct pt_regs *pt, struct switch_stack *sw)
+{
+       u64 *dst_banked, *src_banked, bit, shift, nat_bits;
+       int i;
+
+       /*
+        * First, update the pt-regs and switch-stack structures with the contents stored
+        * in the min-state area:
+        */
+       if (((struct ia64_psr *) &ms->pmsa_ipsr)->ic == 0) {
+               pt->cr_ipsr = ms->pmsa_xpsr;
+               pt->cr_iip = ms->pmsa_xip;
+               pt->cr_ifs = ms->pmsa_xfs;
+       } else {
+               pt->cr_ipsr = ms->pmsa_ipsr;
+               pt->cr_iip = ms->pmsa_iip;
+               pt->cr_ifs = ms->pmsa_ifs;
+       }
+       pt->ar_rsc = ms->pmsa_rsc;
+       pt->pr = ms->pmsa_pr;
+       pt->r1 = ms->pmsa_gr[0];
+       pt->r2 = ms->pmsa_gr[1];
+       pt->r3 = ms->pmsa_gr[2];
+       sw->r4 = ms->pmsa_gr[3];
+       sw->r5 = ms->pmsa_gr[4];
+       sw->r6 = ms->pmsa_gr[5];
+       sw->r7 = ms->pmsa_gr[6];
+       pt->r8 = ms->pmsa_gr[7];
+       pt->r9 = ms->pmsa_gr[8];
+       pt->r10 = ms->pmsa_gr[9];
+       pt->r11 = ms->pmsa_gr[10];
+       pt->r12 = ms->pmsa_gr[11];
+       pt->r13 = ms->pmsa_gr[12];
+       pt->r14 = ms->pmsa_gr[13];
+       pt->r15 = ms->pmsa_gr[14];
+       dst_banked = &pt->r16;          /* r16-r31 are contiguous in struct pt_regs */
+       src_banked = ms->pmsa_bank1_gr;
+       for (i = 0; i < 16; ++i)
+               dst_banked[i] = src_banked[i];
+       pt->b0 = ms->pmsa_br0;
+       sw->b1 = ms->pmsa_br1;
+
+       /* construct the NaT bits for the pt-regs structure: */
+#      define PUT_NAT_BIT(dst, addr)                                   \
+       do {                                                            \
+               bit = nat_bits & 1; nat_bits >>= 1;                     \
+               shift = ((unsigned long) addr >> 3) & 0x3f;             \
+               dst = ((dst) & ~(1UL << shift)) | (bit << shift);       \
+       } while (0)
+
+       /* Rotate the saved NaT bits such that bit 0 corresponds to pmsa_gr[0]: */
+       shift = ((unsigned long) &ms->pmsa_gr[0] >> 3) & 0x3f;
+       nat_bits = (ms->pmsa_nat_bits >> shift) | (ms->pmsa_nat_bits << (64 - shift));
+
+       PUT_NAT_BIT(sw->caller_unat, &pt->r1);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r2);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r3);
+       PUT_NAT_BIT(sw->ar_unat, &sw->r4);
+       PUT_NAT_BIT(sw->ar_unat, &sw->r5);
+       PUT_NAT_BIT(sw->ar_unat, &sw->r6);
+       PUT_NAT_BIT(sw->ar_unat, &sw->r7);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r8);  PUT_NAT_BIT(sw->caller_unat, &pt->r9);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r10); PUT_NAT_BIT(sw->caller_unat, &pt->r11);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r12); PUT_NAT_BIT(sw->caller_unat, &pt->r13);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r14); PUT_NAT_BIT(sw->caller_unat, &pt->r15);
+       nat_bits >>= 16;        /* skip over bank0 NaT bits */
+       PUT_NAT_BIT(sw->caller_unat, &pt->r16); PUT_NAT_BIT(sw->caller_unat, &pt->r17);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r18); PUT_NAT_BIT(sw->caller_unat, &pt->r19);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r20); PUT_NAT_BIT(sw->caller_unat, &pt->r21);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r22); PUT_NAT_BIT(sw->caller_unat, &pt->r23);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r24); PUT_NAT_BIT(sw->caller_unat, &pt->r25);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r26); PUT_NAT_BIT(sw->caller_unat, &pt->r27);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r28); PUT_NAT_BIT(sw->caller_unat, &pt->r29);
+       PUT_NAT_BIT(sw->caller_unat, &pt->r30); PUT_NAT_BIT(sw->caller_unat, &pt->r31);
 }
 
-/*
- * This routine will be used to deal with platform specific handling
- * of the init, i.e. drop into the kernel debugger on server machine,
- * or if the processor is part of some parallel machine without a
- * console, then we would call the appropriate debug hooks here.
- */
 void
-init_handler_platform (struct pt_regs *regs)
+init_handler_platform (sal_log_processor_info_t *proc_ptr,
+                      struct pt_regs *pt, struct switch_stack *sw)
 {
+       struct unw_frame_info info;
+
        /* if a kernel debugger is available call it here else just dump the registers */
 
-       show_regs(regs);                /* dump the state info */
+       /*
+        * Wait for a bit.  On some machines (e.g., HP's zx2000 and zx6000, INIT can be
+        * generated via the BMC's command-line interface, but since the console is on the
+        * same serial line, the user will need some time to switch out of the BMC before
+        * the dump begins.
+        */
+       printk("Delaying for 5 seconds...\n");
+       udelay(5*1000000);
+       show_min_state(&SAL_LPI_PSI_INFO(proc_ptr)->min_state_area);
+
+       printk("Backtrace of current task (pid %d, %s)\n", current->pid, current->comm);
+       fetch_min_state(&SAL_LPI_PSI_INFO(proc_ptr)->min_state_area, pt, sw);
+       unw_init_from_interruption(&info, current, pt, sw);
+       ia64_do_show_stack(&info, NULL);
+
+       if (!tasklist_lock.write_lock)
+               read_lock(&tasklist_lock);
+       {
+               struct task_struct *g, *t;
+               do_each_thread (g, t) {
+                       if (t == current)
+                               continue;
+
+                       printk("\nBacktrace of pid %d (%s)\n", t->pid, t->comm);
+                       show_stack(t);
+               } while_each_thread (g, t);
+       }
+       if (!tasklist_lock.write_lock)
+               read_unlock(&tasklist_lock);
+
+       printk("\nINIT dump complete.  Please reboot now.\n");
        while (1);                      /* hang city if no debugger */
 }
 
@@ -263,7 +435,6 @@ ia64_mca_register_cpev (int cpev)
 /*
  * routine to process and prepare to dump min_state_save
  * information for debugging purposes.
- *
  */
 void
 ia64_process_min_state_save (pal_min_state_area_t *pmss)
@@ -272,8 +443,6 @@ ia64_process_min_state_save (pal_min_state_area_t *pmss)
        u64 *tpmss_ptr = (u64 *)pmss;
        u64 *return_min_state_ptr = ia64_mca_min_state_save_info;
 
-       /* dump out the min_state_area information */
-
        for (i=0;i<max;i++) {
 
                /* copy min-state register info for eventual return to PAL */
@@ -676,7 +845,7 @@ ia64_mca_wakeup_all(void)
  *  Inputs  :   None
  *  Outputs :   None
  */
-void
+irqreturn_t
 ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs)
 {
        unsigned long flags;
@@ -699,6 +868,7 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs)
 
        /* Enable all interrupts */
        local_irq_restore(flags);
+       return IRQ_HANDLED;
 }
 
 
@@ -717,10 +887,10 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs)
  *  Outputs :   None
  *
  */
-void
+irqreturn_t
 ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs)
 {
-
+       return IRQ_HANDLED;
 }
 
 /*
@@ -815,7 +985,7 @@ ia64_mca_ucmc_handler(void)
  * Outputs
  *     None
  */
-void
+irqreturn_t
 ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs)
 {
        static unsigned long    cmc_history[CMC_HISTORY_LENGTH];
@@ -832,7 +1002,7 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs)
        if (!cmc_polling_enabled) {
                int i, count = 1; /* we know 1 happened now */
                unsigned long now = jiffies;
-               
+
                for (i = 0; i < CMC_HISTORY_LENGTH; i++) {
                        if (now - cmc_history[i] <= HZ)
                                count++;
@@ -867,12 +1037,12 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs)
                         * something is generating more than we can handle.
                         */
                        printk(KERN_WARNING "ia64_mca_cmc_int_handler: WARNING: Switching to polling CMC handler, error records may be lost\n");
-                       
+
 
                        mod_timer(&cmc_poll_timer, jiffies + CMC_POLL_INTERVAL);
 
                        /* lock already released, get out now */
-                       return;
+                       return IRQ_HANDLED;
                } else {
                        cmc_history[index++] = now;
                        if (index == CMC_HISTORY_LENGTH)
@@ -880,6 +1050,7 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs)
                }
        }
        spin_unlock(&cmc_history_lock);
+       return IRQ_HANDLED;
 }
 
 /*
@@ -944,7 +1115,7 @@ ia64_mca_cmc_int_caller(void *dummy)
 static void
 ia64_mca_cmc_poll (unsigned long dummy)
 {
-       int start_count;
+       unsigned long start_count;
 
        start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CMC);
 
@@ -986,7 +1157,7 @@ ia64_mca_cpe_int_caller(void *dummy)
  *  ia64_mca_cpe_poll
  *
  *     Poll for Corrected Platform Errors (CPEs), dynamically adjust
- *     polling interval based on occurance of an event.
+ *     polling interval based on occurrence of an event.
  *
  * Inputs   :   dummy(unused)
  * Outputs  :   None
@@ -995,7 +1166,7 @@ ia64_mca_cpe_int_caller(void *dummy)
 static void
 ia64_mca_cpe_poll (unsigned long dummy)
 {
-       int start_count;
+       unsigned long start_count;
        static int poll_time = MAX_CPE_POLL_INTERVAL;
 
        start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CPE);
@@ -1062,7 +1233,7 @@ device_initcall(ia64_mca_late_init);
  *
  */
 void
-ia64_init_handler (struct pt_regs *regs)
+ia64_init_handler (struct pt_regs *pt, struct switch_stack *sw)
 {
        sal_log_processor_info_t *proc_ptr;
        ia64_err_rec_t *plog_ptr;
@@ -1089,7 +1260,7 @@ ia64_init_handler (struct pt_regs *regs)
        /* Clear the INIT SAL logs now that they have been saved in the OS buffer */
        ia64_sal_clear_state_info(SAL_INFO_TYPE_INIT);
 
-       init_handler_platform(regs);              /* call platform specific routines */
+       init_handler_platform(proc_ptr, pt, sw);        /* call platform specific routines */
 }
 
 /*
@@ -1112,7 +1283,8 @@ ia64_log_prt_guid (efi_guid_t *p_guid, prfunc_t prfunc)
 static void
 ia64_log_hexdump(unsigned char *p, unsigned long n_ch, prfunc_t prfunc)
 {
-       int i, j;
+       unsigned long i;
+       int j;
 
        if (!p)
                return;
@@ -1960,7 +2132,7 @@ ia64_log_processor_info_print(sal_log_record_header_t *lh, prfunc_t prfunc)
 {
        sal_log_section_hdr_t       *slsh;
        int                         n_sects;
-       int                         ercd_pos;
+       u32                         ercd_pos;
 
        if (!lh)
                return;
@@ -2022,7 +2194,7 @@ ia64_log_platform_info_print (sal_log_record_header_t *lh, prfunc_t prfunc)
 {
        sal_log_section_hdr_t   *slsh;
        int                     n_sects;
-       int                     ercd_pos;
+       u32                     ercd_pos;
        int                     platform_err = 0;
 
        if (!lh)
@@ -2139,7 +2311,8 @@ ia64_log_print(int sal_info_type, prfunc_t prfunc)
        switch(sal_info_type) {
              case SAL_INFO_TYPE_MCA:
                prfunc("+BEGIN HARDWARE ERROR STATE AT MCA\n");
-               platform_err = ia64_log_platform_info_print(IA64_LOG_CURR_BUFFER(sal_info_type), prfunc);
+               platform_err = ia64_log_platform_info_print(IA64_LOG_CURR_BUFFER(sal_info_type),
+                                                           prfunc);
                prfunc("+END HARDWARE ERROR STATE AT MCA\n");
                break;
              case SAL_INFO_TYPE_INIT: