diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 06d153a..c1b57d0 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -52,6 +52,9 @@ int chsc_sei_nt2_get_event(void *res)
             eccdf = (PciCcdfErr *)nt2_res->ccdf;
             eccdf->fid = cpu_to_be32(sei_cont->fid);
             eccdf->fh = cpu_to_be32(sei_cont->fh);
+            eccdf->e = cpu_to_be32(sei_cont->e);
+            eccdf->faddr = cpu_to_be64(sei_cont->faddr);
+            eccdf->pec = cpu_to_be16(sei_cont->pec);
             break;
         case 2: /* availability event */
             accdf = (PciCcdfAvail *)nt2_res->ccdf;
@@ -184,8 +187,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
     return NULL;
 }
 
-static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
-                                         uint32_t fid)
+static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
+                                    uint32_t fid, uint64_t faddr, uint32_t e)
 {
     SeiContainer *sei_cont = g_malloc0(sizeof(SeiContainer));
     S390pciState *s = S390_PCI_HOST_BRIDGE(
@@ -197,13 +200,28 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
 
     sei_cont->fh = fh;
     sei_cont->fid = fid;
-    sei_cont->cc = 2;
+    sei_cont->cc = cc;
     sei_cont->pec = pec;
+    sei_cont->faddr = faddr;
+    sei_cont->e = e;
 
     QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link);
     css_generate_css_crws(0);
 }
 
+static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
+                                         uint32_t fid)
+{
+    s390_pci_generate_event(2, pec, fh, fid, 0, 0);
+}
+
+static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh,
+                                          uint32_t fid, uint64_t faddr,
+                                          uint32_t e)
+{
+    s390_pci_generate_event(1, pec, fh, fid, faddr, e);
+}
+
 static void s390_pci_set_irq(void *opaque, int irq, int level)
 {
     /* nothing to do */
@@ -313,10 +331,30 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
         return ret;
     }
 
+    if (!pbdev->g_iota) {
+        pbdev->error_state = true;
+        pbdev->lgstg_blocked = true;
+        s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
+                                      addr, 0);
+        return ret;
+    }
+
+    if (addr < pbdev->pba || addr > pbdev->pal) {
+        pbdev->error_state = true;
+        pbdev->lgstg_blocked = true;
+        s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
+                                      addr, 0);
+        return ret;
+    }
+
     pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
                                    addr);
 
     if (!pte) {
+        pbdev->error_state = true;
+        pbdev->lgstg_blocked = true;
+        s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
+                                      addr, ERR_EVENT_Q_BIT);
         return ret;
     }
 
@@ -353,7 +391,7 @@ static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
 
     ind_addr = cpu_physical_memory_map(ind_loc, &len, 1);
     if (!ind_addr) {
-        error_report("%s: unable to access indicator", __func__);
+        s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0);
         return -1;
     }
     do {
@@ -374,12 +412,14 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
     uint64_t ind_bit;
     uint32_t sum_bit;
+    uint32_t e = 0;
 
     DPRINTF("write_msix data 0x%lx fid %d vec 0x%x\n", data, fid, vec);
 
     pbdev = s390_pci_find_dev_by_fid(fid);
     if (!pbdev) {
-        DPRINTF("msix_notify no dev\n");
+        e |= (vec << ERR_EVENT_MVN_OFFSET);
+        s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e);
         return;
     }
 
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
index fc567b7..2a9f735 100644
--- a/hw/s390x/s390-pci-bus.h
+++ b/hw/s390x/s390-pci-bus.h
@@ -33,6 +33,31 @@
 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
 #define HP_EVENT_STANDBY_TO_RESERVED  0x0308
 
+#define ERR_EVENT_INVALAS 0x1
+#define ERR_EVENT_OORANGE 0x2
+#define ERR_EVENT_INVALTF 0x3
+#define ERR_EVENT_TPROTE  0x4
+#define ERR_EVENT_APROTE  0x5
+#define ERR_EVENT_KEYE    0x6
+#define ERR_EVENT_INVALTE 0x7
+#define ERR_EVENT_INVALTL 0x8
+#define ERR_EVENT_TT      0x9
+#define ERR_EVENT_INVALMS 0xa
+#define ERR_EVENT_SERR    0xb
+#define ERR_EVENT_NOMSI   0x10
+#define ERR_EVENT_INVALBV 0x11
+#define ERR_EVENT_AIBV    0x12
+#define ERR_EVENT_AIRERR  0x13
+#define ERR_EVENT_FMBA    0x2a
+#define ERR_EVENT_FMBUP   0x2b
+#define ERR_EVENT_FMBPRO  0x2c
+#define ERR_EVENT_CCONF   0x30
+#define ERR_EVENT_SERVAC  0x3a
+#define ERR_EVENT_PERMERR 0x3b
+
+#define ERR_EVENT_Q_BIT 0x2
+#define ERR_EVENT_MVN_OFFSET 16
+
 #define ZPCI_MSI_VEC_BITS 11
 #define ZPCI_MSI_VEC_MASK 0x7ff
 
@@ -130,13 +155,15 @@ typedef struct SeiContainer {
     uint32_t fh;
     uint8_t cc;
     uint16_t pec;
+    uint64_t faddr;
+    uint32_t e;
 } SeiContainer;
 
 typedef struct PciCcdfErr {
     uint32_t reserved1;
     uint32_t fh;
     uint32_t fid;
-    uint32_t reserved2;
+    uint32_t e;
     uint64_t faddr;
     uint32_t reserved3;
     uint16_t reserved4;
@@ -189,10 +216,16 @@ typedef struct S390MsixInfo {
 typedef struct S390PCIBusDevice {
     PCIDevice *pdev;
     bool configured;
+    bool error_state;
+    bool lgstg_blocked;
     uint32_t fh;
     uint32_t fid;
     uint64_t g_iota;
+    uint64_t pba;
+    uint64_t pal;
     uint8_t isc;
+    uint16_t noi;
+    uint8_t sum;
     S390MsixInfo msix;
     AdapterRoutes routes;
     AddressSpace as;
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index e233046..8648594 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -230,6 +230,8 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
             break;
         case CLP_SET_DISABLE_PCI_FN:
             pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET);
+            pbdev->error_state = false;
+            pbdev->lgstg_blocked = false;
             stl_p(&ressetpci->fh, pbdev->fh);
             stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
             break;
@@ -330,6 +332,12 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
         return 0;
     }
 
+    if (pbdev->lgstg_blocked) {
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
+        return 0;
+    }
+
     if (pcias < 6) {
         if ((8 - (offset & 0x7)) < len) {
             program_interrupt(env, PGM_OPERAND, 4);
@@ -440,6 +448,12 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
         return 0;
     }
 
+    if (pbdev->lgstg_blocked) {
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
+        return 0;
+    }
+
     data = env->regs[r1];
     if (pcias < 6) {
         if ((8 - (offset & 0x7)) < len) {
@@ -586,6 +600,12 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
         return 0;
     }
 
+    if (pbdev->lgstg_blocked) {
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
+        return 0;
+    }
+
     mr = pbdev->pdev->io_regions[pcias].memory;
     if (!memory_region_access_valid(mr, env->regs[r3], len, true)) {
         program_interrupt(env, PGM_ADDRESSING, 6);
@@ -621,6 +641,9 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
     pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
     pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv);
     pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data));
+    pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data));
+    pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data));
+    pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data));
 
     DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
     return 0;
@@ -638,11 +661,47 @@ static int dereg_irqs(S390PCIBusDevice *pbdev)
     pbdev->routes.adapter.summary_offset = 0;
     pbdev->routes.adapter.ind_addr = 0;
     pbdev->routes.adapter.ind_offset = 0;
+    pbdev->isc = 0;
+    pbdev->noi = 0;
+    pbdev->sum = 0;
 
     DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
     return 0;
 }
 
+static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
+{
+    uint64_t pba = ldq_p(&fib.pba);
+    uint64_t pal = ldq_p(&fib.pal);
+    uint64_t g_iota = ldq_p(&fib.iota);
+    uint8_t dt = (g_iota >> 2) & 0x7;
+    uint8_t t = (g_iota >> 11) & 0x1;
+
+    if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
+        program_interrupt(env, PGM_OPERAND, 6);
+        return -EINVAL;
+    }
+
+    /* currently we only support designation type 1 with translation */
+    if (!(dt == ZPCI_IOTA_RTTO && t)) {
+        error_report("unsupported ioat dt %d t %d", dt, t);
+        program_interrupt(env, PGM_OPERAND, 6);
+        return -EINVAL;
+    }
+
+    pbdev->pba = pba;
+    pbdev->pal = pal;
+    pbdev->g_iota = g_iota;
+    return 0;
+}
+
+static void dereg_ioat(S390PCIBusDevice *pbdev)
+{
+    pbdev->pba = 0;
+    pbdev->pal = 0;
+    pbdev->g_iota = 0;
+}
+
 int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
 {
     CPUS390XState *env = &cpu->env;
@@ -650,6 +709,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
     uint32_t fh;
     ZpciFib fib;
     S390PCIBusDevice *pbdev;
+    uint64_t cc = ZPCI_PCI_LS_OK;
 
     cpu_synchronize_state(CPU(cpu));
 
@@ -676,36 +736,42 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
     cpu_physical_memory_read(fiba, (uint8_t *)&fib, sizeof(fib));
 
     switch (oc) {
-    case ZPCI_MOD_FC_REG_INT: {
-        pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data));
-        reg_irqs(env, pbdev, fib);
+    case ZPCI_MOD_FC_REG_INT:
+        if (reg_irqs(env, pbdev, fib)) {
+            cc = ZPCI_PCI_LS_ERR;
+        }
         break;
-    }
     case ZPCI_MOD_FC_DEREG_INT:
         dereg_irqs(pbdev);
         break;
     case ZPCI_MOD_FC_REG_IOAT:
-        if (ldq_p(&fib.pba) > ldq_p(&fib.pal)) {
-            program_interrupt(&cpu->env, PGM_OPERAND, 6);
-            return 0;
+        if (reg_ioat(env, pbdev, fib)) {
+            cc = ZPCI_PCI_LS_ERR;
         }
-        pbdev->g_iota = ldq_p(&fib.iota);
         break;
     case ZPCI_MOD_FC_DEREG_IOAT:
+        dereg_ioat(pbdev);
         break;
     case ZPCI_MOD_FC_REREG_IOAT:
+        dereg_ioat(pbdev);
+        if (reg_ioat(env, pbdev, fib)) {
+            cc = ZPCI_PCI_LS_ERR;
+        }
         break;
     case ZPCI_MOD_FC_RESET_ERROR:
+        pbdev->error_state = false;
+        pbdev->lgstg_blocked = false;
         break;
     case ZPCI_MOD_FC_RESET_BLOCK:
+        pbdev->lgstg_blocked = false;
         break;
     case ZPCI_MOD_FC_SET_MEASURE:
         break;
     default:
         program_interrupt(&cpu->env, PGM_OPERAND, 6);
-        return 0;
+        cc = ZPCI_PCI_LS_ERR;
     }
 
-    setcc(cpu, ZPCI_PCI_LS_OK);
+    setcc(cpu, cc);
     return 0;
 }
