]> git.ipfire.org Git - thirdparty/qemu.git/blobdiff - hw/ppc/spapr_events.c
ppc/spapr: Set the effective address provided flag in mc error log.
[thirdparty/qemu.git] / hw / ppc / spapr_events.c
index 32719a1b72d03fa2440973658b1b5b3067ad579e..a4a540f43d77bb39eab6d5b7945a523f55f72671 100644 (file)
  * THE SOFTWARE.
  *
  */
+
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "cpu.h"
-#include "sysemu/sysemu.h"
-#include "hw/qdev.h"
 #include "sysemu/device_tree.h"
+#include "sysemu/runstate.h"
 
 #include "hw/ppc/fdt.h"
 #include "hw/ppc/spapr.h"
 #include "hw/ppc/spapr_vio.h"
 #include "hw/pci/pci.h"
+#include "hw/irq.h"
 #include "hw/pci-host/spapr.h"
 #include "hw/ppc/spapr_drc.h"
 #include "qemu/help_option.h"
 #include "qemu/bcd.h"
+#include "qemu/main-loop.h"
 #include "hw/ppc/spapr_ovec.h"
 #include <libfdt.h>
+#include "migration/blocker.h"
 
 #define RTAS_LOG_VERSION_MASK                   0xff000000
 #define   RTAS_LOG_VERSION_6                    0x06000000
@@ -193,6 +196,7 @@ struct rtas_event_log_v6_hp {
 #define RTAS_LOG_V6_HP_TYPE_SLOT                         3
 #define RTAS_LOG_V6_HP_TYPE_PHB                          4
 #define RTAS_LOG_V6_HP_TYPE_PCI                          5
+#define RTAS_LOG_V6_HP_TYPE_PMEM                         6
     uint8_t hotplug_action;
 #define RTAS_LOG_V6_HP_ACTION_ADD                        1
 #define RTAS_LOG_V6_HP_ACTION_REMOVE                     2
@@ -212,6 +216,112 @@ struct hp_extended_log {
     struct rtas_event_log_v6_hp hp;
 } QEMU_PACKED;
 
+struct rtas_event_log_v6_mc {
+#define RTAS_LOG_V6_SECTION_ID_MC                   0x4D43 /* MC */
+    struct rtas_event_log_v6_section_header hdr;
+    uint32_t fru_id;
+    uint32_t proc_id;
+    uint8_t error_type;
+#define RTAS_LOG_V6_MC_TYPE_UE                           0
+#define RTAS_LOG_V6_MC_TYPE_SLB                          1
+#define RTAS_LOG_V6_MC_TYPE_ERAT                         2
+#define RTAS_LOG_V6_MC_TYPE_TLB                          4
+#define RTAS_LOG_V6_MC_TYPE_D_CACHE                      5
+#define RTAS_LOG_V6_MC_TYPE_I_CACHE                      7
+    uint8_t sub_err_type;
+#define RTAS_LOG_V6_MC_UE_INDETERMINATE                  0
+#define RTAS_LOG_V6_MC_UE_IFETCH                         1
+#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH         2
+#define RTAS_LOG_V6_MC_UE_LOAD_STORE                     3
+#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE     4
+#define RTAS_LOG_V6_MC_SLB_PARITY                        0
+#define RTAS_LOG_V6_MC_SLB_MULTIHIT                      1
+#define RTAS_LOG_V6_MC_SLB_INDETERMINATE                 2
+#define RTAS_LOG_V6_MC_ERAT_PARITY                       1
+#define RTAS_LOG_V6_MC_ERAT_MULTIHIT                     2
+#define RTAS_LOG_V6_MC_ERAT_INDETERMINATE                3
+#define RTAS_LOG_V6_MC_TLB_PARITY                        1
+#define RTAS_LOG_V6_MC_TLB_MULTIHIT                      2
+#define RTAS_LOG_V6_MC_TLB_INDETERMINATE                 3
+/*
+ * Per PAPR,
+ * For UE error type, set bit 1 of sub_err_type to indicate effective addr is
+ * provided. For other error types (SLB/ERAT/TLB), set bit 0 to indicate
+ * same.
+ */
+#define RTAS_LOG_V6_MC_UE_EA_ADDR_PROVIDED               0x40
+#define RTAS_LOG_V6_MC_EA_ADDR_PROVIDED                  0x80
+    uint8_t reserved_1[6];
+    uint64_t effective_address;
+    uint64_t logical_address;
+} QEMU_PACKED;
+
+struct mc_extended_log {
+    struct rtas_event_log_v6 v6hdr;
+    struct rtas_event_log_v6_mc mc;
+} QEMU_PACKED;
+
+struct MC_ierror_table {
+    unsigned long srr1_mask;
+    unsigned long srr1_value;
+    bool nip_valid; /* nip is a valid indicator of faulting address */
+    uint8_t error_type;
+    uint8_t error_subtype;
+    unsigned int initiator;
+    unsigned int severity;
+};
+
+static const struct MC_ierror_table mc_ierror_table[] = {
+{ 0x00000000081c0000, 0x0000000000040000, true,
+  RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_IFETCH,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000000081c0000, 0x0000000000080000, true,
+  RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000000081c0000, 0x00000000000c0000, true,
+  RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000000081c0000, 0x0000000000100000, true,
+  RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000000081c0000, 0x0000000000140000, true,
+  RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000000081c0000, 0x0000000000180000, true,
+  RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } };
+
+struct MC_derror_table {
+    unsigned long dsisr_value;
+    bool dar_valid; /* dar is a valid indicator of faulting address */
+    uint8_t error_type;
+    uint8_t error_subtype;
+    unsigned int initiator;
+    unsigned int severity;
+};
+
+static const struct MC_derror_table mc_derror_table[] = {
+{ 0x00008000, false,
+  RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_LOAD_STORE,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00004000, true,
+  RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000800, true,
+  RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000400, true,
+  RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000080, true,
+  RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT,  /* Before PARITY */
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
+{ 0x00000100, true,
+  RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY,
+  RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } };
+
+#define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42))
+
 typedef enum EventClass {
     EVENT_CLASS_INTERNAL_ERRORS     = 0,
     EVENT_CLASS_EPOW                = 1,
@@ -229,18 +339,18 @@ static const char * const event_names[EVENT_CLASS_MAX] = {
     [EVENT_CLASS_IO]                    = "ibm,io-events",
 };
 
-struct sPAPREventSource {
+struct SpaprEventSource {
     int irq;
     uint32_t mask;
     bool enabled;
 };
 
-static sPAPREventSource *spapr_event_sources_new(void)
+static SpaprEventSource *spapr_event_sources_new(void)
 {
-    return g_new0(sPAPREventSource, EVENT_CLASS_MAX);
+    return g_new0(SpaprEventSource, EVENT_CLASS_MAX);
 }
 
-static void spapr_event_sources_register(sPAPREventSource *event_sources,
+static void spapr_event_sources_register(SpaprEventSource *event_sources,
                                         EventClassIndex index, int irq)
 {
     /* we only support 1 irq per event class at the moment */
@@ -251,8 +361,8 @@ static void spapr_event_sources_register(sPAPREventSource *event_sources,
     event_sources[index].enabled = true;
 }
 
-static const sPAPREventSource *
-spapr_event_sources_get_source(sPAPREventSource *event_sources,
+static const SpaprEventSource *
+spapr_event_sources_get_source(SpaprEventSource *event_sources,
                                EventClassIndex index)
 {
     g_assert(index < EVENT_CLASS_MAX);
@@ -261,11 +371,11 @@ spapr_event_sources_get_source(sPAPREventSource *event_sources,
     return &event_sources[index];
 }
 
-void spapr_dt_events(sPAPRMachineState *spapr, void *fdt)
+void spapr_dt_events(SpaprMachineState *spapr, void *fdt)
 {
     uint32_t irq_ranges[EVENT_CLASS_MAX * 2];
     int i, count = 0, event_sources;
-    sPAPREventSource *events = spapr->event_sources;
+    SpaprEventSource *events = spapr->event_sources;
 
     g_assert(events);
 
@@ -274,7 +384,7 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt)
     for (i = 0, count = 0; i < EVENT_CLASS_MAX; i++) {
         int node_offset;
         uint32_t interrupts[2];
-        const sPAPREventSource *source =
+        const SpaprEventSource *source =
             spapr_event_sources_get_source(events, i);
         const char *source_name = event_names[i];
 
@@ -282,7 +392,7 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt)
             continue;
         }
 
-        spapr_dt_xics_irq(interrupts, source->irq, false);
+        spapr_dt_irq(interrupts, source->irq, false);
 
         _FDT(node_offset = fdt_add_subnode(fdt, event_sources, source_name));
         _FDT(fdt_setprop(fdt, node_offset, "interrupts", interrupts,
@@ -298,10 +408,10 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt)
                       irq_ranges, count * sizeof(uint32_t))));
 }
 
-static const sPAPREventSource *
-rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type)
+static const SpaprEventSource *
+rtas_event_log_to_source(SpaprMachineState *spapr, int log_type)
 {
-    const sPAPREventSource *source;
+    const SpaprEventSource *source;
 
     g_assert(spapr->event_sources);
 
@@ -313,7 +423,7 @@ rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type)
             g_assert(source->enabled);
             break;
         }
-        /* fall back to epow for legacy hotplug interrupt source */
+        /* fall through back to epow for legacy hotplug interrupt source */
     case RTAS_LOG_TYPE_EPOW:
         source = spapr_event_sources_get_source(spapr->event_sources,
                                                 EVENT_CLASS_EPOW);
@@ -325,9 +435,9 @@ rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type)
     return source;
 }
 
-static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type)
+static int rtas_event_log_to_irq(SpaprMachineState *spapr, int log_type)
 {
-    const sPAPREventSource *source;
+    const SpaprEventSource *source;
 
     source = rtas_event_log_to_source(spapr, log_type);
     g_assert(source);
@@ -336,27 +446,28 @@ static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type)
     return source->irq;
 }
 
-static uint32_t spapr_event_log_entry_type(sPAPREventLogEntry *entry)
+static uint32_t spapr_event_log_entry_type(SpaprEventLogEntry *entry)
 {
     return entry->summary & RTAS_LOG_TYPE_MASK;
 }
 
-static void rtas_event_log_queue(sPAPRMachineState *spapr,
-                                 sPAPREventLogEntry *entry)
+static void rtas_event_log_queue(SpaprMachineState *spapr,
+                                 SpaprEventLogEntry *entry)
 {
     QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next);
 }
 
-static sPAPREventLogEntry *rtas_event_log_dequeue(sPAPRMachineState *spapr,
+static SpaprEventLogEntry *rtas_event_log_dequeue(SpaprMachineState *spapr,
                                                   uint32_t event_mask)
 {
-    sPAPREventLogEntry *entry = NULL;
+    SpaprEventLogEntry *entry = NULL;
 
     QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
-        const sPAPREventSource *source =
+        const SpaprEventSource *source =
             rtas_event_log_to_source(spapr,
                                      spapr_event_log_entry_type(entry));
 
+        g_assert(source);
         if (source->mask & event_mask) {
             break;
         }
@@ -371,11 +482,11 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(sPAPRMachineState *spapr,
 
 static bool rtas_event_log_contains(uint32_t event_mask)
 {
-    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
-    sPAPREventLogEntry *entry = NULL;
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    SpaprEventLogEntry *entry = NULL;
 
     QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
-        const sPAPREventSource *source =
+        const SpaprEventSource *source =
             rtas_event_log_to_source(spapr,
                                      spapr_event_log_entry_type(entry));
 
@@ -401,7 +512,7 @@ static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
 static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
                              int section_count)
 {
-    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
     struct tm tm;
     int year;
 
@@ -424,15 +535,15 @@ static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
 
 static void spapr_powerdown_req(Notifier *n, void *opaque)
 {
-    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
-    sPAPREventLogEntry *entry;
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    SpaprEventLogEntry *entry;
     struct rtas_event_log_v6 *v6hdr;
     struct rtas_event_log_v6_maina *maina;
     struct rtas_event_log_v6_mainb *mainb;
     struct rtas_event_log_v6_epow *epow;
     struct epow_extended_log *new_epow;
 
-    entry = g_new(sPAPREventLogEntry, 1);
+    entry = g_new(SpaprEventLogEntry, 1);
     new_epow = g_malloc0(sizeof(*new_epow));
     entry->extended_log = new_epow;
 
@@ -473,18 +584,18 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
 }
 
 static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
-                                    sPAPRDRConnectorType drc_type,
+                                    SpaprDrcType drc_type,
                                     union drc_identifier *drc_id)
 {
-    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
-    sPAPREventLogEntry *entry;
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    SpaprEventLogEntry *entry;
     struct hp_extended_log *new_hp;
     struct rtas_event_log_v6 *v6hdr;
     struct rtas_event_log_v6_maina *maina;
     struct rtas_event_log_v6_mainb *mainb;
     struct rtas_event_log_v6_hp *hp;
 
-    entry = g_new(sPAPREventLogEntry, 1);
+    entry = g_new(SpaprEventLogEntry, 1);
     new_hp = g_malloc0(sizeof(struct hp_extended_log));
     entry->extended_log = new_hp;
 
@@ -526,6 +637,12 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
     case SPAPR_DR_CONNECTOR_TYPE_CPU:
         hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU;
         break;
+    case SPAPR_DR_CONNECTOR_TYPE_PHB:
+        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB;
+        break;
+    case SPAPR_DR_CONNECTOR_TYPE_PMEM:
+        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PMEM;
+        break;
     default:
         /* we shouldn't be signaling hotplug events for resources
          * that don't support them
@@ -555,9 +672,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
                    rtas_event_log_to_irq(spapr, RTAS_LOG_TYPE_HOTPLUG)));
 }
 
-void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
+void spapr_hotplug_req_add_by_index(SpaprDrc *drc)
 {
-    sPAPRDRConnectorType drc_type = spapr_drc_type(drc);
+    SpaprDrcType drc_type = spapr_drc_type(drc);
     union drc_identifier drc_id;
 
     drc_id.index = spapr_drc_index(drc);
@@ -565,9 +682,9 @@ void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
                             RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
 }
 
-void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc)
+void spapr_hotplug_req_remove_by_index(SpaprDrc *drc)
 {
-    sPAPRDRConnectorType drc_type = spapr_drc_type(drc);
+    SpaprDrcType drc_type = spapr_drc_type(drc);
     union drc_identifier drc_id;
 
     drc_id.index = spapr_drc_index(drc);
@@ -575,7 +692,7 @@ void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc)
                             RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
 }
 
-void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
+void spapr_hotplug_req_add_by_count(SpaprDrcType drc_type,
                                        uint32_t count)
 {
     union drc_identifier drc_id;
@@ -585,7 +702,7 @@ void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
                             RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
 }
 
-void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
+void spapr_hotplug_req_remove_by_count(SpaprDrcType drc_type,
                                           uint32_t count)
 {
     union drc_identifier drc_id;
@@ -595,7 +712,7 @@ void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
                             RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
 }
 
-void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
+void spapr_hotplug_req_add_by_count_indexed(SpaprDrcType drc_type,
                                             uint32_t count, uint32_t index)
 {
     union drc_identifier drc_id;
@@ -606,7 +723,7 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
                             RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
 }
 
-void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
+void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type,
                                                uint32_t count, uint32_t index)
 {
     union drc_identifier drc_id;
@@ -617,14 +734,183 @@ void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
                             RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
 }
 
-static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+static void spapr_mc_set_ea_provided_flag(struct mc_extended_log *ext_elog)
+{
+    switch (ext_elog->mc.error_type) {
+    case RTAS_LOG_V6_MC_TYPE_UE:
+        ext_elog->mc.sub_err_type |= RTAS_LOG_V6_MC_UE_EA_ADDR_PROVIDED;
+        break;
+    case RTAS_LOG_V6_MC_TYPE_SLB:
+    case RTAS_LOG_V6_MC_TYPE_ERAT:
+    case RTAS_LOG_V6_MC_TYPE_TLB:
+        ext_elog->mc.sub_err_type |= RTAS_LOG_V6_MC_EA_ADDR_PROVIDED;
+        break;
+    default:
+        break;
+    }
+}
+
+static uint32_t spapr_mce_get_elog_type(PowerPCCPU *cpu, bool recovered,
+                                        struct mc_extended_log *ext_elog)
+{
+    int i;
+    CPUPPCState *env = &cpu->env;
+    uint32_t summary;
+    uint64_t dsisr = env->spr[SPR_DSISR];
+
+    summary = RTAS_LOG_VERSION_6 | RTAS_LOG_OPTIONAL_PART_PRESENT;
+    if (recovered) {
+        summary |= RTAS_LOG_DISPOSITION_FULLY_RECOVERED;
+    } else {
+        summary |= RTAS_LOG_DISPOSITION_NOT_RECOVERED;
+    }
+
+    if (SRR1_MC_LOADSTORE(env->spr[SPR_SRR1])) {
+        for (i = 0; i < ARRAY_SIZE(mc_derror_table); i++) {
+            if (!(dsisr & mc_derror_table[i].dsisr_value)) {
+                continue;
+            }
+
+            ext_elog->mc.error_type = mc_derror_table[i].error_type;
+            ext_elog->mc.sub_err_type = mc_derror_table[i].error_subtype;
+            if (mc_derror_table[i].dar_valid) {
+                ext_elog->mc.effective_address = cpu_to_be64(env->spr[SPR_DAR]);
+                spapr_mc_set_ea_provided_flag(ext_elog);
+            }
+
+            summary |= mc_derror_table[i].initiator
+                        | mc_derror_table[i].severity;
+
+            return summary;
+        }
+    } else {
+        for (i = 0; i < ARRAY_SIZE(mc_ierror_table); i++) {
+            if ((env->spr[SPR_SRR1] & mc_ierror_table[i].srr1_mask) !=
+                    mc_ierror_table[i].srr1_value) {
+                continue;
+            }
+
+            ext_elog->mc.error_type = mc_ierror_table[i].error_type;
+            ext_elog->mc.sub_err_type = mc_ierror_table[i].error_subtype;
+            if (mc_ierror_table[i].nip_valid) {
+                ext_elog->mc.effective_address = cpu_to_be64(env->nip);
+                spapr_mc_set_ea_provided_flag(ext_elog);
+            }
+
+            summary |= mc_ierror_table[i].initiator
+                        | mc_ierror_table[i].severity;
+
+            return summary;
+        }
+    }
+
+    summary |= RTAS_LOG_INITIATOR_CPU;
+    return summary;
+}
+
+static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered)
+{
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    uint64_t rtas_addr;
+    struct rtas_error_log log;
+    struct mc_extended_log *ext_elog;
+    uint32_t summary;
+
+    ext_elog = g_malloc0(sizeof(*ext_elog));
+    summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog);
+
+    log.summary = cpu_to_be32(summary);
+    log.extended_length = cpu_to_be32(sizeof(*ext_elog));
+
+    spapr_init_v6hdr(&ext_elog->v6hdr);
+    ext_elog->mc.hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MC);
+    ext_elog->mc.hdr.section_length =
+                    cpu_to_be16(sizeof(struct rtas_event_log_v6_mc));
+    ext_elog->mc.hdr.section_version = 1;
+
+    /* get rtas addr from fdt */
+    rtas_addr = spapr_get_rtas_addr();
+    if (!rtas_addr) {
+        qemu_system_guest_panicked(NULL);
+        g_free(ext_elog);
+        return;
+    }
+
+    stq_be_phys(&address_space_memory, rtas_addr + RTAS_ERROR_LOG_OFFSET,
+                env->gpr[3]);
+    cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET +
+                              sizeof(env->gpr[3]), &log, sizeof(log));
+    cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET +
+                              sizeof(env->gpr[3]) + sizeof(log), ext_elog,
+                              sizeof(*ext_elog));
+    g_free(ext_elog);
+
+    env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET;
+
+    ppc_cpu_do_fwnmi_machine_check(cs, spapr->fwnmi_machine_check_addr);
+}
+
+void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
+{
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    CPUState *cs = CPU(cpu);
+    int ret;
+    Error *local_err = NULL;
+
+    if (spapr->fwnmi_machine_check_addr == -1) {
+        /*
+         * This implies that we have hit a machine check either when the
+         * guest has not registered FWNMI (i.e., "ibm,nmi-register" not
+         * called) or between system reset and "ibm,nmi-register".
+         * Fall back to the old machine check behavior in such cases.
+         */
+        cs->exception_index = POWERPC_EXCP_MCHECK;
+        ppc_cpu_do_interrupt(cs);
+        return;
+    }
+
+    while (spapr->fwnmi_machine_check_interlock != -1) {
+        /*
+         * Check whether the same CPU got machine check error
+         * while still handling the mc error (i.e., before
+         * that CPU called "ibm,nmi-interlock")
+         */
+        if (spapr->fwnmi_machine_check_interlock == cpu->vcpu_id) {
+            qemu_system_guest_panicked(NULL);
+            return;
+        }
+        qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond);
+        /* Meanwhile if the system is reset, then just return */
+        if (spapr->fwnmi_machine_check_addr == -1) {
+            return;
+        }
+    }
+
+    ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, &local_err);
+    if (ret == -EBUSY) {
+        /*
+         * We don't want to abort so we let the migration to continue.
+         * In a rare case, the machine check handler will run on the target.
+         * Though this is not preferable, it is better than aborting
+         * the migration or killing the VM.
+         */
+        warn_report("Received a fwnmi while migration was in progress");
+    }
+
+    spapr->fwnmi_machine_check_interlock = cpu->vcpu_id;
+    spapr_mce_dispatch_elog(cpu, recovered);
+}
+
+static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr,
                             uint32_t token, uint32_t nargs,
                             target_ulong args,
                             uint32_t nret, target_ulong rets)
 {
     uint32_t mask, buf, len, event_len;
     uint64_t xinfo;
-    sPAPREventLogEntry *event;
+    SpaprEventLogEntry *event;
     struct rtas_error_log header;
     int i;
 
@@ -668,7 +954,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
      */
     for (i = 0; i < EVENT_CLASS_MAX; i++) {
         if (rtas_event_log_contains(EVENT_CLASS_MASK(i))) {
-            const sPAPREventSource *source =
+            const SpaprEventSource *source =
                 spapr_event_sources_get_source(spapr->event_sources, i);
 
             g_assert(source->enabled);
@@ -682,7 +968,7 @@ out_no_events:
     rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
 }
 
-static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+static void event_scan(PowerPCCPU *cpu, SpaprMachineState *spapr,
                        uint32_t token, uint32_t nargs,
                        target_ulong args,
                        uint32_t nret, target_ulong rets)
@@ -694,9 +980,9 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
 }
 
-void spapr_clear_pending_events(sPAPRMachineState *spapr)
+void spapr_clear_pending_events(SpaprMachineState *spapr)
 {
-    sPAPREventLogEntry *entry = NULL, *next_entry;
+    SpaprEventLogEntry *entry = NULL, *next_entry;
 
     QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) {
         QTAILQ_REMOVE(&spapr->pending_events, entry, next);
@@ -705,7 +991,20 @@ void spapr_clear_pending_events(sPAPRMachineState *spapr)
     }
 }
 
-void spapr_events_init(sPAPRMachineState *spapr)
+void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr)
+{
+    SpaprEventLogEntry *entry = NULL, *next_entry;
+
+    QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) {
+        if (spapr_event_log_entry_type(entry) == RTAS_LOG_TYPE_HOTPLUG) {
+            QTAILQ_REMOVE(&spapr->pending_events, entry, next);
+            g_free(entry->extended_log);
+            g_free(entry);
+        }
+    }
+}
+
+void spapr_events_init(SpaprMachineState *spapr)
 {
     int epow_irq = SPAPR_IRQ_EPOW;