diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index a7233e4..94fb866 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -32,6 +32,7 @@
 
 #include "hw/ppc/spapr.h"
 #include "hw/ppc/spapr_vio.h"
+#include "hw/pci-host/spapr.h"
 
 #include <libfdt.h>
 
@@ -268,6 +269,196 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu,
     rtas_st(rets, 0, ret);
 }
 
+static sPAPRErrInjct *rtas_find_errinjct_token(sPAPREnvironment *spapr,
+                                               uint32_t token_num)
+{
+    sPAPRErrInjct *open_token;
+
+    QLIST_FOREACH(open_token, &spapr->errinjct_open_tokens, list) {
+        if (open_token->token == token_num) {
+            return open_token;
+        }
+    }
+
+    return NULL;
+}
+
+static void rtas_ibm_open_errinjct(PowerPCCPU *cpu,
+                                   sPAPREnvironment *spapr,
+                                   uint32_t token, uint32_t nargs,
+                                   target_ulong args, uint32_t nret,
+                                   target_ulong rets)
+{
+    sPAPRErrInjct *open_token;
+    int32_t ret = RTAS_OUT_HW_ERROR;
+
+    /* Sanity check on number of arguments */
+    if ((nargs != 0) || (nret != 2)) {
+        goto out;
+    }
+
+    /* Allocate token */
+    open_token = g_malloc(sizeof(*open_token));
+    if (!open_token) {
+        goto out;
+    }
+    open_token->token = spapr->errinjct_open_token_cnt++;
+    QLIST_INSERT_HEAD(&spapr->errinjct_open_tokens, open_token, list);
+
+    /* Success */
+    rtas_st(rets, 0, open_token->token);
+    ret = RTAS_OUT_SUCCESS;
+out:
+    rtas_st(rets, 1, ret);
+}
+
+static char *rtas_do_errinjct_ioa(sPAPREnvironment *spapr,
+                                sPAPRErrInjctIOA *ioa)
+{
+    sPAPRPHBState *sphb;
+    sPAPRPHBClass *info;
+
+    /* Sanity check on argument */
+    if (!spapr || !ioa) {
+        return NULL;
+    }
+
+    /* Invalid option ? */
+    if (ioa->func > RTAS_ERRINJCT_IOA_DMA_WR_MEM_TARGET) {
+        return NULL;
+    }
+
+    /* Find PHB */
+    sphb = spapr_find_phb(spapr, ioa->buid);
+    if (!sphb) {
+        return NULL;
+    }
+
+    /* PHB doesn't support error injection ? */
+    info = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+    if (!info->format_errinjct_cmd) {
+        return NULL;
+    }
+
+    return info->format_errinjct_cmd(sphb, ioa);
+}
+
+static void rtas_ibm_errinjct(PowerPCCPU *cpu,
+                              sPAPREnvironment *spapr,
+                              uint32_t token, uint32_t nargs,
+                              target_ulong args, uint32_t nret,
+                              target_ulong rets)
+{
+    sPAPRErrInjctIOA ioa;
+    sPAPRErrInjct *open_token;
+    uint32_t token_num, ei_token;
+    target_ulong param_buf;
+    char *pbuf;
+    int fd;
+    int32_t ret = RTAS_OUT_PARAM_ERROR;
+
+    /* Sanity check on number of arguments */
+    if ((nargs != 3) || (nret != 1)) {
+        goto out;
+    }
+
+    /* Check if we have opened token */
+    token_num = rtas_ld(args, 1);
+    open_token = rtas_find_errinjct_token(spapr, token_num);
+    if (!open_token) {
+        goto out;
+    }
+
+    /* Argument buffer should be aligned with 1KB */
+    param_buf = rtas_ld(args, 2);
+    if (param_buf & 0x3ff) {
+        goto out;
+    }
+
+    /* We only support IOA error for now */
+    ei_token = rtas_ld(args, 0);
+    switch (ei_token) {
+    case RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR:
+        ioa.ei_token = RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR;
+        ioa.addr = rtas_ld(param_buf, 0);
+        ioa.mask = rtas_ld(param_buf, 1);
+        ioa.cfg_addr = rtas_ld(param_buf, 2);
+        ioa.buid = ((uint64_t)rtas_ld(param_buf, 3) << 32) | rtas_ld(param_buf, 4);
+        ioa.func = rtas_ld(param_buf, 5);
+
+        break;
+    case RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR64:
+        ioa.ei_token = RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR64;
+        ioa.addr = ((uint64_t)rtas_ld(param_buf, 0) << 32) | rtas_ld(param_buf, 1);
+        ioa.mask = ((uint64_t)rtas_ld(param_buf, 2) << 32) | rtas_ld(param_buf, 3);
+        ioa.cfg_addr = rtas_ld(param_buf, 4);
+        ioa.buid = ((uint64_t)rtas_ld(param_buf, 5) << 32) | rtas_ld(param_buf, 6);
+        ioa.func = rtas_ld(param_buf, 7);
+
+        break;
+    default:
+        goto out;
+    }
+
+    /* We have nothing written to sysfs */
+    pbuf = rtas_do_errinjct_ioa(spapr, &ioa);
+    if (!pbuf) {
+        goto out;
+    }
+
+    /* Open firmware sysfs file */
+    fd = qemu_open("/sys/firmware/opal/errinjct", O_WRONLY);
+    if (fd < 0) {
+        goto free_buf_out;
+    }
+
+    /* Inject error */
+    if (pwrite(fd, pbuf, strlen(pbuf), 0) != strlen(pbuf)) {
+        goto close_fd_out;
+    }
+
+    /* Success */
+    ret = RTAS_OUT_SUCCESS;
+
+close_fd_out:
+    qemu_close(fd);
+free_buf_out:
+    g_free(pbuf);
+out:
+    rtas_st(rets, 0, ret);
+}
+
+static void rtas_ibm_close_errinjct(PowerPCCPU *cpu,
+                                    sPAPREnvironment *spapr,
+                                    uint32_t token, uint32_t nargs,
+                                    target_ulong args, uint32_t nret,
+                                    target_ulong rets)
+{
+    sPAPRErrInjct *open_token;
+    uint32_t token_num;
+    int32_t ret = RTAS_OUT_HW_ERROR;
+
+    /* Sanity check on number of arguments */
+    if ((nargs != 1) || (nret != 1)) {
+        goto out;
+    }
+
+    /* Do we have opened token ? */
+    token_num = rtas_ld(args, 0);
+    open_token = rtas_find_errinjct_token(spapr, token_num);
+    if (!open_token) {
+        goto out;
+    }
+
+    QLIST_REMOVE(open_token, list);
+    g_free(open_token);
+
+    /* Success */
+    ret = RTAS_OUT_SUCCESS;
+out:
+    rtas_st(rets, 0, ret);
+}
+
 static struct rtas_call {
     const char *name;
     spapr_rtas_fn fn;
@@ -393,6 +584,13 @@ static void core_rtas_register_types(void)
                         rtas_ibm_get_system_parameter);
     spapr_rtas_register("ibm,set-system-parameter",
                         rtas_ibm_set_system_parameter);
+
+    spapr_rtas_register("ibm,open-errinjct",
+                        rtas_ibm_open_errinjct);
+    spapr_rtas_register("ibm,errinjct",
+                        rtas_ibm_errinjct);
+    spapr_rtas_register("ibm,close-errinjct",
+                        rtas_ibm_close_errinjct);
 }
 
 type_init(core_rtas_register_types)
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 4b1f6aa..5fd64d5 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -41,15 +41,26 @@
 #define SPAPR_PCI_HOST_BRIDGE_GET_CLASS(obj) \
      OBJECT_GET_CLASS(sPAPRPHBClass, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE)
 
+typedef struct sPAPRErrInjctIOA sPAPRErrInjctIOA;
 typedef struct sPAPRPHBClass sPAPRPHBClass;
 typedef struct sPAPRPHBState sPAPRPHBState;
 typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState;
 
+struct sPAPRErrInjctIOA {
+    uint32_t ei_token;
+    uint64_t addr;
+    uint64_t mask;
+    uint64_t buid;
+    uint32_t cfg_addr;
+    uint32_t func;
+};
+
 struct sPAPRPHBClass {
     PCIHostBridgeClass parent_class;
 
     void (*finish_realize)(sPAPRPHBState *sphb, Error **errp);
     int (*eeh_handler)(sPAPRPHBState *sphb, int req, int opt);
+    char *(*format_errinjct_cmd)(sPAPRPHBState *sphb, sPAPRErrInjctIOA *ioa);
 };
 
 struct sPAPRPHBState {
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 7167ab2..79f7eb1 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -9,6 +9,13 @@ struct VIOsPAPRBus;
 struct sPAPRPHBState;
 struct sPAPRNVRAM;
 
+typedef struct sPAPRErrInjct sPAPRErrInjct;
+
+struct sPAPRErrInjct {
+	uint32_t token;
+	QLIST_ENTRY(sPAPRErrInjct) list;
+};
+
 #define HPTE64_V_HPTE_DIRTY     0x0000000000000040ULL
 
 typedef struct sPAPREnvironment {
@@ -41,6 +48,10 @@ typedef struct sPAPREnvironment {
     bool htab_first_pass;
     int htab_fd;
 
+    /* Error injection tokens */
+    uint32_t errinjct_open_token_cnt;
+    QLIST_HEAD(, sPAPRErrInjct) errinjct_open_tokens;
+
     /* platform state - sensors and indicators */
     uint32_t state;
 } sPAPREnvironment;
@@ -439,6 +450,30 @@ 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_IOA_BUS_ERROR	7
+#define RTAS_ERRINJCT_TYPE_IOA_BUS_ERROR64	8
+#define  RTAS_ERRINJCT_IOA_LD_MEM_ADDR		0
+#define  RTAS_ERRINJCT_IOA_LD_MEM_DATA		1
+#define  RTAS_ERRINJCT_IOA_LD_IO_ADDR		2
+#define  RTAS_ERRINJCT_IOA_LD_IO_DATA		3
+#define  RTAS_ERRINJCT_IOA_LD_CONFIG_ADDR	4
+#define  RTAS_ERRINJCT_IOA_LD_CONFIG_DATA	5
+#define  RTAS_ERRINJCT_IOA_ST_MEM_ADDR		6
+#define  RTAS_ERRINJCT_IOA_ST_MEM_DATA		7
+#define  RTAS_ERRINJCT_IOA_ST_IO_ADDR		8
+#define  RTAS_ERRINJCT_IOA_ST_IO_DATA		9
+#define  RTAS_ERRINJCT_IOA_ST_CONFIG_ADDR	10
+#define  RTAS_ERRINJCT_IOA_ST_CONFIG_DATA	11
+#define  RTAS_ERRINJCT_IOA_DMA_RD_MEM_ADDR	12
+#define  RTAS_ERRINJCT_IOA_DMA_RD_MEM_DATA	13
+#define  RTAS_ERRINJCT_IOA_DMA_RD_MEM_MASTER	14
+#define  RTAS_ERRINJCT_IOA_DMA_RD_MEM_TARGET	15
+#define  RTAS_ERRINJCT_IOA_DMA_WR_MEM_ADDR	16
+#define  RTAS_ERRINJCT_IOA_DMA_WR_MEM_DATA	17
+#define  RTAS_ERRINJCT_IOA_DMA_WR_MEM_MASTER	18
+#define  RTAS_ERRINJCT_IOA_DMA_WR_MEM_TARGET	19
+
 /* RTAS return codes */
 #define RTAS_OUT_SUCCESS            0
 #define RTAS_OUT_NO_ERRORS_FOUND    1
