summaryrefslogtreecommitdiff
path: root/hw/s390x
diff options
context:
space:
mode:
Diffstat (limited to 'hw/s390x')
-rw-r--r--hw/s390x/Makefile.objs2
-rw-r--r--hw/s390x/ccw-device.c27
-rw-r--r--hw/s390x/ccw-device.h43
-rw-r--r--hw/s390x/css-bridge.c148
-rw-r--r--hw/s390x/css.c278
-rw-r--r--hw/s390x/css.h126
-rw-r--r--hw/s390x/ipl.c102
-rw-r--r--hw/s390x/ipl.h99
-rw-r--r--hw/s390x/s390-pci-bus.c608
-rw-r--r--hw/s390x/s390-pci-bus.h98
-rw-r--r--hw/s390x/s390-pci-inst.c349
-rw-r--r--hw/s390x/s390-pci-inst.h20
-rw-r--r--hw/s390x/s390-skeys.c27
-rw-r--r--hw/s390x/s390-virtio-ccw.c65
-rw-r--r--hw/s390x/s390-virtio.h2
-rw-r--r--hw/s390x/sclp.c4
-rw-r--r--hw/s390x/sclpquiesce.c2
-rw-r--r--hw/s390x/trace-events15
-rw-r--r--hw/s390x/virtio-ccw.c457
-rw-r--r--hw/s390x/virtio-ccw.h39
20 files changed, 1704 insertions, 807 deletions
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index 220361782d..41ac4ec325 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -8,6 +8,8 @@ obj-y += ipl.o
obj-y += css.o
obj-y += s390-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-skeys.o
obj-$(CONFIG_KVM) += s390-skeys-kvm.o
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
new file mode 100644
index 0000000000..28ea20440e
--- /dev/null
+++ b/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)
diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
new file mode 100644
index 0000000000..59ba01b6c5
--- /dev/null
+++ b/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
diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c
new file mode 100644
index 0000000000..9a7f7ee60c
--- /dev/null
+++ b/hw/s390x/css-bridge.c
@@ -0,0 +1,148 @@
+/*
+ * 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 char *virtual_css_bus_get_dev_path(DeviceState *dev)
+{
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
+ SubchDev *sch = ccw_dev->sch;
+ VirtualCssBridge *bridge =
+ VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent);
+
+ /*
+ * We can't provide a dev path for backward compatibility on
+ * older machines, as it is visible in the migration stream.
+ */
+ return bridge->css_dev_path ?
+ g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) :
+ NULL;
+}
+
+static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
+{
+ BusClass *k = BUS_CLASS(klass);
+
+ k->reset = virtual_css_bus_reset;
+ k->get_dev_path = virtual_css_bus_get_dev_path;
+}
+
+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 Property virtual_css_bridge_properties[] = {
+ DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path,
+ true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+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);
+ dc->props = virtual_css_bridge_properties;
+}
+
+static const TypeInfo virtual_css_bridge_info = {
+ .name = TYPE_VIRTUAL_CSS_BRIDGE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(VirtualCssBridge),
+ .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)
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 3a1d919580..bb8e4be339 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -10,12 +10,14 @@
*/
#include "qemu/osdep.h"
-#include <hw/qdev.h>
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "hw/qdev.h"
#include "qemu/bitops.h"
#include "exec/address-spaces.h"
#include "cpu.h"
-#include "ioinst.h"
-#include "css.h"
+#include "hw/s390x/ioinst.h"
+#include "hw/s390x/css.h"
#include "trace.h"
#include "hw/s390x/s390_flic.h"
@@ -192,12 +194,46 @@ out:
return ret;
}
-uint16_t css_build_subchannel_id(SubchDev *sch)
+static void css_clear_io_interrupt(uint16_t subchannel_id,
+ uint16_t subchannel_nr)
+{
+ Error *err = NULL;
+ static bool no_clear_irq;
+ S390FLICState *fs = s390_get_flic();
+ S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
+ int r;
+
+ if (unlikely(no_clear_irq)) {
+ return;
+ }
+ r = fsc->clear_io_irq(fs, subchannel_id, subchannel_nr);
+ switch (r) {
+ case 0:
+ break;
+ case -ENOSYS:
+ no_clear_irq = true;
+ /*
+ * Ignore unavailability, as the user can't do anything
+ * about it anyway.
+ */
+ break;
+ default:
+ error_setg_errno(&err, -r, "unexpected error condition");
+ error_propagate(&error_abort, err);
+ }
+}
+
+static inline uint16_t css_do_build_subchannel_id(uint8_t cssid, uint8_t ssid)
{
if (channel_subsys.max_cssid > 0) {
- return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1;
+ return (cssid << 8) | (1 << 3) | (ssid << 1) | 1;
}
- return (sch->ssid << 1) | 1;
+ return (ssid << 1) | 1;
+}
+
+uint16_t css_build_subchannel_id(SubchDev *sch)
+{
+ return css_do_build_subchannel_id(sch->cssid, sch->ssid);
}
static void css_inject_io_interrupt(SubchDev *sch)
@@ -475,6 +511,7 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
path = 0x80;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
+ /* Start Function triggered via ssch, i.e. we have an ORB */
s->cstat = 0;
s->dstat = 0;
/* Look at the orb and try to execute the channel program. */
@@ -488,9 +525,12 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
return;
}
sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT);
+ s->flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0;
sch->ccw_no_data_cnt = 0;
suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND);
} else {
+ /* Start Function resumed via rsch, i.e. we don't have an
+ * ORB */
s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
/* The channel program had been suspended before. */
suspend_allowed = true;
@@ -573,6 +613,7 @@ static void do_subchannel_work(SubchDev *sch, ORB *orb)
} else if (s->ctrl & SCSW_FCTL_HALT_FUNC) {
sch_handle_halt_func(sch);
} else if (s->ctrl & SCSW_FCTL_START_FUNC) {
+ /* Triggered by both ssch and rsch. */
sch_handle_start_func(sch, orb);
} else {
/* Cannot happen. */
@@ -1304,6 +1345,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 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)
{
if (sch->ssid > channel_subsys.max_ssid) {
@@ -1429,6 +1580,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0,
(guest_cssid << 8) | (ssid << 4));
}
+ /* RW_ERC_IPI --> clear pending interrupts */
+ css_clear_io_interrupt(css_do_build_subchannel_id(cssid, ssid), schid);
}
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid)
@@ -1644,3 +1797,116 @@ void css_reset(void)
channel_subsys.max_cssid = 0;
channel_subsys.max_ssid = 0;
}
+
+static void get_css_devid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ CssDevId *dev_id = qdev_get_prop_ptr(dev, prop);
+ char buffer[] = "xx.x.xxxx";
+ char *p = buffer;
+ int r;
+
+ if (dev_id->valid) {
+
+ r = snprintf(buffer, sizeof(buffer), "%02x.%1x.%04x", dev_id->cssid,
+ dev_id->ssid, dev_id->devid);
+ assert(r == sizeof(buffer) - 1);
+
+ /* drop leading zero */
+ if (dev_id->cssid <= 0xf) {
+ p++;
+ }
+ } else {
+ snprintf(buffer, sizeof(buffer), "<unset>");
+ }
+
+ visit_type_str(v, name, &p, errp);
+}
+
+/*
+ * parse <cssid>.<ssid>.<devid> and assert valid range for cssid/ssid
+ */
+static void set_css_devid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ CssDevId *dev_id = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ char *str;
+ int num, n1, n2;
+ unsigned int cssid, ssid, devid;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, name, &str, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ num = sscanf(str, "%2x.%1x%n.%4x%n", &cssid, &ssid, &n1, &devid, &n2);
+ if (num != 3 || (n2 - n1) != 5 || strlen(str) != n2) {
+ error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+ goto out;
+ }
+ if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) {
+ error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x",
+ cssid, ssid);
+ goto out;
+ }
+
+ dev_id->cssid = cssid;
+ dev_id->ssid = ssid;
+ dev_id->devid = devid;
+ dev_id->valid = true;
+
+out:
+ g_free(str);
+}
+
+PropertyInfo css_devid_propinfo = {
+ .name = "str",
+ .description = "Identifier of an I/O device in the channel "
+ "subsystem, example: fe.1.23ab",
+ .get = get_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;
+}
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
deleted file mode 100644
index a320eea59c..0000000000
--- a/hw/s390x/css.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Channel subsystem structures and definitions.
- *
- * Copyright 2012 IBM Corp.
- * Author(s): Cornelia Huck <cornelia.huck@de.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 CSS_H
-#define CSS_H
-
-#include "hw/s390x/adapter.h"
-#include "hw/s390x/s390_flic.h"
-#include "ioinst.h"
-
-/* Channel subsystem constants. */
-#define MAX_SCHID 65535
-#define MAX_SSID 3
-#define MAX_CSSID 254 /* 255 is reserved */
-#define MAX_CHPID 255
-
-#define MAX_CIWS 62
-
-typedef struct CIW {
- uint8_t type;
- uint8_t command;
- uint16_t count;
-} QEMU_PACKED CIW;
-
-typedef struct SenseId {
- /* common part */
- uint8_t reserved; /* always 0x'FF' */
- uint16_t cu_type; /* control unit type */
- uint8_t cu_model; /* control unit model */
- uint16_t dev_type; /* device type */
- uint8_t dev_model; /* device model */
- uint8_t unused; /* padding byte */
- /* extended part */
- CIW ciw[MAX_CIWS]; /* variable # of CIWs */
-} QEMU_PACKED SenseId;
-
-/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */
-typedef struct CMB {
- uint16_t ssch_rsch_count;
- uint16_t sample_count;
- uint32_t device_connect_time;
- uint32_t function_pending_time;
- uint32_t device_disconnect_time;
- uint32_t control_unit_queuing_time;
- uint32_t device_active_only_time;
- uint32_t reserved[2];
-} QEMU_PACKED CMB;
-
-typedef struct CMBE {
- uint32_t ssch_rsch_count;
- uint32_t sample_count;
- uint32_t device_connect_time;
- uint32_t function_pending_time;
- uint32_t device_disconnect_time;
- uint32_t control_unit_queuing_time;
- uint32_t device_active_only_time;
- uint32_t device_busy_time;
- uint32_t initial_command_response_time;
- uint32_t reserved[7];
-} QEMU_PACKED CMBE;
-
-struct SubchDev {
- /* channel-subsystem related things: */
- uint8_t cssid;
- uint8_t ssid;
- uint16_t schid;
- uint16_t devno;
- SCHIB curr_status;
- uint8_t sense_data[32];
- hwaddr channel_prog;
- CCW1 last_cmd;
- bool last_cmd_valid;
- bool ccw_fmt_1;
- bool thinint_active;
- uint8_t ccw_no_data_cnt;
- /* transport-provided data: */
- int (*ccw_cb) (SubchDev *, CCW1);
- void (*disable_cb)(SubchDev *);
- SenseId id;
- void *driver_data;
-};
-
-typedef struct IndAddr {
- hwaddr addr;
- uint64_t map;
- unsigned long refcnt;
- int len;
- QTAILQ_ENTRY(IndAddr) sibling;
-} IndAddr;
-
-IndAddr *get_indicator(hwaddr ind_addr, int len);
-void release_indicator(AdapterInfo *adapter, IndAddr *indicator);
-int map_indicator(AdapterInfo *adapter, IndAddr *indicator);
-
-typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
- uint16_t schid);
-void subch_device_save(SubchDev *s, QEMUFile *f);
-int subch_device_load(SubchDev *s, QEMUFile *f);
-int css_create_css_image(uint8_t cssid, bool default_image);
-bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno);
-void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
- uint16_t devno, SubchDev *sch);
-void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type);
-uint16_t css_build_subchannel_id(SubchDev *sch);
-void css_reset(void);
-void css_reset_sch(SubchDev *sch);
-void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid);
-void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
- int hotplugged, int add);
-void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
-void css_generate_css_crws(uint8_t cssid);
-void css_clear_sei_pending(void);
-void css_adapter_interrupt(uint8_t isc);
-
-#define CSS_IO_ADAPTER_VIRTIO 1
-int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
- bool maskable, uint32_t *id);
-#endif
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index f104200273..2e2664f22e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -30,6 +30,24 @@
#define ZIPL_IMAGE_START 0x009000UL
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
+static bool iplb_extended_needed(void *opaque)
+{
+ S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
+
+ return ipl->iplbext_migration;
+}
+
+static const VMStateDescription vmstate_iplb_extended = {
+ .name = "ipl/iplb_extended",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = iplb_extended_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_iplb = {
.name = "ipl/iplb",
.version_id = 0,
@@ -39,6 +57,10 @@ static const VMStateDescription vmstate_iplb = {
VMSTATE_UINT16(devno, IplParameterBlock),
VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_iplb_extended,
+ NULL
}
};
@@ -47,8 +69,8 @@ static const VMStateDescription vmstate_ipl = {
.version_id = 0,
.minimum_version_id = 0,
.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_BOOL(iplb_valid, S390IPLState),
VMSTATE_UINT8(cssid, S390IPLState),
@@ -107,7 +129,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
/* Adjust ELF start address to final location */
ipl->bios_start_addr += fwbase;
} else {
- /* Try to load non-ELF file (e.g. s390-ccw.img) */
+ /* Try to load non-ELF file */
bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
4096);
ipl->bios_start_addr = ZIPL_IMAGE_START;
@@ -170,6 +192,13 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
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);
error:
error_propagate(errp, err);
@@ -181,46 +210,52 @@ static Property s390_ipl_properties[] = {
DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
+ DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
-/*
- * In addition to updating the iplstate, this function returns:
- * - 0 if system was ipled with external kernel
- * - -1 if no valid boot device was found
- * - ccw id of the boot device otherwise
- */
-static uint64_t s390_update_iplstate(S390IPLState *ipl)
+static bool s390_gen_initial_iplb(S390IPLState *ipl)
{
DeviceState *dev_st;
- if (ipl->iplb_valid) {
- ipl->cssid = 0;
- ipl->ssid = 0;
- ipl->devno = ipl->iplb.devno;
- goto out;
- }
-
- if (ipl->kernel) {
- return 0;
- }
-
dev_st = get_boot_device(0);
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);
- if (ccw_dev) {
- ipl->cssid = ccw_dev->sch->cssid;
- ipl->ssid = ccw_dev->sch->ssid;
- ipl->devno = ccw_dev->sch->devno;
- goto out;
+ 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.blk0_len =
+ cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
+ ipl->iplb.pbt = S390_IPL_TYPE_CCW;
+ ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
+ ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+ 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;
}
}
- return -1;
-out:
- return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno);
+ return false;
}
void s390_ipl_update_diag308(IplParameterBlock *iplb)
@@ -258,7 +293,9 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
if (!ipl->kernel || ipl->iplb_valid) {
cpu->env.psw.addr = ipl->bios_start_addr;
- cpu->env.regs[7] = s390_update_iplstate(ipl);
+ if (!ipl->iplb_valid) {
+ ipl->iplb_valid = s390_gen_initial_iplb(ipl);
+ }
}
}
@@ -268,6 +305,7 @@ static void s390_ipl_reset(DeviceState *dev)
if (!ipl->reipl_requested) {
ipl->iplb_valid = false;
+ memset(&ipl->iplb, 0, sizeof(IplParameterBlock));
}
ipl->reipl_requested = false;
}
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 6b48ed7b93..c89109585a 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -15,11 +15,71 @@
#include "hw/qdev.h"
#include "cpu.h"
-typedef struct IplParameterBlock {
- uint8_t reserved1[110];
- uint16_t devno;
- uint8_t reserved2[88];
-} IplParameterBlock;
+struct IplBlockCcw {
+ uint8_t reserved0[85];
+ uint8_t ssid;
+ uint16_t devno;
+ uint8_t vm_flags;
+ uint8_t reserved3[3];
+ uint32_t vm_parm_len;
+ uint8_t nss_name[8];
+ uint8_t vm_parm[64];
+ uint8_t reserved4[8];
+} QEMU_PACKED;
+typedef struct IplBlockCcw IplBlockCcw;
+
+struct IplBlockFcp {
+ uint8_t reserved1[305 - 1];
+ uint8_t opt;
+ uint8_t reserved2[3];
+ uint16_t reserved3;
+ uint16_t devno;
+ uint8_t reserved4[4];
+ uint64_t wwpn;
+ uint64_t lun;
+ uint32_t bootprog;
+ uint8_t reserved5[12];
+ uint64_t br_lba;
+ uint32_t scp_data_len;
+ uint8_t reserved6[260];
+ uint8_t scp_data[];
+} QEMU_PACKED;
+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 {
+ struct {
+ uint32_t len;
+ uint8_t reserved0[3];
+ uint8_t version;
+ uint32_t blk0_len;
+ uint8_t pbt;
+ uint8_t flags;
+ uint16_t reserved01;
+ uint8_t loadparm[8];
+ union {
+ IplBlockCcw ccw;
+ IplBlockFcp fcp;
+ IplBlockQemuScsi scsi;
+ };
+ } QEMU_PACKED;
+ struct {
+ uint8_t reserved1[110];
+ uint16_t devno;
+ uint8_t reserved2[88];
+ uint8_t reserved_ext[4096 - 200];
+ } QEMU_PACKED;
+} QEMU_PACKED;
+typedef union IplParameterBlock IplParameterBlock;
void s390_ipl_update_diag308(IplParameterBlock *iplb);
void s390_ipl_prepare_cpu(S390CPU *cpu);
@@ -33,7 +93,9 @@ struct S390IPLState {
/*< private >*/
DeviceState parent_obj;
uint64_t start_addr;
+ uint64_t compat_start_addr;
uint64_t bios_start_addr;
+ uint64_t compat_bios_start_addr;
bool enforce_bios;
IplParameterBlock iplb;
bool iplb_valid;
@@ -47,7 +109,34 @@ struct S390IPLState {
uint8_t cssid;
uint8_t ssid;
uint16_t devno;
+ bool iplbext_migration;
};
typedef struct S390IPLState S390IPLState;
+#define S390_IPL_TYPE_FCP 0x00
+#define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_QEMU_SCSI 0xff
+
+#define S390_IPLB_HEADER_LEN 8
+#define S390_IPLB_MIN_CCW_LEN 200
+#define S390_IPLB_MIN_FCP_LEN 384
+#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
+
+static inline bool iplb_valid_len(IplParameterBlock *iplb)
+{
+ return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock);
+}
+
+static inline bool iplb_valid_ccw(IplParameterBlock *iplb)
+{
+ return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN &&
+ iplb->pbt == S390_IPL_TYPE_CCW;
+}
+
+static inline bool iplb_valid_fcp(IplParameterBlock *iplb)
+{
+ return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN &&
+ iplb->pbt == S390_IPL_TYPE_FCP;
+}
+
#endif
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 918b58543e..9c1c04e590 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -12,12 +12,15 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
#include "qemu-common.h"
#include "cpu.h"
#include "s390-pci-bus.h"
-#include <hw/pci/pci_bus.h>
-#include <hw/pci/msi.h>
-#include <qemu/error-report.h>
+#include "s390-pci-inst.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/msi.h"
+#include "qemu/error-report.h"
/* #define DEBUG_S390PCI_BUS */
#ifdef DEBUG_S390PCI_BUS
@@ -28,6 +31,19 @@
do { } while (0)
#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)
{
ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res;
@@ -35,12 +51,7 @@ int chsc_sei_nt2_get_event(void *res)
PciCcdfErr *eccdf;
int rc = 1;
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);
if (sei_cont) {
@@ -75,30 +86,40 @@ int chsc_sei_nt2_get_event(void *res)
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 *pbdev;
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++) {
- pbdev = &s->pbdev[i];
- if ((pbdev->fh != 0) && (pbdev->fid == fid)) {
+ pbdev = s->pbdev[i];
+ if (pbdev && pbdev->fid == fid) {
return pbdev;
}
}
@@ -106,80 +127,117 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid)
return NULL;
}
-void s390_pci_sclp_configure(int configure, SCCB *sccb)
+void s390_pci_sclp_configure(SCCB *sccb)
{
PciCfgSccb *psccb = (PciCfgSccb *)sccb;
S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid));
uint16_t rc;
- if (pbdev) {
- if ((configure == 1 && pbdev->configured == true) ||
- (configure == 0 && pbdev->configured == false)) {
- rc = SCLP_RC_NO_ACTION_REQUIRED;
- } else {
- pbdev->configured = !pbdev->configured;
- rc = SCLP_RC_NORMAL_COMPLETION;
- }
- } else {
- DPRINTF("sclp config %d no dev found\n", configure);
+ if (be16_to_cpu(sccb->h.length) < 16) {
+ rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH;
+ goto out;
+ }
+
+ if (!pbdev) {
+ DPRINTF("sclp config no dev found\n");
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:
psccb->header.response_code = cpu_to_be16(rc);
}
-static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
+void s390_pci_sclp_deconfigure(SCCB *sccb)
{
- return PCI_SLOT(pdev->devfn);
-}
+ PciCfgSccb *psccb = (PciCfgSccb *)sccb;
+ S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid));
+ uint16_t rc;
-static uint32_t s390_pci_get_pfh(PCIDevice *pdev)
-{
- return PCI_SLOT(pdev->devfn) | FH_VIRT;
+ if (be16_to_cpu(sccb->h.length) < 16) {
+ rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH;
+ goto out;
+ }
+
+ if (!pbdev) {
+ DPRINTF("sclp deconfig no dev found\n");
+ 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:
+ psccb->header.response_code = cpu_to_be16(rc);
}
-S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
+static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid)
{
- S390PCIBusDevice *pbdev;
int i;
- int j = 0;
- S390pciState *s = S390_PCI_HOST_BRIDGE(
- object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
-
- if (!s) {
- return NULL;
- }
+ S390PCIBusDevice *pbdev;
+ S390pciState *s = s390_get_phb();
for (i = 0; i < PCI_SLOT_MAX; i++) {
- pbdev = &s->pbdev[i];
-
- if (pbdev->fh == 0) {
+ pbdev = s->pbdev[i];
+ if (!pbdev) {
continue;
}
- if (j == idx) {
+ if (pbdev->uid == uid) {
return pbdev;
}
- j++;
}
return NULL;
}
-S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
+static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target)
{
- S390PCIBusDevice *pbdev;
int i;
- S390pciState *s = S390_PCI_HOST_BRIDGE(
- object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
+ S390PCIBusDevice *pbdev;
+ S390pciState *s = s390_get_phb();
- if (!s || !fh) {
+ if (!target) {
return NULL;
}
for (i = 0; i < PCI_SLOT_MAX; i++) {
- pbdev = &s->pbdev[i];
- if (pbdev->fh == fh) {
+ pbdev = s->pbdev[i];
+ if (!pbdev) {
+ continue;
+ }
+
+ if (!strcmp(pbdev->target, target)) {
return pbdev;
}
}
@@ -187,16 +245,31 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
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)
+{
+ S390pciState *s = s390_get_phb();
+ S390PCIBusDevice *pbdev;
+
+ pbdev = s->pbdev[fh & FH_MASK_INDEX];
+ if (pbdev && pbdev->fh == fh) {
+ return pbdev;
+ }
+
+ return NULL;
+}
+
static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
uint32_t fid, uint64_t faddr, uint32_t e)
{
SeiContainer *sei_cont;
- 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->fh = fh;
@@ -216,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);
}
-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);
}
@@ -320,7 +392,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
- if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) {
+ switch (pbdev->state) {
+ case ZPCI_FS_ENABLED:
+ case ZPCI_FS_BLOCKED:
+ if (!pbdev->iommu_enabled) {
+ return ret;
+ }
+ break;
+ default:
return ret;
}
@@ -339,30 +418,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
return ret;
}
- if (!pbdev->g_iota) {
- pbdev->error_state = true;
- pbdev->lgstg_blocked = true;
- s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
- addr, 0);
- return ret;
- }
-
if (addr < pbdev->pba || addr > pbdev->pal) {
- pbdev->error_state = true;
- pbdev->lgstg_blocked = true;
- s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
- addr, 0);
return ret;
}
pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
addr);
-
if (!pte) {
- pbdev->error_state = true;
- pbdev->lgstg_blocked = true;
- s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
- addr, ERR_EVENT_Q_BIT);
return ret;
}
@@ -388,7 +450,7 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
{
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)
@@ -416,22 +478,22 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
{
S390PCIBusDevice *pbdev;
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;
uint64_t ind_bit;
uint32_t sum_bit;
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) {
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;
}
- if (!(pbdev->fh & FH_ENABLED)) {
+ if (pbdev->state != ZPCI_FS_ENABLED) {
return;
}
@@ -458,32 +520,33 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable)
+void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
{
- pbdev->configured = false;
-
- if (enable) {
- 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);
- } else {
- memory_region_del_subregion(&pbdev->mr, &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->configured = true;
+void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
+{
+ memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr);
+ object_unparent(OBJECT(&pbdev->iommu_mr));
+ pbdev->iommu_enabled = false;
}
static void s390_pcihost_init_as(S390pciState *s)
{
int i;
- S390PCIBusDevice *pbdev;
+ S390PCIIOMMU *iommu;
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);
- 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),
@@ -510,6 +573,10 @@ static int s390_pcihost_init(SysBusDevice *dev)
bus = BUS(b);
qbus_set_hotplug_handler(bus, DEVICE(dev), NULL);
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);
return 0;
}
@@ -542,51 +609,155 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev)
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,
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,
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);
+
+ for (i = 0 ; i < PCI_SLOT_MAX; i++) {
+ if (s->pbdev[i] && s->pbdev[i]->pdev == pci_dev) {
+ pbdev = s->pbdev[i];
+ break;
+ }
+ }
- if (pbdev->configured) {
- pbdev->configured = false;
- s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
+ 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->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,
pbdev->fh, pbdev->fid);
- pbdev->fh = 0;
- pbdev->fid = 0;
- pbdev->pdev = NULL;
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)
@@ -613,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)
{
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)
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
index 59fd5c9583..4f564e02f2 100644
--- a/hw/s390x/s390-pci-bus.h
+++ b/hw/s390x/s390-pci-bus.h
@@ -14,23 +14,38 @@
#ifndef HW_S390_PCI_BUS_H
#define HW_S390_PCI_BUS_H
-#include <hw/pci/pci.h>
-#include <hw/pci/pci_host.h>
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/css.h"
#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 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) \
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_RESERVED_TO_STANDBY 0x0302
+#define HP_EVENT_DECONFIGURE_REQUEST 0x0303
#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
#define HP_EVENT_STANDBY_TO_RESERVED 0x0308
@@ -150,6 +165,34 @@ enum ZpciIoatDtype {
#define ZPCI_TABLE_VALID_MASK 0x20
#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 {
QTAILQ_ENTRY(SeiContainer) link;
uint32_t fid;
@@ -198,11 +241,11 @@ typedef struct ChscSeiNt2Res {
} QEMU_PACKED ChscSeiNt2Res;
typedef struct PciCfgSccb {
- SCCBHeader header;
- uint8_t atype;
- uint8_t reserved1;
- uint16_t reserved2;
- uint32_t aid;
+ SCCBHeader header;
+ uint8_t atype;
+ uint8_t reserved1;
+ uint16_t reserved2;
+ uint32_t aid;
} QEMU_PACKED PciCfgSccb;
typedef struct S390MsixInfo {
@@ -214,13 +257,21 @@ typedef struct S390MsixInfo {
uint32_t pba_offset;
} S390MsixInfo;
+typedef struct S390PCIIOMMU {
+ AddressSpace as;
+ MemoryRegion mr;
+} S390PCIIOMMU;
+
typedef struct S390PCIBusDevice {
+ DeviceState qdev;
PCIDevice *pdev;
- bool configured;
- bool error_state;
- bool lgstg_blocked;
+ ZpciState state;
+ bool iommu_enabled;
+ char *target;
+ uint16_t uid;
uint32_t fh;
uint32_t fid;
+ bool fid_defined;
uint64_t g_iota;
uint64_t pba;
uint64_t pal;
@@ -230,16 +281,22 @@ typedef struct S390PCIBusDevice {
uint8_t sum;
S390MsixInfo msix;
AdapterRoutes routes;
- AddressSpace as;
- MemoryRegion mr;
+ S390PCIIOMMU *iommu;
MemoryRegion iommu_mr;
IndAddr *summary_ind;
IndAddr *indicator;
+ QEMUTimer *release_timer;
} S390PCIBusDevice;
+typedef struct S390PCIBus {
+ BusState qbus;
+} S390PCIBus;
+
typedef struct S390pciState {
PCIHostState parent_obj;
- S390PCIBusDevice pbdev[PCI_SLOT_MAX];
+ S390PCIBus *bus;
+ S390PCIBusDevice *pbdev[PCI_SLOT_MAX];
+ S390PCIIOMMU *iommu[PCI_SLOT_MAX];
AddressSpace msix_notify_as;
MemoryRegion msix_notify_mr;
QTAILQ_HEAD(, SeiContainer) pending_sei;
@@ -247,10 +304,15 @@ typedef struct S390pciState {
int chsc_sei_nt2_get_event(void *res);
int chsc_sei_nt2_have_event(void);
-void s390_pci_sclp_configure(int configure, SCCB *sccb);
-void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable);
+void s390_pci_sclp_configure(SCCB *sccb);
+void s390_pci_sclp_deconfigure(SCCB *sccb);
+void s390_pci_iommu_enable(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_fh(uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
+S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev);
#endif
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index b28e7d14f8..f069b110b4 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -16,8 +16,8 @@
#include "cpu.h"
#include "s390-pci-inst.h"
#include "s390-pci-bus.h"
-#include <exec/memory-internal.h>
-#include <qemu/error-report.h>
+#include "exec/memory-internal.h"
+#include "qemu/error-report.h"
/* #define DEBUG_S390PCI_INST */
#ifdef DEBUG_S390PCI_INST
@@ -37,9 +37,9 @@ static void s390_set_status_code(CPUS390XState *env,
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;
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 ||
- ldq_p(&rrb->request.reserved1) != 0 ||
- ldq_p(&rrb->request.reserved2) != 0) {
+ ldq_p(&rrb->request.reserved1) != 0) {
res_code = CLP_RC_RESNOT0;
rc = -EINVAL;
goto out;
@@ -72,6 +71,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
rc = -EINVAL;
goto out;
}
+ } else {
+ pbdev = s390_pci_find_next_avail_dev(NULL);
}
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);
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);
+ rrb->response.flags = UID_CHECKING_ENABLED;
rrb->response.entry_size = sizeof(ClpFhListEntry);
- finish = 0;
- idx = resume_token;
+
+ i = 0;
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));
- 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));
- 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);
/* Add endian check for DPRINTF? */
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;
} else {
- resume_token = idx;
+ resume_token = pbdev->fh & FH_MASK_INDEX;
}
stq_p(&rrb->response.resume_token, resume_token);
stw_p(&rrb->response.hdr.len, g_l2);
@@ -212,14 +210,35 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
switch (reqsetpci->oc) {
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);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
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);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
@@ -256,9 +275,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
stq_p(&resquery->sdma, ZPCI_SDMA_ADDR);
stq_p(&resquery->edma, ZPCI_EDMA_ADDR);
+ stl_p(&resquery->fid, pbdev->fid);
stw_p(&resquery->pchid, 0);
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);
break;
}
@@ -317,16 +337,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
offset = env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+ if (!pbdev) {
DPRINTF("pcilg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
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);
s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
return 0;
+ default:
+ break;
}
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 +
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);
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];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+ if (!pbdev) {
DPRINTF("pcistg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
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);
s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
return 0;
+ default:
+ break;
}
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];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+ if (!pbdev) {
DPRINTF("rpcit no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
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;
while (start < end) {
entry = mr->iommu_ops->translate(mr, start, 0);
if (!entry.translated_addr) {
+ 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_SERR, pbdev->fh, pbdev->fid,
+ start, ERR_EVENT_Q_BIT);
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);
- if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
+ if (!pbdev) {
DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
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);
s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
return 0;
+ default:
+ break;
}
mr = pbdev->pdev->io_regions[pcias].memory;
@@ -634,8 +719,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long);
pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len);
- map_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
- map_indicator(&pbdev->routes.adapter, pbdev->indicator);
+ ret = map_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ if (ret) {
+ goto out;
+ }
+
+ ret = map_indicator(&pbdev->routes.adapter, pbdev->indicator);
+ if (ret) {
+ goto out;
+ }
pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb);
pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
@@ -647,9 +739,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
return 0;
+out:
+ release_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ release_indicator(&pbdev->routes.adapter, pbdev->indicator);
+ pbdev->summary_ind = NULL;
+ pbdev->indicator = NULL;
+ return ret;
}
-static int dereg_irqs(S390PCIBusDevice *pbdev)
+int pci_dereg_irqs(S390PCIBusDevice *pbdev)
{
release_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
release_indicator(&pbdev->routes.adapter, pbdev->indicator);
@@ -692,24 +790,23 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
pbdev->pal = pal;
pbdev->g_iota = g_iota;
- s390_pcihost_iommu_configure(pbdev, true);
+ s390_pci_iommu_enable(pbdev);
return 0;
}
-static void dereg_ioat(S390PCIBusDevice *pbdev)
+void pci_dereg_ioat(S390PCIBusDevice *pbdev)
{
+ s390_pci_iommu_disable(pbdev);
pbdev->pba = 0;
pbdev->pal = 0;
pbdev->g_iota = 0;
-
- s390_pcihost_iommu_configure(pbdev, false);
}
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
{
CPUS390XState *env = &cpu->env;
- uint8_t oc;
+ uint8_t oc, dmaas;
uint32_t fh;
ZpciFib fib;
S390PCIBusDevice *pbdev;
@@ -721,6 +818,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
}
oc = env->regs[r1] & 0xff;
+ dmaas = (env->regs[r1] >> 16) & 0xff;
fh = env->regs[r1] >> 32;
if (fiba & 0x7) {
@@ -729,45 +827,108 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
}
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);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
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))) {
return 0;
}
+ if (fib.fmt != 0) {
+ program_interrupt(env, PGM_OPERAND, 6);
+ return 0;
+ }
+
switch (oc) {
case ZPCI_MOD_FC_REG_INT:
- if (reg_irqs(env, pbdev, fib)) {
+ if (pbdev->summary_ind) {
cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+ } else if (reg_irqs(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_RES_NOT_AVAIL);
}
break;
case ZPCI_MOD_FC_DEREG_INT:
- dereg_irqs(pbdev);
+ if (!pbdev->summary_ind) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+ } else {
+ pci_dereg_irqs(pbdev);
+ }
break;
case ZPCI_MOD_FC_REG_IOAT:
- if (reg_ioat(env, pbdev, fib)) {
+ if (dmaas != 0) {
cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
+ } else if (pbdev->iommu_enabled) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+ } else if (reg_ioat(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES);
}
break;
case ZPCI_MOD_FC_DEREG_IOAT:
- dereg_ioat(pbdev);
+ if (dmaas != 0) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
+ } else if (!pbdev->iommu_enabled) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+ } else {
+ pci_dereg_ioat(pbdev);
+ }
break;
case ZPCI_MOD_FC_REREG_IOAT:
- dereg_ioat(pbdev);
- if (reg_ioat(env, pbdev, fib)) {
+ if (dmaas != 0) {
cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
+ } else if (!pbdev->iommu_enabled) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
+ } else {
+ pci_dereg_ioat(pbdev);
+ if (reg_ioat(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES);
+ }
}
break;
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;
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;
case ZPCI_MOD_FC_SET_MEASURE:
pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
@@ -784,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)
{
CPUS390XState *env = &cpu->env;
+ uint8_t dmaas;
uint32_t fh;
ZpciFib fib;
S390PCIBusDevice *pbdev;
@@ -796,19 +958,59 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
}
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) {
program_interrupt(env, PGM_SPECIFICATION, 6);
return 0;
}
- pbdev = s390_pci_find_dev_by_fh(fh);
+ pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX);
if (!pbdev) {
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
}
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.pal, pbdev->pal);
stq_p(&fib.iota, pbdev->g_iota);
@@ -821,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;
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))) {
return 0;
}
diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h
index 70fa71395f..23f4bfa0ed 100644
--- a/hw/s390x/s390-pci-inst.h
+++ b/hw/s390x/s390-pci-inst.h
@@ -14,7 +14,8 @@
#ifndef HW_S390_PCI_INST_H
#define HW_S390_PCI_INST_H
-#include <sysemu/dma.h>
+#include "s390-pci-bus.h"
+#include "sysemu/dma.h"
/* CLP common request & response block size */
#define CLP_BLK_SIZE 4096
@@ -103,7 +104,7 @@ typedef struct ClpRspListPci {
uint64_t resume_token;
uint32_t mdd;
uint16_t max_fn;
- uint8_t reserved2;
+ uint8_t flags;
uint8_t entry_size;
ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES];
} QEMU_PACKED ClpRspListPci;
@@ -230,6 +231,14 @@ typedef struct ClpReqRspQueryPciGrp {
#define ZPCI_PCI_LS_BUSY 2
#define ZPCI_PCI_LS_INVAL_HANDLE 3
+/* Modify PCI status codes */
+#define ZPCI_MOD_ST_RES_NOT_AVAIL 4
+#define ZPCI_MOD_ST_INSUF_RES 16
+#define ZPCI_MOD_ST_SEQUENCE 24
+#define ZPCI_MOD_ST_DMAAS_INVAL 28
+#define ZPCI_MOD_ST_FRAME_INVAL 32
+#define ZPCI_MOD_ST_ERROR_RECOVER 40
+
/* Modify PCI Function Controls */
#define ZPCI_MOD_FC_REG_INT 2
#define ZPCI_MOD_FC_DEREG_INT 3
@@ -240,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp {
#define ZPCI_MOD_FC_RESET_BLOCK 9
#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 */
#define ZPCI_FIB_FC_ENABLED 0x80
#define ZPCI_FIB_FC_ERROR 0x40
@@ -277,6 +291,8 @@ typedef struct ZpciFib {
uint32_t gd;
} QEMU_PACKED ZpciFib;
+int pci_dereg_irqs(S390PCIBusDevice *pbdev);
+void pci_dereg_ioat(S390PCIBusDevice *pbdev);
int clp_service_call(S390CPU *cpu, uint8_t r2);
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c
index 6528ffed17..e2d4e1af79 100644
--- a/hw/s390x/s390-skeys.c
+++ b/hw/s390x/s390-skeys.c
@@ -15,6 +15,7 @@
#include "migration/qemu-file.h"
#include "hw/s390x/storage-keys.h"
#include "qemu/error-report.h"
+#include "sysemu/kvm.h"
#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
#define S390_SKEYS_SAVE_FLAG_EOS 0x01
@@ -46,15 +47,11 @@ void s390_skeys_init(void)
qdev_init_nofail(DEVICE(obj));
}
-static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
+static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
uint64_t count, Error **errp)
{
uint64_t curpage = startgfn;
uint64_t maxpage = curpage + count - 1;
- const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
- " ch=%d, reserved=%d\n";
- char buf[128];
- int len;
for (; curpage <= maxpage; curpage++) {
uint8_t acc = (*keys & 0xF0) >> 4;
@@ -63,10 +60,9 @@ static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
int ch = (*keys & 0x02);
int res = (*keys & 0x01);
- len = snprintf(buf, sizeof(buf), fmt, curpage,
- *keys, acc, fp, ref, ch, res);
- assert(len < sizeof(buf));
- qemu_put_buffer(f, (uint8_t *)buf, len);
+ fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n",
+ curpage, *keys, acc, fp, ref, ch, res);
keys++;
}
}
@@ -115,7 +111,8 @@ void qmp_dump_skeys(const char *filename, Error **errp)
vaddr cur_gfn = 0;
uint8_t *buf;
int ret;
- QEMUFile *f;
+ int fd;
+ FILE *f;
/* Quick check to see if guest is using storage keys*/
if (!skeyclass->skeys_enabled(ss)) {
@@ -124,8 +121,14 @@ void qmp_dump_skeys(const char *filename, Error **errp)
return;
}
- f = qemu_fopen(filename, "wb");
+ fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+ f = fdopen(fd, "wb");
if (!f) {
+ close(fd);
error_setg_file_open(errp, errno, filename);
return;
}
@@ -161,7 +164,7 @@ out_free:
error_propagate(errp, lerr);
g_free(buf);
out:
- qemu_fclose(f);
+ fclose(f);
}
static void qemu_s390_skeys_init(Object *obj)
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index e3df9c78ba..91d9cefbb5 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -18,17 +18,19 @@
#include "s390-virtio.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
-#include "ioinst.h"
-#include "css.h"
+#include "hw/s390x/ioinst.h"
+#include "hw/s390x/css.h"
#include "virtio-ccw.h"
#include "qemu/config-file.h"
#include "s390-pci-bus.h"
#include "hw/s390x/storage-keys.h"
#include "hw/compat.h"
+#include "ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
+#include "hw/s390x/css-bridge.h"
static const char *const reset_dev_types[] = {
- "virtual-css-bridge",
+ TYPE_VIRTUAL_CSS_BRIDGE,
"s390-sclp-event-facility",
"s390-flic",
"diag288",
@@ -179,10 +181,8 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
static void s390_hot_add_cpu(const int64_t id, Error **errp)
{
MachineState *machine = MACHINE(qdev_get_machine());
- Error *err = NULL;
- s390x_new_cpu(machine->cpu_model, id, &err);
- error_propagate(errp, err);
+ s390x_new_cpu(machine->cpu_model, id, errp);
}
static void ccw_machine_class_init(ObjectClass *oc, void *data)
@@ -190,7 +190,9 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+ S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc);
+ s390mc->ri_allowed = true;
mc->init = ccw_init;
mc->reset = s390_machine_reset;
mc->hot_add_cpu = s390_hot_add_cpu;
@@ -201,7 +203,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->no_parallel = 1;
mc->no_sdcard = 1;
mc->use_sclp = 1;
- mc->max_cpus = 255;
+ mc->max_cpus = 248;
mc->get_hotplug_handler = s390_get_hotplug_handler;
hc->plug = s390_machine_device_plug;
nc->nmi_monitor_handler = s390_nmi;
@@ -237,6 +239,20 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value,
ms->dea_key_wrap = value;
}
+bool ri_allowed(void)
+{
+ if (kvm_enabled()) {
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+ if (object_class_dynamic_cast(OBJECT_CLASS(mc),
+ TYPE_S390_CCW_MACHINE)) {
+ S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc);
+
+ return s390mc->ri_allowed;
+ }
+ }
+ return 0;
+}
+
static inline void s390_machine_initfn(Object *obj)
{
object_property_add_bool(obj, "aes-key-wrap",
@@ -262,6 +278,7 @@ static const TypeInfo ccw_machine_info = {
.abstract = true,
.instance_size = sizeof(S390CcwMachineState),
.instance_init = s390_machine_initfn,
+ .class_size = sizeof(S390CcwMachineClass),
.class_init = ccw_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
@@ -299,11 +316,23 @@ static const TypeInfo ccw_machine_info = {
} \
type_init(ccw_machine_register_##suffix)
+#define CCW_COMPAT_2_6 \
+ HW_COMPAT_2_6 \
+ {\
+ .driver = TYPE_S390_IPL,\
+ .property = "iplbext_migration",\
+ .value = "off",\
+ }, {\
+ .driver = TYPE_VIRTUAL_CSS_BRIDGE,\
+ .property = "css_dev_path",\
+ .value = "off",\
+ },
+
#define CCW_COMPAT_2_5 \
+ CCW_COMPAT_2_6 \
HW_COMPAT_2_5
#define CCW_COMPAT_2_4 \
- CCW_COMPAT_2_5 \
HW_COMPAT_2_4 \
{\
.driver = TYPE_S390_SKEYS,\
@@ -343,21 +372,38 @@ static const TypeInfo ccw_machine_info = {
.value = "0",\
},
+static void ccw_machine_2_7_instance_options(MachineState *machine)
+{
+}
+
+static void ccw_machine_2_7_class_options(MachineClass *mc)
+{
+}
+DEFINE_CCW_MACHINE(2_7, "2.7", true);
+
static void ccw_machine_2_6_instance_options(MachineState *machine)
{
+ ccw_machine_2_7_instance_options(machine);
}
static void ccw_machine_2_6_class_options(MachineClass *mc)
{
+ S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc);
+
+ s390mc->ri_allowed = false;
+ ccw_machine_2_7_class_options(mc);
+ SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_6);
}
-DEFINE_CCW_MACHINE(2_6, "2.6", true);
+DEFINE_CCW_MACHINE(2_6, "2.6", false);
static void ccw_machine_2_5_instance_options(MachineState *machine)
{
+ ccw_machine_2_6_instance_options(machine);
}
static void ccw_machine_2_5_class_options(MachineClass *mc)
{
+ ccw_machine_2_6_class_options(mc);
SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5);
}
DEFINE_CCW_MACHINE(2_5, "2.5", false);
@@ -369,6 +415,7 @@ static void ccw_machine_2_4_instance_options(MachineState *machine)
static void ccw_machine_2_4_class_options(MachineClass *mc)
{
+ ccw_machine_2_5_class_options(mc);
SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4);
}
DEFINE_CCW_MACHINE(2_4, "2.4", false);
diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h
index ffd014cb5b..f588b80a6e 100644
--- a/hw/s390x/s390-virtio.h
+++ b/hw/s390x/s390-virtio.h
@@ -10,7 +10,7 @@
*/
#ifndef HW_S390_VIRTIO_H
-#define HW_S390_VIRTIO_H 1
+#define HW_S390_VIRTIO_H
#include "hw/nmi.h"
#include "standard-headers/asm-s390/kvm_virtio.h"
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index 85dbe1b600..fca37f511e 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -357,10 +357,10 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
sclp_c->unassign_storage(sclp, sccb);
break;
case SCLP_CMDW_CONFIGURE_PCI:
- s390_pci_sclp_configure(1, sccb);
+ s390_pci_sclp_configure(sccb);
break;
case SCLP_CMDW_DECONFIGURE_PCI:
- s390_pci_sclp_configure(0, sccb);
+ s390_pci_sclp_deconfigure(sccb);
break;
default:
efc->command_handler(ef, sccb, code);
diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c
index c0ecab9c3b..762cb184ac 100644
--- a/hw/s390x/sclpquiesce.c
+++ b/hw/s390x/sclpquiesce.c
@@ -12,7 +12,7 @@
*
*/
#include "qemu/osdep.h"
-#include <hw/qdev.h>
+#include "hw/qdev.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events
new file mode 100644
index 0000000000..84ea964875
--- /dev/null
+++ b/hw/s390x/trace-events
@@ -0,0 +1,15 @@
+# See docs/tracing.txt for syntax documentation.
+
+# hw/s390x/css.c
+css_enable_facility(const char *facility) "CSS: enable %s"
+css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=%x, erc=%x, rsid=%x %s"
+css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type %02x)"
+css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s"
+css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)"
+css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s"
+css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc %x)"
+
+# hw/s390x/virtio-ccw.c
+virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x"
+virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)"
+virtio_ccw_set_ind(uint64_t ind_loc, uint8_t ind_old, uint8_t ind_new) "VIRTIO-CCW: indicator at %" PRIu64 ": %x->%x"
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index d51642db0d..a554a24d06 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -16,6 +16,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
#include "net/net.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-serial.h"
@@ -28,35 +29,15 @@
#include "hw/s390x/adapter.h"
#include "hw/s390x/s390_flic.h"
-#include "ioinst.h"
-#include "css.h"
+#include "hw/s390x/ioinst.h"
+#include "hw/s390x/css.h"
#include "virtio-ccw.h"
#include "trace.h"
+#include "hw/s390x/css-bridge.h"
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
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 *vdev = NULL;
@@ -68,112 +49,59 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
return vdev;
}
-static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
- bool assign, bool set_handler)
+static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
{
- VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
- VirtQueue *vq = virtio_get_queue(vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
- int r = 0;
- SubchDev *sch = dev->sch;
- uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
-
- if (assign) {
- r = event_notifier_init(notifier, 1);
- if (r < 0) {
- error_report("%s: unable to init event notifier: %d", __func__, r);
- return r;
- }
- virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
- r = s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
- if (r < 0) {
- error_report("%s: unable to assign ioeventfd: %d", __func__, r);
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- event_notifier_cleanup(notifier);
- return r;
- }
- } else {
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
- event_notifier_cleanup(notifier);
- }
- return r;
+ virtio_bus_start_ioeventfd(&dev->bus);
}
-static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
+static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
{
- VirtIODevice *vdev;
- int n, r;
+ virtio_bus_stop_ioeventfd(&dev->bus);
+}
- if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
- dev->ioeventfd_disabled ||
- dev->ioeventfd_started) {
- return;
- }
- vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
- if (r < 0) {
- goto assign_error;
- }
- }
- dev->ioeventfd_started = true;
- return;
+static bool virtio_ccw_ioeventfd_started(DeviceState *d)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- assign_error:
- while (--n >= 0) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
- assert(r >= 0);
- }
- dev->ioeventfd_started = false;
- /* Disable ioeventfd for this device. */
- dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
- error_report("%s: failed. Fallback to userspace (slower).", __func__);
+ return dev->ioeventfd_started;
}
-static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
+static void virtio_ccw_ioeventfd_set_started(DeviceState *d, bool started,
+ bool err)
{
- VirtIODevice *vdev;
- int n, r;
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- if (!dev->ioeventfd_started) {
- return;
- }
- vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
- assert(r >= 0);
+ dev->ioeventfd_started = started;
+ if (err) {
+ /* Disable ioeventfd for this device. */
+ dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
}
- dev->ioeventfd_started = false;
}
-VirtualCssBus *virtual_css_bus_init(void)
+static bool virtio_ccw_ioeventfd_disabled(DeviceState *d)
{
- VirtualCssBus *cbus;
- BusState *bus;
- DeviceState *dev;
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- /* Create bridge device */
- dev = qdev_create(NULL, "virtual-css-bridge");
- qdev_init_nofail(dev);
+ return dev->ioeventfd_disabled ||
+ !(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD);
+}
- /* Create bus on bridge device */
- bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css");
- cbus = VIRTUAL_CSS_BUS(bus);
+static void virtio_ccw_ioeventfd_set_disabled(DeviceState *d, bool disabled)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- /* Enable hotplugging */
- qbus_set_hotplug_handler(bus, dev, &error_abort);
+ dev->ioeventfd_disabled = disabled;
+}
+
+static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
+ int n, bool assign)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
+ SubchDev *sch = ccw_dev->sch;
+ uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
- return cbus;
+ return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
}
/* Communication blocks used by several channel commands. */
@@ -267,6 +195,8 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
{
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
+
virtio_ccw_stop_ioeventfd(dev);
virtio_reset(vdev);
if (dev->indicators) {
@@ -281,7 +211,7 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
release_indicator(&dev->routes.adapter, dev->summary_indicator);
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,
@@ -736,151 +666,44 @@ static void virtio_sch_disable_cb(SubchDev *sch)
static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
{
- unsigned int cssid = 0;
- unsigned int ssid = 0;
- unsigned int schid;
- unsigned int devno;
- bool have_devno = false;
- bool found = false;
- SubchDev *sch;
- int num;
- Error *err = NULL;
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) {
- num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno);
- if (num == 3) {
- if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) {
- error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x",
- cssid, ssid);
- goto out_err;
- }
- /* Enforce use of virtual cssid. */
- if (cssid != VIRTUAL_CSSID) {
- error_setg(errp, "cssid %x not valid for virtio devices",
- cssid);
- goto out_err;
- }
- if (css_devno_used(cssid, ssid, devno)) {
- error_setg(errp, "Device %x.%x.%04x already exists",
- cssid, ssid, devno);
- goto out_err;
- }
- sch->cssid = cssid;
- sch->ssid = ssid;
- sch->devno = devno;
- have_devno = true;
- } else {
- error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id);
- goto out_err;
- }
- }
-
- /* Find the next free id. */
- if (have_devno) {
- for (schid = 0; schid <= MAX_SCHID; schid++) {
- if (!css_find_subch(1, cssid, ssid, schid)) {
- sch->schid = schid;
- css_subch_assign(cssid, ssid, schid, devno, sch);
- found = true;
- break;
- }
- }
- if (!found) {
- error_setg(errp, "No free subchannel found for %x.%x.%04x",
- cssid, ssid, devno);
- goto out_err;
- }
- trace_virtio_ccw_new_device(cssid, ssid, schid, devno,
- "user-configured");
- } else {
- cssid = VIRTUAL_CSSID;
- 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->disable_cb = virtio_sch_disable_cb;
-
- /* Build senseid data. */
- memset(&sch->id, 0, sizeof(SenseId));
sch->id.reserved = 0xff;
sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
-
+ ccw_dev->sch = sch;
+ dev->indicators = NULL;
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) {
k->realize(dev, &err);
}
if (err) {
error_propagate(errp, err);
- css_subch_assign(cssid, ssid, schid, devno, NULL);
- goto out_err;
+ css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
+ ccw_dev->sch = NULL;
+ g_free(sch);
}
-
- return;
-
-out_err:
- dev->sch = NULL;
- g_free(sch);
}
static int virtio_ccw_exit(VirtioCcwDevice *dev)
{
- SubchDev *sch = dev->sch;
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
+ SubchDev *sch = ccw_dev->sch;
if (sch) {
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
@@ -898,15 +721,11 @@ static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
DeviceState *qdev = DEVICE(ccw_dev);
VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
virtio_net_set_netclient_name(&dev->vdev, qdev->id,
object_get_typename(OBJECT(qdev)));
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void virtio_ccw_net_instance_init(Object *obj)
@@ -923,13 +742,9 @@ static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp)
{
VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void virtio_ccw_blk_instance_init(Object *obj)
@@ -949,7 +764,6 @@ static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
DeviceState *proxy = DEVICE(ccw_dev);
- Error *err = NULL;
char *bus_name;
/*
@@ -963,10 +777,7 @@ static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
}
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
@@ -982,13 +793,9 @@ static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp)
{
VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void virtio_ccw_balloon_instance_init(Object *obj)
@@ -1009,7 +816,6 @@ static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
DeviceState *qdev = DEVICE(ccw_dev);
- Error *err = NULL;
char *bus_name;
/*
@@ -1023,10 +829,7 @@ static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
}
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void virtio_ccw_scsi_instance_init(Object *obj)
@@ -1044,13 +847,9 @@ static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
{
VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void vhost_ccw_scsi_instance_init(Object *obj)
@@ -1085,7 +884,9 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp)
*/
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,
@@ -1105,6 +906,7 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
ind_old = *ind_addr;
ind_new = ind_old | to_be_set;
} while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old);
+ trace_virtio_ccw_set_ind(ind_loc, ind_old, ind_new);
cpu_physical_memory_unmap(ind_addr, len, 1, len);
return ind_old;
@@ -1113,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)
{
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;
/* queue indicators + secondary indicators */
@@ -1171,9 +974,10 @@ static void virtio_ccw_reset(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ CcwDevice *ccw_dev = CCW_DEVICE(d);
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)
@@ -1189,29 +993,17 @@ static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
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);
}
-static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
-{
- VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-
- /* Stop using the generic ioeventfd, we are doing eventfd handling
- * ourselves below */
- dev->ioeventfd_disabled = assign;
- if (assign) {
- virtio_ccw_stop_ioeventfd(dev);
- }
- return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
-}
-
static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
{
int r;
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
- if (!dev->sch->thinint_active) {
+ if (!ccw_dev->sch->thinint_active) {
return -EINVAL;
}
@@ -1333,7 +1125,8 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
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;
if (with_irqfd && assigned) {
@@ -1392,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)
{
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);
subch_device_save(s, f);
@@ -1426,7 +1220,8 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
{
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);
int len;
@@ -1471,11 +1266,12 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
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);
if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) {
- error_setg(errp, "The nubmer of virtqueues %d "
+ error_setg(errp, "The number of virtqueues %d "
"exceeds ccw limit %d", n,
VIRTIO_CCW_QUEUE_MAX);
return;
@@ -1515,7 +1311,7 @@ static void virtio_ccw_device_unplugged(DeviceState *d)
/**************** Virtio-ccw Bus Device Descriptions *******************/
static Property virtio_ccw_net_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1544,7 +1340,7 @@ static const TypeInfo virtio_ccw_net = {
};
static Property virtio_ccw_blk_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1573,7 +1369,7 @@ static const TypeInfo virtio_ccw_blk = {
};
static Property virtio_ccw_serial_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1602,7 +1398,7 @@ static const TypeInfo virtio_ccw_serial = {
};
static Property virtio_ccw_balloon_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1631,7 +1427,7 @@ static const TypeInfo virtio_ccw_balloon = {
};
static Property virtio_ccw_scsi_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1661,7 +1457,7 @@ static const TypeInfo virtio_ccw_scsi = {
#ifdef CONFIG_VHOST_SCSI
static Property vhost_ccw_scsi_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
@@ -1699,7 +1495,7 @@ static void virtio_ccw_rng_instance_init(Object *obj)
}
static Property virtio_ccw_rng_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1746,29 +1542,17 @@ static int virtio_ccw_busdev_exit(DeviceState *dev)
static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
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);
-
- /*
- * 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)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ CCWDeviceClass *k = CCW_DEVICE_CLASS(dc);
+ k->unplug = virtio_ccw_busdev_unplug;
dc->realize = virtio_ccw_busdev_realize;
dc->exit = virtio_ccw_busdev_exit;
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
@@ -1776,44 +1560,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
static const TypeInfo virtio_ccw_device_info = {
.name = TYPE_VIRTIO_CCW_DEVICE,
- .parent = TYPE_DEVICE,
+ .parent = TYPE_CCW_DEVICE,
.instance_size = sizeof(VirtioCcwDevice),
.class_init = virtio_ccw_device_class_init,
.class_size = sizeof(VirtIOCCWDeviceClass),
.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 */
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1835,7 +1588,6 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->notify = virtio_ccw_notify;
k->vmstate_change = virtio_ccw_vmstate_change;
k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
- k->set_host_notifier = virtio_ccw_set_host_notifier;
k->set_guest_notifiers = virtio_ccw_set_guest_notifiers;
k->save_queue = virtio_ccw_save_queue;
k->load_queue = virtio_ccw_load_queue;
@@ -1844,6 +1596,11 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->device_plugged = virtio_ccw_device_plugged;
k->post_plugged = virtio_ccw_post_plugged;
k->device_unplugged = virtio_ccw_device_unplugged;
+ k->ioeventfd_started = virtio_ccw_ioeventfd_started;
+ k->ioeventfd_set_started = virtio_ccw_ioeventfd_set_started;
+ k->ioeventfd_disabled = virtio_ccw_ioeventfd_disabled;
+ k->ioeventfd_set_disabled = virtio_ccw_ioeventfd_set_disabled;
+ k->ioeventfd_assign = virtio_ccw_ioeventfd_assign;
}
static const TypeInfo virtio_ccw_bus_info = {
@@ -1855,7 +1612,7 @@ static const TypeInfo virtio_ccw_bus_info = {
#ifdef CONFIG_VIRTFS
static Property virtio_ccw_9p_properties[] = {
- DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
@@ -1867,13 +1624,9 @@ static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp)
{
V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- }
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
@@ -1908,7 +1661,6 @@ static const TypeInfo virtio_ccw_9p_info = {
static void virtio_ccw_register(void)
{
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_serial);
type_register_static(&virtio_ccw_blk);
@@ -1919,7 +1671,6 @@ static void virtio_ccw_register(void)
type_register_static(&vhost_ccw_scsi);
#endif
type_register_static(&virtio_ccw_rng);
- type_register_static(&virtual_css_bridge_info);
#ifdef CONFIG_VIRTFS
type_register_static(&virtio_ccw_9p_info);
#endif
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 66c831ba84..1c6bc86316 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -13,20 +13,21 @@
#ifndef HW_S390X_VIRTIO_CCW_H
#define HW_S390X_VIRTIO_CCW_H
-#include <hw/virtio/virtio-blk.h>
-#include <hw/virtio/virtio-net.h>
-#include <hw/virtio/virtio-serial.h>
-#include <hw/virtio/virtio-scsi.h>
+#include "hw/virtio/virtio-blk.h"
+#include "hw/virtio/virtio-net.h"
+#include "hw/virtio/virtio-serial.h"
+#include "hw/virtio/virtio-scsi.h"
#ifdef CONFIG_VHOST_SCSI
-#include <hw/virtio/vhost-scsi.h>
+#include "hw/virtio/vhost-scsi.h"
#endif
-#include <hw/virtio/virtio-balloon.h>
-#include <hw/virtio/virtio-rng.h>
-#include <hw/virtio/virtio-bus.h>
+#include "hw/virtio/virtio-balloon.h"
+#include "hw/virtio/virtio-rng.h"
+#include "hw/virtio/virtio-bus.h"
-#include "css.h"
-
-#define VIRTUAL_CSSID 0xfe
+#include "hw/s390x/s390_flic.h"
+#include "hw/s390x/css.h"
+#include "ccw-device.h"
+#include "hw/s390x/css-bridge.h"
#define VIRTIO_CCW_CU_TYPE 0x3832
#define VIRTIO_CCW_CHPID_TYPE 0x32
@@ -66,7 +67,7 @@ typedef struct VirtioBusClass VirtioCcwBusClass;
typedef struct VirtioCcwDevice VirtioCcwDevice;
typedef struct VirtIOCCWDeviceClass {
- DeviceClass parent_class;
+ CCWDeviceClass parent_class;
void (*realize)(VirtioCcwDevice *dev, Error **errp);
int (*exit)(VirtioCcwDevice *dev);
} VirtIOCCWDeviceClass;
@@ -77,9 +78,7 @@ typedef struct VirtIOCCWDeviceClass {
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
struct VirtioCcwDevice {
- DeviceState parent_obj;
- SubchDev *sch;
- char *bus_id;
+ CcwDevice parent_obj;
int revision;
uint32_t max_rev;
VirtioBusState bus;
@@ -102,15 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
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 */
#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw"
@@ -190,7 +180,6 @@ typedef struct VirtIORNGCcw {
VirtIORNG vdev;
} VirtIORNGCcw;
-VirtualCssBus *virtual_css_bus_init(void);
void virtio_ccw_device_update_status(SubchDev *sch);
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);