diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index cfd3b7b..fb03c3a 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -682,6 +682,48 @@ param_error_exit:
     rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
 }
 
+int spapr_rtas_errinjct_ioa(sPAPRMachineState *spapr,
+                            target_ulong param_buf,
+                            bool is_64bits)
+{
+    sPAPRPHBState *sphb;
+    sPAPRPHBClass *spc;
+    uint64_t buid, addr, mask;
+    uint32_t func;
+    int ret;
+
+    if (is_64bits) {
+        addr = rtas_ldq(param_buf, 0);
+        mask = rtas_ldq(param_buf, 1);
+        buid = ((uint64_t)rtas_ld(param_buf, 5) << 32) | rtas_ld(param_buf, 6);
+        func = rtas_ld(param_buf, 7);
+    } else {
+        addr = rtas_ld(param_buf, 0);
+        mask = rtas_ld(param_buf, 1);
+        buid = ((uint64_t)rtas_ld(param_buf, 3) << 32) | rtas_ld(param_buf, 4);
+        func = rtas_ld(param_buf, 5);
+    }
+
+    /* Find PHB */
+    sphb = spapr_pci_find_phb(spapr, buid);
+    if (!sphb) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+    if (!spc->eeh_inject_error) {
+        return RTAS_OUT_PARAM_ERROR;
+    }
+
+    /* Handle the request */
+    ret = spc->eeh_inject_error(sphb, func, addr, mask, is_64bits);
+    if (ret < 0) {
+        return RTAS_OUT_HW_ERROR;
+    }
+
+    return RTAS_OUT_SUCCESS;
+}
+
 static int pci_spapr_swizzle(int slot, int pin)
 {
     return (slot + pin) % PCI_NUM_PINS;
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index cca45ed..a3674ee 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -17,6 +17,8 @@
  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <asm/eeh.h>
+
 #include "hw/ppc/spapr.h"
 #include "hw/pci-host/spapr.h"
 #include "hw/pci/msix.h"
@@ -250,6 +252,59 @@ static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
     return RTAS_OUT_SUCCESS;
 }
 
+static int spapr_phb_vfio_eeh_inject_error(sPAPRPHBState *sphb,
+                                           uint32_t func, uint64_t addr,
+                                           uint64_t mask, bool is_64bits)
+{
+    sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+    struct vfio_eeh_pe_op op = {
+        .op = VFIO_EEH_PE_INJECT_ERR,
+        .argsz = sizeof(op)
+    };
+    int ret = RTAS_OUT_SUCCESS;
+
+    op.err.type = is_64bits ? EEH_ERR_TYPE_64 : EEH_ERR_TYPE_32;
+    op.err.addr = addr;
+    op.err.mask = mask;
+
+    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:
+        ret = RTAS_OUT_PARAM_ERROR;
+        goto out;
+    }
+
+    if (vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
+                             VFIO_EEH_PE_OP, &op) < 0) {
+        ret = RTAS_OUT_HW_ERROR;
+        goto out;
+    }
+
+    ret = RTAS_OUT_SUCCESS;
+out:
+    return ret;
+}
+
 static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -262,6 +317,7 @@ static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data)
     spc->eeh_get_state = spapr_phb_vfio_eeh_get_state;
     spc->eeh_reset = spapr_phb_vfio_eeh_reset;
     spc->eeh_configure = spapr_phb_vfio_eeh_configure;
+    spc->eeh_inject_error = spapr_phb_vfio_eeh_inject_error;
 }
 
 static const TypeInfo spapr_phb_vfio_info = {
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 0a9c904..d6894ee 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -637,6 +637,53 @@ out:
     rtas_st(rets, 1, ret);
 }
 
+static void rtas_ibm_errinjct(PowerPCCPU *cpu,
+                              sPAPRMachineState *spapr,
+                              uint32_t token, uint32_t nargs,
+                              target_ulong args, uint32_t nret,
+                              target_ulong rets)
+{
+    target_ulong param_buf;
+    uint32_t type, open_token;
+    int32_t ret;
+
+    /* Sanity check on number of arguments */
+    if ((nargs != 3) || (nret != 1)) {
+        ret = RTAS_OUT_PARAM_ERROR;
+        goto out;
+    }
+
+    /* Check if we have opened token */
+    open_token = rtas_ld(args, 1);
+    if (spapr->errinjct_token != open_token) {
+        ret = RTAS_OUT_TOKEN_OPENED;
+        goto out;
+    }
+
+    /* The parameter buffer should be 1KB aligned */
+    param_buf = rtas_ld(args, 2);
+    if (param_buf & 0x3ff) {
+        ret = RTAS_OUT_PARAM_ERROR;
+        goto out;
+    }
+
+    /* Check the error type */
+    type = rtas_ld(args, 0);
+    switch (type) {
+    case RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR:
+        ret = spapr_rtas_errinjct_ioa(spapr, param_buf, false);
+        break;
+    case RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR64:
+        ret = spapr_rtas_errinjct_ioa(spapr, param_buf, true);
+        break;
+    default:
+        ret = RTAS_OUT_PARAM_ERROR;
+    }
+
+out:
+    rtas_st(rets, 0, ret);
+}
+
 static void rtas_ibm_close_errinjct(PowerPCCPU *cpu,
                                     sPAPRMachineState *spapr,
                                     uint32_t token, uint32_t nargs,
@@ -728,6 +775,42 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
     int i;
     uint32_t lrdr_capacity[5];
     MachineState *machine = MACHINE(qdev_get_machine());
+    char errinjct_tokens[1024];
+    int fdt_offset, offset;
+    const char *tokens[] = {
+        "fatal",
+        "recovered-random-event",
+        "recovered-special-event",
+        "corrupted-page",
+        "corrupted-slb",
+        "translator-failure",
+        "ioa-bus-error",
+        "ioa-bus-error-64",
+        "platform-specific",
+        "corrupted-dcache-start",
+        "corrupted-dcache-end",
+        "corrupted-icache-start",
+        "corrupted-icache-end",
+        "corrupted-tlb-start",
+        "corrupted-tlb-end"
+    };
+
+    /* ibm,errinjct-tokens */
+    offset = 0;
+    for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+        offset += sprintf(errinjct_tokens + offset, "%s", tokens[i]);
+        errinjct_tokens[offset++] = '\0';
+        *(int *)(&errinjct_tokens[offset]) = i+1;
+        offset += sizeof(int);
+    }
+
+    fdt_offset = fdt_path_offset(fdt, "/rtas");
+    ret = fdt_setprop(fdt, fdt_offset, "ibm,errinjct-tokens",
+                      errinjct_tokens, offset);
+    if (ret < 0) {
+        fprintf(stderr, "Couldn't add ibm,errinjct-tokens\n");
+        return ret;
+    }
 
     ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
     if (ret < 0) {
@@ -823,6 +906,8 @@ static void core_rtas_register_types(void)
                         rtas_ibm_configure_connector);
     spapr_rtas_register(RTAS_IBM_OPEN_ERRINJCT, "ibm,open-errinjct",
                         rtas_ibm_open_errinjct);
+    spapr_rtas_register(RTAS_IBM_ERRINJCT, "ibm,errinjct",
+                        rtas_ibm_errinjct);
     spapr_rtas_register(RTAS_IBM_CLOSE_ERRINJCT, "ibm,close-errinjct",
                         rtas_ibm_close_errinjct);
 }
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 5322b56..069300d 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -53,6 +53,8 @@ struct sPAPRPHBClass {
     int (*eeh_get_state)(sPAPRPHBState *sphb, int *state);
     int (*eeh_reset)(sPAPRPHBState *sphb, int option);
     int (*eeh_configure)(sPAPRPHBState *sphb);
+    int (*eeh_inject_error)(sPAPRPHBState *sphb, uint32_t func,
+                            uint64_t addr, uint64_t mask, bool is_64bits);
 };
 
 typedef struct spapr_pci_msi {
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 30d9854..e3135e3 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -408,6 +408,24 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
 #define RTAS_SLOT_TEMP_ERR_LOG           1
 #define RTAS_SLOT_PERM_ERR_LOG           2
 
+/* ibm,errinjct */
+#define RTAS_ERRINJCT_TYPE_FATAL                1
+#define RTAS_ERRINJCT_TYPE_RANDOM_EVENT         2
+#define RTAS_ERRINJCT_TYPE_SPECIAL_EVENT        3
+#define RTAS_ERRINJCT_TYPE_CORRUPTED_PAGE       4
+#define RTAS_ERRINJCT_TYPE_CORRUPTED_SLB        5
+#define RTAS_ERRINJCT_TYPE_TRANSLATOR_FAILURE   6
+#define RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR        7
+#define RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR64      8
+#define RTAS_ERRINJCT_TYPE_PLATFORM_SPECIFIC    9
+#define RTAS_ERRINJCT_TYPE_DCACHE_START         10
+#define RTAS_ERRINJCT_TYPE_DCACHE_END           11
+#define RTAS_ERRINJCT_TYPE_ICACHE_START         12
+#define RTAS_ERRINJCT_TYPE_ICACHE_END           13
+#define RTAS_ERRINJCT_TYPE_TLB_START            14
+#define RTAS_ERRINJCT_TYPE_TLB_END              15
+#define RTAS_ERRINJCT_TYPE_UPSTREAM_IO_ERROR    16
+
 /* RTAS return codes */
 #define RTAS_OUT_SUCCESS            0
 #define RTAS_OUT_NO_ERRORS_FOUND    1
@@ -462,8 +480,9 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
 #define RTAS_IBM_SLOT_ERROR_DETAIL              (RTAS_TOKEN_BASE + 0x25)
 #define RTAS_IBM_OPEN_ERRINJCT                  (RTAS_TOKEN_BASE + 0x26)
 #define RTAS_IBM_CLOSE_ERRINJCT                 (RTAS_TOKEN_BASE + 0x27)
+#define RTAS_IBM_ERRINJCT                       (RTAS_TOKEN_BASE + 0x28)
 
-#define RTAS_TOKEN_MAX                          (RTAS_TOKEN_BASE + 0x28)
+#define RTAS_TOKEN_MAX                          (RTAS_TOKEN_BASE + 0x29)
 
 /* RTAS ibm,get-system-parameter token values */
 #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS      20
@@ -499,6 +518,11 @@ static inline uint32_t rtas_ld(target_ulong phys, int n)
     return ldl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n));
 }
 
+static inline uint64_t rtas_ldq(target_ulong phys, int n)
+{
+    return ldq_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 8*n));
+}
+
 static inline void rtas_st(target_ulong phys, int n, uint32_t val)
 {
     stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val);
@@ -595,6 +619,8 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname,
 int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
                       sPAPRTCETable *tcet);
 void spapr_pci_switch_vga(bool big_endian);
+int spapr_rtas_errinjct_ioa(sPAPRMachineState *spapr,
+                            target_ulong param_buf, bool is_64bits);
 void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
 void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
 
