Move all EEH and error-injection functions from spapr_pci_vfio.c into
a dedicated hw/ppc/spapr_eeh.c.  spapr_eeh.c is compiled only
when CONFIG_VFIO_PCI is enabled;

spapr_pci_vfio.c is retained as a stub-only file compiled on Linux
when CONFIG_VFIO_PCI is not available, providing the non-functional
fallback implementations needed for Linux/pseries builds that do not
include VFIO PCI.

meson.build: replace the single spapr_pci_vfio.c entry with an
if_true/if_false pair keyed on CONFIG_VFIO_PCI:
  CONFIG_VFIO_PCI=y  -> spapr_eeh.c       (real implementation)
  CONFIG_VFIO_PCI=n  -> spapr_pci_vfio-stubs.c  (stubs)

No functional change to the EEH state machine.

Reviewed-by: Pierrick Bouvier <[email protected]>
Signed-off-by: Narayana Murty N <[email protected]>
---
 include/hw/pci-host/spapr.h  |  56 +---
 include/hw/ppc/spapr_vfio.h  |  31 ++
 hw/ppc/spapr_eeh.c           | 571 +++++++++++++++++++++++++++++++++
 hw/ppc/spapr_pci.c           |   1 +
 hw/ppc/spapr_pci_vfio.c      | 591 +----------------------------------
 stubs/spapr_phb_vfio-stubs.c |  62 ++++
 hw/ppc/Kconfig               |   2 +-
 hw/ppc/meson.build           |   1 +
 stubs/meson.build            |   1 +
 9 files changed, 670 insertions(+), 646 deletions(-)
 create mode 100644 include/hw/ppc/spapr_vfio.h
 create mode 100644 hw/ppc/spapr_eeh.c
 create mode 100644 stubs/spapr_phb_vfio-stubs.c

diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 492f66e1ef..d2bc90a3d2 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -116,61 +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 type,
-                            uint32_t func, uint64_t addr, uint64_t mask);
-int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
-                                           uint32_t config_addr,
-                                           uint64_t guest_addr,
-                                           uint64_t *host_pci_bus_addr);
-#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 type,
-                                          uint32_t func, uint64_t addr,
-                                          uint64_t mask)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-static inline int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
-                                                         uint32_t config_addr,
-                                                         uint64_t guest_addr,
-                                                         uint64_t *host_addr)
-{
-    return -ENOTSUP;
-}
-#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..5aabb262f2
--- /dev/null
+++ b/include/hw/ppc/spapr_vfio.h
@@ -0,0 +1,31 @@
+/*
+ * 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 type,
+                            uint32_t func, uint64_t addr, uint64_t mask);
+int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
+                                           uint32_t config_addr,
+                                           uint64_t guest_addr,
+                                           uint64_t *host_pci_bus_addr);
+#endif /* HW_PPC_SPAPR_VFIO_H */
diff --git a/hw/ppc/spapr_eeh.c b/hw/ppc/spapr_eeh.c
new file mode 100644
index 0000000000..a9b7fe2d67
--- /dev/null
+++ b/hw/ppc/spapr_eeh.c
@@ -0,0 +1,571 @@
+/*
+ * QEMU sPAPR PCI host for VFIO
+ *
+ * Copyright (c) 2011-2014 Alexey Kardashevskiy, IBM Corporation.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License,
+ *  or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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/pci/msix.h"
+#include "hw/pci/pci_device.h"
+#include "hw/vfio/vfio-container-legacy.h"
+#include "qemu/error-report.h"
+#include "hw/vfio/pci.h"
+#include "hw/ppc/spapr_vfio.h"
+
+/*
+ * 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;
+}
+
+typedef struct SpaprVFIOErrinjctBarMatch {
+    uint64_t guest_addr;
+
+    PCIDevice *pdev;
+    VFIOPCIDevice *vdev;
+    int bar;
+
+    uint64_t guest_bar_start;
+    uint64_t bar_size;
+    uint64_t offset;
+} SpaprVFIOErrinjctBarMatch;
+
+static VFIOPCIDevice *spapr_vfio_errinjct_pci_to_vfio(PCIDevice *pdev)
+{
+    if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI)) {
+        return NULL;
+    }
+
+    return container_of(pdev, VFIOPCIDevice, parent_obj);
+}
+
+static void spapr_vfio_errinjct_find_bar_cb(PCIBus *bus,
+                                            PCIDevice *pdev,
+                                            void *opaque)
+{
+    SpaprVFIOErrinjctBarMatch *ctx = opaque;
+    VFIOPCIDevice *vdev;
+    int bar;
+
+    if (ctx->pdev) {
+        return;
+    }
+
+    vdev = spapr_vfio_errinjct_pci_to_vfio(pdev);
+    if (!vdev) {
+        return;
+    }
+
+    for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
+        pcibus_t guest_bar_start;
+        uint64_t bar_size;
+        uint64_t offset;
+
+        bar_size = vdev->bars[bar].region.size;
+        if (!bar_size) {
+            continue;
+        }
+
+        guest_bar_start = pci_get_bar_addr(pdev, bar);
+        if (guest_bar_start == PCI_BAR_UNMAPPED) {
+            continue;
+        }
+
+        if (ctx->guest_addr < guest_bar_start ||
+            ctx->guest_addr - guest_bar_start >= bar_size) {
+            if (vdev->bars[bar].mem64) {
+                bar++;
+            }
+            continue;
+        }
+
+        offset = ctx->guest_addr - guest_bar_start;
+
+        ctx->pdev = pdev;
+        ctx->vdev = vdev;
+        ctx->bar = bar;
+        ctx->guest_bar_start = guest_bar_start;
+        ctx->bar_size = bar_size;
+        ctx->offset = offset;
+
+        return;
+    }
+}
+
+static int spapr_vfio_errinjct_get_host_bar(VFIOPCIDevice *vdev,
+                                            int bar,
+                                            uint64_t *host_bar_start)
+{
+    g_autofree char *path = NULL;
+    g_autofree char *contents = NULL;
+    char *line;
+    char *saveptr = NULL;
+    unsigned long long start;
+    unsigned long long end;
+    unsigned long long flags;
+    int i;
+
+    if (!vdev || !host_bar_start || bar < 0 || bar >= PCI_STD_NUM_BARS) {
+        return -EINVAL;
+    }
+
+    /*
+     * Read the host Linux sysfs resource file for the BAR.  Do not use a
+     * VFIO PCI config-space read because that may return the
+     * guest-programmed BAR value rather than the host resource address.
+     */
+    path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%u/resource",
+                           vdev->host.domain,
+                           vdev->host.bus,
+                           vdev->host.slot,
+                           vdev->host.function);
+
+    if (!g_file_get_contents(path, &contents, NULL, NULL)) {
+        error_report("vfio/eeh errinjct: failed to read %s", path);
+        return -ENOENT;
+    }
+
+    line = strtok_r(contents, "\n", &saveptr);
+
+    for (i = 0; line; i++, line = strtok_r(NULL, "\n", &saveptr)) {
+        if (i != bar) {
+            continue;
+        }
+
+        if (sscanf(line, "%llx %llx %llx", &start, &end, &flags) != 3) {
+            error_report("vfio/eeh errinjct: malformed %s BAR%d",
+                         path, bar);
+            return -EINVAL;
+        }
+
+        if (!start || end < start) {
+            error_report("vfio/eeh errinjct: invalid host resource BAR%d "
+                         "start=0x%llx end=0x%llx flags=0x%llx",
+                         bar, start, end, flags);
+            return -EINVAL;
+        }
+
+        *host_bar_start = start;
+        return 0;
+    }
+
+    return -EINVAL;
+}
+
+int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
+                                           uint32_t config_addr,
+                                           uint64_t guest_addr,
+                                           uint64_t *host_pci_bus_addr)
+{
+    PCIHostState *phb;
+    SpaprVFIOErrinjctBarMatch ctx = {
+        .guest_addr = guest_addr,
+        .pdev       = NULL,
+        .vdev       = NULL,
+        .bar        = -1,
+    };
+    uint64_t host_bar_start;
+    int rc;
+
+    if (!sphb || !host_pci_bus_addr) {
+        return -EINVAL;
+    }
+
+    phb = PCI_HOST_BRIDGE(sphb);
+
+    /*
+     * BUID has already selected @sphb before this helper is called.
+     * config_addr is retained for logging/debug only.  Do not rely on it
+     * to find the target device; some RTAS IOA buffers may not carry a
+     * valid guest BDF-style config address.  Scan VFIO BARs under this
+     * PHB and match guest_addr against the guest BAR layout instead.
+     */
+    pci_for_each_device_under_bus(phb->bus,
+                                  spapr_vfio_errinjct_find_bar_cb,
+                                  &ctx);
+
+    if (!ctx.pdev) {
+        error_report("vfio/eeh errinjct: guest addr 0x%" PRIx64
+                     " not within any VFIO BAR under BUID=0x%016" PRIx64
+                     " config_addr=0x%08x",
+                     guest_addr, sphb->buid, config_addr);
+        return -ENODEV;
+    }
+
+    rc = spapr_vfio_errinjct_get_host_bar(ctx.vdev, ctx.bar, &host_bar_start);
+    if (rc) {
+        error_report("vfio/eeh errinjct: failed to get host BAR%d for "
+                     "dev=%s BUID=0x%016" PRIx64 " config_addr=0x%08x rc=%d",
+                     ctx.bar, ctx.pdev->name, sphb->buid, config_addr, rc);
+        return rc;
+    }
+
+    if (host_bar_start == ctx.guest_bar_start) {
+        error_report("vfio/eeh errinjct: refusing guest BAR as host BAR: "
+                     "dev=%s BAR%d guest_bar=0x%" PRIx64
+                     " host_bar=0x%" PRIx64,
+                     ctx.pdev->name, ctx.bar,
+                     ctx.guest_bar_start, host_bar_start);
+        return -EOPNOTSUPP;
+    }
+
+    *host_pci_bus_addr = host_bar_start + ctx.offset;
+    return 0;
+}
+
+static int spapr_vfio_errinjct_rtas_type_to_vfio(uint32_t rtas_type)
+{
+    switch (rtas_type) {
+    case RTAS_ERR_TYPE_IOA_BUS_ERROR:
+        return EEH_ERR_TYPE_32;
+    case RTAS_ERR_TYPE_IOA_BUS_ERROR_64:
+        return EEH_ERR_TYPE_64;
+    default:
+        return -1;
+    }
+}
+
+static bool spapr_vfio_errinjct_func_valid(uint32_t func)
+{
+    return func <= EEH_ERR_FUNC_MAX;
+}
+
+int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t type,
+                            uint32_t func, uint64_t addr, uint64_t mask)
+{
+    VFIOLegacyContainer *container;
+    struct vfio_eeh_pe_op op = {
+        .op   = VFIO_EEH_PE_INJECT_ERR,
+        .argsz = sizeof(op),
+    };
+    int vfio_type;
+
+    if (!sphb) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    if (!spapr_vfio_errinjct_func_valid(func)) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    vfio_type = spapr_vfio_errinjct_rtas_type_to_vfio(type);
+    if (vfio_type < 0) {
+        return RTAS_OUT_NOT_SUPPORTED;
+    }
+
+    container = vfio_eeh_as_container(&sphb->iommu_as);
+    if (!container) {
+        error_report("vfio/eeh errinjct: no VFIO EEH container for PHB");
+        return RTAS_OUT_NOT_SUPPORTED;
+    }
+
+    op.err.type = vfio_type;
+    op.err.func = func;
+    op.err.addr = addr;
+    op.err.mask = mask;
+
+    if (ioctl(container->fd, VFIO_EEH_PE_OP, &op) < 0) {
+        error_report("vfio/eeh errinjct: VFIO_EEH_PE_OP failed: %s",
+                     strerror(errno));
+        switch (errno) {
+        case EINVAL:
+            return RTAS_OUT_PARAM_ERROR;
+        case ENOTTY:
+        case EOPNOTSUPP:
+            return RTAS_OUT_NOT_SUPPORTED;
+        default:
+            return RTAS_OUT_HW_ERROR;
+        }
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 20cb913f12..33a1011890 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 2f428531cc..e6ecc3f1ee 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -22,120 +22,12 @@
 #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 "hw/vfio/pci.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)
 {
@@ -147,484 +39,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;
-}
-
-typedef struct SpaprVFIOErrinjctBarMatch {
-    uint64_t guest_addr;
-
-    PCIDevice *pdev;
-    VFIOPCIDevice *vdev;
-    int bar;
-
-    uint64_t guest_bar_start;
-    uint64_t bar_size;
-    uint64_t offset;
-} SpaprVFIOErrinjctBarMatch;
-
-static VFIOPCIDevice *spapr_vfio_errinjct_pci_to_vfio(PCIDevice *pdev)
-{
-    if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI)) {
-        return NULL;
-    }
-
-    return container_of(pdev, VFIOPCIDevice, parent_obj);
-}
-
-static void spapr_vfio_errinjct_find_bar_cb(PCIBus *bus,
-                                            PCIDevice *pdev,
-                                            void *opaque)
-{
-    SpaprVFIOErrinjctBarMatch *ctx = opaque;
-    VFIOPCIDevice *vdev;
-    int bar;
-
-    if (ctx->pdev) {
-        return;
-    }
-
-    vdev = spapr_vfio_errinjct_pci_to_vfio(pdev);
-    if (!vdev) {
-        return;
-    }
-
-    for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
-        pcibus_t guest_bar_start;
-        uint64_t bar_size;
-        uint64_t offset;
-
-        bar_size = vdev->bars[bar].region.size;
-        if (!bar_size) {
-            continue;
-        }
-
-        guest_bar_start = pci_get_bar_addr(pdev, bar);
-        if (guest_bar_start == PCI_BAR_UNMAPPED) {
-            continue;
-        }
-
-        if (ctx->guest_addr < guest_bar_start ||
-            ctx->guest_addr - guest_bar_start >= bar_size) {
-            if (vdev->bars[bar].mem64) {
-                bar++;
-            }
-            continue;
-        }
-
-        offset = ctx->guest_addr - guest_bar_start;
-
-        ctx->pdev = pdev;
-        ctx->vdev = vdev;
-        ctx->bar = bar;
-        ctx->guest_bar_start = guest_bar_start;
-        ctx->bar_size = bar_size;
-        ctx->offset = offset;
-
-        return;
-    }
-}
-
-static int spapr_vfio_errinjct_get_host_bar(VFIOPCIDevice *vdev,
-                                            int bar,
-                                            uint64_t *host_bar_start)
-{
-    g_autofree char *path = NULL;
-    g_autofree char *contents = NULL;
-    char *line;
-    char *saveptr = NULL;
-    unsigned long long start;
-    unsigned long long end;
-    unsigned long long flags;
-    int i;
-
-    if (!vdev || !host_bar_start || bar < 0 || bar >= PCI_STD_NUM_BARS) {
-        return -EINVAL;
-    }
-
-    /*
-     * Read the host Linux sysfs resource file for the BAR.  Do not use a
-     * VFIO PCI config-space read because that may return the
-     * guest-programmed BAR value rather than the host resource address.
-     */
-    path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%u/resource",
-                           vdev->host.domain,
-                           vdev->host.bus,
-                           vdev->host.slot,
-                           vdev->host.function);
-
-    if (!g_file_get_contents(path, &contents, NULL, NULL)) {
-        error_report("vfio/eeh errinjct: failed to read %s", path);
-        return -ENOENT;
-    }
-
-    line = strtok_r(contents, "\n", &saveptr);
-
-    for (i = 0; line; i++, line = strtok_r(NULL, "\n", &saveptr)) {
-        if (i != bar) {
-            continue;
-        }
-
-        if (sscanf(line, "%llx %llx %llx", &start, &end, &flags) != 3) {
-            error_report("vfio/eeh errinjct: malformed %s BAR%d",
-                         path, bar);
-            return -EINVAL;
-        }
-
-        if (!start || end < start) {
-            error_report("vfio/eeh errinjct: invalid host resource BAR%d "
-                         "start=0x%llx end=0x%llx flags=0x%llx",
-                         bar, start, end, flags);
-            return -EINVAL;
-        }
-
-        *host_bar_start = start;
-        return 0;
-    }
-
-    return -EINVAL;
-}
-
-int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
-                                           uint32_t config_addr,
-                                           uint64_t guest_addr,
-                                           uint64_t *host_pci_bus_addr)
-{
-    PCIHostState *phb;
-    SpaprVFIOErrinjctBarMatch ctx = {
-        .guest_addr = guest_addr,
-        .pdev       = NULL,
-        .vdev       = NULL,
-        .bar        = -1,
-    };
-    uint64_t host_bar_start;
-    int rc;
-
-    if (!sphb || !host_pci_bus_addr) {
-        return -EINVAL;
-    }
-
-    phb = PCI_HOST_BRIDGE(sphb);
-
-    /*
-     * BUID has already selected @sphb before this helper is called.
-     * config_addr is retained for logging/debug only.  Do not rely on it
-     * to find the target device; some RTAS IOA buffers may not carry a
-     * valid guest BDF-style config address.  Scan VFIO BARs under this
-     * PHB and match guest_addr against the guest BAR layout instead.
-     */
-    pci_for_each_device_under_bus(phb->bus,
-                                  spapr_vfio_errinjct_find_bar_cb,
-                                  &ctx);
-
-    if (!ctx.pdev) {
-        error_report("vfio/eeh errinjct: guest addr 0x%" PRIx64
-                     " not within any VFIO BAR under BUID=0x%016" PRIx64
-                     " config_addr=0x%08x",
-                     guest_addr, sphb->buid, config_addr);
-        return -ENODEV;
-    }
-
-    rc = spapr_vfio_errinjct_get_host_bar(ctx.vdev, ctx.bar, &host_bar_start);
-    if (rc) {
-        error_report("vfio/eeh errinjct: failed to get host BAR%d for "
-                     "dev=%s BUID=0x%016" PRIx64 " config_addr=0x%08x rc=%d",
-                     ctx.bar, ctx.pdev->name, sphb->buid, config_addr, rc);
-        return rc;
-    }
-
-    if (host_bar_start == ctx.guest_bar_start) {
-        error_report("vfio/eeh errinjct: refusing guest BAR as host BAR: "
-                     "dev=%s BAR%d guest_bar=0x%" PRIx64
-                     " host_bar=0x%" PRIx64,
-                     ctx.pdev->name, ctx.bar,
-                     ctx.guest_bar_start, host_bar_start);
-        return -EOPNOTSUPP;
-    }
-
-    *host_pci_bus_addr = host_bar_start + ctx.offset;
-    return 0;
-}
-
-static int spapr_vfio_errinjct_rtas_type_to_vfio(uint32_t rtas_type)
-{
-    switch (rtas_type) {
-    case RTAS_ERR_TYPE_IOA_BUS_ERROR:
-        return EEH_ERR_TYPE_32;
-    case RTAS_ERR_TYPE_IOA_BUS_ERROR_64:
-        return EEH_ERR_TYPE_64;
-    default:
-        return -1;
-    }
-}
-
-static bool spapr_vfio_errinjct_func_valid(uint32_t func)
-{
-    return func <= EEH_ERR_FUNC_MAX;
-}
-
-int spapr_phb_vfio_errinjct(SpaprPhbState *sphb, uint32_t type,
-                            uint32_t func, uint64_t addr, uint64_t mask)
-{
-    VFIOLegacyContainer *container;
-    struct vfio_eeh_pe_op op = {
-        .op   = VFIO_EEH_PE_INJECT_ERR,
-        .argsz = sizeof(op),
-    };
-    int vfio_type;
-
-    if (!sphb) {
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    if (!spapr_vfio_errinjct_func_valid(func)) {
-        return RTAS_OUT_PARAM_ERROR;
-    }
-
-    vfio_type = spapr_vfio_errinjct_rtas_type_to_vfio(type);
-    if (vfio_type < 0) {
-        return RTAS_OUT_NOT_SUPPORTED;
-    }
-
-    container = vfio_eeh_as_container(&sphb->iommu_as);
-    if (!container) {
-        error_report("vfio/eeh errinjct: no VFIO EEH container for PHB");
-        return RTAS_OUT_NOT_SUPPORTED;
-    }
-
-    op.err.type = vfio_type;
-    op.err.func = func;
-    op.err.addr = addr;
-    op.err.mask = mask;
-
-    if (ioctl(container->fd, VFIO_EEH_PE_OP, &op) < 0) {
-        error_report("vfio/eeh errinjct: VFIO_EEH_PE_OP failed: %s",
-                     strerror(errno));
-        switch (errno) {
-        case EINVAL:
-            return RTAS_OUT_PARAM_ERROR;
-        case ENOTTY:
-        case EOPNOTSUPP:
-            return RTAS_OUT_NOT_SUPPORTED;
-        default:
-            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, uint32_t type,
-                            uint32_t func, uint64_t addr, uint64_t mask)
-{
-    return RTAS_OUT_NOT_SUPPORTED;
-}
-
-int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
-                                           uint32_t config_addr,
-                                           uint64_t guest_addr,
-                                           uint64_t *host_pci_bus_addr)
-{
-    return -ENOTSUP;
-}
-
-#endif /* CONFIG_VFIO_PCI */
diff --git a/stubs/spapr_phb_vfio-stubs.c b/stubs/spapr_phb_vfio-stubs.c
new file mode 100644
index 0000000000..4d86d9000e
--- /dev/null
+++ b/stubs/spapr_phb_vfio-stubs.c
@@ -0,0 +1,62 @@
+/*
+ * 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 type,
+                            uint32_t func, uint64_t addr, uint64_t mask)
+{
+    return RTAS_OUT_NOT_SUPPORTED;
+}
+
+int spapr_phb_vfio_translate_errinjct_addr(SpaprPhbState *sphb,
+                                           uint32_t config_addr,
+                                           uint64_t guest_addr,
+                                           uint64_t *host_pci_bus_addr)
+{
+    return -ENOTSUP;
+}
+
+
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 347dcce690..3714d708d9 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_eeh.c
     select NVDIMM
     select DIMM
     select PCI
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index 37aa535db2..4e03973440 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_eeh.c',
   ))
 endif
 
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
-- 
2.51.1


Reply via email to