All of lore.kernel.org
 help / color / mirror / Atom feed
From: Narayana Murty N <nnmlinux@linux.ibm.com>
To: qemu-ppc@nongnu.org, qemu-devel@nongnu.org
Cc: npiggin@gmail.com, harshpb@linux.ibm.com, mahesh@linux.ibm.com,
	ganeshgr@linux.ibm.com, sbhat@linux.ibm.com,
	vaibhav@linux.ibm.com, anushree.mathur@linux.vnet.ibm.com,
	clg@redhat.com, pierrick.bouvier@oss.qualcomm.com,
	philmd@linaro.org
Subject: [PATCH v3 5/6] ppc/spapr: Split VFIO code and refactor EEH interface
Date: Wed, 20 May 2026 15:24:45 +0530	[thread overview]
Message-ID: <20260520095446.64206-6-nnmlinux@linux.ibm.com> (raw)
In-Reply-To: <20260520095446.64206-1-nnmlinux@linux.ibm.com>

Split spapr_pci_vfio.c into two files to separate concerns:
- spapr_pci_vfio.c: Contains general VFIO routines
- spapr_pci_vfio_eeh.c: Contains EEH-specific routines

Additionally, consolidate VFIO EEH function declarations into a new
header file (spapr_vfio.h) to improve modularity and reduce header
dependencies.

Changes:
- Split VFIO functionality: keep general VFIO routines in
  spapr_pci_vfio.c and move EEH routines to spapr_pci_vfio_eeh.c
- Created include/hw/ppc/spapr_vfio.h with forward declarations
  to avoid pulling in full spapr headers and libfdt dependencies
- Introduced stubs/spapr_pci_vfio-stubs.c to consolidate all VFIO,
  VFIO EEH stub functions in one place
- Updated hw/ppc/spapr_pci.c to include new spapr_vfio.h header
- Updated stubs/meson.build to reference new stub file

This improves code organization by separating VFIO and EEH concerns,
and enhances build system modularity by making it easier to maintain
VFIO-related code separately from core sPAPR PCI code.

Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
 hw/ppc/Kconfig               |   2 +-
 hw/ppc/meson.build           |   1 +
 hw/ppc/spapr_pci.c           |   1 +
 hw/ppc/spapr_pci_vfio.c      | 367 +----------------------------------
 hw/ppc/spapr_pci_vfio_eeh.c  | 346 +++++++++++++++++++++++++++++++++
 include/hw/pci-host/spapr.h  |  44 +----
 include/hw/ppc/spapr_vfio.h  |  28 +++
 stubs/meson.build            |   1 +
 stubs/spapr_phb_vfio-stubs.c |  52 +++++
 9 files changed, 432 insertions(+), 410 deletions(-)
 create mode 100644 hw/ppc/spapr_pci_vfio_eeh.c
 create mode 100644 include/hw/ppc/spapr_vfio.h
 create mode 100644 stubs/spapr_phb_vfio-stubs.c

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 347dcce690..1fb191fe83 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -6,7 +6,7 @@ config PSERIES
     imply PCI_DEVICES
     imply TEST_DEVICES
     imply VIRTIO_VGA
-    imply VFIO_PCI if LINUX   # needed by spapr_pci_vfio.c
+    imply VFIO_PCI if LINUX   # needed by spapr_pci_vfio.c and spapr_pci_vfio_eeh.c
     select NVDIMM
     select DIMM
     select PCI
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index 37aa535db2..97e4be0dc9 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -36,6 +36,7 @@ ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c'))
 if host_os == 'linux'
   ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files(
     'spapr_pci_vfio.c',
+    'spapr_pci_vfio_eeh.c',
   ))
 endif
 
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index f08f21f03c..576b92229b 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -33,6 +33,7 @@
 #include "hw/pci/msix.h"
 #include "hw/pci/pci_host.h"
 #include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_vfio.h"
 #include "hw/pci-host/spapr.h"
 #include <libfdt.h>
 #include "trace.h"
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index ed0b22a84a..2207654d83 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -22,119 +22,11 @@
 #include <linux/vfio.h>
 #include "hw/ppc/spapr.h"
 #include "hw/pci-host/spapr.h"
+#include "hw/ppc/spapr_vfio.h"
 #include "hw/pci/msix.h"
 #include "hw/pci/pci_device.h"
 #include "hw/vfio/vfio-container-legacy.h"
 #include "qemu/error-report.h"
-#include CONFIG_DEVICES /* CONFIG_VFIO_PCI */
-
-/*
- * Interfaces for IBM EEH (Enhanced Error Handling)
- */
-#ifdef CONFIG_VFIO_PCI
-static bool vfio_eeh_container_ok(VFIOLegacyContainer *container)
-{
-    /*
-     * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO
-     * implementation is broken if there are multiple groups in a
-     * container.  The hardware works in units of Partitionable
-     * Endpoints (== IOMMU groups) and the EEH operations naively
-     * iterate across all groups in the container, without any logic
-     * to make sure the groups have their state synchronized.  For
-     * certain operations (ENABLE) that might be ok, until an error
-     * occurs, but for others (GET_STATE) it's clearly broken.
-     */
-
-    /*
-     * XXX Once fixed kernels exist, test for them here
-     */
-
-    if (QLIST_EMPTY(&container->group_list)) {
-        return false;
-    }
-
-    if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) {
-        return false;
-    }
-
-    return true;
-}
-
-static int vfio_eeh_container_op(VFIOLegacyContainer *container, uint32_t op)
-{
-    struct vfio_eeh_pe_op pe_op = {
-        .argsz = sizeof(pe_op),
-        .op = op,
-    };
-    int ret;
-
-    if (!vfio_eeh_container_ok(container)) {
-        error_report("vfio/eeh: EEH_PE_OP 0x%x: "
-                     "kernel requires a container with exactly one group", op);
-        return -EPERM;
-    }
-
-    ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op);
-    if (ret < 0) {
-        error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op);
-        return -errno;
-    }
-
-    return ret;
-}
-
-static VFIOLegacyContainer *vfio_eeh_as_container(AddressSpace *as)
-{
-    VFIOAddressSpace *space = vfio_address_space_get(as);
-    VFIOContainer *bcontainer = NULL;
-
-    if (QLIST_EMPTY(&space->containers)) {
-        /* No containers to act on */
-        goto out;
-    }
-
-    bcontainer = QLIST_FIRST(&space->containers);
-
-    if (QLIST_NEXT(bcontainer, next)) {
-        /*
-         * We don't yet have logic to synchronize EEH state across
-         * multiple containers
-         */
-        bcontainer = NULL;
-        goto out;
-    }
-
-out:
-    vfio_address_space_put(space);
-    return VFIO_IOMMU_LEGACY(bcontainer);
-}
-
-static bool vfio_eeh_as_ok(AddressSpace *as)
-{
-    VFIOLegacyContainer *container = vfio_eeh_as_container(as);
-
-    return (container != NULL) && vfio_eeh_container_ok(container);
-}
-
-static int vfio_eeh_as_op(AddressSpace *as, uint32_t op)
-{
-    VFIOLegacyContainer *container = vfio_eeh_as_container(as);
-
-    if (!container) {
-        return -ENODEV;
-    }
-    return vfio_eeh_container_op(container, op);
-}
-
-bool spapr_phb_eeh_available(SpaprPhbState *sphb)
-{
-    return vfio_eeh_as_ok(&sphb->iommu_as);
-}
-
-static void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb)
-{
-    vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE);
-}
 
 void spapr_phb_vfio_reset(DeviceState *qdev)
 {
@@ -146,260 +38,3 @@ void spapr_phb_vfio_reset(DeviceState *qdev)
      */
     spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev));
 }
-
-static void spapr_eeh_pci_find_device(PCIBus *bus, PCIDevice *pdev,
-                                      void *opaque)
-{
-    bool *found = opaque;
-
-    if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
-        *found = true;
-    }
-}
-
-int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
-                                  unsigned int addr, int option)
-{
-    uint32_t op;
-    int ret;
-
-    switch (option) {
-    case RTAS_EEH_DISABLE:
-        op = VFIO_EEH_PE_DISABLE;
-        break;
-    case RTAS_EEH_ENABLE: {
-        PCIHostState *phb;
-        bool found = false;
-
-        /*
-         * The EEH functionality is enabled per sphb level instead of
-         * per PCI device. We have already identified this specific sphb
-         * based on buid passed as argument to ibm,set-eeh-option rtas
-         * call. Now we just need to check the validity of the PCI
-         * pass-through devices (vfio-pci) under this sphb bus.
-         * We have already validated that all the devices under this sphb
-         * are from same iommu group (within same PE) before coming here.
-         *
-         * Prior to linux commit 98ba956f6a389 ("powerpc/pseries/eeh:
-         * Rework device EEH PE determination") kernel would call
-         * eeh-set-option for each device in the PE using the device's
-         * config_address as the argument rather than the PE address.
-         * Hence if we check validity of supplied config_addr whether
-         * it matches to this PHB will cause issues with older kernel
-         * versions v5.9 and older. If we return an error from
-         * eeh-set-option when the argument isn't a valid PE address
-         * then older kernels (v5.9 and older) will interpret that as
-         * EEH not being supported.
-         */
-        phb = PCI_HOST_BRIDGE(sphb);
-        pci_for_each_device(phb->bus, (addr >> 16) & 0xFF,
-                            spapr_eeh_pci_find_device, &found);
-
-        if (!found) {
-            return RTAS_OUT_PARAM_ERROR;
-        }
-
-        op = VFIO_EEH_PE_ENABLE;
-        break;
-    }
-    case RTAS_EEH_THAW_IO:
-        op = VFIO_EEH_PE_UNFREEZE_IO;
-        break;
-    case RTAS_EEH_THAW_DMA:
-        op = VFIO_EEH_PE_UNFREEZE_DMA;
-        break;
-    default:
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    ret = vfio_eeh_as_op(&sphb->iommu_as, op);
-    if (ret < 0) {
-        return RTAS_OUT_HW_ERROR;
-    }
-
-    return RTAS_OUT_SUCCESS;
-}
-
-int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state)
-{
-    int ret;
-
-    ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE);
-    if (ret < 0) {
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    *state = ret;
-    return RTAS_OUT_SUCCESS;
-}
-
-static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus,
-                                              PCIDevice *pdev,
-                                              void *opaque)
-{
-    /* Check if the device is VFIO PCI device */
-    if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
-        return;
-    }
-
-    /*
-     * The MSIx table will be cleaned out by reset. We need
-     * disable it so that it can be reenabled properly. Also,
-     * the cached MSIx table should be cleared as it's not
-     * reflecting the contents in hardware.
-     */
-    if (msix_enabled(pdev)) {
-        uint16_t flags;
-
-        flags = pci_host_config_read_common(pdev,
-                                            pdev->msix_cap + PCI_MSIX_FLAGS,
-                                            pci_config_size(pdev), 2);
-        flags &= ~PCI_MSIX_FLAGS_ENABLE;
-        pci_host_config_write_common(pdev,
-                                     pdev->msix_cap + PCI_MSIX_FLAGS,
-                                     pci_config_size(pdev), flags, 2);
-    }
-
-    msix_reset(pdev);
-}
-
-static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque)
-{
-       pci_for_each_device_under_bus(bus, spapr_phb_vfio_eeh_clear_dev_msix,
-                                     NULL);
-}
-
-static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb)
-{
-       PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
-
-       pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
-}
-
-int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
-{
-    uint32_t op;
-    int ret;
-
-    switch (option) {
-    case RTAS_SLOT_RESET_DEACTIVATE:
-        op = VFIO_EEH_PE_RESET_DEACTIVATE;
-        break;
-    case RTAS_SLOT_RESET_HOT:
-        spapr_phb_vfio_eeh_pre_reset(sphb);
-        op = VFIO_EEH_PE_RESET_HOT;
-        break;
-    case RTAS_SLOT_RESET_FUNDAMENTAL:
-        spapr_phb_vfio_eeh_pre_reset(sphb);
-        op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
-        break;
-    default:
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    ret = vfio_eeh_as_op(&sphb->iommu_as, op);
-    if (ret < 0) {
-        return RTAS_OUT_HW_ERROR;
-    }
-
-    return RTAS_OUT_SUCCESS;
-}
-
-int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
-{
-    int ret;
-
-    ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE);
-    if (ret < 0) {
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    return RTAS_OUT_SUCCESS;
-}
-
-int spapr_phb_vfio_errinjct(SpaprPhbState *sphb,
-                            uint32_t func, uint64_t addr,
-                            uint64_t mask, uint32_t type)
-{
-    VFIOLegacyContainer *container = vfio_eeh_as_container(&sphb->iommu_as);
-    struct vfio_eeh_pe_op op = {
-        .op = VFIO_EEH_PE_INJECT_ERR,
-        .argsz = sizeof(op),
-    };
-
-    /* Set error type, address, and mask */
-    op.err.type = type;
-    op.err.addr = addr;
-    op.err.mask = mask;
-
-    /* Validate and set function code */
-    switch (func) {
-    case EEH_ERR_FUNC_LD_MEM_ADDR:
-    case EEH_ERR_FUNC_LD_MEM_DATA:
-    case EEH_ERR_FUNC_LD_IO_ADDR:
-    case EEH_ERR_FUNC_LD_IO_DATA:
-    case EEH_ERR_FUNC_LD_CFG_ADDR:
-    case EEH_ERR_FUNC_LD_CFG_DATA:
-    case EEH_ERR_FUNC_ST_MEM_ADDR:
-    case EEH_ERR_FUNC_ST_MEM_DATA:
-    case EEH_ERR_FUNC_ST_IO_ADDR:
-    case EEH_ERR_FUNC_ST_IO_DATA:
-    case EEH_ERR_FUNC_ST_CFG_ADDR:
-    case EEH_ERR_FUNC_ST_CFG_DATA:
-    case EEH_ERR_FUNC_DMA_RD_ADDR:
-    case EEH_ERR_FUNC_DMA_RD_DATA:
-    case EEH_ERR_FUNC_DMA_RD_MASTER:
-    case EEH_ERR_FUNC_DMA_RD_TARGET:
-    case EEH_ERR_FUNC_DMA_WR_ADDR:
-    case EEH_ERR_FUNC_DMA_WR_DATA:
-    case EEH_ERR_FUNC_DMA_WR_MASTER:
-        op.err.func = func;
-        break;
-    default:
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    /* Perform the ioctl to inject the error */
-    if (ioctl(container->fd, VFIO_EEH_PE_OP, &op) < 0) {
-        return RTAS_OUT_HW_ERROR;
-    }
-
-    return RTAS_OUT_SUCCESS;
-}
-#else
-
-bool spapr_phb_eeh_available(SpaprPhbState *sphb)
-{
-    return false;
-}
-
-void spapr_phb_vfio_reset(DeviceState *qdev)
-{
-}
-
-int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
-                                  unsigned int addr, int option)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-
-int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-
-int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-
-int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-
-int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, int option)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-#endif /* CONFIG_VFIO_PCI */
diff --git a/hw/ppc/spapr_pci_vfio_eeh.c b/hw/ppc/spapr_pci_vfio_eeh.c
new file mode 100644
index 0000000000..6d07ae50c5
--- /dev/null
+++ b/hw/ppc/spapr_pci_vfio_eeh.c
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * QEMU sPAPR PCI VFIO EEH support
+ */
+
+#include "qemu/osdep.h"
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
+#include "hw/ppc/spapr.h"
+#include "hw/pci-host/spapr.h"
+#include "hw/ppc/spapr_vfio.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/pci_device.h"
+#include "hw/vfio/vfio-container-legacy.h"
+#include "qemu/error-report.h"
+#include CONFIG_DEVICES /* CONFIG_VFIO_PCI */
+
+/*
+ * Interfaces for IBM EEH (Enhanced Error Handling)
+ */
+static bool vfio_eeh_container_ok(VFIOLegacyContainer *container)
+{
+    /*
+     * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO
+     * implementation is broken if there are multiple groups in a
+     * container.  The hardware works in units of Partitionable
+     * Endpoints (== IOMMU groups) and the EEH operations naively
+     * iterate across all groups in the container, without any logic
+     * to make sure the groups have their state synchronized.  For
+     * certain operations (ENABLE) that might be ok, until an error
+     * occurs, but for others (GET_STATE) it's clearly broken.
+     */
+
+    /*
+     * XXX Once fixed kernels exist, test for them here
+     */
+
+    if (QLIST_EMPTY(&container->group_list)) {
+        return false;
+    }
+
+    if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) {
+        return false;
+    }
+
+    return true;
+}
+
+static int vfio_eeh_container_op(VFIOLegacyContainer *container, uint32_t op)
+{
+    struct vfio_eeh_pe_op pe_op = {
+        .argsz = sizeof(pe_op),
+        .op = op,
+    };
+    int ret;
+
+    if (!vfio_eeh_container_ok(container)) {
+        error_report("vfio/eeh: EEH_PE_OP 0x%x: "
+                     "kernel requires a container with exactly one group", op);
+        return -EPERM;
+    }
+
+    ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op);
+    if (ret < 0) {
+        error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op);
+        return -errno;
+    }
+
+    return ret;
+}
+
+static VFIOLegacyContainer *vfio_eeh_as_container(AddressSpace *as)
+{
+    VFIOAddressSpace *space = vfio_address_space_get(as);
+    VFIOContainer *bcontainer = NULL;
+
+    if (QLIST_EMPTY(&space->containers)) {
+        /* No containers to act on */
+        goto out;
+    }
+
+    bcontainer = QLIST_FIRST(&space->containers);
+
+    if (QLIST_NEXT(bcontainer, next)) {
+        /*
+         * We don't yet have logic to synchronize EEH state across
+         * multiple containers
+         */
+        bcontainer = NULL;
+        goto out;
+    }
+
+out:
+    vfio_address_space_put(space);
+    return VFIO_IOMMU_LEGACY(bcontainer);
+}
+
+static bool vfio_eeh_as_ok(AddressSpace *as)
+{
+    VFIOLegacyContainer *container = vfio_eeh_as_container(as);
+
+    return (container != NULL) && vfio_eeh_container_ok(container);
+}
+
+static int vfio_eeh_as_op(AddressSpace *as, uint32_t op)
+{
+    VFIOLegacyContainer *container = vfio_eeh_as_container(as);
+
+    if (!container) {
+        return -ENODEV;
+    }
+    return vfio_eeh_container_op(container, op);
+}
+
+bool spapr_phb_eeh_available(SpaprPhbState *sphb)
+{
+    return vfio_eeh_as_ok(&sphb->iommu_as);
+}
+
+void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb)
+{
+    vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE);
+}
+
+
+static void spapr_eeh_pci_find_device(PCIBus *bus, PCIDevice *pdev,
+                                      void *opaque)
+{
+    bool *found = opaque;
+
+    if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+        *found = true;
+    }
+}
+
+int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
+                                  unsigned int addr, int option)
+{
+    uint32_t op;
+    int ret;
+
+    switch (option) {
+    case RTAS_EEH_DISABLE:
+        op = VFIO_EEH_PE_DISABLE;
+        break;
+    case RTAS_EEH_ENABLE: {
+        PCIHostState *phb;
+        bool found = false;
+
+        /*
+         * The EEH functionality is enabled per sphb level instead of
+         * per PCI device. We have already identified this specific sphb
+         * based on buid passed as argument to ibm,set-eeh-option rtas
+         * call. Now we just need to check the validity of the PCI
+         * pass-through devices (vfio-pci) under this sphb bus.
+         * We have already validated that all the devices under this sphb
+         * are from same iommu group (within same PE) before coming here.
+         *
+         * Prior to linux commit 98ba956f6a389 ("powerpc/pseries/eeh:
+         * Rework device EEH PE determination") kernel would call
+         * eeh-set-option for each device in the PE using the device's
+         * config_address as the argument rather than the PE address.
+         * Hence if we check validity of supplied config_addr whether
+         * it matches to this PHB will cause issues with older kernel
+         * versions v5.9 and older. If we return an error from
+         * eeh-set-option when the argument isn't a valid PE address
+         * then older kernels (v5.9 and older) will interpret that as
+         * EEH not being supported.
+         */
+        phb = PCI_HOST_BRIDGE(sphb);
+        pci_for_each_device(phb->bus, (addr >> 16) & 0xFF,
+                            spapr_eeh_pci_find_device, &found);
+
+        if (!found) {
+            return RTAS_OUT_PARAM_ERROR;
+        }
+
+        op = VFIO_EEH_PE_ENABLE;
+        break;
+    }
+    case RTAS_EEH_THAW_IO:
+        op = VFIO_EEH_PE_UNFREEZE_IO;
+        break;
+    case RTAS_EEH_THAW_DMA:
+        op = VFIO_EEH_PE_UNFREEZE_DMA;
+        break;
+    default:
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    ret = vfio_eeh_as_op(&sphb->iommu_as, op);
+    if (ret < 0) {
+        return RTAS_OUT_HW_ERROR;
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
+int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state)
+{
+    int ret;
+
+    ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE);
+    if (ret < 0) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    *state = ret;
+    return RTAS_OUT_SUCCESS;
+}
+
+static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus,
+                                              PCIDevice *pdev,
+                                              void *opaque)
+{
+    /* Check if the device is VFIO PCI device */
+    if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+        return;
+    }
+
+    /*
+     * The MSIx table will be cleaned out by reset. We need
+     * disable it so that it can be reenabled properly. Also,
+     * the cached MSIx table should be cleared as it's not
+     * reflecting the contents in hardware.
+     */
+    if (msix_enabled(pdev)) {
+        uint16_t flags;
+
+        flags = pci_host_config_read_common(pdev,
+                                            pdev->msix_cap + PCI_MSIX_FLAGS,
+                                            pci_config_size(pdev), 2);
+        flags &= ~PCI_MSIX_FLAGS_ENABLE;
+        pci_host_config_write_common(pdev,
+                                     pdev->msix_cap + PCI_MSIX_FLAGS,
+                                     pci_config_size(pdev), flags, 2);
+    }
+
+    msix_reset(pdev);
+}
+
+static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque)
+{
+       pci_for_each_device_under_bus(bus, spapr_phb_vfio_eeh_clear_dev_msix,
+                                     NULL);
+}
+
+static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb)
+{
+       PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
+
+       pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
+}
+
+int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
+{
+    uint32_t op;
+    int ret;
+
+    switch (option) {
+    case RTAS_SLOT_RESET_DEACTIVATE:
+        op = VFIO_EEH_PE_RESET_DEACTIVATE;
+        break;
+    case RTAS_SLOT_RESET_HOT:
+        spapr_phb_vfio_eeh_pre_reset(sphb);
+        op = VFIO_EEH_PE_RESET_HOT;
+        break;
+    case RTAS_SLOT_RESET_FUNDAMENTAL:
+        spapr_phb_vfio_eeh_pre_reset(sphb);
+        op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
+        break;
+    default:
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    ret = vfio_eeh_as_op(&sphb->iommu_as, op);
+    if (ret < 0) {
+        return RTAS_OUT_HW_ERROR;
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
+int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
+{
+    int ret;
+
+    ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE);
+    if (ret < 0) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
+int spapr_phb_vfio_errinjct(SpaprPhbState *sphb,
+                            uint32_t func, uint64_t addr,
+                            uint64_t mask, uint32_t type)
+{
+    VFIOLegacyContainer *container = vfio_eeh_as_container(&sphb->iommu_as);
+    struct vfio_eeh_pe_op op = {
+        .op = VFIO_EEH_PE_INJECT_ERR,
+        .argsz = sizeof(op),
+    };
+
+    /* Set error type, address, and mask */
+    op.err.type = type;
+    op.err.addr = addr;
+    op.err.mask = mask;
+
+    /* Validate and set function code */
+    switch (func) {
+    case EEH_ERR_FUNC_LD_MEM_ADDR:
+    case EEH_ERR_FUNC_LD_MEM_DATA:
+    case EEH_ERR_FUNC_LD_IO_ADDR:
+    case EEH_ERR_FUNC_LD_IO_DATA:
+    case EEH_ERR_FUNC_LD_CFG_ADDR:
+    case EEH_ERR_FUNC_LD_CFG_DATA:
+    case EEH_ERR_FUNC_ST_MEM_ADDR:
+    case EEH_ERR_FUNC_ST_MEM_DATA:
+    case EEH_ERR_FUNC_ST_IO_ADDR:
+    case EEH_ERR_FUNC_ST_IO_DATA:
+    case EEH_ERR_FUNC_ST_CFG_ADDR:
+    case EEH_ERR_FUNC_ST_CFG_DATA:
+    case EEH_ERR_FUNC_DMA_RD_ADDR:
+    case EEH_ERR_FUNC_DMA_RD_DATA:
+    case EEH_ERR_FUNC_DMA_RD_MASTER:
+    case EEH_ERR_FUNC_DMA_RD_TARGET:
+    case EEH_ERR_FUNC_DMA_WR_ADDR:
+    case EEH_ERR_FUNC_DMA_WR_DATA:
+    case EEH_ERR_FUNC_DMA_WR_MASTER:
+        op.err.func = func;
+        break;
+    default:
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    /* Perform the ioctl to inject the error */
+    if (ioctl(container->fd, VFIO_EEH_PE_OP, &op) < 0) {
+        return RTAS_OUT_HW_ERROR;
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 417d1f6c31..d2bc90a3d2 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -116,49 +116,7 @@ void spapr_phb_remove_pci_device_cb(DeviceState *dev);
 int spapr_pci_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
                           void *fdt, int *fdt_start_offset, Error **errp);
 
-/* VFIO EEH hooks */
-#ifdef CONFIG_LINUX
-bool spapr_phb_eeh_available(SpaprPhbState *sphb);
-int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
-                                  unsigned int addr, int option);
-int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state);
-int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option);
-int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb);
-void spapr_phb_vfio_reset(DeviceState *qdev);
-int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t func,
-                            uint64_t addr, uint64_t mask, uint32_t type);
-#else
-static inline bool spapr_phb_eeh_available(SpaprPhbState *sphb)
-{
-    return false;
-}
-static inline int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
-                                                unsigned int addr, int option)
-{
-    return RTAS_OUT_HW_ERROR;
-}
-static inline int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb,
-                                               int *state)
-{
-    return RTAS_OUT_HW_ERROR;
-}
-static inline int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
-{
-    return RTAS_OUT_HW_ERROR;
-}
-static inline int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
-{
-    return RTAS_OUT_HW_ERROR;
-}
-static inline void spapr_phb_vfio_reset(DeviceState *qdev)
-{
-}
-static inline int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t func,
-                                   uint64_t addr, uint64_t mask, uint32_t type)
-{
-    return RTAS_OUT_HW_ERROR;
-}
-#endif
+/* VFIO EEH hooks - see hw/ppc/spapr_vfio.h for declarations */
 
 void spapr_phb_dma_reset(SpaprPhbState *sphb);
 
diff --git a/include/hw/ppc/spapr_vfio.h b/include/hw/ppc/spapr_vfio.h
new file mode 100644
index 0000000000..ab8b5f8527
--- /dev/null
+++ b/include/hw/ppc/spapr_vfio.h
@@ -0,0 +1,28 @@
+/*
+ * sPAPR VFIO EEH Header
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_PPC_SPAPR_VFIO_H
+#define HW_PPC_SPAPR_VFIO_H
+
+/*
+ * Forward declarations to avoid pulling in full spapr headers
+ * This allows stubs and other files to compile without libfdt dependencies
+ */
+typedef struct SpaprPhbState SpaprPhbState;
+typedef struct DeviceState DeviceState;
+
+/* VFIO EEH function declarations */
+bool spapr_phb_eeh_available(SpaprPhbState *sphb);
+int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
+                                  unsigned int addr, int option);
+int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state);
+int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option);
+int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb);
+void spapr_phb_vfio_reset(DeviceState *qdev);
+void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb);
+int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t func,
+                            uint64_t addr, uint64_t mask, uint32_t type);
+
+#endif /* HW_PPC_SPAPR_VFIO_H */
diff --git a/stubs/meson.build b/stubs/meson.build
index 3b2f2680b1..2879d6f70e 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -90,6 +90,7 @@ if have_system
   stub_ss.add(files('hmp-cmd-info_tlb.c'))
   stub_ss.add(files('hmp-cmds-hw-s390x.c'))
   stub_ss.add(files('hmp-cmds-target-i386.c'))
+  stub_ss.add(files('spapr_phb_vfio-stubs.c'))
 endif
 
 if have_system or have_user
diff --git a/stubs/spapr_phb_vfio-stubs.c b/stubs/spapr_phb_vfio-stubs.c
new file mode 100644
index 0000000000..ba043bcaf4
--- /dev/null
+++ b/stubs/spapr_phb_vfio-stubs.c
@@ -0,0 +1,52 @@
+/*
+ * Stubs for sPAPR PCI VFIO EEH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ppc/spapr_vfio.h"
+
+/* RTAS return codes */
+#define RTAS_OUT_NOT_SUPPORTED          (-3)
+
+
+bool spapr_phb_eeh_available(SpaprPhbState *sphb)
+{
+    return false;
+}
+
+int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
+                                  unsigned int addr, int option)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
+
+int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
+
+int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
+
+int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
+
+void spapr_phb_vfio_reset(DeviceState *qdev)
+{
+}
+
+void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb)
+{
+}
+
+int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t func,
+                            uint64_t addr, uint64_t mask, uint32_t type)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
-- 
2.54.0



  parent reply	other threads:[~2026-05-20 11:37 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-20  9:54 [PATCH v3 0/6] ppc/spapr: Add RTAS error injection support for VFIO EEH Narayana Murty N
2026-05-20  9:54 ` [PATCH v3 1/6] ppc/spapr: Add VFIO EEH error injection backend Narayana Murty N
2026-05-20  9:54 ` [PATCH v3 2/6] ppc/spapr: Add ibm,errinjct RTAS call handler Narayana Murty N
2026-05-20  9:54 ` [PATCH v3 3/6] ppc/spapr: Add support for 'ibm, open-errinjct' and 'ibm, close-errinjct' Narayana Murty N
2026-05-20  9:54 ` [PATCH v3 4/6] ppc/spapr: Advertise RTAS error injection call support via FDT property Narayana Murty N
2026-05-22  9:07   ` Shivaprasad G Bhat
2026-06-01  6:05     ` Narayana Murty N
2026-05-20  9:54 ` Narayana Murty N [this message]
2026-05-22 16:07   ` [PATCH v3 5/6] ppc/spapr: Split VFIO code and refactor EEH interface Pierrick Bouvier
2026-05-22 16:29     ` Narayana Murty N
2026-05-20  9:54 ` [PATCH v3 6/6] MAINTAINERS: Add entry for sPAPR PCI VFIO EEH support Narayana Murty N

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260520095446.64206-6-nnmlinux@linux.ibm.com \
    --to=nnmlinux@linux.ibm.com \
    --cc=anushree.mathur@linux.vnet.ibm.com \
    --cc=clg@redhat.com \
    --cc=ganeshgr@linux.ibm.com \
    --cc=harshpb@linux.ibm.com \
    --cc=mahesh@linux.ibm.com \
    --cc=npiggin@gmail.com \
    --cc=philmd@linaro.org \
    --cc=pierrick.bouvier@oss.qualcomm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    --cc=sbhat@linux.ibm.com \
    --cc=vaibhav@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.