瀏覽代碼

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20160711' into staging

Last round of s390x patches for 2.7:
- A large update of the s390x PCI code, bringing it in line with
  the architecture
- Fixes and improvements in the ipl (boot) code
- Refactoring in the css code

# gpg: Signature made Mon 11 Jul 2016 09:04:51 BST
# gpg:                using RSA key 0xDECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20160711: (25 commits)
  s390x/pci: make hot-unplug handler smoother
  s390x/pci: replace fid with idx in msg data of msix
  s390x/pci: fix stpcifc_service_call
  s390x/pci: refactor list_pci
  s390x/pci: refactor s390_pci_find_dev_by_idx
  s390x/pci: add checkings in CLP_SET_PCI_FN
  s390x/pci: enable zpci hot-plug/hot-unplug
  s390x/pci: enable uid-checking
  s390x/pci: introduce S390PCIBusDevice qdev
  s390x/pci: introduce S390PCIIOMMU
  s390x/pci: introduce S390PCIBus
  s390x/pci: enforce zPCI state checking
  s390x/pci: refactor s390_pci_find_dev_by_fh
  s390x/pci: unify FH_ macros
  s390x/pci: write fid in CLP_QUERY_PCI_FN
  s390x/pci: acceleration for getting S390pciState
  s390x/pci: fix failures of dma map/unmap
  s390x/css: Unplug handler of virtual css bridge
  s390x/css: Factor out virtual css bridge and bus
  s390x/css: use define for "virtual-css-bridge" literal
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 9 年之前
父節點
當前提交
f1ef557866

+ 2 - 0
hw/s390x/Makefile.objs

@@ -8,6 +8,8 @@ obj-y += ipl.o
 obj-y += css.o
 obj-y += css.o
 obj-y += s390-virtio-ccw.o
 obj-y += s390-virtio-ccw.o
 obj-y += virtio-ccw.o
 obj-y += virtio-ccw.o
+obj-y += css-bridge.o
+obj-y += ccw-device.o
 obj-y += s390-pci-bus.o s390-pci-inst.o
 obj-y += s390-pci-bus.o s390-pci-inst.o
 obj-y += s390-skeys.o
 obj-y += s390-skeys.o
 obj-$(CONFIG_KVM) += s390-skeys-kvm.o
 obj-$(CONFIG_KVM) += s390-skeys-kvm.o

+ 27 - 0
hw/s390x/ccw-device.c

@@ -0,0 +1,27 @@
+/*
+ * Common device infrastructure for devices in the virtual css
+ *
+ * Copyright 2016 IBM Corp.
+ * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+#include "qemu/osdep.h"
+#include "ccw-device.h"
+
+static const TypeInfo ccw_device_info = {
+    .name = TYPE_CCW_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CcwDevice),
+    .class_size = sizeof(CCWDeviceClass),
+    .abstract = true,
+};
+
+static void ccw_device_register(void)
+{
+    type_register_static(&ccw_device_info);
+}
+
+type_init(ccw_device_register)

+ 43 - 0
hw/s390x/ccw-device.h

@@ -0,0 +1,43 @@
+/*
+ * Common device infrastructure for devices in the virtual css
+ *
+ * Copyright 2016 IBM Corp.
+ * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef HW_S390X_CCW_DEVICE_H
+#define HW_S390X_CCW_DEVICE_H
+#include "qom/object.h"
+#include "hw/qdev-core.h"
+#include "hw/s390x/css.h"
+
+typedef struct CcwDevice {
+    DeviceState parent_obj;
+    SubchDev *sch;
+    /* <cssid>.<ssid>.<device number> */
+    CssDevId bus_id;
+} CcwDevice;
+
+typedef struct CCWDeviceClass {
+    DeviceClass parent_class;
+    void (*unplug)(HotplugHandler *, DeviceState *, Error **);
+} CCWDeviceClass;
+
+static inline CcwDevice *to_ccw_dev_fast(DeviceState *d)
+{
+    return container_of(d, CcwDevice, parent_obj);
+}
+
+#define TYPE_CCW_DEVICE "ccw-device"
+
+#define CCW_DEVICE(obj) OBJECT_CHECK(CcwDevice, (obj), TYPE_CCW_DEVICE)
+#define CCW_DEVICE_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(CCWDeviceClass, (obj), TYPE_CCW_DEVICE)
+#define CCW_DEVICE_CLASS(klass) \
+    OBJECT_CLASS_CHECK(CCWDeviceClass, (klass), TYPE_CCW_DEVICE)
+
+#endif

+ 124 - 0
hw/s390x/css-bridge.c

@@ -0,0 +1,124 @@
+/*
+ * css bridge implementation
+ *
+ * Copyright 2012,2016 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *            Pierre Morel <pmorel@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hotplug.h"
+#include "hw/sysbus.h"
+#include "qemu/bitops.h"
+#include "hw/s390x/css.h"
+#include "ccw-device.h"
+#include "hw/s390x/css-bridge.h"
+
+/*
+ * Invoke device-specific unplug handler, disable the subchannel
+ * (including sending a channel report to the guest) and remove the
+ * device from the virtual css bus.
+ */
+static void ccw_device_unplug(HotplugHandler *hotplug_dev,
+                              DeviceState *dev, Error **errp)
+{
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
+    CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev);
+    SubchDev *sch = ccw_dev->sch;
+    Error *err = NULL;
+
+    if (k->unplug) {
+        k->unplug(hotplug_dev, dev, &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+
+    /*
+     * We should arrive here only for device_del, since we don't support
+     * direct hot(un)plug of channels.
+     */
+    assert(sch != NULL);
+    /* Subchannel is now disabled and no longer valid. */
+    sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA |
+                                     PMCW_FLAGS_MASK_DNV);
+
+    css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0);
+
+    object_unparent(OBJECT(dev));
+}
+
+static void virtual_css_bus_reset(BusState *qbus)
+{
+    /* This should actually be modelled via the generic css */
+    css_reset();
+}
+
+static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->reset = virtual_css_bus_reset;
+}
+
+static const TypeInfo virtual_css_bus_info = {
+    .name = TYPE_VIRTUAL_CSS_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(VirtualCssBus),
+    .class_init = virtual_css_bus_class_init,
+};
+
+VirtualCssBus *virtual_css_bus_init(void)
+{
+    VirtualCssBus *cbus;
+    BusState *bus;
+    DeviceState *dev;
+
+    /* Create bridge device */
+    dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE);
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+    bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css");
+    cbus = VIRTUAL_CSS_BUS(bus);
+
+    /* Enable hotplugging */
+    qbus_set_hotplug_handler(bus, dev, &error_abort);
+
+    return cbus;
+ }
+
+/***************** Virtual-css Bus Bridge Device ********************/
+
+static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
+{
+    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    hc->unplug = ccw_device_unplug;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo virtual_css_bridge_info = {
+    .name          = TYPE_VIRTUAL_CSS_BRIDGE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .class_init    = virtual_css_bridge_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HOTPLUG_HANDLER },
+        { }
+    }
+};
+
+static void virtual_css_register(void)
+{
+    type_register_static(&virtual_css_bridge_info);
+    type_register_static(&virtual_css_bus_info);
+}
+
+type_init(virtual_css_register)

+ 143 - 0
hw/s390x/css.c

@@ -1340,6 +1340,116 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
     return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid];
     return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid];
 }
 }
 
 
+/**
+ * Return free device number in subchannel set.
+ *
+ * Return index of the first free device number in the subchannel set
+ * identified by @p cssid and @p ssid, beginning the search at @p
+ * start and wrapping around at MAX_DEVNO. Return a value exceeding
+ * MAX_SCHID if there are no free device numbers in the subchannel
+ * set.
+ */
+static uint32_t css_find_free_devno(uint8_t cssid, uint8_t ssid,
+                                    uint16_t start)
+{
+    uint32_t round;
+
+    for (round = 0; round <= MAX_DEVNO; round++) {
+        uint16_t devno = (start + round) % MAX_DEVNO;
+
+        if (!css_devno_used(cssid, ssid, devno)) {
+            return devno;
+        }
+    }
+    return MAX_DEVNO + 1;
+}
+
+/**
+ * Return first free subchannel (id) in subchannel set.
+ *
+ * Return index of the first free subchannel in the subchannel set
+ * identified by @p cssid and @p ssid, if there is any. Return a value
+ * exceeding MAX_SCHID if there are no free subchannels in the
+ * subchannel set.
+ */
+static uint32_t css_find_free_subch(uint8_t cssid, uint8_t ssid)
+{
+    uint32_t schid;
+
+    for (schid = 0; schid <= MAX_SCHID; schid++) {
+        if (!css_find_subch(1, cssid, ssid, schid)) {
+            return schid;
+        }
+    }
+    return MAX_SCHID + 1;
+}
+
+/**
+ * Return first free subchannel (id) in subchannel set for a device number
+ *
+ * Verify the device number @p devno is not used yet in the subchannel
+ * set identified by @p cssid and @p ssid. Set @p schid to the index
+ * of the first free subchannel in the subchannel set, if there is
+ * any. Return true if everything succeeded and false otherwise.
+ */
+static bool css_find_free_subch_for_devno(uint8_t cssid, uint8_t ssid,
+                                          uint16_t devno, uint16_t *schid,
+                                          Error **errp)
+{
+    uint32_t free_schid;
+
+    assert(schid);
+    if (css_devno_used(cssid, ssid, devno)) {
+        error_setg(errp, "Device %x.%x.%04x already exists",
+                   cssid, ssid, devno);
+        return false;
+    }
+    free_schid = css_find_free_subch(cssid, ssid);
+    if (free_schid > MAX_SCHID) {
+        error_setg(errp, "No free subchannel found for %x.%x.%04x",
+                   cssid, ssid, devno);
+        return false;
+    }
+    *schid = free_schid;
+    return true;
+}
+
+/**
+ * Return first free subchannel (id) and device number
+ *
+ * Locate the first free subchannel and first free device number in
+ * any of the subchannel sets of the channel subsystem identified by
+ * @p cssid. Return false if no free subchannel / device number could
+ * be found. Otherwise set @p ssid, @p devno and @p schid to identify
+ * the available subchannel and device number and return true.
+ *
+ * May modify @p ssid, @p devno and / or @p schid even if no free
+ * subchannel / device number could be found.
+ */
+static bool css_find_free_subch_and_devno(uint8_t cssid, uint8_t *ssid,
+                                          uint16_t *devno, uint16_t *schid,
+                                          Error **errp)
+{
+    uint32_t free_schid, free_devno;
+
+    assert(ssid && devno && schid);
+    for (*ssid = 0; *ssid <= MAX_SSID; (*ssid)++) {
+        free_schid = css_find_free_subch(cssid, *ssid);
+        if (free_schid > MAX_SCHID) {
+            continue;
+        }
+        free_devno = css_find_free_devno(cssid, *ssid, free_schid);
+        if (free_devno > MAX_DEVNO) {
+            continue;
+        }
+        *schid = free_schid;
+        *devno = free_devno;
+        return true;
+    }
+    error_setg(errp, "Virtual channel subsystem is full!");
+    return false;
+}
+
 bool css_subch_visible(SubchDev *sch)
 bool css_subch_visible(SubchDev *sch)
 {
 {
     if (sch->ssid > channel_subsys.max_ssid) {
     if (sch->ssid > channel_subsys.max_ssid) {
@@ -1762,3 +1872,36 @@ PropertyInfo css_devid_propinfo = {
     .get = get_css_devid,
     .get = get_css_devid,
     .set = set_css_devid,
     .set = set_css_devid,
 };
 };
+
+SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp)
+{
+    uint16_t schid = 0;
+    SubchDev *sch;
+
+    if (bus_id.valid) {
+        /* Enforce use of virtual cssid. */
+        if (bus_id.cssid != VIRTUAL_CSSID) {
+            error_setg(errp, "cssid %hhx not valid for virtual devices",
+                       bus_id.cssid);
+            return NULL;
+        }
+        if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid,
+                                           bus_id.devid, &schid, errp)) {
+            return NULL;
+        }
+    } else {
+        bus_id.cssid = VIRTUAL_CSSID;
+        if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid,
+                                           &bus_id.devid, &schid, errp)) {
+            return NULL;
+        }
+    }
+
+    sch = g_malloc0(sizeof(*sch));
+    sch->cssid = bus_id.cssid;
+    sch->ssid = bus_id.ssid;
+    sch->devno = bus_id.devid;
+    sch->schid = schid;
+    css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch);
+    return sch;
+}

+ 32 - 5
hw/s390x/ipl.c

@@ -69,8 +69,8 @@ static const VMStateDescription vmstate_ipl = {
     .version_id = 0,
     .version_id = 0,
     .minimum_version_id = 0,
     .minimum_version_id = 0,
     .fields = (VMStateField[]) {
     .fields = (VMStateField[]) {
-        VMSTATE_UINT64(start_addr, S390IPLState),
-        VMSTATE_UINT64(bios_start_addr, S390IPLState),
+        VMSTATE_UINT64(compat_start_addr, S390IPLState),
+        VMSTATE_UINT64(compat_bios_start_addr, S390IPLState),
         VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock),
         VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock),
         VMSTATE_BOOL(iplb_valid, S390IPLState),
         VMSTATE_BOOL(iplb_valid, S390IPLState),
         VMSTATE_UINT8(cssid, S390IPLState),
         VMSTATE_UINT8(cssid, S390IPLState),
@@ -192,6 +192,13 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
             stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
             stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
         }
         }
     }
     }
+    /*
+     * Don't ever use the migrated values, they could come from a different
+     * BIOS and therefore don't work. But still migrate the values, so
+     * QEMUs relying on it don't break.
+     */
+    ipl->compat_start_addr = ipl->start_addr;
+    ipl->compat_bios_start_addr = ipl->bios_start_addr;
     qemu_register_reset(qdev_reset_all_fn, dev);
     qemu_register_reset(qdev_reset_all_fn, dev);
 error:
 error:
     error_propagate(errp, err);
     error_propagate(errp, err);
@@ -214,10 +221,14 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
 
 
     dev_st = get_boot_device(0);
     dev_st = get_boot_device(0);
     if (dev_st) {
     if (dev_st) {
-        VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast(
-            OBJECT(qdev_get_parent_bus(dev_st)->parent),
+        VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *)
+            object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent),
                 TYPE_VIRTIO_CCW_DEVICE);
                 TYPE_VIRTIO_CCW_DEVICE);
-        if (ccw_dev) {
+        SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st),
+                                                            TYPE_SCSI_DEVICE);
+        if (virtio_ccw_dev) {
+            CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev);
+
             ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
             ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
             ipl->iplb.blk0_len =
             ipl->iplb.blk0_len =
                 cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
                 cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
@@ -225,6 +236,22 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
             ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
             ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
             ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
             ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
             return true;
             return true;
+        } else if (sd) {
+            SCSIBus *bus = scsi_bus_from_device(sd);
+            VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus);
+            VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev);
+            CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw);
+
+            ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+            ipl->iplb.blk0_len =
+                cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
+            ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
+            ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
+            ipl->iplb.scsi.target = cpu_to_be16(sd->id);
+            ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
+            ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+            ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+            return true;
         }
         }
     }
     }
 
 

+ 15 - 0
hw/s390x/ipl.h

@@ -46,6 +46,16 @@ struct IplBlockFcp {
 } QEMU_PACKED;
 } QEMU_PACKED;
 typedef struct IplBlockFcp IplBlockFcp;
 typedef struct IplBlockFcp IplBlockFcp;
 
 
+struct IplBlockQemuScsi {
+    uint32_t lun;
+    uint16_t target;
+    uint16_t channel;
+    uint8_t  reserved0[77];
+    uint8_t  ssid;
+    uint16_t devno;
+} QEMU_PACKED;
+typedef struct IplBlockQemuScsi IplBlockQemuScsi;
+
 union IplParameterBlock {
 union IplParameterBlock {
     struct {
     struct {
         uint32_t len;
         uint32_t len;
@@ -59,6 +69,7 @@ union IplParameterBlock {
         union {
         union {
             IplBlockCcw ccw;
             IplBlockCcw ccw;
             IplBlockFcp fcp;
             IplBlockFcp fcp;
+            IplBlockQemuScsi scsi;
         };
         };
     } QEMU_PACKED;
     } QEMU_PACKED;
     struct {
     struct {
@@ -82,7 +93,9 @@ struct S390IPLState {
     /*< private >*/
     /*< private >*/
     DeviceState parent_obj;
     DeviceState parent_obj;
     uint64_t start_addr;
     uint64_t start_addr;
+    uint64_t compat_start_addr;
     uint64_t bios_start_addr;
     uint64_t bios_start_addr;
+    uint64_t compat_bios_start_addr;
     bool enforce_bios;
     bool enforce_bios;
     IplParameterBlock iplb;
     IplParameterBlock iplb;
     bool iplb_valid;
     bool iplb_valid;
@@ -102,10 +115,12 @@ typedef struct S390IPLState S390IPLState;
 
 
 #define S390_IPL_TYPE_FCP 0x00
 #define S390_IPL_TYPE_FCP 0x00
 #define S390_IPL_TYPE_CCW 0x02
 #define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_QEMU_SCSI 0xff
 
 
 #define S390_IPLB_HEADER_LEN 8
 #define S390_IPLB_HEADER_LEN 8
 #define S390_IPLB_MIN_CCW_LEN 200
 #define S390_IPLB_MIN_CCW_LEN 200
 #define S390_IPLB_MIN_FCP_LEN 384
 #define S390_IPLB_MIN_FCP_LEN 384
+#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
 
 
 static inline bool iplb_valid_len(IplParameterBlock *iplb)
 static inline bool iplb_valid_len(IplParameterBlock *iplb)
 {
 {

+ 440 - 139
hw/s390x/s390-pci-bus.c

@@ -12,6 +12,8 @@
  */
  */
 
 
 #include "qemu/osdep.h"
 #include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
 #include "qemu-common.h"
 #include "qemu-common.h"
 #include "cpu.h"
 #include "cpu.h"
 #include "s390-pci-bus.h"
 #include "s390-pci-bus.h"
@@ -29,6 +31,19 @@
     do { } while (0)
     do { } while (0)
 #endif
 #endif
 
 
+static S390pciState *s390_get_phb(void)
+{
+    static S390pciState *phb;
+
+    if (!phb) {
+        phb = S390_PCI_HOST_BRIDGE(
+            object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
+        assert(phb != NULL);
+    }
+
+    return phb;
+}
+
 int chsc_sei_nt2_get_event(void *res)
 int chsc_sei_nt2_get_event(void *res)
 {
 {
     ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res;
     ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res;
@@ -36,12 +51,7 @@ int chsc_sei_nt2_get_event(void *res)
     PciCcdfErr *eccdf;
     PciCcdfErr *eccdf;
     int rc = 1;
     int rc = 1;
     SeiContainer *sei_cont;
     SeiContainer *sei_cont;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
-
-    if (!s) {
-        return rc;
-    }
+    S390pciState *s = s390_get_phb();
 
 
     sei_cont = QTAILQ_FIRST(&s->pending_sei);
     sei_cont = QTAILQ_FIRST(&s->pending_sei);
     if (sei_cont) {
     if (sei_cont) {
@@ -76,30 +86,40 @@ int chsc_sei_nt2_get_event(void *res)
 
 
 int chsc_sei_nt2_have_event(void)
 int chsc_sei_nt2_have_event(void)
 {
 {
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
+    S390pciState *s = s390_get_phb();
 
 
-    if (!s) {
-        return 0;
+    return !QTAILQ_EMPTY(&s->pending_sei);
+}
+
+S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev)
+{
+    int idx = 0;
+    S390PCIBusDevice *dev = NULL;
+    S390pciState *s = s390_get_phb();
+
+    if (pbdev) {
+        idx = (pbdev->fh & FH_MASK_INDEX) + 1;
     }
     }
 
 
-    return !QTAILQ_EMPTY(&s->pending_sei);
+    for (; idx < PCI_SLOT_MAX; idx++) {
+        dev = s->pbdev[idx];
+        if (dev && dev->state != ZPCI_FS_RESERVED) {
+            return dev;
+        }
+    }
+
+    return NULL;
 }
 }
 
 
 S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid)
 S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid)
 {
 {
     S390PCIBusDevice *pbdev;
     S390PCIBusDevice *pbdev;
     int i;
     int i;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
-
-    if (!s) {
-        return NULL;
-    }
+    S390pciState *s = s390_get_phb();
 
 
     for (i = 0; i < PCI_SLOT_MAX; i++) {
     for (i = 0; i < PCI_SLOT_MAX; i++) {
-        pbdev = &s->pbdev[i];
-        if ((pbdev->fh != 0) && (pbdev->fid == fid)) {
+        pbdev = s->pbdev[i];
+        if (pbdev && pbdev->fid == fid) {
             return pbdev;
             return pbdev;
         }
         }
     }
     }
@@ -118,16 +138,22 @@ void s390_pci_sclp_configure(SCCB *sccb)
         goto out;
         goto out;
     }
     }
 
 
-    if (pbdev) {
-        if (pbdev->configured) {
-            rc = SCLP_RC_NO_ACTION_REQUIRED;
-        } else {
-            pbdev->configured = true;
-            rc = SCLP_RC_NORMAL_COMPLETION;
-        }
-    } else {
+    if (!pbdev) {
         DPRINTF("sclp config no dev found\n");
         DPRINTF("sclp config no dev found\n");
         rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
         rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
+        goto out;
+    }
+
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+        rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
+        break;
+    case ZPCI_FS_STANDBY:
+        pbdev->state = ZPCI_FS_DISABLED;
+        rc = SCLP_RC_NORMAL_COMPLETION;
+        break;
+    default:
+        rc = SCLP_RC_NO_ACTION_REQUIRED;
     }
     }
 out:
 out:
     psccb->header.response_code = cpu_to_be16(rc);
     psccb->header.response_code = cpu_to_be16(rc);
@@ -144,81 +170,96 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
         goto out;
         goto out;
     }
     }
 
 
-    if (pbdev) {
-        if (!pbdev->configured) {
-            rc = SCLP_RC_NO_ACTION_REQUIRED;
-        } else {
-            if (pbdev->summary_ind) {
-                pci_dereg_irqs(pbdev);
-            }
-            if (pbdev->iommu_enabled) {
-                pci_dereg_ioat(pbdev);
-            }
-            pbdev->configured = false;
-            rc = SCLP_RC_NORMAL_COMPLETION;
-        }
-    } else {
+    if (!pbdev) {
         DPRINTF("sclp deconfig no dev found\n");
         DPRINTF("sclp deconfig no dev found\n");
         rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
         rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
+        goto out;
+    }
+
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+        rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
+        break;
+    case ZPCI_FS_STANDBY:
+        rc = SCLP_RC_NO_ACTION_REQUIRED;
+        break;
+    default:
+        if (pbdev->summary_ind) {
+            pci_dereg_irqs(pbdev);
+        }
+        if (pbdev->iommu_enabled) {
+            pci_dereg_ioat(pbdev);
+        }
+        pbdev->state = ZPCI_FS_STANDBY;
+        rc = SCLP_RC_NORMAL_COMPLETION;
+
+        if (pbdev->release_timer) {
+            qdev_unplug(DEVICE(pbdev->pdev), NULL);
+        }
     }
     }
 out:
 out:
     psccb->header.response_code = cpu_to_be16(rc);
     psccb->header.response_code = cpu_to_be16(rc);
 }
 }
 
 
-static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
+static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid)
 {
 {
-    return PCI_SLOT(pdev->devfn);
-}
+    int i;
+    S390PCIBusDevice *pbdev;
+    S390pciState *s = s390_get_phb();
 
 
-static uint32_t s390_pci_get_pfh(PCIDevice *pdev)
-{
-    return PCI_SLOT(pdev->devfn) | FH_VIRT;
+    for (i = 0; i < PCI_SLOT_MAX; i++) {
+        pbdev = s->pbdev[i];
+        if (!pbdev) {
+            continue;
+        }
+
+        if (pbdev->uid == uid) {
+            return pbdev;
+        }
+    }
+
+    return NULL;
 }
 }
 
 
-S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
+static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target)
 {
 {
-    S390PCIBusDevice *pbdev;
     int i;
     int i;
-    int j = 0;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
+    S390PCIBusDevice *pbdev;
+    S390pciState *s = s390_get_phb();
 
 
-    if (!s) {
+    if (!target) {
         return NULL;
         return NULL;
     }
     }
 
 
     for (i = 0; i < PCI_SLOT_MAX; i++) {
     for (i = 0; i < PCI_SLOT_MAX; i++) {
-        pbdev = &s->pbdev[i];
-
-        if (pbdev->fh == 0) {
+        pbdev = s->pbdev[i];
+        if (!pbdev) {
             continue;
             continue;
         }
         }
 
 
-        if (j == idx) {
+        if (!strcmp(pbdev->target, target)) {
             return pbdev;
             return pbdev;
         }
         }
-        j++;
     }
     }
 
 
     return NULL;
     return NULL;
 }
 }
 
 
+S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
+{
+    S390pciState *s = s390_get_phb();
+
+    return s->pbdev[idx & FH_MASK_INDEX];
+}
+
 S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
 S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
 {
 {
+    S390pciState *s = s390_get_phb();
     S390PCIBusDevice *pbdev;
     S390PCIBusDevice *pbdev;
-    int i;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
 
 
-    if (!s || !fh) {
-        return NULL;
-    }
-
-    for (i = 0; i < PCI_SLOT_MAX; i++) {
-        pbdev = &s->pbdev[i];
-        if (pbdev->fh == fh) {
-            return pbdev;
-        }
+    pbdev = s->pbdev[fh & FH_MASK_INDEX];
+    if (pbdev && pbdev->fh == fh) {
+        return pbdev;
     }
     }
 
 
     return NULL;
     return NULL;
@@ -228,12 +269,7 @@ static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
                                     uint32_t fid, uint64_t faddr, uint32_t e)
                                     uint32_t fid, uint64_t faddr, uint32_t e)
 {
 {
     SeiContainer *sei_cont;
     SeiContainer *sei_cont;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(
-        object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
-
-    if (!s) {
-        return;
-    }
+    S390pciState *s = s390_get_phb();
 
 
     sei_cont = g_malloc0(sizeof(SeiContainer));
     sei_cont = g_malloc0(sizeof(SeiContainer));
     sei_cont->fh = fh;
     sei_cont->fh = fh;
@@ -253,9 +289,8 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
     s390_pci_generate_event(2, pec, fh, fid, 0, 0);
     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)
+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);
     s390_pci_generate_event(1, pec, fh, fid, faddr, e);
 }
 }
@@ -357,8 +392,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
         .perm = IOMMU_NONE,
         .perm = IOMMU_NONE,
     };
     };
 
 
-    if (!pbdev->configured || !pbdev->pdev ||
-        !(pbdev->fh & FH_ENABLED) || !pbdev->iommu_enabled) {
+    switch (pbdev->state) {
+    case ZPCI_FS_ENABLED:
+    case ZPCI_FS_BLOCKED:
+        if (!pbdev->iommu_enabled) {
+            return ret;
+        }
+        break;
+    default:
         return ret;
         return ret;
     }
     }
 
 
@@ -377,30 +418,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
         return ret;
         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) {
     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;
         return ret;
     }
     }
 
 
     pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
     pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
                                    addr);
                                    addr);
-
     if (!pte) {
     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;
         return ret;
     }
     }
 
 
@@ -426,7 +450,7 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
 {
 {
     S390pciState *s = opaque;
     S390pciState *s = opaque;
 
 
-    return &s->pbdev[PCI_SLOT(devfn)].as;
+    return &s->iommu[PCI_SLOT(devfn)]->as;
 }
 }
 
 
 static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
 static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
@@ -454,22 +478,22 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
 {
 {
     S390PCIBusDevice *pbdev;
     S390PCIBusDevice *pbdev;
     uint32_t io_int_word;
     uint32_t io_int_word;
-    uint32_t fid = data >> ZPCI_MSI_VEC_BITS;
+    uint32_t idx = data >> ZPCI_MSI_VEC_BITS;
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
     uint64_t ind_bit;
     uint64_t ind_bit;
     uint32_t sum_bit;
     uint32_t sum_bit;
     uint32_t e = 0;
     uint32_t e = 0;
 
 
-    DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec);
+    DPRINTF("write_msix data 0x%" PRIx64 " idx %d vec 0x%x\n", data, idx, vec);
 
 
-    pbdev = s390_pci_find_dev_by_fid(fid);
+    pbdev = s390_pci_find_dev_by_idx(idx);
     if (!pbdev) {
     if (!pbdev) {
         e |= (vec << ERR_EVENT_MVN_OFFSET);
         e |= (vec << ERR_EVENT_MVN_OFFSET);
-        s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e);
+        s390_pci_generate_error_event(ERR_EVENT_NOMSI, idx, 0, addr, e);
         return;
         return;
     }
     }
 
 
-    if (!(pbdev->fh & FH_ENABLED)) {
+    if (pbdev->state != ZPCI_FS_ENABLED) {
         return;
         return;
     }
     }
 
 
@@ -498,17 +522,15 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
 
 
 void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
 void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
 {
 {
-    uint64_t size = pbdev->pal - pbdev->pba + 1;
-
-    memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr),
-                             &s390_iommu_ops, "iommu-s390", size);
-    memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr);
+    memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr),
+                             &s390_iommu_ops, "iommu-s390", pbdev->pal + 1);
+    memory_region_add_subregion(&pbdev->iommu->mr, 0, &pbdev->iommu_mr);
     pbdev->iommu_enabled = true;
     pbdev->iommu_enabled = true;
 }
 }
 
 
 void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
 void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
 {
 {
-    memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr);
+    memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr);
     object_unparent(OBJECT(&pbdev->iommu_mr));
     object_unparent(OBJECT(&pbdev->iommu_mr));
     pbdev->iommu_enabled = false;
     pbdev->iommu_enabled = false;
 }
 }
@@ -516,13 +538,15 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
 static void s390_pcihost_init_as(S390pciState *s)
 static void s390_pcihost_init_as(S390pciState *s)
 {
 {
     int i;
     int i;
-    S390PCIBusDevice *pbdev;
+    S390PCIIOMMU *iommu;
 
 
     for (i = 0; i < PCI_SLOT_MAX; i++) {
     for (i = 0; i < PCI_SLOT_MAX; i++) {
-        pbdev = &s->pbdev[i];
-        memory_region_init(&pbdev->mr, OBJECT(s),
+        iommu = g_malloc0(sizeof(S390PCIIOMMU));
+        memory_region_init(&iommu->mr, OBJECT(s),
                            "iommu-root-s390", UINT64_MAX);
                            "iommu-root-s390", UINT64_MAX);
-        address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci");
+        address_space_init(&iommu->as, &iommu->mr, "iommu-pci");
+
+        s->iommu[i] = iommu;
     }
     }
 
 
     memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
     memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
@@ -549,6 +573,10 @@ static int s390_pcihost_init(SysBusDevice *dev)
     bus = BUS(b);
     bus = BUS(b);
     qbus_set_hotplug_handler(bus, DEVICE(dev), NULL);
     qbus_set_hotplug_handler(bus, DEVICE(dev), NULL);
     phb->bus = b;
     phb->bus = b;
+
+    s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL));
+    qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL);
+
     QTAILQ_INIT(&s->pending_sei);
     QTAILQ_INIT(&s->pending_sei);
     return 0;
     return 0;
 }
 }
@@ -581,51 +609,155 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev)
     return 0;
     return 0;
 }
 }
 
 
+static S390PCIBusDevice *s390_pci_device_new(const char *target)
+{
+    DeviceState *dev = NULL;
+    S390pciState *s = s390_get_phb();
+
+    dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE);
+    if (!dev) {
+        return NULL;
+    }
+
+    qdev_prop_set_string(dev, "target", target);
+    qdev_init_nofail(dev);
+
+    return S390_PCI_DEVICE(dev);
+}
+
 static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
 static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
                                   DeviceState *dev, Error **errp)
                                   DeviceState *dev, Error **errp)
 {
 {
-    PCIDevice *pci_dev = PCI_DEVICE(dev);
-    S390PCIBusDevice *pbdev;
-    S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev)
-                                           ->qbus.parent);
+    PCIDevice *pdev = NULL;
+    S390PCIBusDevice *pbdev = NULL;
+    S390pciState *s = s390_get_phb();
 
 
-    pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+        pdev = PCI_DEVICE(dev);
 
 
-    pbdev->fid = s390_pci_get_pfid(pci_dev);
-    pbdev->pdev = pci_dev;
-    pbdev->configured = true;
-    pbdev->fh = s390_pci_get_pfh(pci_dev);
+        if (!dev->id) {
+            /* In the case the PCI device does not define an id */
+            /* we generate one based on the PCI address         */
+            dev->id = g_strdup_printf("auto_%02x:%02x.%01x",
+                                      pci_bus_num(pdev->bus),
+                                      PCI_SLOT(pdev->devfn),
+                                      PCI_FUNC(pdev->devfn));
+        }
 
 
-    s390_pcihost_setup_msix(pbdev);
+        pbdev = s390_pci_find_dev_by_target(dev->id);
+        if (!pbdev) {
+            pbdev = s390_pci_device_new(dev->id);
+            if (!pbdev) {
+                error_setg(errp, "create zpci device failed");
+            }
+        }
 
 
-    if (dev->hotplugged) {
-        s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY,
-                                     pbdev->fh, pbdev->fid);
-        s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED,
-                                     pbdev->fh, pbdev->fid);
+        if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) {
+            pbdev->fh |= FH_SHM_VFIO;
+        } else {
+            pbdev->fh |= FH_SHM_EMUL;
+        }
+
+        pbdev->pdev = pdev;
+        pbdev->iommu = s->iommu[PCI_SLOT(pdev->devfn)];
+        pbdev->state = ZPCI_FS_STANDBY;
+        s390_pcihost_setup_msix(pbdev);
+
+        if (dev->hotplugged) {
+            s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY,
+                                         pbdev->fh, pbdev->fid);
+        }
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
+        int idx;
+
+        pbdev = S390_PCI_DEVICE(dev);
+        for (idx = 0; idx < PCI_SLOT_MAX; idx++) {
+            if (!s->pbdev[idx]) {
+                s->pbdev[idx] = pbdev;
+                pbdev->fh = idx;
+                return;
+            }
+        }
+
+        error_setg(errp, "no slot for plugging zpci device");
     }
     }
 }
 }
 
 
+static void s390_pcihost_timer_cb(void *opaque)
+{
+    S390PCIBusDevice *pbdev = opaque;
+
+    if (pbdev->summary_ind) {
+        pci_dereg_irqs(pbdev);
+    }
+    if (pbdev->iommu_enabled) {
+        pci_dereg_ioat(pbdev);
+    }
+
+    pbdev->state = ZPCI_FS_STANDBY;
+    s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
+                                 pbdev->fh, pbdev->fid);
+    qdev_unplug(DEVICE(pbdev), NULL);
+}
+
 static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
 static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
                                     DeviceState *dev, Error **errp)
                                     DeviceState *dev, Error **errp)
 {
 {
-    PCIDevice *pci_dev = PCI_DEVICE(dev);
-    S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev)
-                                           ->qbus.parent);
-    S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];
+    int i;
+    PCIDevice *pci_dev = NULL;
+    S390PCIBusDevice *pbdev = NULL;
+    S390pciState *s = s390_get_phb();
+
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+        pci_dev = PCI_DEVICE(dev);
 
 
-    if (pbdev->configured) {
-        pbdev->configured = false;
-        s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
+        for (i = 0 ; i < PCI_SLOT_MAX; i++) {
+            if (s->pbdev[i]->pdev == pci_dev) {
+                pbdev = s->pbdev[i];
+                break;
+            }
+        }
+
+        if (!pbdev) {
+            object_unparent(OBJECT(pci_dev));
+            return;
+        }
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
+        pbdev = S390_PCI_DEVICE(dev);
+        pci_dev = pbdev->pdev;
+    }
+
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+        goto out;
+    case ZPCI_FS_STANDBY:
+        break;
+    default:
+        s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
                                      pbdev->fh, pbdev->fid);
                                      pbdev->fh, pbdev->fid);
+        pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                            s390_pcihost_timer_cb,
+                                            pbdev);
+        timer_mod(pbdev->release_timer,
+                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT);
+        return;
+    }
+
+    if (pbdev->release_timer && timer_pending(pbdev->release_timer)) {
+        timer_del(pbdev->release_timer);
+        timer_free(pbdev->release_timer);
+        pbdev->release_timer = NULL;
     }
     }
 
 
     s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
     s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
                                  pbdev->fh, pbdev->fid);
                                  pbdev->fh, pbdev->fid);
-    pbdev->fh = 0;
-    pbdev->fid = 0;
-    pbdev->pdev = NULL;
     object_unparent(OBJECT(pci_dev));
     object_unparent(OBJECT(pci_dev));
+    pbdev->pdev = NULL;
+    pbdev->state = ZPCI_FS_RESERVED;
+out:
+    pbdev->fid = 0;
+    s->pbdev[pbdev->fh & FH_MASK_INDEX] = NULL;
+    object_unparent(OBJECT(pbdev));
 }
 }
 
 
 static void s390_pcihost_class_init(ObjectClass *klass, void *data)
 static void s390_pcihost_class_init(ObjectClass *klass, void *data)
@@ -652,9 +784,178 @@ static const TypeInfo s390_pcihost_info = {
     }
     }
 };
 };
 
 
+static const TypeInfo s390_pcibus_info = {
+    .name = TYPE_S390_PCI_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(S390PCIBus),
+};
+
+static uint16_t s390_pci_generate_uid(void)
+{
+    uint16_t uid = 0;
+
+    do {
+        uid++;
+        if (!s390_pci_find_dev_by_uid(uid)) {
+            return uid;
+        }
+    } while (uid < ZPCI_MAX_UID);
+
+    return UID_UNDEFINED;
+}
+
+static uint32_t s390_pci_generate_fid(Error **errp)
+{
+    uint32_t fid = 0;
+
+    while (fid <= ZPCI_MAX_FID) {
+        if (!s390_pci_find_dev_by_fid(fid)) {
+            return fid;
+        }
+
+        if (fid == ZPCI_MAX_FID) {
+            break;
+        }
+
+        fid++;
+    }
+
+    error_setg(errp, "no free fid could be found");
+    return 0;
+}
+
+static void s390_pci_device_realize(DeviceState *dev, Error **errp)
+{
+    S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev);
+
+    if (!zpci->target) {
+        error_setg(errp, "target must be defined");
+        return;
+    }
+
+    if (s390_pci_find_dev_by_target(zpci->target)) {
+        error_setg(errp, "target %s already has an associated zpci device",
+                   zpci->target);
+        return;
+    }
+
+    if (zpci->uid == UID_UNDEFINED) {
+        zpci->uid = s390_pci_generate_uid();
+        if (!zpci->uid) {
+            error_setg(errp, "no free uid could be found");
+            return;
+        }
+    } else if (s390_pci_find_dev_by_uid(zpci->uid)) {
+        error_setg(errp, "uid %u already in use", zpci->uid);
+        return;
+    }
+
+    if (!zpci->fid_defined) {
+        Error *local_error = NULL;
+
+        zpci->fid = s390_pci_generate_fid(&local_error);
+        if (local_error) {
+            error_propagate(errp, local_error);
+            return;
+        }
+    } else if (s390_pci_find_dev_by_fid(zpci->fid)) {
+        error_setg(errp, "fid %u already in use", zpci->fid);
+        return;
+    }
+
+    zpci->state = ZPCI_FS_RESERVED;
+}
+
+static void s390_pci_device_reset(DeviceState *dev)
+{
+    S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
+
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+        return;
+    case ZPCI_FS_STANDBY:
+        break;
+    default:
+        pbdev->fh &= ~FH_MASK_ENABLE;
+        pbdev->state = ZPCI_FS_DISABLED;
+        break;
+    }
+
+    if (pbdev->summary_ind) {
+        pci_dereg_irqs(pbdev);
+    }
+    if (pbdev->iommu_enabled) {
+        pci_dereg_ioat(pbdev);
+    }
+
+    pbdev->fmb_addr = 0;
+}
+
+static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    Property *prop = opaque;
+    uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop);
+
+    visit_type_uint32(v, name, ptr, errp);
+}
+
+static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj);
+    Property *prop = opaque;
+    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint32(v, name, ptr, errp);
+    zpci->fid_defined = true;
+}
+
+static PropertyInfo s390_pci_fid_propinfo = {
+    .name = "zpci_fid",
+    .get = s390_pci_get_fid,
+    .set = s390_pci_set_fid,
+};
+
+#define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \
+    DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t)
+
+static Property s390_pci_device_properties[] = {
+    DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED),
+    DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid),
+    DEFINE_PROP_STRING("target", S390PCIBusDevice, target),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void s390_pci_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "zpci device";
+    dc->reset = s390_pci_device_reset;
+    dc->bus_type = TYPE_S390_PCI_BUS;
+    dc->realize = s390_pci_device_realize;
+    dc->props = s390_pci_device_properties;
+}
+
+static const TypeInfo s390_pci_device_info = {
+    .name = TYPE_S390_PCI_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(S390PCIBusDevice),
+    .class_init = s390_pci_device_class_init,
+};
+
 static void s390_pci_register_types(void)
 static void s390_pci_register_types(void)
 {
 {
     type_register_static(&s390_pcihost_info);
     type_register_static(&s390_pcihost_info);
+    type_register_static(&s390_pcibus_info);
+    type_register_static(&s390_pci_device_info);
 }
 }
 
 
 type_init(s390_pci_register_types)
 type_init(s390_pci_register_types)

+ 68 - 9
hw/s390x/s390-pci-bus.h

@@ -21,16 +21,31 @@
 #include "hw/s390x/css.h"
 #include "hw/s390x/css.h"
 
 
 #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
 #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
-#define FH_VIRT 0x00ff0000
-#define ENABLE_BIT_OFFSET 31
-#define FH_ENABLED (1 << ENABLE_BIT_OFFSET)
+#define TYPE_S390_PCI_BUS "s390-pcibus"
+#define TYPE_S390_PCI_DEVICE "zpci"
+#define FH_MASK_ENABLE   0x80000000
+#define FH_MASK_INSTANCE 0x7f000000
+#define FH_MASK_SHM      0x00ff0000
+#define FH_MASK_INDEX    0x0000001f
+#define FH_SHM_VFIO      0x00010000
+#define FH_SHM_EMUL      0x00020000
 #define S390_PCIPT_ADAPTER 2
 #define S390_PCIPT_ADAPTER 2
+#define ZPCI_MAX_FID 0xffffffff
+#define ZPCI_MAX_UID 0xffff
+#define UID_UNDEFINED 0
+#define UID_CHECKING_ENABLED 0x01
+#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5)
 
 
 #define S390_PCI_HOST_BRIDGE(obj) \
 #define S390_PCI_HOST_BRIDGE(obj) \
     OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE)
     OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE)
+#define S390_PCI_BUS(obj) \
+    OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS)
+#define S390_PCI_DEVICE(obj) \
+    OBJECT_CHECK(S390PCIBusDevice, (obj), TYPE_S390_PCI_DEVICE)
 
 
 #define HP_EVENT_TO_CONFIGURED        0x0301
 #define HP_EVENT_TO_CONFIGURED        0x0301
 #define HP_EVENT_RESERVED_TO_STANDBY  0x0302
 #define HP_EVENT_RESERVED_TO_STANDBY  0x0302
+#define HP_EVENT_DECONFIGURE_REQUEST  0x0303
 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
 #define HP_EVENT_STANDBY_TO_RESERVED  0x0308
 #define HP_EVENT_STANDBY_TO_RESERVED  0x0308
 
 
@@ -150,6 +165,34 @@ enum ZpciIoatDtype {
 #define ZPCI_TABLE_VALID_MASK           0x20
 #define ZPCI_TABLE_VALID_MASK           0x20
 #define ZPCI_TABLE_PROT_MASK            0x200
 #define ZPCI_TABLE_PROT_MASK            0x200
 
 
+/* PCI Function States
+ *
+ * reserved: default; device has just been plugged or is in progress of being
+ *           unplugged
+ * standby: device is present but not configured; transition from any
+ *          configured state/to this state via sclp configure/deconfigure
+ *
+ * The following states make up the "configured" meta-state:
+ * disabled: device is configured but not enabled; transition between this
+ *           state and enabled via clp enable/disable
+ * enbaled: device is ready for use; transition to disabled via clp disable;
+ *          may enter an error state
+ * blocked: ignore all DMA and interrupts; transition back to enabled or from
+ *          error state via mpcifc
+ * error: an error occured; transition back to enabled via mpcifc
+ * permanent error: an unrecoverable error occured; transition to standby via
+ *                  sclp deconfigure
+ */
+typedef enum {
+    ZPCI_FS_RESERVED,
+    ZPCI_FS_STANDBY,
+    ZPCI_FS_DISABLED,
+    ZPCI_FS_ENABLED,
+    ZPCI_FS_BLOCKED,
+    ZPCI_FS_ERROR,
+    ZPCI_FS_PERMANENT_ERROR,
+} ZpciState;
+
 typedef struct SeiContainer {
 typedef struct SeiContainer {
     QTAILQ_ENTRY(SeiContainer) link;
     QTAILQ_ENTRY(SeiContainer) link;
     uint32_t fid;
     uint32_t fid;
@@ -214,14 +257,21 @@ typedef struct S390MsixInfo {
     uint32_t pba_offset;
     uint32_t pba_offset;
 } S390MsixInfo;
 } S390MsixInfo;
 
 
+typedef struct S390PCIIOMMU {
+    AddressSpace as;
+    MemoryRegion mr;
+} S390PCIIOMMU;
+
 typedef struct S390PCIBusDevice {
 typedef struct S390PCIBusDevice {
+    DeviceState qdev;
     PCIDevice *pdev;
     PCIDevice *pdev;
-    bool configured;
-    bool error_state;
-    bool lgstg_blocked;
+    ZpciState state;
     bool iommu_enabled;
     bool iommu_enabled;
+    char *target;
+    uint16_t uid;
     uint32_t fh;
     uint32_t fh;
     uint32_t fid;
     uint32_t fid;
+    bool fid_defined;
     uint64_t g_iota;
     uint64_t g_iota;
     uint64_t pba;
     uint64_t pba;
     uint64_t pal;
     uint64_t pal;
@@ -231,16 +281,22 @@ typedef struct S390PCIBusDevice {
     uint8_t sum;
     uint8_t sum;
     S390MsixInfo msix;
     S390MsixInfo msix;
     AdapterRoutes routes;
     AdapterRoutes routes;
-    AddressSpace as;
-    MemoryRegion mr;
+    S390PCIIOMMU *iommu;
     MemoryRegion iommu_mr;
     MemoryRegion iommu_mr;
     IndAddr *summary_ind;
     IndAddr *summary_ind;
     IndAddr *indicator;
     IndAddr *indicator;
+    QEMUTimer *release_timer;
 } S390PCIBusDevice;
 } S390PCIBusDevice;
 
 
+typedef struct S390PCIBus {
+    BusState qbus;
+} S390PCIBus;
+
 typedef struct S390pciState {
 typedef struct S390pciState {
     PCIHostState parent_obj;
     PCIHostState parent_obj;
-    S390PCIBusDevice pbdev[PCI_SLOT_MAX];
+    S390PCIBus *bus;
+    S390PCIBusDevice *pbdev[PCI_SLOT_MAX];
+    S390PCIIOMMU *iommu[PCI_SLOT_MAX];
     AddressSpace msix_notify_as;
     AddressSpace msix_notify_as;
     MemoryRegion msix_notify_mr;
     MemoryRegion msix_notify_mr;
     QTAILQ_HEAD(, SeiContainer) pending_sei;
     QTAILQ_HEAD(, SeiContainer) pending_sei;
@@ -252,8 +308,11 @@ void s390_pci_sclp_configure(SCCB *sccb);
 void s390_pci_sclp_deconfigure(SCCB *sccb);
 void s390_pci_sclp_deconfigure(SCCB *sccb);
 void s390_pci_iommu_enable(S390PCIBusDevice *pbdev);
 void s390_pci_iommu_enable(S390PCIBusDevice *pbdev);
 void s390_pci_iommu_disable(S390PCIBusDevice *pbdev);
 void s390_pci_iommu_disable(S390PCIBusDevice *pbdev);
+void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
+                                   uint64_t faddr, uint32_t e);
 S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
 S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
 S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
 S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
 S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
 S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
+S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev);
 
 
 #endif
 #endif

+ 201 - 65
hw/s390x/s390-pci-inst.c

@@ -37,9 +37,9 @@ static void s390_set_status_code(CPUS390XState *env,
 
 
 static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
 static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
 {
 {
-    S390PCIBusDevice *pbdev;
-    uint32_t res_code, initial_l2, g_l2, finish;
-    int rc, idx;
+    S390PCIBusDevice *pbdev = NULL;
+    uint32_t res_code, initial_l2, g_l2;
+    int rc, i;
     uint64_t resume_token;
     uint64_t resume_token;
 
 
     rc = 0;
     rc = 0;
@@ -56,8 +56,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
     }
     }
 
 
     if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 ||
     if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 ||
-        ldq_p(&rrb->request.reserved1) != 0 ||
-        ldq_p(&rrb->request.reserved2) != 0) {
+        ldq_p(&rrb->request.reserved1) != 0) {
         res_code = CLP_RC_RESNOT0;
         res_code = CLP_RC_RESNOT0;
         rc = -EINVAL;
         rc = -EINVAL;
         goto out;
         goto out;
@@ -72,6 +71,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
             rc = -EINVAL;
             rc = -EINVAL;
             goto out;
             goto out;
         }
         }
+    } else {
+        pbdev = s390_pci_find_next_avail_dev(NULL);
     }
     }
 
 
     if (lduw_p(&rrb->response.hdr.len) < 48) {
     if (lduw_p(&rrb->response.hdr.len) < 48) {
@@ -91,43 +92,40 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
 
 
     stl_p(&rrb->response.fmt, 0);
     stl_p(&rrb->response.fmt, 0);
     stq_p(&rrb->response.reserved1, 0);
     stq_p(&rrb->response.reserved1, 0);
-    stq_p(&rrb->response.reserved2, 0);
-    stl_p(&rrb->response.mdd, FH_VIRT);
+    stl_p(&rrb->response.mdd, FH_MASK_SHM);
     stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS);
     stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS);
+    rrb->response.flags = UID_CHECKING_ENABLED;
     rrb->response.entry_size = sizeof(ClpFhListEntry);
     rrb->response.entry_size = sizeof(ClpFhListEntry);
-    finish = 0;
-    idx = resume_token;
+
+    i = 0;
     g_l2 = LIST_PCI_HDR_LEN;
     g_l2 = LIST_PCI_HDR_LEN;
-    do {
-        pbdev = s390_pci_find_dev_by_idx(idx);
-        if (!pbdev) {
-            finish = 1;
-            break;
-        }
-        stw_p(&rrb->response.fh_list[idx - resume_token].device_id,
+    while (g_l2 < initial_l2 && pbdev) {
+        stw_p(&rrb->response.fh_list[i].device_id,
             pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
             pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
-        stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id,
+        stw_p(&rrb->response.fh_list[i].vendor_id,
             pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
             pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
-        stl_p(&rrb->response.fh_list[idx - resume_token].config,
-            pbdev->configured << 31);
-        stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid);
-        stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh);
+        /* Ignore RESERVED devices. */
+        stl_p(&rrb->response.fh_list[i].config,
+            pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31);
+        stl_p(&rrb->response.fh_list[i].fid, pbdev->fid);
+        stl_p(&rrb->response.fh_list[i].fh, pbdev->fh);
 
 
         g_l2 += sizeof(ClpFhListEntry);
         g_l2 += sizeof(ClpFhListEntry);
         /* Add endian check for DPRINTF? */
         /* Add endian check for DPRINTF? */
         DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n",
         DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n",
-            g_l2,
-            lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id),
-            lduw_p(&rrb->response.fh_list[idx - resume_token].device_id),
-            ldl_p(&rrb->response.fh_list[idx - resume_token].fid),
-            ldl_p(&rrb->response.fh_list[idx - resume_token].fh));
-        idx++;
-    } while (g_l2 < initial_l2);
-
-    if (finish == 1) {
+                g_l2,
+                lduw_p(&rrb->response.fh_list[i].vendor_id),
+                lduw_p(&rrb->response.fh_list[i].device_id),
+                ldl_p(&rrb->response.fh_list[i].fid),
+                ldl_p(&rrb->response.fh_list[i].fh));
+        pbdev = s390_pci_find_next_avail_dev(pbdev);
+        i++;
+    }
+
+    if (!pbdev) {
         resume_token = 0;
         resume_token = 0;
     } else {
     } else {
-        resume_token = idx;
+        resume_token = pbdev->fh & FH_MASK_INDEX;
     }
     }
     stq_p(&rrb->response.resume_token, resume_token);
     stq_p(&rrb->response.resume_token, resume_token);
     stw_p(&rrb->response.hdr.len, g_l2);
     stw_p(&rrb->response.hdr.len, g_l2);
@@ -212,14 +210,35 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
 
 
         switch (reqsetpci->oc) {
         switch (reqsetpci->oc) {
         case CLP_SET_ENABLE_PCI_FN:
         case CLP_SET_ENABLE_PCI_FN:
-            pbdev->fh = pbdev->fh | FH_ENABLED;
+            switch (reqsetpci->ndas) {
+            case 0:
+                stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS);
+                goto out;
+            case 1:
+                break;
+            default:
+                stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES);
+                goto out;
+            }
+
+            if (pbdev->fh & FH_MASK_ENABLE) {
+                stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP);
+                goto out;
+            }
+
+            pbdev->fh |= FH_MASK_ENABLE;
+            pbdev->state = ZPCI_FS_ENABLED;
             stl_p(&ressetpci->fh, pbdev->fh);
             stl_p(&ressetpci->fh, pbdev->fh);
             stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
             stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
             break;
             break;
         case CLP_SET_DISABLE_PCI_FN:
         case CLP_SET_DISABLE_PCI_FN:
-            pbdev->fh = pbdev->fh & ~FH_ENABLED;
-            pbdev->error_state = false;
-            pbdev->lgstg_blocked = false;
+            if (!(pbdev->fh & FH_MASK_ENABLE)) {
+                stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP);
+                goto out;
+            }
+            device_reset(DEVICE(pbdev));
+            pbdev->fh &= ~FH_MASK_ENABLE;
+            pbdev->state = ZPCI_FS_DISABLED;
             stl_p(&ressetpci->fh, pbdev->fh);
             stl_p(&ressetpci->fh, pbdev->fh);
             stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
             stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
             break;
             break;
@@ -256,9 +275,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
 
 
         stq_p(&resquery->sdma, ZPCI_SDMA_ADDR);
         stq_p(&resquery->sdma, ZPCI_SDMA_ADDR);
         stq_p(&resquery->edma, ZPCI_EDMA_ADDR);
         stq_p(&resquery->edma, ZPCI_EDMA_ADDR);
+        stl_p(&resquery->fid, pbdev->fid);
         stw_p(&resquery->pchid, 0);
         stw_p(&resquery->pchid, 0);
         stw_p(&resquery->ug, 1);
         stw_p(&resquery->ug, 1);
-        stl_p(&resquery->uid, pbdev->fid);
+        stl_p(&resquery->uid, pbdev->uid);
         stw_p(&resquery->hdr.rsp, CLP_RC_OK);
         stw_p(&resquery->hdr.rsp, CLP_RC_OK);
         break;
         break;
     }
     }
@@ -317,16 +337,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
     offset = env->regs[r2 + 1];
     offset = env->regs[r2 + 1];
 
 
     pbdev = s390_pci_find_dev_by_fh(fh);
     pbdev = s390_pci_find_dev_by_fh(fh);
-    if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+    if (!pbdev) {
         DPRINTF("pcilg no pci dev\n");
         DPRINTF("pcilg no pci dev\n");
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         return 0;
         return 0;
     }
     }
 
 
-    if (pbdev->lgstg_blocked) {
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+    case ZPCI_FS_DISABLED:
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    case ZPCI_FS_ERROR:
         setcc(cpu, ZPCI_PCI_LS_ERR);
         setcc(cpu, ZPCI_PCI_LS_ERR);
         s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
         s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
         return 0;
         return 0;
+    default:
+        break;
     }
     }
 
 
     if (pcias < 6) {
     if (pcias < 6) {
@@ -390,7 +419,8 @@ static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset,
 
 
     msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE +
     msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE +
                PCI_MSIX_ENTRY_VECTOR_CTRL;
                PCI_MSIX_ENTRY_VECTOR_CTRL;
-    val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS);
+    val = pci_get_long(msg_data) |
+        ((pbdev->fh & FH_MASK_INDEX) << ZPCI_MSI_VEC_BITS);
     pci_set_long(msg_data, val);
     pci_set_long(msg_data, val);
     DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data);
     DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data);
 }
 }
@@ -434,16 +464,25 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
     offset = env->regs[r2 + 1];
     offset = env->regs[r2 + 1];
 
 
     pbdev = s390_pci_find_dev_by_fh(fh);
     pbdev = s390_pci_find_dev_by_fh(fh);
-    if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+    if (!pbdev) {
         DPRINTF("pcistg no pci dev\n");
         DPRINTF("pcistg no pci dev\n");
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         return 0;
         return 0;
     }
     }
 
 
-    if (pbdev->lgstg_blocked) {
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+    case ZPCI_FS_DISABLED:
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    case ZPCI_FS_ERROR:
         setcc(cpu, ZPCI_PCI_LS_ERR);
         setcc(cpu, ZPCI_PCI_LS_ERR);
         s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
         s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
         return 0;
         return 0;
+    default:
+        break;
     }
     }
 
 
     data = env->regs[r1];
     data = env->regs[r1];
@@ -525,18 +564,55 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
     end = start + env->regs[r2 + 1];
     end = start + env->regs[r2 + 1];
 
 
     pbdev = s390_pci_find_dev_by_fh(fh);
     pbdev = s390_pci_find_dev_by_fh(fh);
-    if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+    if (!pbdev) {
         DPRINTF("rpcit no pci dev\n");
         DPRINTF("rpcit no pci dev\n");
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         goto out;
         goto out;
     }
     }
 
 
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+    case ZPCI_FS_DISABLED:
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    case ZPCI_FS_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_MOD_ST_ERROR_RECOVER);
+        return 0;
+    default:
+        break;
+    }
+
+    if (!pbdev->g_iota) {
+        pbdev->state = ZPCI_FS_ERROR;
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
+        s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
+                                      start, 0);
+        goto out;
+    }
+
+    if (end < pbdev->pba || start > pbdev->pal) {
+        pbdev->state = ZPCI_FS_ERROR;
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
+        s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
+                                      start, 0);
+        goto out;
+    }
+
     mr = &pbdev->iommu_mr;
     mr = &pbdev->iommu_mr;
     while (start < end) {
     while (start < end) {
         entry = mr->iommu_ops->translate(mr, start, 0);
         entry = mr->iommu_ops->translate(mr, start, 0);
 
 
         if (!entry.translated_addr) {
         if (!entry.translated_addr) {
+            pbdev->state = ZPCI_FS_ERROR;
             setcc(cpu, ZPCI_PCI_LS_ERR);
             setcc(cpu, ZPCI_PCI_LS_ERR);
+            s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
+            s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
+                                          start, ERR_EVENT_Q_BIT);
             goto out;
             goto out;
         }
         }
 
 
@@ -589,16 +665,25 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
     }
     }
 
 
     pbdev = s390_pci_find_dev_by_fh(fh);
     pbdev = s390_pci_find_dev_by_fh(fh);
-    if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+    if (!pbdev) {
         DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
         DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         return 0;
         return 0;
     }
     }
 
 
-    if (pbdev->lgstg_blocked) {
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+    case ZPCI_FS_DISABLED:
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    case ZPCI_FS_ERROR:
         setcc(cpu, ZPCI_PCI_LS_ERR);
         setcc(cpu, ZPCI_PCI_LS_ERR);
         s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
         s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
         return 0;
         return 0;
+    default:
+        break;
     }
     }
 
 
     mr = pbdev->pdev->io_regions[pcias].memory;
     mr = pbdev->pdev->io_regions[pcias].memory;
@@ -742,12 +827,23 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
     }
     }
 
 
     pbdev = s390_pci_find_dev_by_fh(fh);
     pbdev = s390_pci_find_dev_by_fh(fh);
-    if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+    if (!pbdev) {
         DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
         DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         return 0;
         return 0;
     }
     }
 
 
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+    case ZPCI_FS_DISABLED:
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    default:
+        break;
+    }
+
     if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
     if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
         return 0;
         return 0;
     }
     }
@@ -814,11 +910,25 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
         }
         }
         break;
         break;
     case ZPCI_MOD_FC_RESET_ERROR:
     case ZPCI_MOD_FC_RESET_ERROR:
-        pbdev->error_state = false;
-        pbdev->lgstg_blocked = false;
+        switch (pbdev->state) {
+        case ZPCI_FS_BLOCKED:
+        case ZPCI_FS_ERROR:
+            pbdev->state = ZPCI_FS_ENABLED;
+            break;
+        default:
+            cc = ZPCI_PCI_LS_ERR;
+            s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+        }
         break;
         break;
     case ZPCI_MOD_FC_RESET_BLOCK:
     case ZPCI_MOD_FC_RESET_BLOCK:
-        pbdev->lgstg_blocked = false;
+        switch (pbdev->state) {
+        case ZPCI_FS_ERROR:
+            pbdev->state = ZPCI_FS_BLOCKED;
+            break;
+        default:
+            cc = ZPCI_PCI_LS_ERR;
+            s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+        }
         break;
         break;
     case ZPCI_MOD_FC_SET_MEASURE:
     case ZPCI_MOD_FC_SET_MEASURE:
         pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
         pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
@@ -835,6 +945,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
 int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
 int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
 {
 {
     CPUS390XState *env = &cpu->env;
     CPUS390XState *env = &cpu->env;
+    uint8_t dmaas;
     uint32_t fh;
     uint32_t fh;
     ZpciFib fib;
     ZpciFib fib;
     S390PCIBusDevice *pbdev;
     S390PCIBusDevice *pbdev;
@@ -847,19 +958,59 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
     }
     }
 
 
     fh = env->regs[r1] >> 32;
     fh = env->regs[r1] >> 32;
+    dmaas = (env->regs[r1] >> 16) & 0xff;
+
+    if (dmaas) {
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_INVAL_DMAAS);
+        return 0;
+    }
 
 
     if (fiba & 0x7) {
     if (fiba & 0x7) {
         program_interrupt(env, PGM_SPECIFICATION, 6);
         program_interrupt(env, PGM_SPECIFICATION, 6);
         return 0;
         return 0;
     }
     }
 
 
-    pbdev = s390_pci_find_dev_by_fh(fh);
+    pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX);
     if (!pbdev) {
     if (!pbdev) {
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
         return 0;
         return 0;
     }
     }
 
 
     memset(&fib, 0, sizeof(fib));
     memset(&fib, 0, sizeof(fib));
+
+    switch (pbdev->state) {
+    case ZPCI_FS_RESERVED:
+    case ZPCI_FS_STANDBY:
+        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+        return 0;
+    case ZPCI_FS_DISABLED:
+        if (fh & FH_MASK_ENABLE) {
+            setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
+            return 0;
+        }
+        goto out;
+    /* BLOCKED bit is set to one coincident with the setting of ERROR bit.
+     * FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */
+    case ZPCI_FS_ERROR:
+        fib.fc |= 0x20;
+    case ZPCI_FS_BLOCKED:
+        fib.fc |= 0x40;
+    case ZPCI_FS_ENABLED:
+        fib.fc |= 0x80;
+        if (pbdev->iommu_enabled) {
+            fib.fc |= 0x10;
+        }
+        if (!(fh & FH_MASK_ENABLE)) {
+            env->regs[r1] |= 1ULL << 63;
+        }
+        break;
+    case ZPCI_FS_PERMANENT_ERROR:
+        setcc(cpu, ZPCI_PCI_LS_ERR);
+        s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_PERM_ERROR);
+        return 0;
+    }
+
     stq_p(&fib.pba, pbdev->pba);
     stq_p(&fib.pba, pbdev->pba);
     stq_p(&fib.pal, pbdev->pal);
     stq_p(&fib.pal, pbdev->pal);
     stq_p(&fib.iota, pbdev->g_iota);
     stq_p(&fib.iota, pbdev->g_iota);
@@ -872,22 +1023,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
            ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset;
            ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset;
     stl_p(&fib.data, data);
     stl_p(&fib.data, data);
 
 
-    if (pbdev->fh & FH_ENABLED) {
-        fib.fc |= 0x80;
-    }
-
-    if (pbdev->error_state) {
-        fib.fc |= 0x40;
-    }
-
-    if (pbdev->lgstg_blocked) {
-        fib.fc |= 0x20;
-    }
-
-    if (pbdev->g_iota) {
-        fib.fc |= 0x10;
-    }
-
+out:
     if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
     if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
         return 0;
         return 0;
     }
     }

+ 6 - 1
hw/s390x/s390-pci-inst.h

@@ -104,7 +104,7 @@ typedef struct ClpRspListPci {
     uint64_t resume_token;
     uint64_t resume_token;
     uint32_t mdd;
     uint32_t mdd;
     uint16_t max_fn;
     uint16_t max_fn;
-    uint8_t reserved2;
+    uint8_t flags;
     uint8_t entry_size;
     uint8_t entry_size;
     ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES];
     ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES];
 } QEMU_PACKED ClpRspListPci;
 } QEMU_PACKED ClpRspListPci;
@@ -249,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp {
 #define ZPCI_MOD_FC_RESET_BLOCK 9
 #define ZPCI_MOD_FC_RESET_BLOCK 9
 #define ZPCI_MOD_FC_SET_MEASURE 10
 #define ZPCI_MOD_FC_SET_MEASURE 10
 
 
+/* Store PCI Function Controls status codes */
+#define ZPCI_STPCIFC_ST_PERM_ERROR    8
+#define ZPCI_STPCIFC_ST_INVAL_DMAAS   28
+#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40
+
 /* FIB function controls */
 /* FIB function controls */
 #define ZPCI_FIB_FC_ENABLED     0x80
 #define ZPCI_FIB_FC_ENABLED     0x80
 #define ZPCI_FIB_FC_ERROR       0x40
 #define ZPCI_FIB_FC_ERROR       0x40

+ 2 - 1
hw/s390x/s390-virtio-ccw.c

@@ -27,9 +27,10 @@
 #include "hw/compat.h"
 #include "hw/compat.h"
 #include "ipl.h"
 #include "ipl.h"
 #include "hw/s390x/s390-virtio-ccw.h"
 #include "hw/s390x/s390-virtio-ccw.h"
+#include "hw/s390x/css-bridge.h"
 
 
 static const char *const reset_dev_types[] = {
 static const char *const reset_dev_types[] = {
-    "virtual-css-bridge",
+    TYPE_VIRTUAL_CSS_BRIDGE,
     "s390-sclp-event-facility",
     "s390-sclp-event-facility",
     "s390-flic",
     "s390-flic",
     "diag288",
     "diag288",

+ 53 - 218
hw/s390x/virtio-ccw.c

@@ -33,31 +33,11 @@
 #include "hw/s390x/css.h"
 #include "hw/s390x/css.h"
 #include "virtio-ccw.h"
 #include "virtio-ccw.h"
 #include "trace.h"
 #include "trace.h"
+#include "hw/s390x/css-bridge.h"
 
 
 static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
 static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
                                VirtioCcwDevice *dev);
                                VirtioCcwDevice *dev);
 
 
-static void virtual_css_bus_reset(BusState *qbus)
-{
-    /* This should actually be modelled via the generic css */
-    css_reset();
-}
-
-
-static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
-{
-    BusClass *k = BUS_CLASS(klass);
-
-    k->reset = virtual_css_bus_reset;
-}
-
-static const TypeInfo virtual_css_bus_info = {
-    .name = TYPE_VIRTUAL_CSS_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(VirtualCssBus),
-    .class_init = virtual_css_bus_class_init,
-};
-
 VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
 VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
 {
 {
     VirtIODevice *vdev = NULL;
     VirtIODevice *vdev = NULL;
@@ -117,32 +97,13 @@ static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
                                        int n, bool assign)
                                        int n, bool assign)
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-    SubchDev *sch = dev->sch;
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
+    SubchDev *sch = ccw_dev->sch;
     uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
     uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
 
 
     return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
     return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
 }
 }
 
 
-VirtualCssBus *virtual_css_bus_init(void)
-{
-    VirtualCssBus *cbus;
-    BusState *bus;
-    DeviceState *dev;
-
-    /* Create bridge device */
-    dev = qdev_create(NULL, "virtual-css-bridge");
-    qdev_init_nofail(dev);
-
-    /* Create bus on bridge device */
-    bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css");
-    cbus = VIRTUAL_CSS_BUS(bus);
-
-    /* Enable hotplugging */
-    qbus_set_hotplug_handler(bus, dev, &error_abort);
-
-    return cbus;
-}
-
 /* Communication blocks used by several channel commands. */
 /* Communication blocks used by several channel commands. */
 typedef struct VqInfoBlockLegacy {
 typedef struct VqInfoBlockLegacy {
     uint64_t queue;
     uint64_t queue;
@@ -234,6 +195,8 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
 
 
 static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
 static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
 {
 {
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
+
     virtio_ccw_stop_ioeventfd(dev);
     virtio_ccw_stop_ioeventfd(dev);
     virtio_reset(vdev);
     virtio_reset(vdev);
     if (dev->indicators) {
     if (dev->indicators) {
@@ -248,7 +211,7 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
         release_indicator(&dev->routes.adapter, dev->summary_indicator);
         release_indicator(&dev->routes.adapter, dev->summary_indicator);
         dev->summary_indicator = NULL;
         dev->summary_indicator = NULL;
     }
     }
-    dev->sch->thinint_active = false;
+    ccw_dev->sch->thinint_active = false;
 }
 }
 
 
 static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len,
 static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len,
@@ -703,116 +666,28 @@ static void virtio_sch_disable_cb(SubchDev *sch)
 
 
 static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
 static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
 {
 {
-    unsigned int schid;
-    bool found = false;
-    SubchDev *sch;
-    Error *err = NULL;
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
+    SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp);
+    Error *err = NULL;
 
 
-    sch = g_malloc0(sizeof(SubchDev));
-
-    sch->driver_data = dev;
-    dev->sch = sch;
-
-    dev->indicators = NULL;
-
-    /* Initialize subchannel structure. */
-    sch->channel_prog = 0x0;
-    sch->last_cmd_valid = false;
-    sch->thinint_active = false;
-    /*
-     * Use a device number if provided. Otherwise, fall back to subchannel
-     * number.
-     */
-    if (dev->bus_id.valid) {
-        /* Enforce use of virtual cssid. */
-        if (dev->bus_id.cssid != VIRTUAL_CSSID) {
-            error_setg(errp, "cssid %x not valid for virtio devices",
-                       dev->bus_id.cssid);
-            goto out_err;
-        }
-        if (css_devno_used(dev->bus_id.cssid, dev->bus_id.ssid,
-                           dev->bus_id.devid)) {
-                error_setg(errp, "Device %x.%x.%04x already exists",
-                           dev->bus_id.cssid, dev->bus_id.ssid,
-                           dev->bus_id.devid);
-                goto out_err;
-        }
-        sch->cssid = dev->bus_id.cssid;
-        sch->ssid = dev->bus_id.ssid;
-        sch->devno = dev->bus_id.devid;
-
-        /* Find the next free id. */
-        for (schid = 0; schid <= MAX_SCHID; schid++) {
-            if (!css_find_subch(1, sch->cssid, sch->ssid, schid)) {
-                sch->schid = schid;
-                css_subch_assign(sch->cssid, sch->ssid, sch->schid,
-                                 sch->devno, sch);
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
-            error_setg(errp, "No free subchannel found for %x.%x.%04x",
-                       sch->cssid, sch->ssid, sch->devno);
-            goto out_err;
-        }
-        trace_virtio_ccw_new_device(sch->cssid, sch->ssid, sch->schid,
-                                    sch->devno, "user-configured");
-    } else {
-        unsigned int cssid = VIRTUAL_CSSID, ssid, devno;
-
-        for (ssid = 0; ssid <= MAX_SSID; ssid++) {
-            for (schid = 0; schid <= MAX_SCHID; schid++) {
-                if (!css_find_subch(1, cssid, ssid, schid)) {
-                    sch->cssid = cssid;
-                    sch->ssid = ssid;
-                    sch->schid = schid;
-                    devno = schid;
-                    /*
-                     * If the devno is already taken, look further in this
-                     * subchannel set.
-                     */
-                    while (css_devno_used(cssid, ssid, devno)) {
-                        if (devno == MAX_SCHID) {
-                            devno = 0;
-                        } else if (devno == schid - 1) {
-                            error_setg(errp, "No free devno found");
-                            goto out_err;
-                        } else {
-                            devno++;
-                        }
-                    }
-                    sch->devno = devno;
-                    css_subch_assign(cssid, ssid, schid, devno, sch);
-                    found = true;
-                    break;
-                }
-            }
-            if (found) {
-                break;
-            }
-        }
-        if (!found) {
-            error_setg(errp, "Virtual channel subsystem is full!");
-            goto out_err;
-        }
-        trace_virtio_ccw_new_device(cssid, ssid, schid, devno,
-                                    "auto-configured");
+    if (!sch) {
+        return;
     }
     }
 
 
-    /* Build initial schib. */
-    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
-
+    sch->driver_data = dev;
     sch->ccw_cb = virtio_ccw_cb;
     sch->ccw_cb = virtio_ccw_cb;
     sch->disable_cb = virtio_sch_disable_cb;
     sch->disable_cb = virtio_sch_disable_cb;
-
-    /* Build senseid data. */
-    memset(&sch->id, 0, sizeof(SenseId));
     sch->id.reserved = 0xff;
     sch->id.reserved = 0xff;
     sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
     sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
-
+    ccw_dev->sch = sch;
+    dev->indicators = NULL;
     dev->revision = -1;
     dev->revision = -1;
+    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
+
+    trace_virtio_ccw_new_device(
+        sch->cssid, sch->ssid, sch->schid, sch->devno,
+        ccw_dev->bus_id.valid ? "user-configured" : "auto-configured");
 
 
     if (k->realize) {
     if (k->realize) {
         k->realize(dev, &err);
         k->realize(dev, &err);
@@ -820,19 +695,15 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
     if (err) {
     if (err) {
         error_propagate(errp, err);
         error_propagate(errp, err);
         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
-        goto out_err;
+        ccw_dev->sch = NULL;
+        g_free(sch);
     }
     }
-
-    return;
-
-out_err:
-    dev->sch = NULL;
-    g_free(sch);
 }
 }
 
 
 static int virtio_ccw_exit(VirtioCcwDevice *dev)
 static int virtio_ccw_exit(VirtioCcwDevice *dev)
 {
 {
-    SubchDev *sch = dev->sch;
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
+    SubchDev *sch = ccw_dev->sch;
 
 
     if (sch) {
     if (sch) {
         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
@@ -1013,7 +884,9 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp)
  */
  */
 static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d)
 static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d)
 {
 {
-    return container_of(d, VirtioCcwDevice, parent_obj);
+    CcwDevice *ccw_dev = to_ccw_dev_fast(d);
+
+    return container_of(ccw_dev, VirtioCcwDevice, parent_obj);
 }
 }
 
 
 static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
 static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
@@ -1042,7 +915,8 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
 static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
 static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
 {
 {
     VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d);
     VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d);
-    SubchDev *sch = dev->sch;
+    CcwDevice *ccw_dev = to_ccw_dev_fast(d);
+    SubchDev *sch = ccw_dev->sch;
     uint64_t indicators;
     uint64_t indicators;
 
 
     /* queue indicators + secondary indicators */
     /* queue indicators + secondary indicators */
@@ -1100,9 +974,10 @@ static void virtio_ccw_reset(DeviceState *d)
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+    CcwDevice *ccw_dev = CCW_DEVICE(d);
 
 
     virtio_ccw_reset_virtio(dev, vdev);
     virtio_ccw_reset_virtio(dev, vdev);
-    css_reset_sch(dev->sch);
+    css_reset_sch(ccw_dev->sch);
 }
 }
 
 
 static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
 static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
@@ -1118,7 +993,7 @@ static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
 
 
 static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
 static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
 {
 {
-    VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+    CcwDevice *dev = CCW_DEVICE(d);
 
 
     return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
     return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
 }
 }
@@ -1126,8 +1001,9 @@ static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
 static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
 static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
 {
 {
     int r;
     int r;
+    CcwDevice *ccw_dev = CCW_DEVICE(dev);
 
 
-    if (!dev->sch->thinint_active) {
+    if (!ccw_dev->sch->thinint_active) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
@@ -1249,7 +1125,8 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
-    bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled();
+    CcwDevice *ccw_dev = CCW_DEVICE(d);
+    bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled();
     int r, n;
     int r, n;
 
 
     if (with_irqfd && assigned) {
     if (with_irqfd && assigned) {
@@ -1308,7 +1185,8 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
 static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
 static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-    SubchDev *s = dev->sch;
+    CcwDevice *ccw_dev = CCW_DEVICE(d);
+    SubchDev *s = ccw_dev->sch;
     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
 
 
     subch_device_save(s, f);
     subch_device_save(s, f);
@@ -1342,7 +1220,8 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
 static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
 static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-    SubchDev *s = dev->sch;
+    CcwDevice *ccw_dev = CCW_DEVICE(d);
+    SubchDev *s = ccw_dev->sch;
     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
     int len;
     int len;
 
 
@@ -1387,7 +1266,8 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
 {
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
-    SubchDev *sch = dev->sch;
+    CcwDevice *ccw_dev = CCW_DEVICE(d);
+    SubchDev *sch = ccw_dev->sch;
     int n = virtio_get_num_queues(vdev);
     int n = virtio_get_num_queues(vdev);
 
 
     if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) {
     if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) {
@@ -1431,7 +1311,7 @@ static void virtio_ccw_device_unplugged(DeviceState *d)
 /**************** Virtio-ccw Bus Device Descriptions *******************/
 /**************** Virtio-ccw Bus Device Descriptions *******************/
 
 
 static Property virtio_ccw_net_properties[] = {
 static Property virtio_ccw_net_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1460,7 +1340,7 @@ static const TypeInfo virtio_ccw_net = {
 };
 };
 
 
 static Property virtio_ccw_blk_properties[] = {
 static Property virtio_ccw_blk_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1489,7 +1369,7 @@ static const TypeInfo virtio_ccw_blk = {
 };
 };
 
 
 static Property virtio_ccw_serial_properties[] = {
 static Property virtio_ccw_serial_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1518,7 +1398,7 @@ static const TypeInfo virtio_ccw_serial = {
 };
 };
 
 
 static Property virtio_ccw_balloon_properties[] = {
 static Property virtio_ccw_balloon_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1547,7 +1427,7 @@ static const TypeInfo virtio_ccw_balloon = {
 };
 };
 
 
 static Property virtio_ccw_scsi_properties[] = {
 static Property virtio_ccw_scsi_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1577,7 +1457,7 @@ static const TypeInfo virtio_ccw_scsi = {
 
 
 #ifdef CONFIG_VHOST_SCSI
 #ifdef CONFIG_VHOST_SCSI
 static Property vhost_ccw_scsi_properties[] = {
 static Property vhost_ccw_scsi_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
                        VIRTIO_CCW_MAX_REV),
                        VIRTIO_CCW_MAX_REV),
     DEFINE_PROP_END_OF_LIST(),
     DEFINE_PROP_END_OF_LIST(),
@@ -1615,7 +1495,7 @@ static void virtio_ccw_rng_instance_init(Object *obj)
 }
 }
 
 
 static Property virtio_ccw_rng_properties[] = {
 static Property virtio_ccw_rng_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1662,29 +1542,17 @@ static int virtio_ccw_busdev_exit(DeviceState *dev)
 static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
 static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
                                      DeviceState *dev, Error **errp)
                                      DeviceState *dev, Error **errp)
 {
 {
-    VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
-    SubchDev *sch = _dev->sch;
+    VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev);
 
 
     virtio_ccw_stop_ioeventfd(_dev);
     virtio_ccw_stop_ioeventfd(_dev);
-
-    /*
-     * We should arrive here only for device_del, since we don't support
-     * direct hot(un)plug of channels, but only through virtio.
-     */
-    assert(sch != NULL);
-    /* Subchannel is now disabled and no longer valid. */
-    sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA |
-                                     PMCW_FLAGS_MASK_DNV);
-
-    css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0);
-
-    object_unparent(OBJECT(dev));
 }
 }
 
 
 static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
 static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
 {
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
+    CCWDeviceClass *k = CCW_DEVICE_CLASS(dc);
 
 
+    k->unplug = virtio_ccw_busdev_unplug;
     dc->realize = virtio_ccw_busdev_realize;
     dc->realize = virtio_ccw_busdev_realize;
     dc->exit = virtio_ccw_busdev_exit;
     dc->exit = virtio_ccw_busdev_exit;
     dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
     dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
@@ -1692,44 +1560,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
 
 
 static const TypeInfo virtio_ccw_device_info = {
 static const TypeInfo virtio_ccw_device_info = {
     .name = TYPE_VIRTIO_CCW_DEVICE,
     .name = TYPE_VIRTIO_CCW_DEVICE,
-    .parent = TYPE_DEVICE,
+    .parent = TYPE_CCW_DEVICE,
     .instance_size = sizeof(VirtioCcwDevice),
     .instance_size = sizeof(VirtioCcwDevice),
     .class_init = virtio_ccw_device_class_init,
     .class_init = virtio_ccw_device_class_init,
     .class_size = sizeof(VirtIOCCWDeviceClass),
     .class_size = sizeof(VirtIOCCWDeviceClass),
     .abstract = true,
     .abstract = true,
 };
 };
 
 
-/***************** Virtual-css Bus Bridge Device ********************/
-/* Only required to have the virtio bus as child in the system bus */
-
-static int virtual_css_bridge_init(SysBusDevice *dev)
-{
-    /* nothing */
-    return 0;
-}
-
-static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = virtual_css_bridge_init;
-    hc->unplug = virtio_ccw_busdev_unplug;
-    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo virtual_css_bridge_info = {
-    .name          = "virtual-css-bridge",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SysBusDevice),
-    .class_init    = virtual_css_bridge_class_init,
-    .interfaces = (InterfaceInfo[]) {
-        { TYPE_HOTPLUG_HANDLER },
-        { }
-    }
-};
-
 /* virtio-ccw-bus */
 /* virtio-ccw-bus */
 
 
 static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
 static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1775,7 +1612,7 @@ static const TypeInfo virtio_ccw_bus_info = {
 
 
 #ifdef CONFIG_VIRTFS
 #ifdef CONFIG_VIRTFS
 static Property virtio_ccw_9p_properties[] = {
 static Property virtio_ccw_9p_properties[] = {
-    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id),
+    DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
             VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
             VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
     DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1824,7 +1661,6 @@ static const TypeInfo virtio_ccw_9p_info = {
 static void virtio_ccw_register(void)
 static void virtio_ccw_register(void)
 {
 {
     type_register_static(&virtio_ccw_bus_info);
     type_register_static(&virtio_ccw_bus_info);
-    type_register_static(&virtual_css_bus_info);
     type_register_static(&virtio_ccw_device_info);
     type_register_static(&virtio_ccw_device_info);
     type_register_static(&virtio_ccw_serial);
     type_register_static(&virtio_ccw_serial);
     type_register_static(&virtio_ccw_blk);
     type_register_static(&virtio_ccw_blk);
@@ -1835,7 +1671,6 @@ static void virtio_ccw_register(void)
     type_register_static(&vhost_ccw_scsi);
     type_register_static(&vhost_ccw_scsi);
 #endif
 #endif
     type_register_static(&virtio_ccw_rng);
     type_register_static(&virtio_ccw_rng);
-    type_register_static(&virtual_css_bridge_info);
 #ifdef CONFIG_VIRTFS
 #ifdef CONFIG_VIRTFS
     type_register_static(&virtio_ccw_9p_info);
     type_register_static(&virtio_ccw_9p_info);
 #endif
 #endif

+ 4 - 16
hw/s390x/virtio-ccw.h

@@ -26,8 +26,8 @@
 
 
 #include <hw/s390x/s390_flic.h>
 #include <hw/s390x/s390_flic.h>
 #include <hw/s390x/css.h>
 #include <hw/s390x/css.h>
-
-#define VIRTUAL_CSSID 0xfe
+#include "ccw-device.h"
+#include "hw/s390x/css-bridge.h"
 
 
 #define VIRTIO_CCW_CU_TYPE 0x3832
 #define VIRTIO_CCW_CU_TYPE 0x3832
 #define VIRTIO_CCW_CHPID_TYPE 0x32
 #define VIRTIO_CCW_CHPID_TYPE 0x32
@@ -67,7 +67,7 @@ typedef struct VirtioBusClass VirtioCcwBusClass;
 typedef struct VirtioCcwDevice VirtioCcwDevice;
 typedef struct VirtioCcwDevice VirtioCcwDevice;
 
 
 typedef struct VirtIOCCWDeviceClass {
 typedef struct VirtIOCCWDeviceClass {
-    DeviceClass parent_class;
+    CCWDeviceClass parent_class;
     void (*realize)(VirtioCcwDevice *dev, Error **errp);
     void (*realize)(VirtioCcwDevice *dev, Error **errp);
     int (*exit)(VirtioCcwDevice *dev);
     int (*exit)(VirtioCcwDevice *dev);
 } VirtIOCCWDeviceClass;
 } VirtIOCCWDeviceClass;
@@ -78,9 +78,7 @@ typedef struct VirtIOCCWDeviceClass {
 #define VIRTIO_CCW_FLAG_USE_IOEVENTFD   (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
 #define VIRTIO_CCW_FLAG_USE_IOEVENTFD   (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
 
 
 struct VirtioCcwDevice {
 struct VirtioCcwDevice {
-    DeviceState parent_obj;
-    SubchDev *sch;
-    CssDevId bus_id;
+    CcwDevice parent_obj;
     int revision;
     int revision;
     uint32_t max_rev;
     uint32_t max_rev;
     VirtioBusState bus;
     VirtioBusState bus;
@@ -103,15 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
     return dev->max_rev;
     return dev->max_rev;
 }
 }
 
 
-/* virtual css bus type */
-typedef struct VirtualCssBus {
-    BusState parent_obj;
-} VirtualCssBus;
-
-#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus"
-#define VIRTUAL_CSS_BUS(obj) \
-     OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS)
-
 /* virtio-scsi-ccw */
 /* virtio-scsi-ccw */
 
 
 #define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw"
 #define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw"
@@ -191,7 +180,6 @@ typedef struct VirtIORNGCcw {
     VirtIORNG vdev;
     VirtIORNG vdev;
 } VirtIORNGCcw;
 } VirtIORNGCcw;
 
 
-VirtualCssBus *virtual_css_bus_init(void);
 void virtio_ccw_device_update_status(SubchDev *sch);
 void virtio_ccw_device_update_status(SubchDev *sch);
 VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
 VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
 
 

+ 31 - 0
include/hw/s390x/css-bridge.h

@@ -0,0 +1,31 @@
+/*
+ * virtual css bridge definition
+ *
+ * Copyright 2012,2016 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *            Pierre Morel <pmorel@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef HW_S390X_CSS_BRIDGE_H
+#define HW_S390X_CSS_BRIDGE_H
+#include "qom/object.h"
+#include "hw/qdev-core.h"
+
+/* virtual css bridge */
+#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge"
+
+/* virtual css bus type */
+typedef struct VirtualCssBus {
+    BusState parent_obj;
+} VirtualCssBus;
+
+#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus"
+#define VIRTUAL_CSS_BUS(obj) \
+     OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS)
+VirtualCssBus *virtual_css_bus_init(void);
+
+#endif

+ 18 - 0
include/hw/s390x/css.h

@@ -17,6 +17,7 @@
 #include "hw/s390x/ioinst.h"
 #include "hw/s390x/ioinst.h"
 
 
 /* Channel subsystem constants. */
 /* Channel subsystem constants. */
+#define MAX_DEVNO 65535
 #define MAX_SCHID 65535
 #define MAX_SCHID 65535
 #define MAX_SSID 3
 #define MAX_SSID 3
 #define MAX_CSSID 254 /* 255 is reserved */
 #define MAX_CSSID 254 /* 255 is reserved */
@@ -24,6 +25,8 @@
 
 
 #define MAX_CIWS 62
 #define MAX_CIWS 62
 
 
+#define VIRTUAL_CSSID 0xfe
+
 typedef struct CIW {
 typedef struct CIW {
     uint8_t type;
     uint8_t type;
     uint8_t command;
     uint8_t command;
@@ -169,4 +172,19 @@ extern PropertyInfo css_devid_propinfo;
 #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \
 #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \
     DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId)
     DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId)
 
 
+/**
+ * Create a subchannel for the given bus id.
+ *
+ * If @p bus_id is valid, verify that it uses the virtual channel
+ * subsystem id and is not already in use, and find a free subchannel
+ * id for it. If @p bus_id is not valid, find a free subchannel id and
+ * device number across all subchannel sets. If either of the former
+ * actions succeed, allocate a subchannel structure, initialise it
+ * with the bus id, subchannel id and device number, register it with
+ * the CSS and return it. Otherwise return NULL.
+ *
+ * The caller becomes owner of the returned subchannel structure and
+ * is responsible for unregistering and freeing it.
+ */
+SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp);
 #endif
 #endif

+ 1 - 0
include/hw/s390x/sclp.h

@@ -58,6 +58,7 @@
 #define SCLP_RC_CONTAINED_EQUIPMENT_CHECK       0x0340
 #define SCLP_RC_CONTAINED_EQUIPMENT_CHECK       0x0340
 #define SCLP_RC_INSUFFICIENT_SCCB_LENGTH        0x0300
 #define SCLP_RC_INSUFFICIENT_SCCB_LENGTH        0x0300
 #define SCLP_RC_STANDBY_READ_COMPLETION         0x0410
 #define SCLP_RC_STANDBY_READ_COMPLETION         0x0410
+#define SCLP_RC_ADAPTER_IN_RESERVED_STATE       0x05f0
 #define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED       0x09f0
 #define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED       0x09f0
 #define SCLP_RC_INVALID_FUNCTION                0x40f0
 #define SCLP_RC_INVALID_FUNCTION                0x40f0
 #define SCLP_RC_NO_EVENT_BUFFERS_STORED         0x60f0
 #define SCLP_RC_NO_EVENT_BUFFERS_STORED         0x60f0

二進制
pc-bios/s390-ccw.img


+ 12 - 0
pc-bios/s390-ccw/iplb.h

@@ -43,6 +43,16 @@ struct IplBlockFcp {
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 typedef struct IplBlockFcp IplBlockFcp;
 typedef struct IplBlockFcp IplBlockFcp;
 
 
+struct IplBlockQemuScsi {
+    uint32_t lun;
+    uint16_t target;
+    uint16_t channel;
+    uint8_t  reserved0[77];
+    uint8_t  ssid;
+    uint16_t devno;
+} __attribute__ ((packed));
+typedef struct IplBlockQemuScsi IplBlockQemuScsi;
+
 struct IplParameterBlock {
 struct IplParameterBlock {
     uint32_t len;
     uint32_t len;
     uint8_t  reserved0[3];
     uint8_t  reserved0[3];
@@ -55,6 +65,7 @@ struct IplParameterBlock {
     union {
     union {
         IplBlockCcw ccw;
         IplBlockCcw ccw;
         IplBlockFcp fcp;
         IplBlockFcp fcp;
+        IplBlockQemuScsi scsi;
     };
     };
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 typedef struct IplParameterBlock IplParameterBlock;
 typedef struct IplParameterBlock IplParameterBlock;
@@ -63,6 +74,7 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
 
 
 #define S390_IPL_TYPE_FCP 0x00
 #define S390_IPL_TYPE_FCP 0x00
 #define S390_IPL_TYPE_CCW 0x02
 #define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_QEMU_SCSI 0xff
 
 
 static inline bool store_iplb(IplParameterBlock *iplb)
 static inline bool store_iplb(IplParameterBlock *iplb)
 {
 {

+ 12 - 0
pc-bios/s390-ccw/main.c

@@ -84,6 +84,18 @@ static void virtio_setup(void)
             debug_print_int("ssid ", blk_schid.ssid);
             debug_print_int("ssid ", blk_schid.ssid);
             found = find_dev(&schib, dev_no);
             found = find_dev(&schib, dev_no);
             break;
             break;
+        case S390_IPL_TYPE_QEMU_SCSI:
+        {
+            VDev *vdev = virtio_get_device();
+
+            vdev->scsi_device_selected = true;
+            vdev->selected_scsi_device.channel = iplb.scsi.channel;
+            vdev->selected_scsi_device.target = iplb.scsi.target;
+            vdev->selected_scsi_device.lun = iplb.scsi.lun;
+            blk_schid.ssid = iplb.scsi.ssid & 0x3;
+            found = find_dev(&schib, iplb.scsi.devno);
+            break;
+        }
         default:
         default:
             panic("List-directed IPL not supported yet!\n");
             panic("List-directed IPL not supported yet!\n");
         }
         }

+ 11 - 0
pc-bios/s390-ccw/virtio-scsi.c

@@ -204,6 +204,17 @@ static void virtio_scsi_locate_device(VDev *vdev)
     debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target);
     debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target);
     debug_print_int("config.scsi.max_lun    ", vdev->config.scsi.max_lun);
     debug_print_int("config.scsi.max_lun    ", vdev->config.scsi.max_lun);
 
 
+    if (vdev->scsi_device_selected) {
+        sdev->channel = vdev->selected_scsi_device.channel;
+        sdev->target = vdev->selected_scsi_device.target;
+        sdev->lun = vdev->selected_scsi_device.lun;
+
+        IPL_check(sdev->channel == 0, "non-zero channel requested");
+        IPL_check(sdev->target <= vdev->config.scsi.max_target, "target# high");
+        IPL_check(sdev->lun <= vdev->config.scsi.max_lun, "LUN# high");
+        return;
+    }
+
     for (target = 0; target <= vdev->config.scsi.max_target; target++) {
     for (target = 0; target <= vdev->config.scsi.max_target; target++) {
         sdev->channel = channel;
         sdev->channel = channel;
         sdev->target = target; /* sdev->lun will be 0 here */
         sdev->target = target; /* sdev->lun will be 0 here */

+ 2 - 0
pc-bios/s390-ccw/virtio.h

@@ -274,6 +274,8 @@ struct VDev {
     uint64_t scsi_last_block;
     uint64_t scsi_last_block;
     uint32_t scsi_dev_cyls;
     uint32_t scsi_dev_cyls;
     uint8_t scsi_dev_heads;
     uint8_t scsi_dev_heads;
+    bool scsi_device_selected;
+    ScsiDevice selected_scsi_device;
 };
 };
 typedef struct VDev VDev;
 typedef struct VDev VDev;
 
 

+ 2 - 2
target-s390x/kvm.c

@@ -2246,10 +2246,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
                              uint64_t address, uint32_t data, PCIDevice *dev)
                              uint64_t address, uint32_t data, PCIDevice *dev)
 {
 {
     S390PCIBusDevice *pbdev;
     S390PCIBusDevice *pbdev;
-    uint32_t fid = data >> ZPCI_MSI_VEC_BITS;
+    uint32_t idx = data >> ZPCI_MSI_VEC_BITS;
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
 
 
-    pbdev = s390_pci_find_dev_by_fid(fid);
+    pbdev = s390_pci_find_dev_by_idx(idx);
     if (!pbdev) {
     if (!pbdev) {
         DPRINTF("add_msi_route no dev\n");
         DPRINTF("add_msi_route no dev\n");
         return -ENODEV;
         return -ENODEV;