qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v7 3/3] sPAPR: EEH support for VFIO PCI device


From: Gavin Shan
Subject: [Qemu-devel] [PATCH v7 3/3] sPAPR: EEH support for VFIO PCI device
Date: Tue, 27 May 2014 18:51:10 +1000

The patch introduces EEH RTAS servers on sPAPR platform and handle
them there. Each sPAPRPHBVFIOState is binding with only one IOMMU
group, so it can be regarded as PE in nature. The PE address is
maintained in sPAPRPHBState, which has default value 0xffffffff
for non-VFIO PHBs. Otherwise, the PE address is equal to IOMMU
group ID. It can be used to distinguish sPAPRVFIOPHBState from
sPAPRPHBState.

As we're going to support EEH functonality for VFIO PCI devices,
we simply return error if the EEH RTAS call is routed to non-VFIO
PHB whose PE address is 0xffffffff.

Signed-off-by: Gavin Shan <address@hidden>
---
 hw/ppc/spapr.c              |   1 +
 hw/ppc/spapr_pci.c          |   1 +
 hw/ppc/spapr_pci_vfio.c     | 335 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/spapr.h |   1 +
 include/hw/ppc/spapr.h      |   1 +
 5 files changed, 339 insertions(+)

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index eee0a7c..e394d0c 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1325,6 +1325,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
     spapr_pci_msi_init(spapr, SPAPR_PCI_MSI_WINDOW);
     spapr_pci_rtas_init();
     spapr_tce_ddw_rtas_init(spapr);
+    spapr_eeh_rtas_init(spapr);
 
     phb = spapr_create_phb(spapr, 0);
 
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index a9f307a..0f2ac02 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -578,6 +578,7 @@ static void spapr_phb_realize(DeviceState *dev, Error 
**errp)
         return;
     }
 
+    sphb->pe_addr = 0xffffffff;
     sphb->dtbusname = g_strdup_printf("address@hidden" PRIx64, sphb->buid);
 
     namebuf = alloca(strlen(sphb->dtbusname) + 32);
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index decf3dd..5dbddd0 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -23,6 +23,8 @@
  */
 #include <sys/types.h>
 #include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
 
 #include "hw/hw.h"
 #include "hw/ppc/spapr.h"
@@ -52,6 +54,12 @@ static void spapr_phb_vfio_finish_realize(sPAPRPHBState 
*sphb, Error **errp)
         return;
     }
 
+    /*
+     * PE address isn't relevant to PCI config address any more.
+     * However, 0 is invalid PE address and can't be used.
+     */
+    sphb->pe_addr = svphb->iommugroupid + 1;
+
     ret = vfio_container_spapr_get_info(&svphb->phb.iommu_as,
                                         svphb->iommugroupid,
                                         &info);
@@ -354,3 +362,330 @@ void spapr_tce_ddw_rtas_init(sPAPREnvironment *spapr)
         spapr_rtas_register("ibm,reset-pe-dma-window",
                             rtas_ibm_reset_pe_dma_window);
 }
+
+static bool spapr_vfio_pci_addr_check(sPAPRPHBState *sphb,
+                                      uint32_t addr, bool is_pe_addr)
+{
+    PCIDevice *pdev = NULL;
+
+    if (!sphb || !sphb->parent_obj.bus) {
+        return false;
+    }
+
+    if (!is_pe_addr) {
+        pdev = pci_find_device(sphb->parent_obj.bus,
+                               (addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
+        if (!pdev) {
+            return false;
+        }
+    } else {
+        if (sphb->pe_addr != addr) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
+                                    sPAPREnvironment *spapr,
+                                    uint32_t token, uint32_t nargs,
+                                    target_ulong args, uint32_t nret,
+                                    target_ulong rets)
+{
+    struct vfio_eeh_pe_set_option option;
+    uint32_t addr;
+    uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+    sPAPRPHBState *sphb = spapr_find_phb(spapr, buid);
+    sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+    int32_t fd, ret;
+    bool valid = false;
+
+    if (!sphb || sphb->pe_addr == 0xffffffff) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    if ((nargs != 4) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    option.argsz = sizeof(option);
+    option.option = rtas_ld(args, 3);
+    addr = rtas_ld(args, 0);
+    switch (option.option) {
+    case VFIO_EEH_PE_SET_OPT_ENABLE:
+        valid = spapr_vfio_pci_addr_check(sphb, addr, false);
+        break;
+    case VFIO_EEH_PE_SET_OPT_DISABLE:
+    case VFIO_EEH_PE_SET_OPT_IO:
+    case VFIO_EEH_PE_SET_OPT_DMA:
+        valid = spapr_vfio_pci_addr_check(sphb, addr, true);
+        break;
+    default:
+        valid = false;
+    }
+
+    if (!valid) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    fd = vfio_get_container_fd_by_group_id(svphb->iommugroupid);
+    if (fd < 0) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ret = ioctl(fd, VFIO_EEH_PE_SET_OPTION, &option);
+    if (ret == 0) {
+        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+    } else {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+    }
+}
+
+static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
+                                           sPAPREnvironment *spapr,
+                                           uint32_t token, uint32_t nargs,
+                                           target_ulong args, uint32_t nret,
+                                           target_ulong rets)
+{
+    uint32_t addr, option;
+    uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+    sPAPRPHBState *sphb = spapr_find_phb(spapr, buid);
+    bool valid = false;
+
+    if (!sphb || sphb->pe_addr == 0xffffffff) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    if ((nargs != 4) || (nret != 2)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    addr = rtas_ld(args, 0);
+    option = rtas_ld(args, 3);
+    switch (option) {
+    case VFIO_EEH_PE_GET_ADDRESS:
+        valid = spapr_vfio_pci_addr_check(sphb, addr, false);
+        if (valid) {
+            rtas_st(rets, 1, sphb->pe_addr);
+        }
+
+        break;
+    case VFIO_EEH_PE_GET_MODE:
+        valid = spapr_vfio_pci_addr_check(sphb, addr, false);
+
+        /* We always have shared PE */
+        if (valid) {
+            rtas_st(rets, 1, VFIO_EEH_PE_MODE_SHARED);
+        }
+
+        break;
+    default:
+        valid = false;
+    }
+
+    if (valid) {
+        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+    } else {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+    }
+}
+
+static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
+                                            sPAPREnvironment *spapr,
+                                            uint32_t token, uint32_t nargs,
+                                            target_ulong args, uint32_t nret,
+                                            target_ulong rets)
+{
+    struct vfio_eeh_pe_get_state get_state;
+    uint32_t addr;
+    uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+    sPAPRPHBState *sphb = spapr_find_phb(spapr, buid);
+    sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+    int32_t fd, ret;
+    bool valid = false;
+
+    if (!sphb || sphb->pe_addr == 0xffffffff) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    if ((nargs != 3) || (nret != 4 && nret != 5)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    get_state.argsz = sizeof(get_state);
+    addr = rtas_ld(args, 0);
+    valid = spapr_vfio_pci_addr_check(sphb, addr, true);
+    if (!valid) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    fd = vfio_get_container_fd_by_group_id(svphb->iommugroupid);
+    if (fd <= 0) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ret = ioctl(fd, VFIO_EEH_PE_GET_STATE, &get_state);
+    if (ret == 0) {
+        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+        rtas_st(rets, 1, get_state.state);
+        rtas_st(rets, 2, 1);
+        rtas_st(rets, 3, 1000);
+        if (nret >= 5) {
+            rtas_st(rets, 4, 0);
+        }
+    } else {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+    }
+}
+
+static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
+                                    sPAPREnvironment *spapr,
+                                    uint32_t token, uint32_t nargs,
+                                    target_ulong args, uint32_t nret,
+                                    target_ulong rets)
+{
+    struct vfio_eeh_pe_reset pe_reset;
+    uint32_t addr;
+    uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+    sPAPRPHBState *sphb = spapr_find_phb(spapr, buid);
+    sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+    int32_t fd, ret;
+    bool valid = false;
+
+    if (!sphb || sphb->pe_addr == 0xffffffff) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    if ((nargs != 4) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    pe_reset.argsz = sizeof(pe_reset);
+    pe_reset.option = rtas_ld(args, 3);
+    addr = rtas_ld(args, 0);
+    switch (pe_reset.option) {
+    case VFIO_EEH_PE_RESET_DEACTIVATE:
+    case VFIO_EEH_PE_RESET_HOT:
+    case VFIO_EEH_PE_RESET_FUNDAMENTAL:
+        valid = spapr_vfio_pci_addr_check(sphb, addr, true);
+        break;
+    default:
+        valid = false;
+    }
+
+    if (!valid) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    fd = vfio_get_container_fd_by_group_id(svphb->iommugroupid);
+    if (fd <= 0) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ret = ioctl(fd, VFIO_EEH_PE_RESET, &pe_reset);
+    if (ret == 0) {
+        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+    } else {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+    }
+}
+
+static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
+                                  sPAPREnvironment *spapr,
+                                  uint32_t token, uint32_t nargs,
+                                  target_ulong args, uint32_t nret,
+                                  target_ulong rets)
+{
+    uint32_t addr;
+    uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+    struct vfio_eeh_pe_configure configure;
+    sPAPRPHBState *sphb = spapr_find_phb(spapr, buid);
+    sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+    int32_t fd, ret;
+    bool valid = false;
+
+    if (!sphb || sphb->pe_addr == 0xffffffff) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    if ((nargs != 3) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    configure.argsz = sizeof(configure);
+    addr = rtas_ld(args, 0);
+    valid = spapr_vfio_pci_addr_check(sphb, addr, true);
+    if (!valid) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    fd = vfio_get_container_fd_by_group_id(svphb->iommugroupid);
+    if (fd <= 0) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ret = ioctl(fd, VFIO_EEH_PE_CONFIGURE, &configure);
+    if (ret == 0) {
+        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+    } else {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+    }
+}
+
+static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
+                                       sPAPREnvironment *spapr,
+                                       uint32_t token, uint32_t nargs,
+                                       target_ulong args, uint32_t nret,
+                                       target_ulong rets)
+{
+    int option;
+
+    /* To support it later */
+    if ((nargs != 8) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    option = rtas_ld(args, 7);
+    if (option != 1 && option != 2) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+void spapr_eeh_rtas_init(sPAPREnvironment *spapr)
+{
+    spapr_rtas_register("ibm,set-eeh-option",
+                        rtas_ibm_set_eeh_option);
+    spapr_rtas_register("ibm,get-config-addr-info2",
+                        rtas_ibm_get_config_addr_info2);
+    spapr_rtas_register("ibm,read-slot-reset-state2",
+                        rtas_ibm_read_slot_reset_state2);
+    spapr_rtas_register("ibm,set-slot-reset",
+                        rtas_ibm_set_slot_reset);
+    spapr_rtas_register("ibm,configure-pe",
+                        rtas_ibm_configure_pe);
+    spapr_rtas_register("ibm,slot-error-detail",
+                        rtas_ibm_slot_error_detail);
+}
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index a695f12..d6d165c 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -58,6 +58,7 @@ typedef struct sPAPRPHBState {
 
     int32_t index;
     uint64_t buid;
+    uint32_t pe_addr;
     char *dtbusname;
 
     MemoryRegion memspace, iospace;
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index f1d307f..7cf352b 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -406,6 +406,7 @@ struct sPAPRTCETable {
 
 sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn);
 void spapr_tce_ddw_rtas_init(sPAPREnvironment *spapr);
+void spapr_eeh_rtas_init(sPAPREnvironment *spapr);
 void spapr_events_init(sPAPREnvironment *spapr);
 void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq);
 sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
-- 
1.8.3.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]