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
next prev parent reply other threads:[~2026-05-20 11:37 UTC|newest]
Thread overview: 19+ 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-08-13 16:02 ` Nikhil Kumar Singh
2026-08-13 16:35 ` Nikhil Kumar Singh
2026-05-20 9:54 ` [PATCH v3 2/6] ppc/spapr: Add ibm,errinjct RTAS call handler Narayana Murty N
2026-08-13 16:11 ` Nikhil Kumar Singh
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-08-13 16:39 ` Nikhil Kumar Singh
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-08-13 16:49 ` Nikhil Kumar Singh
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-08-14 6:26 ` Harsh Prateek Bora
2026-05-20 9:54 ` [PATCH v3 6/6] MAINTAINERS: Add entry for sPAPR PCI VFIO EEH support Narayana Murty N
2026-06-08 17:31 ` [PATCH v3 0/6] ppc/spapr: Add RTAS error injection support for VFIO EEH Anushree Mathur
2026-06-09 8:55 ` 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox