rfid
        b       .       /* prevent speculative execution */
 
-#ifdef CONFIG_PPC_PSERIES
+#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
 /*
  * Data area reserved for FWNMI option.
  * This address (0x7000) is fixed by the RPA.
        .= 0x7000
        .globl fwnmi_data_area
 fwnmi_data_area:
-#endif /* CONFIG_PPC_PSERIES */
+#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
 
        /* iSeries does not use the FWNMI stuff, so it is safe to put
         * this here, even if we later allow kernels that will boot on
 
 #endif /* CONFIG_PPC_ISERIES */
 
-#ifdef CONFIG_PPC_PSERIES
+#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
+       /* pseries and powernv need to keep the whole page from
+        * 0x7000 to 0x8000 free for use by the firmware
+        */
         . = 0x8000
-#endif /* CONFIG_PPC_PSERIES */
+#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
 
 /*
  * Space for CPU0's segment table.
        .globl initial_stab
 initial_stab:
        .space  4096
+#ifdef CONFIG_PPC_POWERNV
+_GLOBAL(opal_mc_secondary_handler)
+       HMT_MEDIUM
+       SET_SCRATCH0(r13)
+       GET_PACA(r13)
+       clrldi  r3,r3,2
+       tovirt(r3,r3)
+       std     r3,PACA_OPAL_MC_EVT(r13)
+       ld      r13,OPAL_MC_SRR0(r3)
+       mtspr   SPRN_SRR0,r13
+       ld      r13,OPAL_MC_SRR1(r3)
+       mtspr   SPRN_SRR1,r13
+       ld      r3,OPAL_MC_GPR3(r3)
+       GET_SCRATCH0(r13)
+       b       machine_check_pSeries
+#endif /* CONFIG_PPC_POWERNV */
 
 
 static struct device_node *opal_node;
 static DEFINE_SPINLOCK(opal_write_lock);
+extern u64 opal_mc_secondary_handler[];
 
 int __init early_init_dt_scan_opal(unsigned long node,
                                   const char *uname, int depth, void *data)
 {
        const void *basep, *entryp;
        unsigned long basesz, entrysz;
+       u64 glue;
 
        if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
                return 0;
                printk("OPAL V1 detected !\n");
        }
 
+       /* Hookup some exception handlers. We use the fwnmi area at 0x7000
+        * to provide the glue space to OPAL
+        */
+       glue = 0x7000;
+       opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
+                                       __pa(opal_mc_secondary_handler[0]),
+                                       glue);
+       glue += 128;
+       opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
+                                       0, glue);
+       glue += 128;
+       opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
+
        return 1;
 }
 
        return written;
 }
 
+int opal_machine_check(struct pt_regs *regs)
+{
+       struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
+       struct opal_machine_check_event evt;
+       const char *level, *sevstr, *subtype;
+       static const char *opal_mc_ue_types[] = {
+               "Indeterminate",
+               "Instruction fetch",
+               "Page table walk ifetch",
+               "Load/Store",
+               "Page table walk Load/Store",
+       };
+       static const char *opal_mc_slb_types[] = {
+               "Indeterminate",
+               "Parity",
+               "Multihit",
+       };
+       static const char *opal_mc_erat_types[] = {
+               "Indeterminate",
+               "Parity",
+               "Multihit",
+       };
+       static const char *opal_mc_tlb_types[] = {
+               "Indeterminate",
+               "Parity",
+               "Multihit",
+       };
+
+       /* Copy the event structure and release the original */
+       evt = *opal_evt;
+       opal_evt->in_use = 0;
+
+       /* Print things out */
+       if (evt.version != OpalMCE_V1) {
+               pr_err("Machine Check Exception, Unknown event version %d !\n",
+                      evt.version);
+               return 0;
+       }
+       switch(evt.severity) {
+       case OpalMCE_SEV_NO_ERROR:
+               level = KERN_INFO;
+               sevstr = "Harmless";
+               break;
+       case OpalMCE_SEV_WARNING:
+               level = KERN_WARNING;
+               sevstr = "";
+               break;
+       case OpalMCE_SEV_ERROR_SYNC:
+               level = KERN_ERR;
+               sevstr = "Severe";
+               break;
+       case OpalMCE_SEV_FATAL:
+       default:
+               level = KERN_ERR;
+               sevstr = "Fatal";
+               break;
+       }
+
+       printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
+              evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
+              "Recovered" : "[Not recovered");
+       printk("%s  Initiator: %s\n", level,
+              evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
+       switch(evt.error_type) {
+       case OpalMCE_ERROR_TYPE_UE:
+               subtype = evt.u.ue_error.ue_error_type <
+                       ARRAY_SIZE(opal_mc_ue_types) ?
+                       opal_mc_ue_types[evt.u.ue_error.ue_error_type]
+                       : "Unknown";
+               printk("%s  Error type: UE [%s]\n", level, subtype);
+               if (evt.u.ue_error.effective_address_provided)
+                       printk("%s    Effective address: %016llx\n",
+                              level, evt.u.ue_error.effective_address);
+               if (evt.u.ue_error.physical_address_provided)
+                       printk("%s      Physial address: %016llx\n",
+                              level, evt.u.ue_error.physical_address);
+               break;
+       case OpalMCE_ERROR_TYPE_SLB:
+               subtype = evt.u.slb_error.slb_error_type <
+                       ARRAY_SIZE(opal_mc_slb_types) ?
+                       opal_mc_slb_types[evt.u.slb_error.slb_error_type]
+                       : "Unknown";
+               printk("%s  Error type: SLB [%s]\n", level, subtype);
+               if (evt.u.slb_error.effective_address_provided)
+                       printk("%s    Effective address: %016llx\n",
+                              level, evt.u.slb_error.effective_address);
+               break;
+       case OpalMCE_ERROR_TYPE_ERAT:
+               subtype = evt.u.erat_error.erat_error_type <
+                       ARRAY_SIZE(opal_mc_erat_types) ?
+                       opal_mc_erat_types[evt.u.erat_error.erat_error_type]
+                       : "Unknown";
+               printk("%s  Error type: ERAT [%s]\n", level, subtype);
+               if (evt.u.erat_error.effective_address_provided)
+                       printk("%s    Effective address: %016llx\n",
+                              level, evt.u.erat_error.effective_address);
+               break;
+       case OpalMCE_ERROR_TYPE_TLB:
+               subtype = evt.u.tlb_error.tlb_error_type <
+                       ARRAY_SIZE(opal_mc_tlb_types) ?
+                       opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
+                       : "Unknown";
+               printk("%s  Error type: TLB [%s]\n", level, subtype);
+               if (evt.u.tlb_error.effective_address_provided)
+                       printk("%s    Effective address: %016llx\n",
+                              level, evt.u.tlb_error.effective_address);
+               break;
+       default:
+       case OpalMCE_ERROR_TYPE_UNKNOWN:
+               printk("%s  Error type: Unknown\n", level);
+               break;
+       }
+       return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
+}
+
 static irqreturn_t opal_interrupt(int irq, void *data)
 {
        uint64_t events;