qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw.
@ 2012-09-04 15:13 Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/5] Update headers for upcoming s390 changes Cornelia Huck
                   ` (4 more replies)
  0 siblings, 5 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Hi,

here's the second version of virtual channel I/O and the new virtio-ccw
transport.

Changes to the first version include coding style fixes, changes in the
organization of objects (not quite finished yet), adaptions to changes
in the kernel interface and implementation of the improved virtio-ccw
primitives.

Cornelia Huck (5):
  Update headers for upcoming s390 changes.
  s390: Virtual channel subsystem support.
  s390: Add new channel I/O based virtio transport.
  s390: Virtual channel subsystem support for !KVM.
  [HACK] Handle multiple virtio aliases.

 blockdev.c                           |    6 +-
 hw/qdev-monitor.c                    |   90 ++-
 hw/s390-virtio.c                     |  277 ++++++--
 hw/s390x/Makefile.objs               |    2 +
 hw/s390x/css.c                       | 1280 ++++++++++++++++++++++++++++++++++
 hw/s390x/css.h                       |   89 +++
 hw/s390x/virtio-ccw.c                |  875 +++++++++++++++++++++++
 hw/s390x/virtio-ccw.h                |   79 +++
 linux-headers/asm-generic/kvm_para.h |    5 +
 linux-headers/asm-x86/kvm.h          |    1 +
 linux-headers/linux/kvm.h            |   80 ++-
 target-s390x/Makefile.objs           |    2 +-
 target-s390x/cpu.h                   |  277 ++++++++
 target-s390x/helper.c                |  140 ++++
 target-s390x/ioinst.c                |  734 +++++++++++++++++++
 target-s390x/ioinst.h                |  206 ++++++
 target-s390x/kvm.c                   |  282 +++++++-
 target-s390x/op_helper.c             |   22 +-
 vl.c                                 |    7 +-
 19 files changed, 4313 insertions(+), 141 deletions(-)
 create mode 100644 hw/s390x/css.c
 create mode 100644 hw/s390x/css.h
 create mode 100644 hw/s390x/virtio-ccw.c
 create mode 100644 hw/s390x/virtio-ccw.h
 create mode 100644 linux-headers/asm-generic/kvm_para.h
 create mode 100644 target-s390x/ioinst.c
 create mode 100644 target-s390x/ioinst.h

-- 
1.7.11.5

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v2 1/5] Update headers for upcoming s390 changes.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/5] s390: Virtual channel subsystem support Cornelia Huck
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:
- update to latest kvm interface changes

---
 linux-headers/asm-generic/kvm_para.h |  5 +++
 linux-headers/asm-x86/kvm.h          |  1 +
 linux-headers/linux/kvm.h            | 80 ++++++++++++++++++++++++++++++++++--
 3 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 linux-headers/asm-generic/kvm_para.h

diff --git a/linux-headers/asm-generic/kvm_para.h b/linux-headers/asm-generic/kvm_para.h
new file mode 100644
index 0000000..63df88b
--- /dev/null
+++ b/linux-headers/asm-generic/kvm_para.h
@@ -0,0 +1,5 @@
+#ifndef _ASM_GENERIC_KVM_PARA_H
+#define _ASM_GENERIC_KVM_PARA_H
+
+
+#endif
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index 246617e..521bf25 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -25,6 +25,7 @@
 #define __KVM_HAVE_DEBUGREGS
 #define __KVM_HAVE_XSAVE
 #define __KVM_HAVE_XCRS
+#define __KVM_HAVE_READONLY_MEM
 
 /* Architectural interrupt line count. */
 #define KVM_NR_INTERRUPTS 256
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 4b9e575..1e87d71 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -101,9 +101,13 @@ struct kvm_userspace_memory_region {
 	__u64 userspace_addr; /* start of the userspace allocated memory */
 };
 
-/* for kvm_memory_region::flags */
-#define KVM_MEM_LOG_DIRTY_PAGES  1UL
-#define KVM_MEMSLOT_INVALID      (1UL << 1)
+/*
+ * The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace,
+ * other bits are reserved for kvm internal use which are defined in
+ * include/linux/kvm_host.h.
+ */
+#define KVM_MEM_LOG_DIRTY_PAGES	(1UL << 0)
+#define KVM_MEM_READONLY	(1UL << 1)
 
 /* for KVM_IRQ_LINE */
 struct kvm_irq_level {
@@ -163,6 +167,7 @@ struct kvm_pit_config {
 #define KVM_EXIT_OSI              18
 #define KVM_EXIT_PAPR_HCALL	  19
 #define KVM_EXIT_S390_UCONTROL	  20
+#define KVM_EXIT_S390_SCH_IO      21
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 #define KVM_INTERNAL_ERROR_EMULATION 1
@@ -276,6 +281,20 @@ struct kvm_run {
 			__u64 ret;
 			__u64 args[9];
 		} papr_hcall;
+		/* KVM_EXIT_S390_SCH_IO */
+		struct {
+			__u32 sch_id;
+#define SCH_DO_CSCH 0
+#define SCH_DO_HSCH 1
+#define SCH_DO_SSCH 2
+#define SCH_DO_RSCH 3
+#define SCH_DO_XSCH 4
+			__u8 func;
+			__u8 pad;
+			__u64 orb;
+			__u32 scsw[3];
+			__u32 pmcw[7];
+		} s390_sch_io;
 		/* Fix the size of the union. */
 		char padding[256];
 	};
@@ -388,10 +407,17 @@ struct kvm_s390_psw {
 #define KVM_S390_PROGRAM_INT		0xfffe0001u
 #define KVM_S390_SIGP_SET_PREFIX	0xfffe0002u
 #define KVM_S390_RESTART		0xfffe0003u
+#define KVM_S390_MCHK			0xfffe1000u
 #define KVM_S390_INT_VIRTIO		0xffff2603u
 #define KVM_S390_INT_SERVICE		0xffff2401u
 #define KVM_S390_INT_EMERGENCY		0xffff1201u
 #define KVM_S390_INT_EXTERNAL_CALL	0xffff1202u
+#define KVM_S390_INT_IO(ai,cssid,ssid,schid)   \
+	(((schid)) |			       \
+	 ((ssid) << 16) |		       \
+	 ((cssid) << 18) |		       \
+	 ((ai) << 26))
+
 
 struct kvm_s390_interrupt {
 	__u32 type;
@@ -473,6 +499,45 @@ struct kvm_ppc_smmu_info {
 	struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ];
 };
 
+/* for KVM_S390_CSS_NOTIFY */
+struct kvm_css_notify {
+	__u8 cssid;
+	__u8 ssid;
+	__u16 schid;
+	__u32 scsw[3];
+	__u32 pmcw[7];
+	__u8 sense_data[32];
+	__u8 unsolicited;
+	__u8 func;
+};
+
+/* for KVM_S390_CCW_HOTPLUG */
+struct kvm_s390_sch_info {
+	__u8 cssid;
+	__u8 ssid;
+	__u16 schid;
+	__u16 devno;
+	__u32 schib[12];
+	int hotplugged;
+	int add;
+	int virtual;
+};
+
+/* for KVM_S390_CHP_HOTPLUG */
+struct kvm_s390_chp_info {
+	__u8 cssid;
+	__u8 chpid;
+	__u8 type;
+	int add;
+	int virtual;
+};
+
+/* for KVM_S390_ADD_CSS */
+struct kvm_s390_css_info {
+	__u8 cssid;
+	__u8 default_image;
+};
+
 #define KVMIO 0xAE
 
 /* machine type bits, to be used as argument to KVM_CREATE_VM */
@@ -618,6 +683,10 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
 #define KVM_CAP_PPC_ALLOC_HTAB 80
+#ifdef __KVM_HAVE_READONLY_MEM
+#define KVM_CAP_READONLY_MEM 81
+#endif
+#define KVM_CAP_S390_CSS_SUPPORT 82
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -831,6 +900,11 @@ struct kvm_s390_ucas_mapping {
 #define KVM_PPC_GET_SMMU_INFO	  _IOR(KVMIO,  0xa6, struct kvm_ppc_smmu_info)
 /* Available with KVM_CAP_PPC_ALLOC_HTAB */
 #define KVM_PPC_ALLOCATE_HTAB	  _IOWR(KVMIO, 0xa7, __u32)
+/* Available with KVM_CAP_S390_CSS_SUPPORT */
+#define KVM_S390_CSS_NOTIFY       _IOW(KVMIO, 0xae, struct kvm_css_notify)
+#define KVM_S390_CCW_HOTPLUG      _IOW(KVMIO, 0xab, struct kvm_s390_sch_info)
+#define KVM_S390_CHP_HOTPLUG      _IOW(KVMIO, 0xac, struct kvm_s390_chp_info)
+#define KVM_S390_ADD_CSS          _IOW(KVMIO, 0xad, struct kvm_s390_css_info)
 
 /*
  * ioctls for vcpu fds
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v2 2/5] s390: Virtual channel subsystem support.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/5] Update headers for upcoming s390 changes Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Provide a mechanism for qemu to provide fully virtual subchannels to
the guest. In the KVM case, this relies on the kernel's css support.
The !KVM case is not yet supported.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1 -> v2:
- coding style
- re-organization of hardware structures (channel subsystem vs. channel
  subsystem image)
- use new KVM_S390_ADD_CSS ioctl

---
 hw/s390x/Makefile.objs     |   1 +
 hw/s390x/css.c             | 490 +++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h             |  60 ++++++
 target-s390x/Makefile.objs |   2 +-
 target-s390x/cpu.h         | 126 ++++++++++++
 target-s390x/ioinst.c      |  38 ++++
 target-s390x/ioinst.h      | 173 ++++++++++++++++
 target-s390x/kvm.c         | 118 +++++++++++
 8 files changed, 1007 insertions(+), 1 deletion(-)
 create mode 100644 hw/s390x/css.c
 create mode 100644 hw/s390x/css.h
 create mode 100644 target-s390x/ioinst.c
 create mode 100644 target-s390x/ioinst.h

diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index dcdcac8..93b41fb 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -1,3 +1,4 @@
 obj-y = s390-virtio-bus.o s390-virtio.o
 
 obj-y := $(addprefix ../,$(obj-y))
+obj-y += css.o
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
new file mode 100644
index 0000000..b9b6e48
--- /dev/null
+++ b/hw/s390x/css.c
@@ -0,0 +1,490 @@
+/*
+ * Channel subsystem base support.
+ *
+ * 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.
+ */
+
+#include "qemu-thread.h"
+#include "qemu-queue.h"
+#include <hw/qdev.h>
+#include "bitops.h"
+#include "kvm.h"
+#include "cpu.h"
+#include "ioinst.h"
+#include "css.h"
+
+typedef struct ChpInfo {
+    uint8_t in_use;
+    uint8_t type;
+} ChpInfo;
+
+typedef struct SubchSet {
+    SubchDev *sch[MAX_SCHID + 1];
+    unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)];
+    unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)];
+} SubchSet;
+
+typedef struct CssImage {
+    SubchSet *sch_set[MAX_SSID + 1];
+    ChpInfo chpids[MAX_CHPID + 1];
+} CssImage;
+
+typedef struct ChannelSubSys {
+    CssImage *css[MAX_CSSID + 1];
+    uint8_t default_cssid;
+} ChannelSubSys;
+
+static ChannelSubSys *channel_subsys;
+
+int css_create_css_image(uint8_t cssid, bool default_image)
+{
+    if (cssid > MAX_CSSID) {
+        return -EINVAL;
+    }
+    if (channel_subsys->css[cssid]) {
+        return -EBUSY;
+    }
+    channel_subsys->css[cssid] = g_try_malloc0(sizeof(CssImage));
+    if (!channel_subsys->css[cssid]) {
+        return -ENOMEM;
+    }
+    if (default_image) {
+        channel_subsys->default_cssid = cssid;
+    }
+    s390_new_css_image(cssid, default_image);
+    return 0;
+}
+
+static void css_inject_io_interrupt(SubchDev *sch, uint8_t func)
+{
+    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
+                      &sch->curr_status.pmcw, &sch->sense_data, 0,
+                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm,
+                      func);
+}
+
+void css_conditional_io_interrupt(SubchDev *sch)
+{
+    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
+                      &sch->curr_status.pmcw, &sch->sense_data, 1,
+                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm, 0);
+}
+
+static void sch_handle_clear_func(SubchDev *sch)
+{
+    PMCW *p = &sch->curr_status.pmcw;
+    SCSW *s = &sch->curr_status.scsw;
+    int path;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    /* Reset values prior to 'issueing the clear signal'. */
+    p->lpum = 0;
+    p->pom = 0xff;
+    s->pno = 0;
+
+    /* We always 'attempt to issue the clear signal', and we always succeed. */
+    sch->orb = NULL;
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    s->actl &= ~SCSW_ACTL_CLEAR_PEND;
+    s->stctl |= SCSW_STCTL_STATUS_PEND;
+
+    s->dstat = 0;
+    s->cstat = 0;
+    p->lpum = path;
+
+}
+
+static void sch_handle_halt_func(SubchDev *sch)
+{
+
+    PMCW *p = &sch->curr_status.pmcw;
+    SCSW *s = &sch->curr_status.scsw;
+    int path;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    /* We always 'attempt to issue the halt signal', and we always succeed. */
+    sch->orb = NULL;
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    s->actl &= ~SCSW_ACTL_HALT_PEND;
+    s->stctl |= SCSW_STCTL_STATUS_PEND;
+
+    if ((s->actl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) ||
+        !((s->actl & SCSW_ACTL_START_PEND) ||
+          (s->actl & SCSW_ACTL_SUSP))) {
+        s->dstat = SCSW_DSTAT_DEVICE_END;
+    }
+    s->cstat = 0;
+    p->lpum = path;
+
+}
+
+static int css_interpret_ccw(SubchDev *sch, CCW1 *ccw)
+{
+    int ret;
+    bool check_len;
+    int len;
+    int i;
+
+    if (!ccw) {
+        return -EIO;
+    }
+
+    /* Check for invalid command codes. */
+    if ((ccw->cmd_code & 0x0f) == 0) {
+        return -EINVAL;
+    }
+    if (((ccw->cmd_code & 0x0f) == CCW_CMD_TIC) &&
+        ((ccw->cmd_code & 0xf0) != 0)) {
+        return -EINVAL;
+    }
+
+    if (ccw->flags & CCW_FLAG_SUSPEND) {
+        return -ERESTART;
+    }
+
+    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
+
+    /* Look at the command. */
+    switch (ccw->cmd_code) {
+    case CCW_CMD_NOOP:
+        /* Nothing to do. */
+        ret = 0;
+        break;
+    case CCW_CMD_BASIC_SENSE:
+        if (check_len) {
+            if (ccw->count != sizeof(sch->sense_data)) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, sizeof(sch->sense_data));
+        cpu_physical_memory_write(ccw->cda, sch->sense_data, len);
+        sch->curr_status.scsw.count = ccw->count - len;
+        memset(sch->sense_data, 0, sizeof(sch->sense_data));
+        ret = 0;
+        break;
+    case CCW_CMD_SENSE_ID:
+    {
+        uint8_t sense_bytes[256];
+
+        /* Sense ID information is device specific. */
+        memcpy(sense_bytes, &sch->id, sizeof(sense_bytes));
+        if (check_len) {
+            if (ccw->count != sizeof(sense_bytes)) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, sizeof(sense_bytes));
+        /*
+         * Only indicate 0xff in the first sense byte if we actually
+         * have enough place to store at least bytes 0-3.
+         */
+        if (len >= 4) {
+            stb_phys(ccw->cda, 0xff);
+        } else {
+            stb_phys(ccw->cda, 0);
+        }
+        i = 1;
+        for (i = 1; i < len - 1; i++) {
+            stb_phys(ccw->cda + i, sense_bytes[i]);
+        }
+        sch->curr_status.scsw.count = ccw->count - len;
+        ret = 0;
+        break;
+    }
+    case CCW_CMD_TIC:
+        if (sch->last_cmd->cmd_code == CCW_CMD_TIC) {
+            ret = -EINVAL;
+            break;
+        }
+        if (ccw->flags & (CCW_FLAG_CC | CCW_FLAG_DC)) {
+            ret = -EINVAL;
+            break;
+        }
+        sch->channel_prog = qemu_get_ram_ptr(ccw->cda);
+        ret = sch->channel_prog ? -EAGAIN : -EFAULT;
+        break;
+    default:
+        if (sch->ccw_cb) {
+            /* Handle device specific commands. */
+            ret = sch->ccw_cb(sch, ccw);
+        } else {
+            ret = -EOPNOTSUPP;
+        }
+        break;
+    }
+    sch->last_cmd = ccw;
+    if (ret == 0) {
+        if (ccw->flags & CCW_FLAG_CC) {
+            sch->channel_prog += 8;
+            ret = -EAGAIN;
+        }
+    }
+
+    return ret;
+}
+
+static void sch_handle_start_func(SubchDev *sch)
+{
+
+    PMCW *p = &sch->curr_status.pmcw;
+    SCSW *s = &sch->curr_status.scsw;
+    ORB *orb = sch->orb;
+    int path;
+    int ret;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    if (!s->actl & SCSW_ACTL_SUSP) {
+        /* Look at the orb and try to execute the channel program. */
+        p->intparm = orb->intparm;
+        if (!(orb->lpm & path)) {
+            /* Generate a deferred cc 3 condition. */
+            s->cc = 3;
+            s->stctl = (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND);
+            return;
+        }
+    } else {
+        s->actl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
+    }
+    sch->last_cmd = NULL;
+    do {
+        ret = css_interpret_ccw(sch, sch->channel_prog);
+        switch (ret) {
+        case -EAGAIN:
+            /* ccw chain, continue processing */
+            break;
+        case 0:
+            /* success */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_STATUS_PEND;
+            s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END;
+            break;
+        case -EOPNOTSUPP:
+            /* unsupported command, generate unit check (command reject) */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->dstat = SCSW_DSTAT_UNIT_CHECK;
+            /* Set sense bit 0 in ecw0. */
+            sch->sense_data[0] = 0x80;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -EFAULT:
+            /* memory problem, generate channel data check */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->cstat = SCSW_CSTAT_DATA_CHECK;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -EBUSY:
+            /* subchannel busy, generate deferred cc 1 */
+            s->cc = 1;
+            s->stctl = SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -ERESTART:
+            /* channel program has been suspended */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->actl |= SCSW_ACTL_SUSP;
+            break;
+        default:
+            /* error, generate channel program check */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->cstat = SCSW_CSTAT_PROG_CHECK;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        }
+    } while (ret == -EAGAIN);
+
+}
+
+int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
+                      void *pmcw)
+{
+    SubchDev *sch;
+    int cssid;
+    int ssid;
+    int schid;
+    int m;
+    int ret;
+    int notify = 0;
+
+    ret = ioinst_disassemble_sch_ident(sch_id, &m, &cssid, &ssid, &schid);
+    if (ret) {
+        return ret;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (!sch) {
+        return -ENODEV;
+    }
+    qemu_mutex_lock(&sch->mutex);
+    memcpy(&sch->curr_status.pmcw, pmcw, sizeof(PMCW));
+    switch (func) {
+    case CSS_DO_CSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(SCSW));
+        /* fallthrough */
+    case CSS_DO_CSCH_SIMPLE:
+        sch_handle_clear_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_HSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(SCSW));
+        /* fallthrough */
+    case CSS_DO_HSCH_SIMPLE:
+        sch_handle_halt_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_SSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(SCSW));
+        sch->orb = qemu_get_ram_ptr(orb);
+        sch->channel_prog = qemu_get_ram_ptr(sch->orb->cpa);
+        /* fallthrough */
+    case CSS_DO_SSCH_SIMPLE:
+        sch_handle_start_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_RSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(SCSW));
+        sch_handle_start_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_XSCH:
+        sch->orb = NULL;
+        sch->channel_prog = NULL;
+        sch->last_cmd = NULL;
+        break;
+    }
+    if (notify) {
+        css_inject_io_interrupt(sch, func);
+    }
+    qemu_mutex_unlock(&sch->mutex);
+    return 0;
+}
+
+static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
+{
+    CssImage *css;
+
+    if (cssid > MAX_CSSID) {
+        return -EINVAL;
+    }
+    css = channel_subsys->css[cssid];
+    if (!css) {
+        return -EINVAL;
+    }
+    if (css->chpids[chpid].in_use) {
+        return -EEXIST;
+    }
+    css->chpids[chpid].in_use = 1;
+    css->chpids[chpid].type = type;
+
+    s390_chp_hotplug(cssid, chpid, type, 1, 1);
+
+    return 0;
+}
+
+void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type)
+{
+    PMCW *p = &sch->curr_status.pmcw;
+    SCSW *s = &sch->curr_status.scsw;
+    int i;
+    CssImage *css = channel_subsys->css[sch->cssid];
+
+    assert(css != NULL);
+    memset(p, 0, sizeof(PMCW));
+    p->dnv = 1;
+    p->dev = sch->devno;
+    /* single path */
+    p->pim = 0x80;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->chpid[0] = chpid;
+    if (!css->chpids[chpid].in_use) {
+        css_add_virtual_chpid(sch->cssid, chpid, type);
+    }
+
+    memset(s, 0, sizeof(SCSW));
+    sch->curr_status.mba = 0;
+    for (i = 0; i < 4; i++) {
+        sch->curr_status.mda[i] = 0;
+    }
+}
+
+SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    uint8_t real_cssid;
+
+    real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid;
+
+    if (!channel_subsys->css[real_cssid]) {
+        return NULL;
+    }
+
+    if (!channel_subsys->css[real_cssid]->sch_set[ssid]) {
+        return NULL;
+    }
+
+    return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid];
+}
+
+bool css_present(uint8_t cssid)
+{
+    return (channel_subsys->css[cssid] != NULL);
+}
+
+static void css_init(void)
+{
+    channel_subsys = g_malloc0(sizeof(*channel_subsys));
+}
+machine_init(css_init);
+
+void css_reset_sch(SubchDev *sch)
+{
+    PMCW *p = &sch->curr_status.pmcw;
+
+    p->intparm = 0;
+    p->isc = 0;
+    p->ena = 0;
+    p->lm = 0;
+    p->mme = 0;
+    p->mp = 0;
+    p->tf = 0;
+    p->dnv = 1;
+    p->dev = sch->devno;
+    p->pim = 0x80;
+    p->lpm = p->pim;
+    p->pnom = 0;
+    p->lpum = 0;
+    p->mbi = 0;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->mbfc = 0;
+    p->xmwme = 0;
+    p->csense = 0;
+
+    memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw));
+    sch->curr_status.mba = 0;
+
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    sch->orb = NULL;
+}
+
+void css_reset(void)
+{
+    /* Nothing for now. */
+}
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
new file mode 100644
index 0000000..f3590eb
--- /dev/null
+++ b/hw/s390x/css.h
@@ -0,0 +1,60 @@
+/*
+ * 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 "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 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 */
+    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
+} QEMU_PACKED SenseId;
+
+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];
+    CCW1 *channel_prog;
+    CCW1 *last_cmd;
+    ORB *orb;
+    QemuMutex mutex;
+    /* transport-provided data: */
+    int (*ccw_cb) (SubchDev *, CCW1 *);
+    SenseId id;
+    void *driver_data;
+};
+
+int css_create_css_image(uint8_t cssid, bool default_image);
+void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type);
+void css_reset(void);
+void css_reset_sch(SubchDev *sch);
+
+#endif
diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs
index 80be3bb..6e5bef3 100644
--- a/target-s390x/Makefile.objs
+++ b/target-s390x/Makefile.objs
@@ -1,5 +1,5 @@
 obj-y += translate.o op_helper.o helper.o cpu.o interrupt.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o
 obj-$(CONFIG_KVM) += kvm.o
 
 $(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 18ac6e3..c072e1d 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -339,6 +339,25 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
 void cpu_lock(void);
 void cpu_unlock(void);
 
+typedef struct SubchDev SubchDev;
+typedef struct SCHIB SCHIB;
+typedef struct ORB ORB;
+
+#ifndef CONFIG_USER_ONLY
+SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid);
+void css_conditional_io_interrupt(SubchDev *sch);
+bool css_present(uint8_t cssid);
+#else
+static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid)
+{
+    return NULL;
+}
+static inline void css_conditional_io_interrupt(SubchDev *sch)
+{
+}
+#endif
+
 static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
 {
     env->aregs[0] = newtls >> 32;
@@ -999,4 +1018,111 @@ static inline void cpu_pc_from_tb(CPUS390XState *env, TranslationBlock* tb)
     env->psw.addr = tb->pc;
 }
 
+int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
+                      void *pmcw);
+#ifdef CONFIG_KVM
+int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                         uint16_t devno, void *data, int hotplugged, int add,
+                         int virtual);
+int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
+                         int virtual);
+int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                          void *scsw, void *pmcw, void *sense,
+                          int unsolicited, uint8_t func);
+void kvm_s390_enable_css_support(CPUS390XState *env);
+int kvm_s390_new_css_image(uint8_t cssid, bool default_image);
+#else
+static inline int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid, uint16_t devno,
+                                       void *data, int hotplugged, int add,
+                                       int virtual)
+{
+    return -EOPNOTSUPP;
+}
+static inline int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid,
+                                       uint8_t type, int add, int virtual)
+{
+    return -EOPNOTSUPP;
+}
+static inline int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid,
+                                        uint16_t schid, void *scsw, void *pmcw,
+                                        void *sense, int unsolicited, uint8_t func)
+{
+    return -EOPNOTSUPP;
+}
+static inline void kvm_s390_enable_css_support(CPUS390XState *env)
+{
+}
+static inline int kvm_s390_new_css_image(uint8_t cssid, bool default_image)
+{
+    return -EOPNOTSUPP;
+}
+#endif
+
+static inline void s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                                    uint16_t devno, void *data, int hotplugged,
+                                    int add, int virtual)
+{
+    int ret;
+
+    ret = kvm_s390_sch_hotplug(cssid, ssid, schid, devno, data, hotplugged,
+                               add, virtual);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Hotplugging subchannels not supported\n");
+    }
+}
+
+static inline void s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type,
+                                    int add, int virtual)
+{
+    int ret;
+
+    ret = kvm_s390_chp_hotplug(cssid, chpid, type, add, virtual);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Hotplugging chpids not supported\n");
+    }
+}
+
+static inline void s390_io_interrupt(uint8_t cssid, uint8_t ssid,
+                                     uint16_t schid, void *scsw, void *pmcw,
+                                     void *sense, int unsolicited,
+                                     uint8_t isc, uint32_t intparm, uint8_t func)
+{
+    int ret;
+
+    ret = kvm_s390_io_interrupt(cssid, ssid, schid, scsw, pmcw, sense,
+                                unsolicited, func);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Injecting I/O interrupts not supported\n");
+    }
+}
+
+static inline void s390_new_css_image(uint8_t cssid, bool default_image)
+{
+    int ret;
+
+    ret = kvm_s390_new_css_image(cssid, default_image);
+
+    if (ret == -EOPNOTSUPP) {
+        /* Currently nothing needs to be done. */
+    }
+}
+
+#ifdef CONFIG_KVM
+#define CSS_DO_CSCH SCH_DO_CSCH
+#define CSS_DO_HSCH SCH_DO_HSCH
+#define CSS_DO_SSCH SCH_DO_SSCH
+#define CSS_DO_RSCH SCH_DO_RSCH
+#define CSS_DO_XSCH SCH_DO_XSCH
+#else
+#define CSS_DO_CSCH 0
+#define CSS_DO_HSCH 1
+#define CSS_DO_SSCH 2
+#define CSS_DO_RSCH 3
+#define CSS_DO_XSCH 4
+#endif
+#define CSS_DO_CSCH_SIMPLE 0xf0
+#define CSS_DO_HSCH_SIMPLE 0xf1
+#define CSS_DO_SSCH_SIMPLE 0xf2
+
 #endif
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
new file mode 100644
index 0000000..8f358d5
--- /dev/null
+++ b/target-s390x/ioinst.c
@@ -0,0 +1,38 @@
+/*
+ * I/O instructions for S/390
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "cpu.h"
+#include "ioinst.h"
+
+int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
+                                 int *schid)
+{
+    if (!(value & 0x00010000)) {
+        return -EINVAL;
+    }
+    if (!(value & 0x00080000)) {
+        if (value & 0xff000000) {
+            return -EINVAL;
+        }
+        *cssid = 0;
+        *m = 0;
+    } else {
+        *cssid = (value & 0xff000000) >> 24;
+        *m = 1;
+    }
+    *ssid = (value & 0x00060000) >> 17;
+    *schid = value & 0x0000ffff;
+    return 0;
+}
diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
new file mode 100644
index 0000000..c1377ca
--- /dev/null
+++ b/target-s390x/ioinst.h
@@ -0,0 +1,173 @@
+/*
+ * S/390 channel I/O instructions
+ *
+ * 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 IOINST_S390X_H
+#define IOINST_S390X_H
+
+/*
+ * Channel I/O related definitions, as defined in the Principles
+ * Of Operation (and taken from the Linux implementation).
+ */
+
+/* subchannel status word (command mode only) */
+typedef struct SCSW {
+    uint32_t key:4;
+    uint32_t sctl:1;
+    uint32_t eswf:1;
+    uint32_t cc:2;
+    uint32_t fmt:1;
+    uint32_t pfch:1;
+    uint32_t isic:1;
+    uint32_t alcc:1;
+    uint32_t ssi:1;
+    uint32_t zcc:1;
+    uint32_t ectl:1;
+    uint32_t pno:1;
+    uint32_t res:1;
+    uint32_t fctl:3;
+    uint32_t actl:7;
+    uint32_t stctl:5;
+    uint32_t cpa;
+    uint32_t dstat:8;
+    uint32_t cstat:8;
+    uint32_t count:16;
+} SCSW;
+
+/* path management control word */
+typedef struct PMCW {
+    uint32_t intparm;
+    uint32_t qf:1;
+    uint32_t w:1;
+    uint32_t isc:3;
+    uint32_t zeroes0:3;
+    uint32_t ena:1;
+    uint32_t lm:2;
+    uint32_t mme:2;
+    uint32_t mp:1;
+    uint32_t tf:1;
+    uint32_t dnv:1;
+    uint32_t dev:16;
+    uint8_t  lpm;
+    uint8_t  pnom;
+    uint8_t  lpum;
+    uint8_t  pim;
+    uint16_t mbi;
+    uint8_t  pom;
+    uint8_t  pam;
+    uint8_t  chpid[8];
+    uint32_t zeroes1:8;
+    uint32_t st:3;
+    uint32_t zeroes2:18;
+    uint32_t mbfc:1;
+    uint32_t xmwme:1;
+    uint32_t csense:1;
+} PMCW;
+
+/* subchannel information block */
+struct SCHIB {
+    PMCW pmcw;
+    SCSW scsw;
+    uint64_t mba;
+    uint8_t mda[4];
+};
+
+/* interruption response block */
+typedef struct IRB {
+    SCSW scsw;
+    uint32_t esw[5];
+    uint32_t ecw[8];
+    uint32_t emw[8];
+} IRB;
+
+/* operation request block */
+struct ORB {
+    uint32_t intparm;
+    uint32_t key:4;
+    uint32_t spnd:1;
+    uint32_t str:1;
+    uint32_t mod:1;
+    uint32_t sync:1;
+    uint32_t fmt:1;
+    uint32_t pfch:1;
+    uint32_t isic:1;
+    uint32_t alcc:1;
+    uint32_t ssic:1;
+    uint32_t zero0:1;
+    uint32_t c64:1;
+    uint32_t i2k:1;
+    uint32_t lpm:8;
+    uint32_t ils:1;
+    uint32_t midaw:1;
+    uint32_t zero1:5;
+    uint32_t orbx:1;
+    uint32_t cpa;
+};
+
+/* channel command word (type 1) */
+typedef struct CCW1 {
+    uint8_t cmd_code;
+    uint8_t flags;
+    uint16_t count;
+    uint32_t cda;
+} CCW1;
+
+#define CCW_FLAG_DC              0x80
+#define CCW_FLAG_CC              0x40
+#define CCW_FLAG_SLI             0x20
+#define CCW_FLAG_SKIP            0x10
+#define CCW_FLAG_PCI             0x08
+#define CCW_FLAG_IDA             0x04
+#define CCW_FLAG_SUSPEND         0x02
+
+#define CCW_CMD_NOOP             0x03
+#define CCW_CMD_BASIC_SENSE      0x04
+#define CCW_CMD_TIC              0x08
+#define CCW_CMD_SENSE_ID         0xe4
+
+#define SCSW_FCTL_CLEAR_FUNC     0x1
+#define SCSW_FCTL_HALT_FUNC      0x2
+#define SCSW_FCTL_START_FUNC     0x4
+
+#define SCSW_ACTL_SUSP           0x1
+#define SCSW_ACTL_DEVICE_ACTIVE  0x2
+#define SCSW_ACTL_SUBCH_ACTIVE   0x4
+#define SCSW_ACTL_CLEAR_PEND     0x8
+#define SCSW_ACTL_HALT_PEND      0x10
+#define SCSW_ACTL_START_PEND     0x20
+#define SCSW_ACTL_RESUME_PEND    0x40
+
+#define SCSW_STCTL_STATUS_PEND   0x1
+#define SCSW_STCTL_SECONDARY     0x2
+#define SCSW_STCTL_PRIMARY       0x4
+#define SCSW_STCTL_INTERMEDIATE  0x8
+#define SCSW_STCTL_ALERT         0x10
+
+#define SCSW_DSTAT_ATTENTION     0x80
+#define SCSW_DSTAT_STAT_MOD      0x40
+#define SCSW_DSTAT_CU_END        0x20
+#define SCSW_DSTAT_BUSY          0x10
+#define SCSW_DSTAT_CHANNEL_END   0x08
+#define SCSW_DSTAT_DEVICE_END    0x04
+#define SCSW_DSTAT_UNIT_CHECK    0x02
+#define SCSW_DSTAT_UNIT_EXCEP    0x01
+
+#define SCSW_CSTAT_PCI           0x80
+#define SCSW_CSTAT_INCORR_LEN    0x40
+#define SCSW_CSTAT_PROG_CHECK    0x20
+#define SCSW_CSTAT_PROT_CHECK    0x10
+#define SCSW_CSTAT_DATA_CHECK    0x08
+#define SCSW_CSTAT_CHN_CTRL_CHK  0x04
+#define SCSW_CSTAT_INTF_CTRL_CHK 0x02
+#define SCSW_CSTAT_CHAIN_CHECK   0x01
+
+int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
+                                 int *schid);
+#endif
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 07edf93..9aab6a8 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -26,6 +26,7 @@
 
 #include "qemu-common.h"
 #include "qemu-timer.h"
+#include "qemu-thread.h"
 #include "sysemu.h"
 #include "kvm.h"
 #include "cpu.h"
@@ -110,6 +111,7 @@ int kvm_arch_put_registers(CPUS390XState *env, int level)
 
     env->kvm_run->psw_addr = env->psw.addr;
     env->kvm_run->psw_mask = env->psw.mask;
+    env->kvm_run->s.regs.prefix = env->psa;
 
     return ret;
 }
@@ -131,6 +133,7 @@ int kvm_arch_get_registers(CPUS390XState *env)
 
     env->psw.addr = env->kvm_run->psw_addr;
     env->psw.mask = env->kvm_run->psw_mask;
+    env->psa = env->kvm_run->s.regs.prefix;
 
     return 0;
 }
@@ -507,6 +510,13 @@ int kvm_arch_handle_exit(CPUS390XState *env, struct kvm_run *run)
         case KVM_EXIT_S390_RESET:
             qemu_system_reset_request();
             break;
+        case KVM_EXIT_S390_SCH_IO:
+            ret = css_handle_sch_io(run->s390_sch_io.sch_id,
+                                    run->s390_sch_io.func,
+                                    run->s390_sch_io.orb,
+                                    run->s390_sch_io.scsw,
+                                    run->s390_sch_io.pmcw);
+            break;
         default:
             fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
             break;
@@ -532,3 +542,111 @@ int kvm_arch_on_sigbus(int code, void *addr)
 {
     return 1;
 }
+
+int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                         uint16_t devno, void *data, int hotplugged, int add,
+                         int virtual)
+{
+    struct kvm_s390_sch_info sch_info;
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    /* Notify the kernel. */
+    sch_info.cssid = cssid;
+    sch_info.ssid = ssid;
+    sch_info.schid = schid;
+    sch_info.devno = devno;
+    memcpy(&sch_info.schib, data, sizeof(sch_info.schib));
+    sch_info.hotplugged = hotplugged;
+    sch_info.add = add;
+    sch_info.virtual = virtual;
+    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CCW_HOTPLUG, &sch_info);
+    assert(ret == 0);
+    return ret;
+}
+
+int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
+                         int virtual)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    struct kvm_s390_chp_info chpid_info;
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    /* Notify the kernel. */
+    chpid_info.cssid = cssid;
+    chpid_info.chpid = chpid;
+    chpid_info.type = type;
+    chpid_info.add = 1;
+    chpid_info.virtual = 1;
+    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CHP_HOTPLUG, &chpid_info);
+    assert(ret == 0);
+    return ret;
+}
+
+int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                          void *scsw, void *pmcw, void *sense,
+                          int unsolicited, uint8_t func)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    struct kvm_css_notify notify;
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    notify.cssid = cssid;
+    notify.ssid = ssid;
+    notify.schid = schid;
+    if (!unsolicited) {
+        memcpy(&notify.scsw, scsw, sizeof(notify.scsw));
+        memcpy(&notify.pmcw, pmcw, sizeof(notify.pmcw));
+        memcpy(&notify.sense_data, sense, sizeof(notify.sense_data));
+        notify.func = func;
+    }
+    notify.unsolicited = unsolicited;
+    ret = kvm_vcpu_ioctl(&cpu->env, KVM_S390_CSS_NOTIFY, &notify);
+    assert(ret == 0);
+    return ret;
+}
+
+void kvm_s390_enable_css_support(CPUS390XState *env)
+{
+    struct kvm_enable_cap cap = {};
+    int r;
+
+    /* Activate host kernel channel subsystem support. */
+    if (kvm_enabled()) {
+        /* One CPU has to run */
+        s390_add_running_cpu(env);
+
+        cap.cap = KVM_CAP_S390_CSS_SUPPORT;
+        r = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap);
+        assert(r == 0);
+    }
+}
+
+int kvm_s390_new_css_image(uint8_t cssid, bool default_image)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    struct kvm_s390_css_info css_info;
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    css_info.cssid = cssid;
+    css_info.default_image = default_image;
+    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_ADD_CSS, &css_info);
+    assert(ret == 0);
+    return ret;
+}
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/5] Update headers for upcoming s390 changes Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/5] s390: Virtual channel subsystem support Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-19 16:12   ` Alexander Graf
                     ` (2 more replies)
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
  4 siblings, 3 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Add a new virtio transport that uses channel commands to perform
virtio operations.

Add a new machine type s390-ccw that uses this virtio-ccw transport
and make it the default machine for s390.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:
- update to virtio-ccw interface changes

---
 hw/qdev-monitor.c      |   5 +
 hw/s390-virtio.c       | 277 ++++++++++++----
 hw/s390x/Makefile.objs |   1 +
 hw/s390x/css.c         |  45 +++
 hw/s390x/css.h         |   3 +
 hw/s390x/virtio-ccw.c  | 875 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/virtio-ccw.h  |  79 +++++
 vl.c                   |   1 +
 8 files changed, 1215 insertions(+), 71 deletions(-)
 create mode 100644 hw/s390x/virtio-ccw.c
 create mode 100644 hw/s390x/virtio-ccw.h

diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index 33b7f79..92b7c59 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -42,6 +42,11 @@ static const QDevAlias qdev_alias_table[] = {
     { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
     { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
     { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
+    { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
+    { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
+    { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
+    { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
+    { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
     { "lsi53c895a", "lsi" },
     { "ich9-ahci", "ahci" },
     { }
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index 47eed35..2509291 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -30,8 +30,11 @@
 #include "hw/sysbus.h"
 #include "kvm.h"
 #include "exec-memory.h"
+#include "qemu-thread.h"
 
 #include "hw/s390-virtio-bus.h"
+#include "hw/s390x/css.h"
+#include "hw/s390x/virtio-ccw.h"
 
 //#define DEBUG_S390
 
@@ -46,6 +49,7 @@
 #define KVM_S390_VIRTIO_NOTIFY          0
 #define KVM_S390_VIRTIO_RESET           1
 #define KVM_S390_VIRTIO_SET_STATUS      2
+#define KVM_S390_VIRTIO_CCW_NOTIFY      3
 
 #define KERN_IMAGE_START                0x010000UL
 #define KERN_PARM_AREA                  0x010480UL
@@ -62,6 +66,7 @@
 
 static VirtIOS390Bus *s390_bus;
 static S390CPU **ipi_states;
+VirtioCcwBus *ccw_bus;
 
 S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
 {
@@ -75,15 +80,21 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
 int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
 {
     int r = 0, i;
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
 
     dprintf("KVM hypercall: %ld\n", hypercall);
     switch (hypercall) {
     case KVM_S390_VIRTIO_NOTIFY:
         if (mem > ram_size) {
-            VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
-                                                               mem, &i);
-            if (dev) {
-                virtio_queue_notify(dev->vdev, i);
+            if (s390_bus) {
+                VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
+                                                                   mem, &i);
+                if (dev) {
+                    virtio_queue_notify(dev->vdev, i);
+                } else {
+                    r = -EINVAL;
+                }
             } else {
                 r = -EINVAL;
             }
@@ -92,28 +103,49 @@ int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
         }
         break;
     case KVM_S390_VIRTIO_RESET:
-    {
-        VirtIOS390Device *dev;
-
-        dev = s390_virtio_bus_find_mem(s390_bus, mem);
-        virtio_reset(dev->vdev);
-        stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
-        s390_virtio_device_sync(dev);
-        s390_virtio_reset_idx(dev);
+        if (s390_bus) {
+            VirtIOS390Device *dev;
+
+            dev = s390_virtio_bus_find_mem(s390_bus, mem);
+            virtio_reset(dev->vdev);
+            stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
+            s390_virtio_device_sync(dev);
+            s390_virtio_reset_idx(dev);
+        } else {
+            r = -EINVAL;
+        }
         break;
-    }
     case KVM_S390_VIRTIO_SET_STATUS:
-    {
-        VirtIOS390Device *dev;
+        if (s390_bus) {
+            VirtIOS390Device *dev;
 
-        dev = s390_virtio_bus_find_mem(s390_bus, mem);
-        if (dev) {
-            s390_virtio_device_update_status(dev);
+            dev = s390_virtio_bus_find_mem(s390_bus, mem);
+            if (dev) {
+                s390_virtio_device_update_status(dev);
+            } else {
+                r = -EINVAL;
+            }
         } else {
             r = -EINVAL;
         }
         break;
-    }
+    case KVM_S390_VIRTIO_CCW_NOTIFY:
+        if (ccw_bus) {
+            if (ioinst_disassemble_sch_ident(env->regs[2], &m, &cssid, &ssid,
+                                             &schid)) {
+                r = -EINVAL;
+            } else {
+                sch = css_find_subch(m, cssid, ssid, schid);
+                if (sch) {
+                    virtio_queue_notify(virtio_ccw_get_vdev(sch), env->regs[3]);
+                } else {
+                    r = -EINVAL;
+                }
+            }
+         } else {
+             r = -EINVAL;
+         }
+         break;
     default:
         r = -EINVAL;
         break;
@@ -150,58 +182,12 @@ unsigned s390_del_running_cpu(CPUS390XState *env)
     return s390_running_cpus;
 }
 
-/* PC hardware initialisation */
-static void s390_init(ram_addr_t my_ram_size,
-                      const char *boot_device,
-                      const char *kernel_filename,
-                      const char *kernel_cmdline,
-                      const char *initrd_filename,
-                      const char *cpu_model)
+static CPUS390XState *s390_init_cpus(const char *cpu_model,
+                                     uint8_t *storage_keys)
 {
     CPUS390XState *env = NULL;
-    MemoryRegion *sysmem = get_system_memory();
-    MemoryRegion *ram = g_new(MemoryRegion, 1);
-    ram_addr_t kernel_size = 0;
-    ram_addr_t initrd_offset;
-    ram_addr_t initrd_size = 0;
-    int shift = 0;
-    uint8_t *storage_keys;
-    void *virtio_region;
-    target_phys_addr_t virtio_region_len;
-    target_phys_addr_t virtio_region_start;
     int i;
 
-    /* s390x ram size detection needs a 16bit multiplier + an increment. So
-       guests > 64GB can be specified in 2MB steps etc. */
-    while ((my_ram_size >> (20 + shift)) > 65535) {
-        shift++;
-    }
-    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
-
-    /* lets propagate the changed ram size into the global variable. */
-    ram_size = my_ram_size;
-
-    /* get a BUS */
-    s390_bus = s390_virtio_bus_init(&my_ram_size);
-
-    /* allocate RAM */
-    memory_region_init_ram(ram, "s390.ram", my_ram_size);
-    vmstate_register_ram_global(ram);
-    memory_region_add_subregion(sysmem, 0, ram);
-
-    /* clear virtio region */
-    virtio_region_len = my_ram_size - ram_size;
-    virtio_region_start = ram_size;
-    virtio_region = cpu_physical_memory_map(virtio_region_start,
-                                            &virtio_region_len, true);
-    memset(virtio_region, 0, virtio_region_len);
-    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
-                              virtio_region_len);
-
-    /* allocate storage keys */
-    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
-    /* init CPUs */
     if (cpu_model == NULL) {
         cpu_model = "host";
     }
@@ -222,6 +208,17 @@ static void s390_init(ram_addr_t my_ram_size,
         tmp_env->exception_index = EXCP_HLT;
         tmp_env->storage_keys = storage_keys;
     }
+    return env;
+}
+
+static void s390_set_up_kernel(CPUS390XState *env,
+                               const char *kernel_filename,
+                               const char *kernel_cmdline,
+                               const char *initrd_filename)
+{
+    ram_addr_t kernel_size = 0;
+    ram_addr_t initrd_offset;
+    ram_addr_t initrd_size = 0;
 
     /* One CPU has to run */
     s390_add_running_cpu(env);
@@ -294,8 +291,13 @@ static void s390_init(ram_addr_t my_ram_size,
                strlen(kernel_cmdline) + 1);
     }
 
-    /* Create VirtIO network adapters */
-    for(i = 0; i < nb_nics; i++) {
+}
+
+static void s390_create_virtio_net(BusState *bus, const char *name)
+{
+    int i;
+
+    for (i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
         DeviceState *dev;
 
@@ -308,7 +310,7 @@ static void s390_init(ram_addr_t my_ram_size,
             exit(1);
         }
 
-        dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
+        dev = qdev_create(bus, name);
         qdev_set_nic_properties(dev, nd);
         qdev_init_nofail(dev);
     }
@@ -329,6 +331,63 @@ static void s390_init(ram_addr_t my_ram_size,
     }
 }
 
+/* PC hardware initialisation */
+static void s390_init(ram_addr_t my_ram_size,
+                      const char *boot_device,
+                      const char *kernel_filename,
+                      const char *kernel_cmdline,
+                      const char *initrd_filename,
+                      const char *cpu_model)
+{
+    CPUS390XState *env = NULL;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    int shift = 0;
+    uint8_t *storage_keys;
+    void *virtio_region;
+    target_phys_addr_t virtio_region_len;
+    target_phys_addr_t virtio_region_start;
+
+    /* s390x ram size detection needs a 16bit multiplier + an increment. So
+       guests > 64GB can be specified in 2MB steps etc. */
+    while ((my_ram_size >> (20 + shift)) > 65535) {
+        shift++;
+    }
+    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
+
+    /* lets propagate the changed ram size into the global variable. */
+    ram_size = my_ram_size;
+
+    /* get a BUS */
+    s390_bus = s390_virtio_bus_init(&my_ram_size);
+
+    /* allocate RAM */
+    memory_region_init_ram(ram, "s390.ram", my_ram_size);
+    vmstate_register_ram_global(ram);
+    memory_region_add_subregion(sysmem, 0, ram);
+
+    /* clear virtio region */
+    virtio_region_len = my_ram_size - ram_size;
+    virtio_region_start = ram_size;
+    virtio_region = cpu_physical_memory_map(virtio_region_start,
+                                            &virtio_region_len, true);
+    memset(virtio_region, 0, virtio_region_len);
+    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
+                              virtio_region_len);
+
+    /* allocate storage keys */
+    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
+
+    /* init CPUs */
+    env = s390_init_cpus(cpu_model, storage_keys);
+
+    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
+
+    /* Create VirtIO network adapters */
+    s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
+
+}
+
 static QEMUMachine s390_machine = {
     .name = "s390-virtio",
     .alias = "s390",
@@ -341,7 +400,6 @@ static QEMUMachine s390_machine = {
     .no_sdcard = 1,
     .use_virtcon = 1,
     .max_cpus = 255,
-    .is_default = 1,
 };
 
 static void s390_machine_init(void)
@@ -350,3 +408,80 @@ static void s390_machine_init(void)
 }
 
 machine_init(s390_machine_init);
+
+static void ccw_init(ram_addr_t my_ram_size,
+                     const char *boot_device,
+                     const char *kernel_filename,
+                     const char *kernel_cmdline,
+                     const char *initrd_filename,
+                     const char *cpu_model)
+{
+    CPUS390XState *env = NULL;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    int shift = 0;
+    uint8_t *storage_keys;
+    int ret;
+
+    /* s390x ram size detection needs a 16bit multiplier + an increment. So
+       guests > 64GB can be specified in 2MB steps etc. */
+    while ((my_ram_size >> (20 + shift)) > 65535) {
+        shift++;
+    }
+    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
+
+    /* lets propagate the changed ram size into the global variable. */
+    ram_size = my_ram_size;
+
+    /* get a BUS */
+    ccw_bus = virtio_ccw_bus_init();
+
+    /* allocate RAM */
+    memory_region_init_ram(ram, "s390.ram", my_ram_size);
+    vmstate_register_ram_global(ram);
+    memory_region_add_subregion(sysmem, 0, ram);
+
+    /* allocate storage keys */
+    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
+
+    /* init CPUs */
+    env = s390_init_cpus(cpu_model, storage_keys);
+
+    kvm_s390_enable_css_support(env);
+
+    /*
+     * Create virtual css and set it as default so that non mcss-e
+     * enabled guests only see virtio devices.
+     */
+    ret = css_create_css_image(VIRTUAL_CSSID, true);
+    assert(ret == 0);
+
+
+    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
+
+    /* Create VirtIO network adapters */
+    s390_create_virtio_net((BusState *)ccw_bus, "virtio-net-ccw");
+
+}
+
+static QEMUMachine ccw_machine = {
+    .name = "s390-ccw-virtio",
+    .alias = "s390-ccw",
+    .desc = "VirtIO-ccw based S390 machine",
+    .init = ccw_init,
+    .no_cdrom = 1,
+    .no_floppy = 1,
+    .no_serial = 1,
+    .no_parallel = 1,
+    .no_sdcard = 1,
+    .use_virtcon = 1,
+    .max_cpus = 255,
+    .is_default = 1,
+};
+
+static void ccw_machine_init(void)
+{
+    qemu_register_machine(&ccw_machine);
+}
+
+machine_init(ccw_machine_init);
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index 93b41fb..e4c3d6f 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -2,3 +2,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o
 
 obj-y := $(addprefix ../,$(obj-y))
 obj-y += css.o
+obj-y += virtio-ccw.o
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index b9b6e48..a671e28 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -441,6 +441,51 @@ 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];
 }
 
+bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
+{
+    if (!channel_subsys->css[cssid]) {
+        return false;
+    }
+    if (!channel_subsys->css[cssid]->sch_set[ssid]) {
+        return false;
+    }
+
+    return !!test_bit(devno,
+                      channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
+}
+
+void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno,
+                      SubchDev *sch)
+{
+    CssImage *css;
+    SubchSet *s_set;
+
+    if (!channel_subsys->css[cssid]) {
+        fprintf(stderr,
+                "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
+                __func__, cssid, ssid, schid);
+       return;
+   }
+    css = channel_subsys->css[cssid];
+
+   if (!css->sch_set[ssid]) {
+        size_t set_size = sizeof(SubchSet) +
+            BITS_TO_LONGS(MAX_SCHID + 1) * 2;
+        css->sch_set[ssid] = g_malloc0(set_size);
+    }
+    s_set = css->sch_set[ssid];
+
+    s_set->sch[schid] = sch;
+    if (sch) {
+        set_bit(schid, s_set->schids_used);
+        set_bit(devno, s_set->devnos_used);
+    } else {
+        clear_bit(schid, s_set->schids_used);
+        clear_bit(schid, s_set->devnos_used);
+    }
+}
+
+
 bool css_present(uint8_t cssid)
 {
     return (channel_subsys->css[cssid] != NULL);
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index f3590eb..6876633 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -53,6 +53,9 @@ struct SubchDev {
 };
 
 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);
 void css_reset(void);
 void css_reset_sch(SubchDev *sch);
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
new file mode 100644
index 0000000..28be1a7
--- /dev/null
+++ b/hw/s390x/virtio-ccw.c
@@ -0,0 +1,875 @@
+/*
+ * virtio ccw target implementation
+ *
+ * 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.
+ */
+
+#include <hw/hw.h>
+#include "block.h"
+#include "blockdev.h"
+#include "sysemu.h"
+#include "net.h"
+#include "monitor.h"
+#include "qemu-thread.h"
+#include "hw/virtio.h"
+#include "hw/virtio-serial.h"
+#include "hw/virtio-net.h"
+#include "hw/sysbus.h"
+#include "bitops.h"
+
+#include "ioinst.h"
+#include "css.h"
+#include "virtio-ccw.h"
+
+static const TypeInfo virtio_ccw_bus_info = {
+    .name = TYPE_VIRTIO_CCW_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(VirtioCcwBus),
+};
+
+static const VirtIOBindings virtio_ccw_bindings;
+
+VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
+{
+    VirtIODevice *vdev = NULL;
+
+    if (sch->driver_data) {
+        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
+    }
+    return vdev;
+}
+
+static void virtio_ccw_reset_subchannels(void *opaque)
+{
+    VirtioCcwBus *bus = opaque;
+    BusChild *kid;
+    VirtioCcwData *data;
+
+    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+        data = (VirtioCcwData *)kid->child;
+        virtio_reset(data->vdev);
+        css_reset_sch(data->sch);
+    }
+    css_reset();
+}
+
+VirtioCcwBus *virtio_ccw_bus_init(void)
+{
+    VirtioCcwBus *cbus;
+    BusState *bus;
+    DeviceState *dev;
+
+    /* Create bridge device */
+    dev = qdev_create(NULL, "virtio-ccw-bridge");
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
+    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
+
+    /* Enable hotplugging */
+    bus->allow_hotplug = 1;
+
+    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
+    return cbus;
+}
+
+/* Communication blocks used by several channel commands. */
+typedef struct VqInfoBlock {
+    uint64_t queue;
+    uint32_t align;
+    uint16_t index;
+    uint16_t num;
+} QEMU_PACKED VqInfoBlock;
+
+typedef struct VqConfigBlock {
+    uint16_t index;
+    uint16_t num_max;
+} QEMU_PACKED VqConfigBlock;
+
+typedef struct VirtioFeatDesc {
+    uint32_t features;
+    uint8_t index;
+} QEMU_PACKED VirtioFeatDesc;
+
+/* Specify where the virtqueues for the subchannel are in guest memory. */
+static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
+                              uint16_t index, uint16_t num)
+{
+    VirtioCcwData *data = sch->driver_data;
+
+    if (index > VIRTIO_PCI_QUEUE_MAX) {
+        return -EINVAL;
+    }
+
+    /* Current code in virtio.c relies on 4K alignment. */
+    if (align != 4096) {
+        return -EINVAL;
+    }
+
+    if (!data) {
+        return -EINVAL;
+    }
+
+    virtio_queue_set_addr(data->vdev, index, addr);
+    if (!addr) {
+        virtio_queue_set_vector(data->vdev, index, 0);
+    } else {
+        /* Fail if we don't have a big enough queue. */
+        /* TODO: Add interface to handle vring.num changing */
+        if (virtio_queue_get_num(data->vdev, index) > num) {
+            return -EINVAL;
+        }
+        virtio_queue_set_vector(data->vdev, index, index);
+    }
+    return 0;
+}
+
+static int virtio_ccw_cb(SubchDev *sch, CCW1 *ccw)
+{
+    int ret;
+    VqInfoBlock info;
+    uint8_t status;
+    VirtioFeatDesc features;
+    void *config;
+    uint64_t *indicators;
+    VqConfigBlock vq_config;
+    VirtioCcwData *data = sch->driver_data;
+    bool check_len;
+    int len;
+
+    if (!ccw) {
+        return -EIO;
+    }
+
+    if (!data) {
+        return -EINVAL;
+    }
+
+    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
+
+    /* Look at the command. */
+    switch (ccw->cmd_code) {
+    case CCW_CMD_SET_VQ:
+        if (check_len) {
+            if (ccw->count != sizeof(info)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(info)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            info.queue = ldq_phys(ccw->cda);
+            info.align = ldl_phys(ccw->cda + sizeof(info.queue));
+            info.index = lduw_phys(ccw->cda + sizeof(info.queue)
+                                   + sizeof(info.align));
+            info.num = lduw_phys(ccw->cda + sizeof(info.queue)
+                                 + sizeof(info.align)
+                                 + sizeof(info.index));
+            ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index,
+                                     info.num);
+            sch->curr_status.scsw.count = 0;
+        }
+        break;
+    case CCW_CMD_VDEV_RESET:
+        virtio_reset(data->vdev);
+        ret = 0;
+        break;
+    case CCW_CMD_READ_FEAT:
+        if (check_len) {
+            if (ccw->count != sizeof(features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            features.index = ldub_phys(ccw->cda + sizeof(features.features));
+            if (features.index < ARRAY_SIZE(data->host_features)) {
+                features.features = data->host_features[features.index];
+            } else {
+                /* Return zeroes if the guest supports more feature bits. */
+                features.features = 0;
+            }
+            stw_le_phys(ccw->cda, features.features);
+            sch->curr_status.scsw.count = ccw->count - sizeof(features);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_WRITE_FEAT:
+        if (check_len) {
+            if (ccw->count != sizeof(features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            features.index = ldub_phys(ccw->cda + sizeof(features.features));
+            features.features = bswap32(lduw_phys(ccw->cda));
+            if (features.index < ARRAY_SIZE(data->host_features)) {
+                if (data->vdev->set_features) {
+                    data->vdev->set_features(data->vdev, features.features);
+                }
+                data->vdev->guest_features = features.features;
+            } else {
+                /*
+                 * If the guest supports more feature bits, assert that it
+                 * passes us zeroes for those we don't support.
+                 */
+                if (features.features) {
+                    fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n",
+                            features.index, features.features);
+                    /* XXX: do a unit check here? */
+                }
+            }
+            sch->curr_status.scsw.count = ccw->count - sizeof(features);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_READ_CONF:
+        if (check_len) {
+            if (ccw->count > data->vdev->config_len) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, data->vdev->config_len);
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            data->vdev->get_config(data->vdev, data->vdev->config);
+            cpu_physical_memory_write(ccw->cda, data->vdev->config, len);
+            sch->curr_status.scsw.count = ccw->count - len;
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_WRITE_CONF:
+        if (check_len) {
+            if (ccw->count > data->vdev->config_len) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, data->vdev->config_len);
+        config = qemu_get_ram_ptr(ccw->cda);
+        if (!config) {
+            ret = -EFAULT;
+        } else {
+            memcpy(data->vdev->config, config, len);
+            if (data->vdev->set_config) {
+                data->vdev->set_config(data->vdev, data->vdev->config);
+            }
+            sch->curr_status.scsw.count = ccw->count - len;
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_WRITE_STATUS:
+        if (check_len) {
+            if (ccw->count != sizeof(status)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(status)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            status = ldub_phys(ccw->cda);
+            virtio_set_status(data->vdev, status);
+            sch->curr_status.scsw.count = ccw->count - sizeof(status);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_SET_IND:
+        if (check_len) {
+            if (ccw->count != sizeof(*indicators)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(*indicators)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        indicators = qemu_get_ram_ptr(ccw->cda);
+        if (!indicators) {
+            ret = -EFAULT;
+        } else {
+            data->indicators = ccw->cda;
+            sch->curr_status.scsw.count = ccw->count - sizeof(*indicators);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_READ_VQ_CONF:
+        if (check_len) {
+            if (ccw->count != sizeof(vq_config)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(vq_config)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            vq_config.index = lduw_phys(ccw->cda);
+            vq_config.num_max = virtio_queue_get_num(data->vdev,
+                                                     vq_config.index);
+            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num_max);
+            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
+            ret = 0;
+        }
+        break;
+    default:
+        ret = -EOPNOTSUPP;
+        break;
+    }
+    return ret;
+}
+
+static int virtio_ccw_device_init(VirtioCcwData *dev, VirtIODevice *vdev)
+{
+    unsigned int cssid = 0;
+    unsigned int ssid = 0;
+    unsigned int schid;
+    unsigned int devno;
+    bool have_devno = false;
+    bool found = false;
+    SubchDev *sch;
+    int ret;
+    int num;
+
+    sch = g_malloc0(sizeof(SubchDev));
+
+    sch->driver_data = dev;
+    dev->sch = sch;
+
+    dev->vdev = vdev;
+    dev->indicators = 0;
+
+    /* Initialize subchannel structure. */
+    qemu_mutex_init(&sch->mutex);
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    sch->orb = NULL;
+    /*
+     * 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)) {
+                ret = -EINVAL;
+                error_report("Invalid cssid or ssid: cssid %x, ssid %x",
+                             cssid, ssid);
+                goto out_err;
+            }
+            /* Enforce use of virtual cssid. */
+            if (cssid != VIRTUAL_CSSID) {
+                ret = -EINVAL;
+                error_report("cssid %x not valid for virtio devices", cssid);
+                goto out_err;
+            }
+            if (css_devno_used(cssid, ssid, devno)) {
+                ret = -EEXIST;
+                error_report("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 {
+            ret = -EINVAL;
+            error_report("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) {
+            ret = -ENODEV;
+            error_report("No free subchannel found for %x.%x.%04x", cssid, ssid,
+                         devno);
+            goto out_err;
+        }
+    } 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) {
+                            ret = -ENODEV;
+                            error_report("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) {
+            ret = -ENODEV;
+            error_report("Virtual channel subsystem is full!");
+            goto out_err;
+        }
+    }
+
+    /* Build initial schib. */
+    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
+
+    sch->ccw_cb = virtio_ccw_cb;
+
+    /* Build senseid data. */
+    memset(&sch->id, 0, sizeof(SenseId));
+    sch->id.reserved = 0xff;
+    sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
+    sch->id.cu_model = dev->vdev->device_id;
+
+    virtio_bind_device(vdev, &virtio_ccw_bindings, dev);
+    /* Only the first 32 feature bits are used. */
+    dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]);
+
+    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
+                     &sch->curr_status, dev->qdev.hotplugged, 1, 1);
+    return 0;
+
+out_err:
+    dev->sch = NULL;
+    g_free(sch);
+    return ret;
+}
+
+static int virtio_ccw_exit(VirtioCcwData *dev)
+{
+    SubchDev *sch = dev->sch;
+
+    if (sch) {
+        css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
+        g_free(sch);
+    }
+    dev->indicators = 0;
+    return 0;
+}
+
+static int virtio_ccw_net_init(VirtioCcwData *dev)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
+    if (!vdev) {
+        return -1;
+    }
+
+    return virtio_ccw_device_init(dev, vdev);
+}
+
+static int virtio_ccw_net_exit(VirtioCcwData *dev)
+{
+    virtio_net_exit(dev->vdev);
+    return virtio_ccw_exit(dev);
+}
+
+static int virtio_ccw_blk_init(VirtioCcwData *dev)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_blk_init((DeviceState *)dev, &dev->blk);
+    if (!vdev) {
+        return -1;
+    }
+
+    return virtio_ccw_device_init(dev, vdev);
+}
+
+static int virtio_ccw_blk_exit(VirtioCcwData *dev)
+{
+    virtio_blk_exit(dev->vdev);
+    blockdev_mark_auto_del(dev->blk.conf.bs);
+    return virtio_ccw_exit(dev);
+}
+
+static int virtio_ccw_serial_init(VirtioCcwData *dev)
+{
+    VirtioCcwBus *bus;
+    VirtIODevice *vdev;
+    int r;
+
+    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
+
+    vdev = virtio_serial_init((DeviceState *)dev, &dev->serial);
+    if (!vdev) {
+        return -1;
+    }
+
+    r = virtio_ccw_device_init(dev, vdev);
+    if (!r) {
+        bus->console = dev;
+    }
+
+    return r;
+}
+
+static int virtio_ccw_serial_exit(VirtioCcwData *dev)
+{
+    VirtioCcwBus *bus;
+
+    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
+    bus->console = NULL;
+    virtio_serial_exit(dev->vdev);
+    return virtio_ccw_exit(dev);
+}
+
+static int virtio_ccw_balloon_init(VirtioCcwData *dev)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_balloon_init((DeviceState *)dev);
+    if (!vdev) {
+        return -1;
+    }
+
+    return virtio_ccw_device_init(dev, vdev);
+}
+
+static int virtio_ccw_balloon_exit(VirtioCcwData *dev)
+{
+    virtio_balloon_exit(dev->vdev);
+    return virtio_ccw_exit(dev);
+}
+
+static int virtio_ccw_scsi_init(VirtioCcwData *dev)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
+    if (!vdev) {
+        return -1;
+    }
+
+    return virtio_ccw_device_init(dev, vdev);
+}
+
+static int virtio_ccw_scsi_exit(VirtioCcwData *dev)
+{
+    virtio_scsi_exit(dev->vdev);
+    return virtio_ccw_exit(dev);
+}
+
+VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus)
+{
+    return bus->console;
+}
+
+static void virtio_ccw_notify(void *opaque, uint16_t vector)
+{
+    VirtioCcwData *dev = opaque;
+    SubchDev *sch = dev->sch;
+    uint64_t indicators;
+
+    if (vector >= VIRTIO_PCI_QUEUE_MAX) {
+        return;
+    }
+
+    qemu_mutex_lock(&sch->mutex);
+    indicators = ldq_phys(dev->indicators);
+    set_bit(vector, &indicators);
+    stq_phys(dev->indicators, indicators);
+
+    css_conditional_io_interrupt(sch);
+
+    qemu_mutex_unlock(&sch->mutex);
+}
+
+static unsigned virtio_ccw_get_features(void *opaque)
+{
+    VirtioCcwData *dev = opaque;
+
+    /* Only the first 32 feature bits are used. */
+    return dev->host_features[0];
+}
+
+/**************** Virtio-ccw Bus Device Descriptions *******************/
+
+static const VirtIOBindings virtio_ccw_bindings = {
+    .notify = virtio_ccw_notify,
+    .get_features = virtio_ccw_get_features,
+};
+
+static Property virtio_ccw_net_properties[] = {
+    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
+    DEFINE_NIC_PROPERTIES(VirtioCcwData, nic),
+    DEFINE_PROP_UINT32("x-txtimer", VirtioCcwData,
+                       net.txtimer, TX_TIMER_INTERVAL),
+    DEFINE_PROP_INT32("x-txburst", VirtioCcwData,
+                      net.txburst, TX_BURST),
+    DEFINE_PROP_STRING("tx", VirtioCcwData, net.tx),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_net_init;
+    k->exit = virtio_ccw_net_exit;
+    dc->props = virtio_ccw_net_properties;
+}
+
+static TypeInfo virtio_ccw_net = {
+    .name          = "virtio-net-ccw",
+    .parent        = TYPE_VIRTIO_CCW_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init    = virtio_ccw_net_class_init,
+};
+
+static Property virtio_ccw_blk_properties[] = {
+    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
+    DEFINE_BLOCK_PROPERTIES(VirtioCcwData, blk.conf),
+    DEFINE_PROP_STRING("serial", VirtioCcwData, blk.serial),
+#ifdef __linux__
+    DEFINE_PROP_BIT("scsi", VirtioCcwData, blk.scsi, 0, true),
+#endif
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_blk_init;
+    k->exit = virtio_ccw_blk_exit;
+    dc->props = virtio_ccw_blk_properties;
+}
+
+static TypeInfo virtio_ccw_blk = {
+    .name          = "virtio-blk-ccw",
+    .parent        = TYPE_VIRTIO_CCW_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init    = virtio_ccw_blk_class_init,
+};
+
+static Property virtio_ccw_serial_properties[] = {
+    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
+    DEFINE_PROP_UINT32("max_ports", VirtioCcwData, serial.max_virtserial_ports,
+                       31),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_serial_init;
+    k->exit = virtio_ccw_serial_exit;
+    dc->props = virtio_ccw_serial_properties;
+}
+
+static TypeInfo virtio_ccw_serial = {
+    .name          = "virtio-serial-ccw",
+    .parent        = TYPE_VIRTIO_CCW_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init    = virtio_ccw_serial_class_init,
+};
+
+static Property virtio_ccw_balloon_properties[] = {
+    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_balloon_init;
+    k->exit = virtio_ccw_balloon_exit;
+    dc->props = virtio_ccw_balloon_properties;
+}
+
+static TypeInfo virtio_ccw_balloon = {
+    .name          = "virtio-balloon-ccw",
+    .parent        = TYPE_VIRTIO_CCW_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init    = virtio_ccw_balloon_class_init,
+};
+
+static Property virtio_ccw_scsi_properties[] = {
+    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
+    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwData, host_features[0], scsi),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_scsi_init;
+    k->exit = virtio_ccw_scsi_exit;
+    dc->props = virtio_ccw_scsi_properties;
+}
+
+static TypeInfo virtio_ccw_scsi = {
+    .name          = "virtio-scsi-ccw",
+    .parent        = TYPE_VIRTIO_CCW_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init    = virtio_ccw_scsi_class_init,
+};
+
+static int virtio_ccw_busdev_init(DeviceState *dev)
+{
+    VirtioCcwData *_dev = (VirtioCcwData *)dev;
+    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
+
+    return _info->init(_dev);
+}
+
+static int virtio_ccw_busdev_exit(DeviceState *dev)
+{
+    VirtioCcwData *_dev = (VirtioCcwData *)dev;
+    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
+
+    return _info->exit(_dev);
+}
+
+static int virtio_ccw_busdev_unplug(DeviceState *dev)
+{
+    VirtioCcwData *_dev = (VirtioCcwData *)dev;
+    SubchDev *sch = _dev->sch;
+
+    /*
+     * 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. */
+    qemu_mutex_lock(&sch->mutex);
+    sch->curr_status.pmcw.ena = 0;
+    sch->curr_status.pmcw.dnv = 0;
+    qemu_mutex_unlock(&sch->mutex);
+
+    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
+                     &sch->curr_status, 1, 0, 1);
+
+    object_unparent(OBJECT(dev));
+    qdev_free(dev);
+    return 0;
+}
+
+static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->init = virtio_ccw_busdev_init;
+    dc->exit = virtio_ccw_busdev_exit;
+    dc->unplug = virtio_ccw_busdev_unplug;
+    dc->bus_type = TYPE_VIRTIO_CCW_BUS;
+
+}
+
+static TypeInfo virtio_ccw_device_info = {
+    .name = TYPE_VIRTIO_CCW_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(VirtioCcwData),
+    .class_init = virtio_ccw_device_class_init,
+    .class_size = sizeof(VirtIOCCWDeviceClass),
+    .abstract = true,
+};
+
+/***************** Virtio-ccw Bus Bridge Device ********************/
+/* Only required to have the virtio bus as child in the system bus */
+
+static int virtio_ccw_bridge_init(SysBusDevice *dev)
+{
+    /* nothing */
+    return 0;
+}
+
+static void virtio_ccw_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = virtio_ccw_bridge_init;
+    dc->no_user = 1;
+}
+
+static TypeInfo virtio_ccw_bridge_info = {
+    .name          = "virtio-ccw-bridge",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .class_init    = virtio_ccw_bridge_class_init,
+};
+
+static void virtio_ccw_register(void)
+{
+    type_register_static(&virtio_ccw_bus_info);
+    type_register_static(&virtio_ccw_device_info);
+    type_register_static(&virtio_ccw_serial);
+    type_register_static(&virtio_ccw_blk);
+    type_register_static(&virtio_ccw_net);
+    type_register_static(&virtio_ccw_balloon);
+    type_register_static(&virtio_ccw_scsi);
+    type_register_static(&virtio_ccw_bridge_info);
+}
+type_init(virtio_ccw_register);
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
new file mode 100644
index 0000000..8125acf
--- /dev/null
+++ b/hw/s390x/virtio-ccw.h
@@ -0,0 +1,79 @@
+/*
+ * virtio ccw target 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.
+ */
+
+#include <hw/virtio-blk.h>
+#include <hw/virtio-net.h>
+#include <hw/virtio-serial.h>
+#include <hw/virtio-scsi.h>
+
+#define VIRTUAL_CSSID 0xfe
+
+#define VIRTIO_CCW_CU_TYPE 0x3832
+#define VIRTIO_CCW_CHPID_TYPE 0x32
+
+#define CCW_CMD_SET_VQ       0x13
+#define CCW_CMD_VDEV_RESET   0x33
+#define CCW_CMD_READ_FEAT    0x12
+#define CCW_CMD_WRITE_FEAT   0x11
+#define CCW_CMD_READ_CONF    0x22
+#define CCW_CMD_WRITE_CONF   0x21
+#define CCW_CMD_WRITE_STATUS 0x31
+#define CCW_CMD_SET_IND      0x43
+#define CCW_CMD_READ_VQ_CONF 0x32
+
+#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
+#define VIRTIO_CCW_DEVICE(obj) \
+     OBJECT_CHECK(VirtioCcwData, (obj), TYPE_VIRTIO_CCW_DEVICE)
+#define VIRTIO_CCW_DEVICE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE)
+#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE)
+
+#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus"
+#define VIRTIO_CCW_BUS(obj) \
+     OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS)
+
+typedef struct VirtioCcwData VirtioCcwData;
+
+typedef struct VirtIOCCWDeviceClass {
+    DeviceClass qdev;
+    int (*init)(VirtioCcwData *dev);
+    int (*exit)(VirtioCcwData *dev);
+} VirtIOCCWDeviceClass;
+
+/* Change here if we want to support more feature bits. */
+#define VIRTIO_CCW_FEATURE_SIZE 1
+
+struct VirtioCcwData {
+    DeviceState qdev;
+    SubchDev *sch;
+    VirtIODevice *vdev;
+    char *bus_id;
+    VirtIOBlkConf blk;
+    NICConf nic;
+    uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
+    virtio_serial_conf serial;
+    virtio_net_conf net;
+    VirtIOSCSIConf scsi;
+    /* Guest provided values: */
+    target_phys_addr_t indicators;
+};
+
+/* virtio-ccw bus type */
+typedef struct VirtioCcwBus {
+    BusState bus;
+    VirtioCcwData *console;
+} VirtioCcwBus;
+
+VirtioCcwBus *virtio_ccw_bus_init(void);
+void virtio_ccw_device_update_status(SubchDev *sch);
+VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus);
+VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
diff --git a/vl.c b/vl.c
index 7c577fa..2b8cae6 100644
--- a/vl.c
+++ b/vl.c
@@ -289,6 +289,7 @@ static struct {
     { .driver = "scsi-cd",              .flag = &default_cdrom     },
     { .driver = "virtio-serial-pci",    .flag = &default_virtcon   },
     { .driver = "virtio-serial-s390",   .flag = &default_virtcon   },
+    { .driver = "virtio-serial-ccw",    .flag = &default_virtcon   },
     { .driver = "virtio-serial",        .flag = &default_virtcon   },
     { .driver = "VGA",                  .flag = &default_vga       },
     { .driver = "isa-vga",              .flag = &default_vga       },
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
                   ` (2 preceding siblings ...)
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-19 16:22   ` Alexander Graf
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
  4 siblings, 1 reply; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Provide css support for the !KVM case as well.

This includes the following:
- Handling of instruction intercepts for I/O instructions.
- Extended channel subsystem functions, like monitoring.
- Support for injecting I/O interrupts and machine checks.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:
- coding style

---
 hw/s390x/css.c           | 749 ++++++++++++++++++++++++++++++++++++++++++++++-
 hw/s390x/css.h           |  26 ++
 target-s390x/cpu.h       | 157 +++++++++-
 target-s390x/helper.c    | 140 +++++++++
 target-s390x/ioinst.c    | 696 +++++++++++++++++++++++++++++++++++++++++++
 target-s390x/ioinst.h    |  35 ++-
 target-s390x/kvm.c       | 164 ++++++++++-
 target-s390x/op_helper.c |  22 +-
 8 files changed, 1961 insertions(+), 28 deletions(-)

diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index a671e28..3aab586 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -17,6 +17,12 @@
 #include "cpu.h"
 #include "ioinst.h"
 #include "css.h"
+#include "virtio-ccw.h"
+
+typedef struct CrwContainer {
+    CRW crw;
+    QTAILQ_ENTRY(CrwContainer) sibling;
+} CrwContainer;
 
 typedef struct ChpInfo {
     uint8_t in_use;
@@ -35,6 +41,13 @@ typedef struct CssImage {
 } CssImage;
 
 typedef struct ChannelSubSys {
+    QTAILQ_HEAD(, CrwContainer) pending_crws;
+    bool do_crw_mchk;
+    bool crws_lost;
+    uint8_t max_cssid;
+    uint8_t max_ssid;
+    bool chnmon_active;
+    uint64_t chnmon_area;
     CssImage *css[MAX_CSSID + 1];
     uint8_t default_cssid;
 } ChannelSubSys;
@@ -60,6 +73,76 @@ int css_create_css_image(uint8_t cssid, bool default_image)
     return 0;
 }
 
+static void css_write_phys_pmcw(uint32_t addr, PMCW *pmcw)
+{
+    int i;
+    uint32_t offset = 0;
+    struct copy_pmcw {
+        uint32_t intparm;
+        uint16_t flags;
+        uint16_t devno;
+        uint8_t lpm;
+        uint8_t pnom;
+        uint8_t lpum;
+        uint8_t pim;
+        uint16_t mbi;
+        uint8_t pom;
+        uint8_t pam;
+        uint8_t chpid[8];
+        uint32_t chars;
+    } *copy;
+
+    copy = (struct copy_pmcw *)pmcw;
+    stl_phys(addr + offset, copy->intparm);
+    offset += sizeof(copy->intparm);
+    stw_phys(addr + offset, copy->flags);
+    offset += sizeof(copy->flags);
+    stw_phys(addr + offset, copy->devno);
+    offset += sizeof(copy->devno);
+    stb_phys(addr + offset, copy->lpm);
+    offset += sizeof(copy->lpm);
+    stb_phys(addr + offset, copy->pnom);
+    offset += sizeof(copy->pnom);
+    stb_phys(addr + offset, copy->lpum);
+    offset += sizeof(copy->lpum);
+    stb_phys(addr + offset, copy->pim);
+    offset += sizeof(copy->pim);
+    stw_phys(addr + offset, copy->mbi);
+    offset += sizeof(copy->mbi);
+    stb_phys(addr + offset, copy->pom);
+    offset += sizeof(copy->pom);
+    stb_phys(addr + offset, copy->pam);
+    offset += sizeof(copy->pam);
+    for (i = 0; i < 8; i++) {
+        stb_phys(addr + offset, copy->chpid[i]);
+        offset += sizeof(copy->chpid[i]);
+    }
+    stl_phys(addr + offset, copy->chars);
+}
+
+static void css_write_phys_scsw(uint32_t addr, SCSW *scsw)
+{
+    uint32_t offset = 0;
+    struct copy_scsw {
+        uint32_t flags;
+        uint32_t cpa;
+        uint8_t dstat;
+        uint8_t cstat;
+        uint16_t count;
+    } *copy;
+
+    copy = (struct copy_scsw *)scsw;
+    stl_phys(addr + offset, copy->flags);
+    offset += sizeof(copy->flags);
+    stl_phys(addr + offset, copy->cpa);
+    offset += sizeof(copy->cpa);
+    stb_phys(addr + offset, copy->dstat);
+    offset += sizeof(copy->dstat);
+    stb_phys(addr + offset, copy->cstat);
+    offset += sizeof(copy->cstat);
+    stw_phys(addr + offset, copy->count);
+}
+
 static void css_inject_io_interrupt(SubchDev *sch, uint8_t func)
 {
     s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
@@ -375,6 +458,543 @@ int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
     return 0;
 }
 
+/*
+ * This function should run asynchronously to the I/O instructions in order
+ * to match the implementation on real machines. For this simple virtual
+ * css it is fine to run the I/O work synchronously instead since it won't
+ * call out to real hardware.
+ * Note: This is only used in the !KVM case.
+ */
+static void do_subchannel_work(SubchDev *sch)
+{
+
+    SCSW *s = &sch->curr_status.scsw;
+    uint8_t func;
+
+    if (s->fctl & SCSW_FCTL_CLEAR_FUNC) {
+        func = CSS_DO_CSCH_SIMPLE;
+    } else if (s->fctl & SCSW_FCTL_HALT_FUNC) {
+        func = CSS_DO_HSCH_SIMPLE;
+    } else if (s->fctl & SCSW_FCTL_START_FUNC) {
+        func = CSS_DO_SSCH_SIMPLE;
+    } else {
+        /* Cannot happen. */
+        return;
+    }
+    css_handle_sch_io((sch->cssid << 24) | (1 << 29) | (sch->ssid << 16) |
+                      (1 << 16) | sch->schid,
+                      func, 0, NULL, NULL);
+}
+
+/* The various css_do_<instr> functions are only hit when KVM is not active. */
+
+int css_do_stsch(SubchDev *sch, uint32_t addr)
+{
+    int i;
+    uint32_t offset = 0;
+
+    qemu_mutex_lock(&sch->mutex);
+    /* Use current status. */
+    css_write_phys_pmcw(addr, &sch->curr_status.pmcw);
+    offset += sizeof(PMCW);
+    css_write_phys_scsw(addr + offset, &sch->curr_status.scsw);
+    offset += sizeof(SCSW);
+    stq_phys(addr + offset, sch->curr_status.mba);
+    offset += sizeof(sch->curr_status.mba);
+    for (i = 0; i < 4; i++) {
+        stb_phys(addr + offset, sch->curr_status.mda[i]);
+        offset += sizeof(sch->curr_status.mda[i]);
+    }
+    qemu_mutex_unlock(&sch->mutex);
+    return 0;
+}
+
+int css_do_msch(SubchDev *sch, SCHIB *schib)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!sch->curr_status.pmcw.dnv) {
+        ret = 0;
+        goto out;
+    }
+
+    if (s->stctl & SCSW_STCTL_STATUS_PEND) {
+        ret = -EINPROGRESS;
+        goto out;
+    }
+
+    if (s->fctl &
+        (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) {
+        ret = -EBUSY;
+        goto out;
+    }
+
+    /* Only update the program-modifiable fields. */
+    p->ena = schib->pmcw.ena;
+    p->intparm = schib->pmcw.intparm;
+    p->isc = schib->pmcw.isc;
+    p->mp = schib->pmcw.mp;
+    p->lpm = schib->pmcw.lpm;
+    p->pom = schib->pmcw.pom;
+    p->lm = schib->pmcw.lm;
+    p->csense = schib->pmcw.csense;
+
+    p->mme = schib->pmcw.mme;
+    p->mbi = schib->pmcw.mbi;
+    p->mbfc = schib->pmcw.mbfc;
+    sch->curr_status.mba = schib->mba;
+
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_xsch(SubchDev *sch)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    if (!s->fctl || (s->fctl != SCSW_FCTL_START_FUNC) ||
+        (!(s->actl &
+           (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) ||
+        (s->actl & SCSW_ACTL_SUBCH_ACTIVE)) {
+        ret = -EINPROGRESS;
+        goto out;
+    }
+
+    if (s->stctl != 0) {
+        ret = -EBUSY;
+        goto out;
+    }
+
+    /* Cancel the current operation. */
+    s->fctl &= ~SCSW_FCTL_START_FUNC;
+    s->actl &= ~(SCSW_ACTL_RESUME_PEND|SCSW_ACTL_START_PEND|SCSW_ACTL_SUSP);
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    sch->orb = NULL;
+    s->dstat = 0;
+    s->cstat = 0;
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_csch(SubchDev *sch)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    /* Trigger the clear function. */
+    s->fctl = SCSW_FCTL_CLEAR_FUNC;
+    s->actl = SCSW_ACTL_CLEAR_PEND;
+
+    do_subchannel_work(sch);
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_hsch(SubchDev *sch)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    if ((s->stctl == SCSW_STCTL_STATUS_PEND) ||
+        (s->stctl & (SCSW_STCTL_PRIMARY |
+                     SCSW_STCTL_SECONDARY |
+                     SCSW_STCTL_ALERT))) {
+        ret = -EINPROGRESS;
+        goto out;
+    }
+
+    if (s->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+        ret = -EBUSY;
+        goto out;
+    }
+
+    /* Trigger the halt function. */
+    s->fctl |= SCSW_FCTL_HALT_FUNC;
+    s->fctl &= ~SCSW_FCTL_START_FUNC;
+    if ((s->actl == (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) &&
+        (s->stctl == SCSW_STCTL_INTERMEDIATE)) {
+        s->stctl &= ~SCSW_STCTL_STATUS_PEND;
+    }
+    s->actl |= SCSW_ACTL_HALT_PEND;
+
+    do_subchannel_work(sch);
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+static void css_update_chnmon(SubchDev *sch)
+{
+    if (!sch->curr_status.pmcw.mme) {
+        /* Not active. */
+        return;
+    }
+    if (sch->curr_status.pmcw.mbfc) {
+        /* Format 1, per-subchannel area. */
+        struct cmbe *cmbe;
+
+        cmbe = qemu_get_ram_ptr(sch->curr_status.mba);
+        if (cmbe) {
+            cmbe->ssch_rsch_count++;
+        }
+    } else {
+        /* Format 0, global area. */
+        struct cmb *cmb;
+        uint32_t offset;
+
+        offset = sch->curr_status.pmcw.mbi << 5;
+        cmb = qemu_get_ram_ptr(channel_subsys->chnmon_area + offset);
+        if (cmb) {
+            cmb->ssch_rsch_count++;
+        }
+    }
+}
+
+int css_do_ssch(SubchDev *sch, ORB *orb)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    if (s->stctl & SCSW_STCTL_STATUS_PEND) {
+        ret = -EINPROGRESS;
+        goto out;
+    }
+
+    if (s->fctl & (SCSW_FCTL_START_FUNC |
+                   SCSW_FCTL_HALT_FUNC |
+                   SCSW_FCTL_CLEAR_FUNC)) {
+        ret = -EBUSY;
+        goto out;
+    }
+
+    /* If monitoring is active, update counter. */
+    if (channel_subsys->chnmon_active) {
+        css_update_chnmon(sch);
+        css_inject_io_interrupt(sch, CSS_DO_SSCH_SIMPLE);
+    }
+    sch->orb = orb;
+    sch->channel_prog = qemu_get_ram_ptr(orb->cpa);
+    /* Trigger the start function. */
+    s->fctl |= SCSW_FCTL_START_FUNC;
+    s->actl |= SCSW_ACTL_START_PEND;
+    s->pno = 0;
+
+    do_subchannel_work(sch);
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_tsch(SubchDev *sch, uint32_t addr)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    uint8_t stctl;
+    uint8_t fctl;
+    uint8_t actl;
+    IRB irb;
+    int ret;
+    int i;
+    uint32_t offset = 0;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    stctl = s->stctl;
+    fctl = s->fctl;
+    actl = s->actl;
+
+    /* Prepare the irb for the guest. */
+    memset(&irb, 0, sizeof(IRB));
+
+    /* Copy scsw from current status. */
+    memcpy(&irb.scsw, s, sizeof(SCSW));
+    if (stctl & SCSW_STCTL_STATUS_PEND) {
+        if (s->cstat & (SCSW_CSTAT_DATA_CHECK |
+                        SCSW_CSTAT_CHN_CTRL_CHK |
+                        SCSW_CSTAT_INTF_CTRL_CHK)) {
+            irb.scsw.eswf = 1;
+            irb.esw[0] = 0x04804000;
+        } else {
+            irb.esw[0] = 0x00800000;
+        }
+        /* If a unit check is pending, copy sense data. */
+        if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && p->csense) {
+            irb.scsw.eswf = 1;
+            irb.scsw.ectl = 1;
+            memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data));
+            irb.esw[1] = 0x02000000 | (sizeof(sch->sense_data) << 8);
+        }
+    }
+    /* Store the irb to the guest. */
+    css_write_phys_scsw(addr + offset, &irb.scsw);
+    offset += sizeof(SCSW);
+    for (i = 0; i < 5; i++) {
+        stl_phys(addr + offset, irb.esw[i]);
+        offset += sizeof(irb.esw[i]);
+    }
+    for (i = 0; i < 8; i++) {
+        stl_phys(addr + offset, irb.ecw[i]);
+        offset += sizeof(irb.ecw[i]);
+    }
+    for (i = 0; i < 8; i++) {
+        stl_phys(addr + offset, irb.emw[i]);
+        offset += sizeof(irb.emw[i]);
+    }
+
+    /* Clear conditions on subchannel, if applicable. */
+    if (stctl & SCSW_STCTL_STATUS_PEND) {
+        s->stctl = 0;
+        if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) ||
+            ((fctl & SCSW_FCTL_HALT_FUNC) &&
+             (actl & SCSW_ACTL_SUSP))) {
+            s->fctl = 0;
+        }
+        if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) {
+            s->pno = 0;
+            s->actl &= ~(SCSW_ACTL_RESUME_PEND |
+                         SCSW_ACTL_START_PEND |
+                         SCSW_ACTL_HALT_PEND |
+                         SCSW_ACTL_CLEAR_PEND |
+                         SCSW_ACTL_SUSP);
+        } else {
+            if ((actl & SCSW_ACTL_SUSP) &&
+                (fctl & SCSW_FCTL_START_FUNC)) {
+                s->pno = 0;
+                if (fctl & SCSW_FCTL_HALT_FUNC) {
+                    s->actl &= ~(SCSW_ACTL_RESUME_PEND |
+                                 SCSW_ACTL_START_PEND |
+                                 SCSW_ACTL_HALT_PEND |
+                                 SCSW_ACTL_CLEAR_PEND |
+                                 SCSW_ACTL_SUSP);
+                } else {
+                    s->actl &= ~SCSW_ACTL_RESUME_PEND;
+                }
+            }
+        }
+        /* Clear pending sense data. */
+        if (p->csense) {
+            memset(sch->sense_data, 0 , sizeof(sch->sense_data));
+        }
+    }
+
+    ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0);
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_stcrw(uint32_t addr)
+{
+    CrwContainer *crw_cont;
+    int ret;
+
+    crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws);
+    if (crw_cont) {
+        QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+        stl_phys(addr, *(uint32_t *)&crw_cont->crw);
+        g_free(crw_cont);
+        ret = 0;
+    } else {
+        /* List was empty, turn crw machine checks on again. */
+        stl_phys(addr, 0);
+        channel_subsys->do_crw_mchk = true;
+        ret = 1;
+    }
+
+    return ret;
+}
+
+int css_do_tpi(uint32_t addr, int lowcore)
+{
+    /* No pending interrupts for !KVM. */
+    return 0;
+ }
+
+int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid,
+                         int rfmt, void *buf)
+{
+    int i, desc_size;
+    uint32_t words[8];
+    CssImage *css;
+
+    if (m && !cssid) {
+        css = channel_subsys->css[channel_subsys->default_cssid];
+    } else {
+        css = channel_subsys->css[cssid];
+    }
+    if (!css) {
+        return 0;
+    }
+    desc_size = 0;
+    for (i = f_chpid; i <= l_chpid; i++) {
+        if (css->chpids[i].in_use) {
+            if (rfmt == 0) {
+                words[0] = 0x80000000 | (css->chpids[i].type << 8) | i;
+                words[1] = 0;
+                memcpy(buf + desc_size, words, 8);
+                desc_size += 8;
+            } else if (rfmt == 1) {
+                words[0] = 0x80000000 | (css->chpids[i].type << 8) | i;
+                words[1] = 0;
+                words[2] = 0;
+                words[3] = 0;
+                words[4] = 0;
+                words[5] = 0;
+                words[6] = 0;
+                words[7] = 0;
+                memcpy(buf + desc_size, words, 32);
+                desc_size += 32;
+            }
+        }
+    }
+    return desc_size;
+}
+
+void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
+{
+    /* dct is currently ignored (not really meaningful for our devices) */
+    /* TODO: Don't ignore mbk. */
+    /* TODO: Will need serialization with ssch when we are multithreaded. */
+    if (update && !channel_subsys->chnmon_active) {
+        /* Enable measuring. */
+        channel_subsys->chnmon_area = mbo;
+        channel_subsys->chnmon_active = true;
+    }
+    if (!update && channel_subsys->chnmon_active) {
+        /* Disable measuring. */
+        channel_subsys->chnmon_area = 0;
+        channel_subsys->chnmon_active = false;
+    }
+}
+
+int css_do_rsch(SubchDev *sch)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    int ret;
+
+    qemu_mutex_lock(&sch->mutex);
+
+    if (!p->dnv || !p->ena) {
+        ret = -ENODEV;
+        goto out;
+    }
+
+    if (s->stctl & SCSW_STCTL_STATUS_PEND) {
+        ret = -EINPROGRESS;
+        goto out;
+    }
+
+    if ((s->fctl != SCSW_FCTL_START_FUNC) ||
+        (s->actl & SCSW_ACTL_RESUME_PEND) ||
+        (!(s->actl & SCSW_ACTL_SUSP))) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    /* If monitoring is active, update counter. */
+    if (channel_subsys->chnmon_active) {
+        css_update_chnmon(sch);
+    }
+
+    s->actl |= SCSW_ACTL_RESUME_PEND;
+    do_subchannel_work(sch);
+    ret = 0;
+
+out:
+    qemu_mutex_unlock(&sch->mutex);
+    return ret;
+}
+
+int css_do_rchp(uint8_t cssid, uint8_t chpid)
+{
+    if (cssid > channel_subsys->max_cssid) {
+        return -EINVAL;
+    }
+
+    if (!channel_subsys->css[cssid]->chpids[chpid].in_use) {
+        return -ENODEV;
+    }
+
+    /* We don't really use a channel path, so we're done here. */
+    css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, channel_subsys->max_cssid > 0 ? 1 : 0,
+                  chpid);
+    if (channel_subsys->max_cssid > 0) {
+        css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, cssid << 8);
+    }
+    return 0;
+}
+
+bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    SubchSet *set;
+
+    if (cssid > MAX_CSSID || ssid > MAX_SSID || !channel_subsys->css[cssid] ||
+        !channel_subsys->css[cssid]->sch_set[ssid]) {
+        return true;
+    }
+    set = channel_subsys->css[cssid]->sch_set[ssid];
+    return schid > find_last_bit(set->schids_used,
+                                 (MAX_SCHID + 1) / sizeof(unsigned long));
+}
+
 static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
 {
     CssImage *css;
@@ -485,15 +1105,124 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devn
     }
 }
 
-
 bool css_present(uint8_t cssid)
 {
     return (channel_subsys->css[cssid] != NULL);
 }
 
+void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
+{
+    CrwContainer *crw_cont;
+
+    /* TODO: Maybe use a static crw pool? */
+    crw_cont = g_try_malloc0(sizeof(CrwContainer));
+    if (!crw_cont) {
+        channel_subsys->crws_lost = true;
+        return;
+    }
+    crw_cont->crw.rsc = rsc;
+    crw_cont->crw.erc = erc;
+    crw_cont->crw.c = chain;
+    crw_cont->crw.rsid = rsid;
+    crw_cont->crw.r = channel_subsys->crws_lost ? 1 : 0;
+    channel_subsys->crws_lost = false;
+
+    QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling);
+
+    if (channel_subsys->do_crw_mchk) {
+        channel_subsys->do_crw_mchk = false;
+        /* Inject crw pending machine check. */
+        if (!kvm_enabled()) {
+            S390CPU *cpu = s390_cpu_addr2state(0);
+            cpu_inject_crw_mchk(&cpu->env);
+        }
+    }
+}
+
+void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                           int hotplugged, int add)
+{
+    uint8_t guest_cssid;
+    bool chain_crw;
+
+    if (add && !hotplugged) {
+        return;
+    }
+    if (channel_subsys->max_cssid == 0) {
+        /* Default cssid shows up as 0. */
+        guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid;
+    } else {
+        /* Show real cssid to the guest. */
+        guest_cssid = cssid;
+    }
+    /*
+     * Only notify for higher subchannel sets/channel subsystems if the
+     * guest has enabled it.
+     */
+    if ((ssid > channel_subsys->max_ssid) ||
+        (guest_cssid > channel_subsys->max_cssid) ||
+        ((channel_subsys->max_cssid == 0) &&
+         (cssid != channel_subsys->default_cssid))) {
+        return;
+    }
+    chain_crw = (channel_subsys->max_ssid > 0) || (channel_subsys->max_cssid > 0);
+    css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid);
+    if (chain_crw) {
+        css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0,
+                      (guest_cssid << 8) | (ssid << 4));
+    }
+}
+
+void css_generate_chp_crws(uint8_t cssid, uint8_t chpid)
+{
+    /* TODO */
+}
+
+void css_inject_io(uint8_t cssid, uint8_t ssid, uint16_t schid, uint8_t isc,
+                   uint32_t intparm, int unsolicited)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+
+    if (unsolicited) {
+        SubchDev *sch = css_find_subch(1, cssid, ssid, schid);
+        /*
+         * If the subchannel is not currently status pending, make it pending
+         * with alert status.
+         */
+        if (sch && !(sch->curr_status.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
+            sch->curr_status.scsw.stctl =
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            /* Inject an I/O interrupt. */
+            cpu_inject_io(&cpu->env,
+                          (channel_subsys->max_cssid > 0) ? sch->cssid : 0,
+                          sch->ssid, sch->schid, sch->curr_status.pmcw.isc,
+                          sch->curr_status.pmcw.intparm);
+        }
+    } else {
+        cpu_inject_io(&cpu->env, (channel_subsys->max_cssid > 0) ? cssid : 0, ssid,
+                      schid, isc, intparm);
+    }
+}
+
+int css_enable_mcsse(void)
+{
+    channel_subsys->max_cssid = MAX_CSSID;
+    return 0;
+}
+
+int css_enable_mss(void)
+{
+    channel_subsys->max_ssid = MAX_SSID;
+    return 0;
+}
+
 static void css_init(void)
 {
     channel_subsys = g_malloc0(sizeof(*channel_subsys));
+    QTAILQ_INIT(&channel_subsys->pending_crws);
+    channel_subsys->do_crw_mchk = true;
+    channel_subsys->crws_lost = false;
+    channel_subsys->chnmon_active = false;
 }
 machine_init(css_init);
 
@@ -531,5 +1260,21 @@ void css_reset_sch(SubchDev *sch)
 
 void css_reset(void)
 {
-    /* Nothing for now. */
+    CrwContainer *crw_cont;
+
+    /* Clean up monitoring. */
+    channel_subsys->chnmon_active = false;
+    channel_subsys->chnmon_area = 0;
+
+    /* Clear pending CRWs. */
+    while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) {
+        QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+        g_free(crw_cont);
+    }
+    channel_subsys->do_crw_mchk = true;
+    channel_subsys->crws_lost = false;
+
+    /* Reset maximum ids. */
+    channel_subsys->max_cssid = 0;
+    channel_subsys->max_ssid = 0;
 }
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index 6876633..b71f6a2 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -34,6 +34,31 @@ typedef struct SenseId {
     uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
 } QEMU_PACKED SenseId;
 
+/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */
+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];
+};
+
+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];
+};
+
 struct SubchDev {
     /* channel-subsystem related things: */
     uint8_t cssid;
@@ -59,5 +84,6 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devn
 void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type);
 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);
 
 #endif
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index c072e1d..6830952 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -47,6 +47,11 @@
 #define MMU_USER_IDX 1
 
 #define MAX_EXT_QUEUE 16
+#define MAX_IO_QUEUE 16
+#define MAX_MCHK_QUEUE 16
+
+#define PSW_MCHK_MASK 0x0004000000000000
+#define PSW_IO_MASK 0x0200000000000000
 
 typedef struct PSW {
     uint64_t mask;
@@ -59,6 +64,17 @@ typedef struct ExtQueue {
     uint32_t param64;
 } ExtQueue;
 
+typedef struct IOQueue {
+    uint16_t id;
+    uint16_t nr;
+    uint32_t parm;
+    uint32_t word;
+} IOQueue;
+
+typedef struct MchkQueue {
+    uint16_t type;
+} MchkQueue;
+
 typedef struct CPUS390XState {
     uint64_t regs[16];	/* GP registers */
 
@@ -88,8 +104,12 @@ typedef struct CPUS390XState {
 
     int pending_int;
     ExtQueue ext_queue[MAX_EXT_QUEUE];
+    IOQueue io_queue[MAX_IO_QUEUE][8];
+    MchkQueue mchk_queue[MAX_MCHK_QUEUE];
 
     int ext_index;
+    int io_index[8];
+    int mchk_index;
 
     CPU_COMMON
 
@@ -103,6 +123,8 @@ typedef struct CPUS390XState {
     QEMUTimer *tod_timer;
 
     QEMUTimer *cpu_timer;
+
+    void *chsc_page;
 } CPUS390XState;
 
 #include "cpu-qom.h"
@@ -278,6 +300,7 @@ void s390x_translate_init(void);
 int cpu_s390x_exec(CPUS390XState *s);
 void cpu_s390x_close(CPUS390XState *s);
 void do_interrupt (CPUS390XState *env);
+void program_interrupt(CPUS390XState *env, uint32_t code, int ilc);
 
 /* you can call this signal handler from your SIGBUS and SIGSEGV
    signal handlers to inform the virtual CPU of exceptions. non zero
@@ -346,6 +369,23 @@ typedef struct ORB ORB;
 #ifndef CONFIG_USER_ONLY
 SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid);
 void css_conditional_io_interrupt(SubchDev *sch);
+int css_do_stsch(SubchDev *sch, uint32_t addr);
+bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid);
+int css_do_msch(SubchDev *sch, SCHIB *schib);
+int css_do_xsch(SubchDev *sch);
+int css_do_csch(SubchDev *sch);
+int css_do_hsch(SubchDev *sch);
+int css_do_ssch(SubchDev *sch, ORB *orb);
+int css_do_tsch(SubchDev *sch, uint32_t addr);
+int css_do_stcrw(uint32_t addr);
+int css_do_tpi(uint32_t addr, int lowcore);
+int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid,
+                         int rfmt, void *buf);
+void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo);
+int css_enable_mcsse(void);
+int css_enable_mss(void);
+int css_do_rsch(SubchDev *sch);
+int css_do_rchp(uint8_t cssid, uint8_t chpid);
 bool css_present(uint8_t cssid);
 #else
 static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
@@ -356,6 +396,70 @@ static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
 static inline void css_conditional_io_interrupt(SubchDev *sch)
 {
 }
+static inline int css_do_stsch(SubchDev *sch, uint32_t addr)
+{
+    return -ENODEV;
+}
+static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return true;
+}
+static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
+{
+    return -ENODEV;
+}
+static inline int css_do_xsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_csch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_hsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_ssch(SubchDev *sch, ORB *orb)
+{
+    return -ENODEV;
+}
+static inline int css_do_tsch(SubchDev *sch, uint32_t addr)
+{
+    return -ENODEV;
+}
+static inline int css_do_stcrw(uint32_t addr)
+{
+    return 1;
+}
+static inline int css_do_tpi(uint32_t addr, int lowcore)
+{
+    return 0;
+}
+static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
+                                       int rfmt, uint8_t l_chpid, void *buf)
+{
+    return 0;
+}
+static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
+{
+}
+static inline int css_enable_mss(void)
+{
+    return -EINVAL;
+}
+static inline int css_do_rsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
+{
+    return -ENODEV;
+}
+static inline bool css_present(uint8_t cssid)
+{
+    return false;
+}
 #endif
 
 static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
@@ -383,12 +487,16 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
 #define EXCP_EXT 1 /* external interrupt */
 #define EXCP_SVC 2 /* supervisor call (syscall) */
 #define EXCP_PGM 3 /* program interruption */
+#define EXCP_IO  7 /* I/O interrupt */
+#define EXCP_MCHK 8 /* machine check */
 
 #endif /* CONFIG_USER_ONLY */
 
 #define INTERRUPT_EXT        (1 << 0)
 #define INTERRUPT_TOD        (1 << 1)
 #define INTERRUPT_CPUTIMER   (1 << 2)
+#define INTERRUPT_IO         (1 << 3)
+#define INTERRUPT_MCHK       (1 << 4)
 
 /* Program Status Word.  */
 #define S390_PSWM_REGNUM 0
@@ -1007,6 +1115,44 @@ static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t pa
     cpu_interrupt(env, CPU_INTERRUPT_HARD);
 }
 
+static inline void cpu_inject_io(CPUS390XState *env, uint8_t cssid,
+                                 uint8_t ssid, uint16_t schid, uint8_t isc,
+                                 uint32_t intparm)
+{
+    if (env->io_index[isc] == MAX_IO_QUEUE - 1) {
+        /* ugh - can't queue anymore. Let's drop. */
+        return;
+    }
+
+    env->io_index[isc]++;
+    assert(env->io_index[isc] < MAX_IO_QUEUE);
+
+    env->io_queue[env->io_index[isc]][isc].id = (cssid != 0) ?
+        (cssid << 8) | (1 << 3) | (ssid << 2) | 1 : (ssid << 2) | 1;
+    env->io_queue[env->io_index[isc]][isc].nr = schid;
+    env->io_queue[env->io_index[isc]][isc].parm = intparm;
+    env->io_queue[env->io_index[isc]][isc].word = (0x80 >> isc) << 24;
+
+    env->pending_int |= INTERRUPT_IO;
+    cpu_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+static inline void cpu_inject_crw_mchk(CPUS390XState *env)
+{
+    if (env->mchk_index == MAX_MCHK_QUEUE - 1) {
+        /* ugh - can't queue anymore. Let's drop. */
+        return;
+    }
+
+    env->mchk_index++;
+    assert(env->mchk_index < MAX_MCHK_QUEUE);
+
+    env->mchk_queue[env->mchk_index].type = 1;
+
+    env->pending_int |= INTERRUPT_MCHK;
+    cpu_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
 static inline bool cpu_has_work(CPUS390XState *env)
 {
     return (env->interrupt_request & CPU_INTERRUPT_HARD) &&
@@ -1020,6 +1166,11 @@ static inline void cpu_pc_from_tb(CPUS390XState *env, TranslationBlock* tb)
 
 int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
                       void *pmcw);
+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_inject_io(uint8_t cssid, uint8_t ssid, uint16_t schid, uint8_t isc,
+                   uint32_t intparm, int unsolicited);
 #ifdef CONFIG_KVM
 int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
                          uint16_t devno, void *data, int hotplugged, int add,
@@ -1068,7 +1219,7 @@ static inline void s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
     ret = kvm_s390_sch_hotplug(cssid, ssid, schid, devno, data, hotplugged,
                                add, virtual);
     if (ret == -EOPNOTSUPP) {
-        fprintf(stderr, "Hotplugging subchannels not supported\n");
+        css_generate_sch_crws(cssid, ssid, schid, hotplugged, add);
     }
 }
 
@@ -1079,7 +1230,7 @@ static inline void s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type,
 
     ret = kvm_s390_chp_hotplug(cssid, chpid, type, add, virtual);
     if (ret == -EOPNOTSUPP) {
-        fprintf(stderr, "Hotplugging chpids not supported\n");
+        css_generate_chp_crws(cssid, chpid);
     }
 }
 
@@ -1093,7 +1244,7 @@ static inline void s390_io_interrupt(uint8_t cssid, uint8_t ssid,
     ret = kvm_s390_io_interrupt(cssid, ssid, schid, scsw, pmcw, sense,
                                 unsolicited, func);
     if (ret == -EOPNOTSUPP) {
-        fprintf(stderr, "Injecting I/O interrupts not supported\n");
+        css_inject_io(cssid, ssid, schid, isc, intparm, unsolicited);
     }
 }
 
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index d0a1180..d759adf 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -571,12 +571,139 @@ static void do_ext_interrupt(CPUS390XState *env)
     load_psw(env, mask, addr);
 }
 
+static void do_io_interrupt(CPUS390XState *env)
+{
+    uint64_t mask, addr;
+    LowCore *lowcore;
+    target_phys_addr_t len = TARGET_PAGE_SIZE;
+    IOQueue *q;
+    uint8_t isc;
+    int disable = 1;
+    int found = 0;
+
+    if (!(env->psw.mask & PSW_MASK_IO)) {
+        cpu_abort(env, "I/O int w/o I/O mask\n");
+    }
+
+
+    for (isc = 0; isc < 8; isc++) {
+        if (env->io_index[isc] < 0) {
+            continue;
+        }
+        if (env->io_index[isc] > MAX_IO_QUEUE) {
+            cpu_abort(env, "I/O queue overrun for isc %d: %d\n",
+                      isc, env->io_index[isc]);
+        }
+
+        q = &env->io_queue[env->io_index[isc]][isc];
+        if (!(env->cregs[6] & q->word)) {
+            disable = 0;
+            continue;
+        }
+        found = 1;
+        lowcore = cpu_physical_memory_map(env->psa, &len, 1);
+
+        lowcore->subchannel_id = cpu_to_be16(q->id);
+        lowcore->subchannel_nr = cpu_to_be16(q->nr);
+        lowcore->io_int_parm = cpu_to_be32(q->parm);
+        lowcore->io_int_word = cpu_to_be32(q->word);
+        lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+        lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
+        mask = be64_to_cpu(lowcore->io_new_psw.mask);
+        addr = be64_to_cpu(lowcore->io_new_psw.addr);
+
+        cpu_physical_memory_unmap(lowcore, len, 1, len);
+
+        env->io_index[isc]--;
+        if (env->io_index >= 0) {
+            disable = 0;
+        }
+        break;
+    }
+
+    if (disable) {
+        env->pending_int &= ~INTERRUPT_IO;
+    }
+    if (found) {
+        DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
+                env->psw.mask, env->psw.addr);
+
+        load_psw(env, mask, addr);
+    }
+}
+
+static void do_mchk_interrupt(CPUS390XState *env)
+{
+    uint64_t mask, addr;
+    LowCore *lowcore;
+    target_phys_addr_t len = TARGET_PAGE_SIZE;
+    MchkQueue *q;
+    int i;
+
+    if (!(env->psw.mask & PSW_MASK_MCHECK)) {
+        cpu_abort(env, "Machine check w/o mchk mask\n");
+    }
+
+    if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) {
+        cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index);
+    }
+
+    q = &env->mchk_queue[env->mchk_index];
+
+    if (q->type != 1) {
+        /* Don't know how to handle this... */
+        cpu_abort(env, "Unknown machine check type %d\n", q->type);
+    }
+    if (!(env->cregs[14] & (1 << 28))) {
+        /* CRW machine checks disabled */
+        return;
+    }
+
+    lowcore = cpu_physical_memory_map(env->psa, &len, 1);
+
+    for (i = 0; i < 16; i++) {
+        lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll);
+        lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
+        lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
+        lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
+    }
+    lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
+    lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
+    /* TODO: some clock/timer related stuff missing here */
+
+    lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
+    lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
+    lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+    lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
+    mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
+    addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
+
+    cpu_physical_memory_unmap(lowcore, len, 1, len);
+
+    env->mchk_index--;
+    if (env->mchk_index == -1) {
+        env->pending_int &= ~INTERRUPT_MCHK;
+    }
+
+    DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
+            env->psw.mask, env->psw.addr);
+
+    load_psw(env, mask, addr);
+}
+
 void do_interrupt (CPUS390XState *env)
 {
     qemu_log("%s: %d at pc=%" PRIx64 "\n", __FUNCTION__, env->exception_index,
              env->psw.addr);
 
     s390_add_running_cpu(env);
+    /* handle machine checks */
+    if ((env->psw.mask & PSW_MASK_MCHECK) &&
+        (env->exception_index == -1)) {
+        if (env->pending_int & INTERRUPT_MCHK) {
+            env->exception_index = EXCP_MCHK;
+        }
+    }
     /* handle external interrupts */
     if ((env->psw.mask & PSW_MASK_EXT) &&
         env->exception_index == -1) {
@@ -595,6 +722,13 @@ void do_interrupt (CPUS390XState *env)
             env->pending_int &= ~INTERRUPT_TOD;
         }
     }
+    /* handle I/O interrupts */
+    if ((env->psw.mask & PSW_MASK_IO) &&
+        (env->exception_index == -1)) {
+        if (env->pending_int & INTERRUPT_IO) {
+            env->exception_index = EXCP_IO;
+        }
+    }
 
     switch (env->exception_index) {
     case EXCP_PGM:
@@ -606,6 +740,12 @@ void do_interrupt (CPUS390XState *env)
     case EXCP_EXT:
         do_ext_interrupt(env);
         break;
+    case EXCP_IO:
+        do_io_interrupt(env);
+        break;
+    case EXCP_MCHK:
+        do_mchk_interrupt(env);
+        break;
     }
     env->exception_index = -1;
 
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
index 8f358d5..443eda1 100644
--- a/target-s390x/ioinst.c
+++ b/target-s390x/ioinst.c
@@ -16,6 +16,26 @@
 #include "cpu.h"
 #include "ioinst.h"
 
+#ifdef DEBUG_IOINST
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* Special handling for the prefix page. */
+static void *s390_get_address(CPUS390XState *env, ram_addr_t guest_addr)
+{
+    if (guest_addr < 8192) {
+        guest_addr += env->psa;
+    } else if ((env->psa <= guest_addr) && (guest_addr < env->psa + 8192)) {
+        guest_addr -= env->psa;
+    }
+
+    return qemu_get_ram_ptr(guest_addr);
+}
+
 int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
                                  int *schid)
 {
@@ -36,3 +56,679 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
     *schid = value & 0x0000ffff;
     return 0;
 }
+
+int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: xsch (%x.%x.%04x)\n", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_xsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: csch (%x.%x.%04x)\n", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_csch(sch);
+    }
+    if (ret == -ENODEV) {
+        cc = 3;
+    } else {
+        cc = 0;
+    }
+    return cc;
+}
+
+int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: hsch (%x.%x.%04x)\n", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_hsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+static int ioinst_schib_valid(SCHIB *schib)
+{
+    if ((schib->pmcw.zeroes0 & 0x3) != 0) {
+        return 0;
+    }
+    if ((schib->pmcw.zeroes1 != 0) || (schib->pmcw.zeroes2 != 0)) {
+        return 0;
+    }
+    /* Disallow extended measurements for now. */
+    if (schib->pmcw.xmwme) {
+        return 0;
+    }
+    return 1;
+}
+
+int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    SCHIB *schib;
+    uint32_t addr;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: msch (%x.%x.%04x)\n", cssid, ssid, schid);
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    schib = s390_get_address(env, addr);
+    if (!schib) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    if (!ioinst_schib_valid(schib)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_msch(sch, schib);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+static int ioinst_orb_valid(ORB *orb)
+{
+    if (orb->zero0 != 0) {
+        return 0;
+    }
+    if (orb->zero1 != 0) {
+        return 0;
+    }
+    if ((orb->cpa & 0x80000000) != 0) {
+        return 0;
+    }
+    return 1;
+}
+
+int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    ORB *orb;
+    uint32_t addr;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: ssch (%x.%x.%04x)\n", cssid, ssid, schid);
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    orb = s390_get_address(env, addr);
+    if (!orb) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    if (!ioinst_orb_valid(orb)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_ssch(sch, orb);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
+{
+    CRW *crw;
+    uint32_t addr;
+    int cc;
+
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    crw = s390_get_address(env, addr);
+    if (!crw) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    if (addr < 8192) {
+        addr += env->psa;
+    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+        addr -= env->psa;
+    }
+    cc = css_do_stcrw(addr);
+    /* 0 - crw stored, 1 - zeroes stored */
+    return cc;
+}
+
+int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    uint32_t addr;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: stsch (%x.%x.%04x)\n", cssid, ssid, schid);
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    if (addr < 8192) {
+        addr += env->psa;
+    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+        addr -= env->psa;
+    }
+    if (!qemu_get_ram_ptr(addr)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        css_do_stsch(sch, addr);
+        cc = 0;
+    } else {
+        if (css_schid_final(cssid, ssid, schid)) {
+            cc = 3; /* No more subchannels in this css/ss */
+        } else {
+            int i;
+
+            /* Store an empty schib. */
+            for (i = 0; i < sizeof(SCHIB); i++) {
+                stb_phys(addr + i, 0);
+            }
+            cc = 0;
+        }
+    }
+    return cc;
+}
+
+int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    IRB *irb;
+    uint32_t addr;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: tsch (%x.%x.%04x)\n", cssid, ssid, schid);
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    irb = s390_get_address(env, addr);
+    if (!irb) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        if (addr < 8192) {
+            addr += env->psa;
+        } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+            addr -= env->psa;
+        }
+        ret = css_do_tsch(sch, addr);
+        /* 0 - status pending, 1 - not status pending */
+        cc = ret;
+    } else {
+        cc = 3;
+    }
+    return cc;
+}
+
+typedef struct ChscReq {
+    uint16_t len;
+    uint16_t command;
+    uint32_t param0;
+    uint32_t param1;
+    uint32_t param2;
+} QEMU_PACKED ChscReq;
+
+typedef struct ChscResp {
+    uint16_t len;
+    uint16_t code;
+    uint32_t param;
+    char data[0];
+} QEMU_PACKED ChscResp;
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
+{
+    uint16_t resp_code;
+    int rfmt;
+    uint16_t cssid;
+    uint8_t f_chpid, l_chpid;
+    int desc_size;
+    int m;
+
+    rfmt = (req->param0 & 0x00000f00) >> 8;
+    if ((rfmt == 0) ||  (rfmt == 1)) {
+        rfmt = (req->param0 & 0x10000000) >> 28;
+    }
+    if ((req->len != 0x0010) || (req->param0 & 0xc000f000) ||
+        (req->param1 & 0xffffff00) || req->param2) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    if (req->param0 & 0x0f000000) {
+        resp_code = 0x0007;
+        goto out_err;
+    }
+    cssid = (req->param0 & 0x00ff0000) >> 16;
+    m = req->param0 & 0x20000000;
+    if (cssid != 0) {
+        if (!m || !css_present(cssid)) {
+            resp_code = 0x0008;
+            goto out_err;
+        }
+    }
+    f_chpid = req->param0 & 0x000000ff;
+    l_chpid = req->param1 & 0x000000ff;
+    if (l_chpid < f_chpid) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt, &res->data);
+    res->code = 0x0001;
+    res->len = 8 + desc_size;
+    res->param = rfmt;
+    return;
+
+  out_err:
+    res->code = resp_code;
+    res->len = 8;
+    res->param = rfmt;
+}
+
+static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
+{
+    uint8_t cssid;
+    uint16_t resp_code;
+    uint32_t general_chars[510];
+    uint32_t chsc_chars[508];
+
+    if (req->param0 & 0x000f0000) {
+        resp_code = 0x0007;
+        goto out_err;
+    }
+    cssid = (req->param0 & 0x0000ff00) >> 8;
+    if (cssid != 0) {
+        if (!(req->param0 & 0x20000000) || !css_present(cssid)) {
+            resp_code = 0x0008;
+            goto out_err;
+        }
+    }
+    if ((req->param0 & 0xdff000ff) || req->param1 || req->param2) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    res->code = 0x0001;
+    res->len = 4080;
+    res->param = 0;
+
+    memset(general_chars, 0, sizeof(general_chars));
+    memset(chsc_chars, 0, sizeof(chsc_chars));
+
+    general_chars[0] = 0x03000000;
+    general_chars[1] = 0x00059000;
+
+    chsc_chars[0] = 0x40000000;
+    chsc_chars[3] = 0x00040000;
+
+    memcpy(res->data, general_chars, sizeof(general_chars));
+    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
+    return;
+
+  out_err:
+    res->code = resp_code;
+    res->len = 8;
+    res->param = 0;
+}
+
+#define CHSC_SDA_OC_MCSSE 0x0
+#define CHSC_SDA_OC_MSS 0x2
+static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
+{
+    uint16_t resp_code = 0x0001;
+    uint16_t oc;
+    int ret;
+
+    if ((req->len != 0x0400) || (req->param0 & 0xf0ff0000)) {
+        resp_code = 0x0003;
+        goto out;
+    }
+
+    if (req->param0 & 0x0f000000) {
+        resp_code = 0x0007;
+        goto out;
+    }
+
+    oc = req->param0 & 0x0000ffff;
+    switch (oc) {
+    case CHSC_SDA_OC_MCSSE:
+        ret = css_enable_mcsse();
+        if (ret == -EINVAL) {
+            resp_code = 0x0101;
+            goto out;
+        }
+        break;
+    case CHSC_SDA_OC_MSS:
+        ret = css_enable_mss();
+        if (ret == -EINVAL) {
+            resp_code = 0x0101;
+            goto out;
+        }
+        break;
+    default:
+        resp_code = 0x0003;
+        goto out;
+    }
+
+out:
+    res->code = resp_code;
+    res->len = 8;
+    res->param = 0;
+}
+
+static void ioinst_handle_chsc_unimplemented(ChscResp *res)
+{
+    res->len = 8;
+    res->code = 0x0004;
+    res->param = 0;
+}
+
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
+{
+    ChscReq *req;
+    ChscResp *res;
+    uint64_t addr;
+    int reg;
+    int i;
+
+    dprintf("%s\n", "IOINST: CHSC");
+    reg = (ipb >> 20) & 0x00f;
+    addr = env->regs[reg];
+    req = s390_get_address(env, addr);
+    if (!req) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    if (!env->chsc_page) {
+        env->chsc_page = g_malloc0(TARGET_PAGE_SIZE);
+    } else {
+        memset(env->chsc_page, 0, TARGET_PAGE_SIZE);
+    }
+    res = env->chsc_page;
+    dprintf("IOINST: CHSC: command 0x%04x, len=0x%04x\n",
+            req->command, req->len);
+    switch (req->command) {
+    case CHSC_SCSC:
+        ioinst_handle_chsc_scsc(req, res);
+        break;
+    case CHSC_SCPD:
+        ioinst_handle_chsc_scpd(req, res);
+        break;
+    case CHSC_SDA:
+        ioinst_handle_chsc_sda(req, res);
+        break;
+    default:
+        ioinst_handle_chsc_unimplemented(res);
+        break;
+    }
+    if (addr < 8192) {
+        addr += env->psa;
+    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+        addr -= env->psa;
+    }
+    for (i = 0; i < res->len; i++) {
+        stb_phys(addr + req->len + i, *(uint8_t *)(res + i));
+    }
+    return 0;
+}
+
+int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
+{
+    uint32_t addr;
+    int lowcore;
+
+    dprintf("%s\n", "IOINST: tpi");
+    addr = ipb >> 28;
+    if (addr > 0) {
+        addr = env->regs[addr];
+    }
+    addr += (ipb & 0xfff0000) >> 16;
+    lowcore = addr ? 0 : 1;
+    if (addr < 8192) {
+        addr += env->psa;
+    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+        addr -= env->psa;
+    }
+    return css_do_tpi(addr, lowcore);
+}
+
+int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
+                       uint32_t ipb)
+{
+    uint8_t mbk;
+    int update;
+    int dct;
+
+    dprintf("%s\n", "IOINST: schm");
+
+    if (reg1 & 0x000000000ffffffc) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    mbk = (reg1 & 0x00000000f0000000) >> 28;
+    update = (reg1 & 0x0000000000000002) >> 1;
+    dct = reg1 & 0x0000000000000001;
+
+    if (update && (reg2 & 0x0000000000000fff)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    css_do_schm(mbk, update, dct, update ? reg2 : 0);
+
+    return 0;
+}
+
+int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    dprintf("IOINST: rsch (%x.%x.%04x)\n", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        ret = css_do_rsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EINVAL:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+
+}
+
+int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1)
+{
+    int cc;
+    uint8_t cssid;
+    uint8_t chpid;
+    int ret;
+
+    if (reg1 & 0xff00ff00) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    cssid = (reg1 >> 16) & 0xff;
+    chpid = reg1 & 0xff;
+    dprintf("IOINST: rchp (%x.%02x)\n", cssid, chpid);
+
+    ret = css_do_rchp(cssid, chpid);
+
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        /* Invalid channel subsystem. */
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    return cc;
+}
+
+int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1)
+{
+    /* We do not provide address limit checking, so let's suppress it. */
+    if (env->regs[1] & 0x000000008000ffff) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    return 0;
+}
diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
index c1377ca..4860d4c 100644
--- a/target-s390x/ioinst.h
+++ b/target-s390x/ioinst.h
@@ -11,7 +11,6 @@
 
 #ifndef IOINST_S390X_H
 #define IOINST_S390X_H
-
 /*
  * Channel I/O related definitions, as defined in the Principles
  * Of Operation (and taken from the Linux implementation).
@@ -168,6 +167,40 @@ typedef struct CCW1 {
 #define SCSW_CSTAT_INTF_CTRL_CHK 0x02
 #define SCSW_CSTAT_CHAIN_CHECK   0x01
 
+typedef struct CRW {
+    uint16_t zero0:1;
+    uint16_t s:1;
+    uint16_t r:1;
+    uint16_t c:1;
+    uint16_t rsc:4;
+    uint16_t a:1;
+    uint16_t zero1:1;
+    uint16_t erc:6;
+    uint16_t rsid;
+} CRW;
+
+#define CRW_ERC_INIT 0x02
+#define CRW_ERC_IPI  0x04
+
+#define CRW_RSC_SUBCH 0x3
+#define CRW_RSC_CHP   0x4
+
 int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
                                  int *schid);
+int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
+                       uint32_t ipb);
+int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1);
+
 #endif
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 9aab6a8..a3bf67a 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -31,6 +31,8 @@
 #include "kvm.h"
 #include "cpu.h"
 #include "device_tree.h"
+#include "trace.h"
+#include "ioinst.h"
 
 /* #define DEBUG_KVM */
 
@@ -44,9 +46,27 @@
 
 #define IPA0_DIAG                       0x8300
 #define IPA0_SIGP                       0xae00
-#define IPA0_PRIV                       0xb200
+#define IPA0_B2                         0xb200
+#define IPA0_B9                         0xb900
+#define IPA0_EB                         0xeb00
 
 #define PRIV_SCLP_CALL                  0x20
+#define PRIV_CSCH                       0x30
+#define PRIV_HSCH                       0x31
+#define PRIV_MSCH                       0x32
+#define PRIV_SSCH                       0x33
+#define PRIV_STSCH                      0x34
+#define PRIV_TSCH                       0x35
+#define PRIV_TPI                        0x36
+#define PRIV_SAL                        0x37
+#define PRIV_RSCH                       0x38
+#define PRIV_STCRW                      0x39
+#define PRIV_STCPS                      0x3a
+#define PRIV_RCHP                       0x3b
+#define PRIV_SCHM                       0x3c
+#define PRIV_CHSC                       0x5f
+#define PRIV_SIGA                       0x74
+#define PRIV_XSCH                       0x76
 #define DIAG_KVM_HYPERCALL              0x500
 #define DIAG_KVM_BREAKPOINT             0x501
 
@@ -284,10 +304,118 @@ static int kvm_sclp_service_call(CPUS390XState *env, struct kvm_run *run,
     return 0;
 }
 
-static int handle_priv(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1)
+static int kvm_handle_css_inst(CPUS390XState *env, struct kvm_run *run,
+                               uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
+{
+    int r = 0;
+    int no_cc = 0;
+
+    if (ipa0 != 0xb2) {
+        /* Not handled for now. */
+        return -1;
+    }
+    cpu_synchronize_state(env);
+    switch (ipa1) {
+    case PRIV_XSCH:
+        r = ioinst_handle_xsch(env, env->regs[1]);
+        break;
+    case PRIV_CSCH:
+        r = ioinst_handle_csch(env, env->regs[1]);
+        break;
+    case PRIV_HSCH:
+        r = ioinst_handle_hsch(env, env->regs[1]);
+        break;
+    case PRIV_MSCH:
+        r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb);
+        break;
+    case PRIV_SSCH:
+        r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb);
+        break;
+    case PRIV_STCRW:
+        r = ioinst_handle_stcrw(env, run->s390_sieic.ipb);
+        break;
+    case PRIV_STSCH:
+        r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb);
+        break;
+    case PRIV_TSCH:
+        r = ioinst_handle_tsch(env, env->regs[1], run->s390_sieic.ipb);
+        break;
+    case PRIV_CHSC:
+        r = ioinst_handle_chsc(env, run->s390_sieic.ipb);
+        break;
+    case PRIV_TPI:
+        r = ioinst_handle_tpi(env, run->s390_sieic.ipb);
+        break;
+    case PRIV_SCHM:
+        no_cc = 1;
+        r = ioinst_handle_schm(env, env->regs[1], env->regs[2],
+                               run->s390_sieic.ipb);
+        break;
+    case PRIV_RSCH:
+        r = ioinst_handle_rsch(env, env->regs[1]);
+        break;
+    case PRIV_RCHP:
+        r = ioinst_handle_rchp(env, env->regs[1]);
+        break;
+    case PRIV_STCPS:
+        /* We do not provide this instruction, it is suppressed. */
+        no_cc = 1;
+        r = 0;
+        break;
+    case PRIV_SAL:
+        no_cc = 1;
+        r = ioinst_handle_sal(env, env->regs[1]);
+        break;
+    default:
+        r = -1;
+        break;
+    }
+
+    if (r >= 0) {
+        if (!no_cc) {
+            setcc(env, r);
+        }
+        r = 0;
+    } else if (r < -1) {
+        r = 0;
+    }
+    return r;
+}
+
+static int is_ioinst(uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
+{
+    int ret = 0;
+
+    switch (ipa0) {
+    case 0xb2:
+        if (((ipa1 >= 0x30) && (ipa1 <= 0x3c)) ||
+            (ipa1 == 0x5f) ||
+            (ipa1 == 0x74) ||
+            (ipa1 == 0x76)) {
+            ret = 1;
+        }
+        break;
+    case 0xb9:
+        if (ipa1 == 0x9c) {
+            ret = 1;
+        }
+        break;
+    case 0xeb:
+        if (ipb == 0x8a) {
+            ret = 1;
+        }
+        break;
+    }
+
+    return ret;
+}
+
+static int handle_priv(CPUS390XState *env, struct kvm_run *run,
+                       uint8_t ipa0, uint8_t ipa1)
 {
     int r = 0;
     uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
+    uint8_t ipb = run->s390_sieic.ipb & 0xff;
 
     dprintf("KVM: PRIV: %d\n", ipa1);
     switch (ipa1) {
@@ -295,8 +423,16 @@ static int handle_priv(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1)
             r = kvm_sclp_service_call(env, run, ipbh0);
             break;
         default:
-            dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
-            r = -1;
+            if (is_ioinst(ipa0, ipa1, ipb)) {
+                r = kvm_handle_css_inst(env, run, ipa0, ipa1, ipb);
+                if (r == -1) {
+                    setcc(env, 3);
+                    r = 0;
+                }
+            } else {
+                dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
+                r = -1;
+            }
             break;
     }
 
@@ -434,15 +570,17 @@ static int handle_instruction(CPUS390XState *env, struct kvm_run *run)
 
     dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb);
     switch (ipa0) {
-        case IPA0_PRIV:
-            r = handle_priv(env, run, ipa1);
-            break;
-        case IPA0_DIAG:
-            r = handle_diag(env, run, ipb_code);
-            break;
-        case IPA0_SIGP:
-            r = handle_sigp(env, run, ipa1);
-            break;
+    case IPA0_B2:
+    case IPA0_B9:
+    case IPA0_EB:
+        r = handle_priv(env, run, ipa0 >> 8, ipa1);
+        break;
+    case IPA0_DIAG:
+        r = handle_diag(env, run, ipb_code);
+        break;
+    case IPA0_SIGP:
+        r = handle_sigp(env, run, ipa1);
+        break;
     }
 
     if (r < 0) {
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index abc35dd..f7d41cd 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -2338,18 +2338,11 @@ void HELPER(tr)(uint32_t len, uint64_t array, uint64_t trans)
     }
 }
 
-#ifndef CONFIG_USER_ONLY
-
-void HELPER(load_psw)(uint64_t mask, uint64_t addr)
-{
-    load_psw(env, mask, addr);
-    cpu_loop_exit(env);
-}
-
-static void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
+void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
 {
     qemu_log("program interrupt at %#" PRIx64 "\n", env->psw.addr);
 
+#ifndef CONFIG_USER_ONLY
     if (kvm_enabled()) {
 #ifdef CONFIG_KVM
         kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code);
@@ -2360,6 +2353,17 @@ static void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
         env->exception_index = EXCP_PGM;
         cpu_loop_exit(env);
     }
+#else
+    cpu_abort(env, "Program check %x\n", code);
+#endif
+}
+
+#ifndef CONFIG_USER_ONLY
+
+void HELPER(load_psw)(uint64_t mask, uint64_t addr)
+{
+    load_psw(env, mask, addr);
+    cpu_loop_exit(env);
 }
 
 /*
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
                   ` (3 preceding siblings ...)
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-19 16:24   ` Alexander Graf
  2012-09-20 14:27   ` Anthony Liguori
  4 siblings, 2 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

This patch enables using both virtio-xxx-s390 and virtio-xxx-ccw
by making the alias lookup code verify that a driver is actually
registered.

(Only included in order to allow testing of virtio-ccw; should be
replaced by cleaning up the virtio bus model.)

Not-signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 blockdev.c        |  6 +---
 hw/qdev-monitor.c | 85 +++++++++++++++++++++++++++++++++----------------------
 vl.c              |  6 +---
 3 files changed, 53 insertions(+), 44 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 7c83baa..a7c39b6 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -560,11 +560,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
     case IF_VIRTIO:
         /* add virtio block device */
         opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
-        if (arch_type == QEMU_ARCH_S390X) {
-            qemu_opt_set(opts, "driver", "virtio-blk-s390");
-        } else {
-            qemu_opt_set(opts, "driver", "virtio-blk-pci");
-        }
+        qemu_opt_set(opts, "driver", "virtio-blk");
         qemu_opt_set(opts, "drive", dinfo->id);
         if (devaddr)
             qemu_opt_set(opts, "addr", devaddr);
diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index 92b7c59..9245a1e 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -118,9 +118,53 @@ static int set_property(const char *name, const char *value, void *opaque)
     return 0;
 }
 
-static const char *find_typename_by_alias(const char *alias)
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+                                     const char *bus_typename)
+{
+    BusChild *kid;
+    BusState *child, *ret;
+    int match = 1;
+
+    if (name && (strcmp(bus->name, name) != 0)) {
+        match = 0;
+    }
+    if (bus_typename &&
+        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
+        match = 0;
+    }
+    if (match) {
+        return bus;
+    }
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        DeviceState *dev = kid->child;
+        QLIST_FOREACH(child, &dev->child_bus, sibling) {
+            ret = qbus_find_recursive(child, name, bus_typename);
+            if (ret) {
+                return ret;
+            }
+        }
+    }
+    return NULL;
+}
+
+static bool qdev_verify_bus(DeviceClass *dc)
+{
+    BusState *bus;
+
+    if (dc) {
+        bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
+        if (bus) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static const char *find_typename_by_alias(const char *alias, bool check_bus)
 {
     int i;
+    ObjectClass *oc;
 
     for (i = 0; qdev_alias_table[i].alias; i++) {
         if (qdev_alias_table[i].arch_mask &&
@@ -129,7 +173,10 @@ static const char *find_typename_by_alias(const char *alias)
         }
 
         if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
-            return qdev_alias_table[i].typename;
+            oc = object_class_by_name(qdev_alias_table[i].typename);
+            if (oc && (!check_bus || qdev_verify_bus(DEVICE_CLASS(oc)))) {
+                return qdev_alias_table[i].typename;
+            }
         }
     }
 
@@ -155,7 +202,7 @@ int qdev_device_help(QemuOpts *opts)
 
     klass = object_class_by_name(driver);
     if (!klass) {
-        const char *typename = find_typename_by_alias(driver);
+        const char *typename = find_typename_by_alias(driver, false);
 
         if (typename) {
             driver = typename;
@@ -283,36 +330,6 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem)
     return NULL;
 }
 
-static BusState *qbus_find_recursive(BusState *bus, const char *name,
-                                     const char *bus_typename)
-{
-    BusChild *kid;
-    BusState *child, *ret;
-    int match = 1;
-
-    if (name && (strcmp(bus->name, name) != 0)) {
-        match = 0;
-    }
-    if (bus_typename &&
-        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
-        match = 0;
-    }
-    if (match) {
-        return bus;
-    }
-
-    QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        DeviceState *dev = kid->child;
-        QLIST_FOREACH(child, &dev->child_bus, sibling) {
-            ret = qbus_find_recursive(child, name, bus_typename);
-            if (ret) {
-                return ret;
-            }
-        }
-    }
-    return NULL;
-}
-
 static BusState *qbus_find(const char *path)
 {
     DeviceState *dev;
@@ -417,7 +434,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
     /* find driver */
     obj = object_class_by_name(driver);
     if (!obj) {
-        const char *typename = find_typename_by_alias(driver);
+        const char *typename = find_typename_by_alias(driver, true);
 
         if (typename) {
             driver = typename;
diff --git a/vl.c b/vl.c
index 2b8cae6..788a536 100644
--- a/vl.c
+++ b/vl.c
@@ -2113,11 +2113,7 @@ static int virtcon_parse(const char *devname)
     }
 
     bus_opts = qemu_opts_create(device, NULL, 0, NULL);
-    if (arch_type == QEMU_ARCH_S390X) {
-        qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
-    } else {
-        qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
-    } 
+    qemu_opt_set(bus_opts, "driver", "virtio-serial");
 
     dev_opts = qemu_opts_create(device, NULL, 0, NULL);
     qemu_opt_set(dev_opts, "driver", "virtconsole");
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
@ 2012-09-19 16:12   ` Alexander Graf
  2012-09-20  7:39     ` Jens Freimann
  2012-09-20 14:24   ` Anthony Liguori
  2012-12-18  8:45   ` Paolo Bonzini
  2 siblings, 1 reply; 17+ messages in thread
From: Alexander Graf @ 2012-09-19 16:12 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Just a really quick glimpse. This patch is huge :).

On 04.09.2012, at 17:13, Cornelia Huck wrote:

> Add a new virtio transport that uses channel commands to perform
> virtio operations.
> 
> Add a new machine type s390-ccw that uses this virtio-ccw transport
> and make it the default machine for s390.
> 
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
> 
> Changes v1->v2:
> - update to virtio-ccw interface changes
> 
> ---
> hw/qdev-monitor.c      |   5 +
> hw/s390-virtio.c       | 277 ++++++++++++----
> hw/s390x/Makefile.objs |   1 +
> hw/s390x/css.c         |  45 +++
> hw/s390x/css.h         |   3 +
> hw/s390x/virtio-ccw.c  | 875 +++++++++++++++++++++++++++++++++++++++++++++++++
> hw/s390x/virtio-ccw.h  |  79 +++++
> vl.c                   |   1 +
> 8 files changed, 1215 insertions(+), 71 deletions(-)
> create mode 100644 hw/s390x/virtio-ccw.c
> create mode 100644 hw/s390x/virtio-ccw.h
> 
> diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
> index 33b7f79..92b7c59 100644
> --- a/hw/qdev-monitor.c
> +++ b/hw/qdev-monitor.c
> @@ -42,6 +42,11 @@ static const QDevAlias qdev_alias_table[] = {
>     { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
>     { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
>     { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
> +    { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
> +    { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
> +    { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
> +    { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
> +    { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },

How does this work? We want to default to virtio-xxx-pci on !s390, to virtio-xxx-s390 on the legacy machine and to virtio-xxx-ccw for the s390-ccw machine, right?

>     { "lsi53c895a", "lsi" },
>     { "ich9-ahci", "ahci" },
>     { }
> diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
> index 47eed35..2509291 100644
> --- a/hw/s390-virtio.c
> +++ b/hw/s390-virtio.c
> @@ -30,8 +30,11 @@
> #include "hw/sysbus.h"
> #include "kvm.h"
> #include "exec-memory.h"
> +#include "qemu-thread.h"
> 
> #include "hw/s390-virtio-bus.h"
> +#include "hw/s390x/css.h"
> +#include "hw/s390x/virtio-ccw.h"
> 
> //#define DEBUG_S390
> 
> @@ -46,6 +49,7 @@
> #define KVM_S390_VIRTIO_NOTIFY          0
> #define KVM_S390_VIRTIO_RESET           1
> #define KVM_S390_VIRTIO_SET_STATUS      2
> +#define KVM_S390_VIRTIO_CCW_NOTIFY      3
> 
> #define KERN_IMAGE_START                0x010000UL
> #define KERN_PARM_AREA                  0x010480UL
> @@ -62,6 +66,7 @@
> 
> static VirtIOS390Bus *s390_bus;
> static S390CPU **ipi_states;
> +VirtioCcwBus *ccw_bus;
> 
> S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
> {
> @@ -75,15 +80,21 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
> int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
> {
>     int r = 0, i;
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> 
>     dprintf("KVM hypercall: %ld\n", hypercall);
>     switch (hypercall) {
>     case KVM_S390_VIRTIO_NOTIFY:
>         if (mem > ram_size) {
> -            VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
> -                                                               mem, &i);
> -            if (dev) {
> -                virtio_queue_notify(dev->vdev, i);
> +            if (s390_bus) {
> +                VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
> +                                                                   mem, &i);
> +                if (dev) {
> +                    virtio_queue_notify(dev->vdev, i);
> +                } else {
> +                    r = -EINVAL;
> +                }
>             } else {
>                 r = -EINVAL;
>             }
> @@ -92,28 +103,49 @@ int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
>         }
>         break;
>     case KVM_S390_VIRTIO_RESET:
> -    {
> -        VirtIOS390Device *dev;
> -
> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
> -        virtio_reset(dev->vdev);
> -        stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
> -        s390_virtio_device_sync(dev);
> -        s390_virtio_reset_idx(dev);
> +        if (s390_bus) {
> +            VirtIOS390Device *dev;
> +
> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
> +            virtio_reset(dev->vdev);
> +            stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
> +            s390_virtio_device_sync(dev);
> +            s390_virtio_reset_idx(dev);
> +        } else {
> +            r = -EINVAL;
> +        }
>         break;
> -    }
>     case KVM_S390_VIRTIO_SET_STATUS:
> -    {
> -        VirtIOS390Device *dev;
> +        if (s390_bus) {
> +            VirtIOS390Device *dev;
> 
> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
> -        if (dev) {
> -            s390_virtio_device_update_status(dev);
> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
> +            if (dev) {
> +                s390_virtio_device_update_status(dev);
> +            } else {
> +                r = -EINVAL;
> +            }
>         } else {
>             r = -EINVAL;
>         }
>         break;
> -    }
> +    case KVM_S390_VIRTIO_CCW_NOTIFY:
> +        if (ccw_bus) {
> +            if (ioinst_disassemble_sch_ident(env->regs[2], &m, &cssid, &ssid,
> +                                             &schid)) {
> +                r = -EINVAL;
> +            } else {
> +                sch = css_find_subch(m, cssid, ssid, schid);
> +                if (sch) {
> +                    virtio_queue_notify(virtio_ccw_get_vdev(sch), env->regs[3]);
> +                } else {
> +                    r = -EINVAL;
> +                }
> +            }
> +         } else {
> +             r = -EINVAL;
> +         }
> +         break;
>     default:
>         r = -EINVAL;
>         break;
> @@ -150,58 +182,12 @@ unsigned s390_del_running_cpu(CPUS390XState *env)
>     return s390_running_cpus;
> }
> 
> -/* PC hardware initialisation */
> -static void s390_init(ram_addr_t my_ram_size,
> -                      const char *boot_device,
> -                      const char *kernel_filename,
> -                      const char *kernel_cmdline,
> -                      const char *initrd_filename,
> -                      const char *cpu_model)
> +static CPUS390XState *s390_init_cpus(const char *cpu_model,
> +                                     uint8_t *storage_keys)
> {
>     CPUS390XState *env = NULL;
> -    MemoryRegion *sysmem = get_system_memory();
> -    MemoryRegion *ram = g_new(MemoryRegion, 1);
> -    ram_addr_t kernel_size = 0;
> -    ram_addr_t initrd_offset;
> -    ram_addr_t initrd_size = 0;
> -    int shift = 0;
> -    uint8_t *storage_keys;
> -    void *virtio_region;
> -    target_phys_addr_t virtio_region_len;
> -    target_phys_addr_t virtio_region_start;
>     int i;
> 
> -    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> -       guests > 64GB can be specified in 2MB steps etc. */
> -    while ((my_ram_size >> (20 + shift)) > 65535) {
> -        shift++;
> -    }
> -    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> -
> -    /* lets propagate the changed ram size into the global variable. */
> -    ram_size = my_ram_size;
> -
> -    /* get a BUS */
> -    s390_bus = s390_virtio_bus_init(&my_ram_size);
> -
> -    /* allocate RAM */
> -    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> -    vmstate_register_ram_global(ram);
> -    memory_region_add_subregion(sysmem, 0, ram);
> -
> -    /* clear virtio region */
> -    virtio_region_len = my_ram_size - ram_size;
> -    virtio_region_start = ram_size;
> -    virtio_region = cpu_physical_memory_map(virtio_region_start,
> -                                            &virtio_region_len, true);
> -    memset(virtio_region, 0, virtio_region_len);
> -    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
> -                              virtio_region_len);
> -
> -    /* allocate storage keys */
> -    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> -
> -    /* init CPUs */
>     if (cpu_model == NULL) {
>         cpu_model = "host";
>     }
> @@ -222,6 +208,17 @@ static void s390_init(ram_addr_t my_ram_size,
>         tmp_env->exception_index = EXCP_HLT;
>         tmp_env->storage_keys = storage_keys;
>     }
> +    return env;
> +}
> +
> +static void s390_set_up_kernel(CPUS390XState *env,
> +                               const char *kernel_filename,
> +                               const char *kernel_cmdline,
> +                               const char *initrd_filename)
> +{
> +    ram_addr_t kernel_size = 0;
> +    ram_addr_t initrd_offset;
> +    ram_addr_t initrd_size = 0;
> 
>     /* One CPU has to run */
>     s390_add_running_cpu(env);
> @@ -294,8 +291,13 @@ static void s390_init(ram_addr_t my_ram_size,
>                strlen(kernel_cmdline) + 1);
>     }
> 
> -    /* Create VirtIO network adapters */
> -    for(i = 0; i < nb_nics; i++) {
> +}
> +
> +static void s390_create_virtio_net(BusState *bus, const char *name)
> +{
> +    int i;
> +
> +    for (i = 0; i < nb_nics; i++) {
>         NICInfo *nd = &nd_table[i];
>         DeviceState *dev;
> 
> @@ -308,7 +310,7 @@ static void s390_init(ram_addr_t my_ram_size,
>             exit(1);
>         }
> 
> -        dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
> +        dev = qdev_create(bus, name);
>         qdev_set_nic_properties(dev, nd);
>         qdev_init_nofail(dev);
>     }
> @@ -329,6 +331,63 @@ static void s390_init(ram_addr_t my_ram_size,
>     }
> }
> 
> +/* PC hardware initialisation */
> +static void s390_init(ram_addr_t my_ram_size,
> +                      const char *boot_device,
> +                      const char *kernel_filename,
> +                      const char *kernel_cmdline,
> +                      const char *initrd_filename,
> +                      const char *cpu_model)
> +{
> +    CPUS390XState *env = NULL;
> +    MemoryRegion *sysmem = get_system_memory();
> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
> +    int shift = 0;
> +    uint8_t *storage_keys;
> +    void *virtio_region;
> +    target_phys_addr_t virtio_region_len;
> +    target_phys_addr_t virtio_region_start;
> +
> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> +       guests > 64GB can be specified in 2MB steps etc. */
> +    while ((my_ram_size >> (20 + shift)) > 65535) {
> +        shift++;
> +    }
> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> +
> +    /* lets propagate the changed ram size into the global variable. */
> +    ram_size = my_ram_size;
> +
> +    /* get a BUS */
> +    s390_bus = s390_virtio_bus_init(&my_ram_size);
> +
> +    /* allocate RAM */
> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> +    vmstate_register_ram_global(ram);
> +    memory_region_add_subregion(sysmem, 0, ram);
> +
> +    /* clear virtio region */
> +    virtio_region_len = my_ram_size - ram_size;
> +    virtio_region_start = ram_size;
> +    virtio_region = cpu_physical_memory_map(virtio_region_start,
> +                                            &virtio_region_len, true);
> +    memset(virtio_region, 0, virtio_region_len);
> +    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
> +                              virtio_region_len);
> +
> +    /* allocate storage keys */
> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> +
> +    /* init CPUs */
> +    env = s390_init_cpus(cpu_model, storage_keys);
> +
> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
> +
> +    /* Create VirtIO network adapters */
> +    s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
> +
> +}
> +
> static QEMUMachine s390_machine = {
>     .name = "s390-virtio",
>     .alias = "s390",
> @@ -341,7 +400,6 @@ static QEMUMachine s390_machine = {
>     .no_sdcard = 1,
>     .use_virtcon = 1,
>     .max_cpus = 255,
> -    .is_default = 1,
> };
> 
> static void s390_machine_init(void)
> @@ -350,3 +408,80 @@ static void s390_machine_init(void)
> }
> 
> machine_init(s390_machine_init);
> +
> +static void ccw_init(ram_addr_t my_ram_size,
> +                     const char *boot_device,
> +                     const char *kernel_filename,
> +                     const char *kernel_cmdline,
> +                     const char *initrd_filename,
> +                     const char *cpu_model)
> +{
> +    CPUS390XState *env = NULL;
> +    MemoryRegion *sysmem = get_system_memory();
> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
> +    int shift = 0;
> +    uint8_t *storage_keys;
> +    int ret;
> +
> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> +       guests > 64GB can be specified in 2MB steps etc. */
> +    while ((my_ram_size >> (20 + shift)) > 65535) {
> +        shift++;
> +    }
> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> +
> +    /* lets propagate the changed ram size into the global variable. */
> +    ram_size = my_ram_size;
> +
> +    /* get a BUS */
> +    ccw_bus = virtio_ccw_bus_init();
> +
> +    /* allocate RAM */
> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> +    vmstate_register_ram_global(ram);
> +    memory_region_add_subregion(sysmem, 0, ram);
> +
> +    /* allocate storage keys */
> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> +
> +    /* init CPUs */
> +    env = s390_init_cpus(cpu_model, storage_keys);
> +
> +    kvm_s390_enable_css_support(env);
> +
> +    /*
> +     * Create virtual css and set it as default so that non mcss-e
> +     * enabled guests only see virtio devices.
> +     */
> +    ret = css_create_css_image(VIRTUAL_CSSID, true);
> +    assert(ret == 0);
> +
> +
> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
> +
> +    /* Create VirtIO network adapters */
> +    s390_create_virtio_net((BusState *)ccw_bus, "virtio-net-ccw");
> +
> +}
> +
> +static QEMUMachine ccw_machine = {
> +    .name = "s390-ccw-virtio",
> +    .alias = "s390-ccw",
> +    .desc = "VirtIO-ccw based S390 machine",
> +    .init = ccw_init,
> +    .no_cdrom = 1,
> +    .no_floppy = 1,
> +    .no_serial = 1,
> +    .no_parallel = 1,
> +    .no_sdcard = 1,
> +    .use_virtcon = 1,
> +    .max_cpus = 255,
> +    .is_default = 1,
> +};
> +
> +static void ccw_machine_init(void)
> +{
> +    qemu_register_machine(&ccw_machine);
> +}
> +
> +machine_init(ccw_machine_init);
> diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
> index 93b41fb..e4c3d6f 100644
> --- a/hw/s390x/Makefile.objs
> +++ b/hw/s390x/Makefile.objs
> @@ -2,3 +2,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o
> 
> obj-y := $(addprefix ../,$(obj-y))
> obj-y += css.o
> +obj-y += virtio-ccw.o
> diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> index b9b6e48..a671e28 100644
> --- a/hw/s390x/css.c
> +++ b/hw/s390x/css.c
> @@ -441,6 +441,51 @@ 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];
> }
> 
> +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
> +{
> +    if (!channel_subsys->css[cssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys->css[cssid]->sch_set[ssid]) {
> +        return false;
> +    }
> +
> +    return !!test_bit(devno,
> +                      channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
> +}
> +
> +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno,
> +                      SubchDev *sch)
> +{
> +    CssImage *css;
> +    SubchSet *s_set;
> +
> +    if (!channel_subsys->css[cssid]) {
> +        fprintf(stderr,
> +                "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
> +                __func__, cssid, ssid, schid);
> +       return;
> +   }
> +    css = channel_subsys->css[cssid];
> +
> +   if (!css->sch_set[ssid]) {
> +        size_t set_size = sizeof(SubchSet) +
> +            BITS_TO_LONGS(MAX_SCHID + 1) * 2;
> +        css->sch_set[ssid] = g_malloc0(set_size);
> +    }
> +    s_set = css->sch_set[ssid];
> +
> +    s_set->sch[schid] = sch;
> +    if (sch) {
> +        set_bit(schid, s_set->schids_used);
> +        set_bit(devno, s_set->devnos_used);
> +    } else {
> +        clear_bit(schid, s_set->schids_used);
> +        clear_bit(schid, s_set->devnos_used);
> +    }
> +}
> +
> +
> bool css_present(uint8_t cssid)
> {
>     return (channel_subsys->css[cssid] != NULL);
> diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> index f3590eb..6876633 100644
> --- a/hw/s390x/css.h
> +++ b/hw/s390x/css.h
> @@ -53,6 +53,9 @@ struct SubchDev {
> };
> 
> 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);
> void css_reset(void);
> void css_reset_sch(SubchDev *sch);
> diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> new file mode 100644
> index 0000000..28be1a7
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.c
> @@ -0,0 +1,875 @@
> +/*
> + * virtio ccw target implementation
> + *
> + * 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.
> + */
> +
> +#include <hw/hw.h>
> +#include "block.h"
> +#include "blockdev.h"
> +#include "sysemu.h"
> +#include "net.h"
> +#include "monitor.h"
> +#include "qemu-thread.h"
> +#include "hw/virtio.h"
> +#include "hw/virtio-serial.h"
> +#include "hw/virtio-net.h"
> +#include "hw/sysbus.h"
> +#include "bitops.h"
> +
> +#include "ioinst.h"
> +#include "css.h"
> +#include "virtio-ccw.h"
> +
> +static const TypeInfo virtio_ccw_bus_info = {
> +    .name = TYPE_VIRTIO_CCW_BUS,
> +    .parent = TYPE_BUS,
> +    .instance_size = sizeof(VirtioCcwBus),
> +};
> +
> +static const VirtIOBindings virtio_ccw_bindings;
> +
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> +{
> +    VirtIODevice *vdev = NULL;
> +
> +    if (sch->driver_data) {
> +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> +    }
> +    return vdev;
> +}
> +
> +static void virtio_ccw_reset_subchannels(void *opaque)
> +{
> +    VirtioCcwBus *bus = opaque;
> +    BusChild *kid;
> +    VirtioCcwData *data;
> +
> +    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
> +        data = (VirtioCcwData *)kid->child;
> +        virtio_reset(data->vdev);
> +        css_reset_sch(data->sch);
> +    }
> +    css_reset();
> +}
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void)
> +{
> +    VirtioCcwBus *cbus;
> +    BusState *bus;
> +    DeviceState *dev;
> +
> +    /* Create bridge device */
> +    dev = qdev_create(NULL, "virtio-ccw-bridge");
> +    qdev_init_nofail(dev);
> +
> +    /* Create bus on bridge device */
> +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
> +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
> +
> +    /* Enable hotplugging */
> +    bus->allow_hotplug = 1;
> +
> +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
> +    return cbus;
> +}
> +
> +/* Communication blocks used by several channel commands. */
> +typedef struct VqInfoBlock {
> +    uint64_t queue;
> +    uint32_t align;
> +    uint16_t index;
> +    uint16_t num;
> +} QEMU_PACKED VqInfoBlock;
> +
> +typedef struct VqConfigBlock {
> +    uint16_t index;
> +    uint16_t num_max;
> +} QEMU_PACKED VqConfigBlock;
> +
> +typedef struct VirtioFeatDesc {
> +    uint32_t features;
> +    uint8_t index;
> +} QEMU_PACKED VirtioFeatDesc;
> +
> +/* Specify where the virtqueues for the subchannel are in guest memory. */
> +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
> +                              uint16_t index, uint16_t num)
> +{
> +    VirtioCcwData *data = sch->driver_data;
> +
> +    if (index > VIRTIO_PCI_QUEUE_MAX) {
> +        return -EINVAL;
> +    }
> +
> +    /* Current code in virtio.c relies on 4K alignment. */
> +    if (align != 4096) {
> +        return -EINVAL;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    virtio_queue_set_addr(data->vdev, index, addr);
> +    if (!addr) {
> +        virtio_queue_set_vector(data->vdev, index, 0);
> +    } else {
> +        /* Fail if we don't have a big enough queue. */
> +        /* TODO: Add interface to handle vring.num changing */
> +        if (virtio_queue_get_num(data->vdev, index) > num) {
> +            return -EINVAL;
> +        }
> +        virtio_queue_set_vector(data->vdev, index, index);
> +    }
> +    return 0;
> +}
> +
> +static int virtio_ccw_cb(SubchDev *sch, CCW1 *ccw)
> +{
> +    int ret;
> +    VqInfoBlock info;
> +    uint8_t status;
> +    VirtioFeatDesc features;
> +    void *config;
> +    uint64_t *indicators;
> +    VqConfigBlock vq_config;
> +    VirtioCcwData *data = sch->driver_data;
> +    bool check_len;
> +    int len;
> +
> +    if (!ccw) {
> +        return -EIO;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
> +
> +    /* Look at the command. */
> +    switch (ccw->cmd_code) {
> +    case CCW_CMD_SET_VQ:
> +        if (check_len) {
> +            if (ccw->count != sizeof(info)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(info)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            info.queue = ldq_phys(ccw->cda);
> +            info.align = ldl_phys(ccw->cda + sizeof(info.queue));
> +            info.index = lduw_phys(ccw->cda + sizeof(info.queue)
> +                                   + sizeof(info.align));
> +            info.num = lduw_phys(ccw->cda + sizeof(info.queue)
> +                                 + sizeof(info.align)
> +                                 + sizeof(info.index));
> +            ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index,
> +                                     info.num);
> +            sch->curr_status.scsw.count = 0;
> +        }
> +        break;
> +    case CCW_CMD_VDEV_RESET:
> +        virtio_reset(data->vdev);
> +        ret = 0;
> +        break;
> +    case CCW_CMD_READ_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
> +            if (features.index < ARRAY_SIZE(data->host_features)) {
> +                features.features = data->host_features[features.index];
> +            } else {
> +                /* Return zeroes if the guest supports more feature bits. */
> +                features.features = 0;
> +            }
> +            stw_le_phys(ccw->cda, features.features);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
> +            features.features = bswap32(lduw_phys(ccw->cda));
> +            if (features.index < ARRAY_SIZE(data->host_features)) {
> +                if (data->vdev->set_features) {
> +                    data->vdev->set_features(data->vdev, features.features);
> +                }
> +                data->vdev->guest_features = features.features;
> +            } else {
> +                /*
> +                 * If the guest supports more feature bits, assert that it
> +                 * passes us zeroes for those we don't support.
> +                 */
> +                if (features.features) {
> +                    fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n",
> +                            features.index, features.features);
> +                    /* XXX: do a unit check here? */
> +                }
> +            }
> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_READ_CONF:
> +        if (check_len) {
> +            if (ccw->count > data->vdev->config_len) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, data->vdev->config_len);
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            data->vdev->get_config(data->vdev, data->vdev->config);
> +            cpu_physical_memory_write(ccw->cda, data->vdev->config, len);
> +            sch->curr_status.scsw.count = ccw->count - len;
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_CONF:
> +        if (check_len) {
> +            if (ccw->count > data->vdev->config_len) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, data->vdev->config_len);
> +        config = qemu_get_ram_ptr(ccw->cda);
> +        if (!config) {
> +            ret = -EFAULT;
> +        } else {
> +            memcpy(data->vdev->config, config, len);
> +            if (data->vdev->set_config) {
> +                data->vdev->set_config(data->vdev, data->vdev->config);
> +            }
> +            sch->curr_status.scsw.count = ccw->count - len;
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_STATUS:
> +        if (check_len) {
> +            if (ccw->count != sizeof(status)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(status)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            status = ldub_phys(ccw->cda);
> +            virtio_set_status(data->vdev, status);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(status);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_SET_IND:
> +        if (check_len) {
> +            if (ccw->count != sizeof(*indicators)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(*indicators)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        indicators = qemu_get_ram_ptr(ccw->cda);
> +        if (!indicators) {
> +            ret = -EFAULT;
> +        } else {
> +            data->indicators = ccw->cda;
> +            sch->curr_status.scsw.count = ccw->count - sizeof(*indicators);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_READ_VQ_CONF:
> +        if (check_len) {
> +            if (ccw->count != sizeof(vq_config)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(vq_config)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            vq_config.index = lduw_phys(ccw->cda);
> +            vq_config.num_max = virtio_queue_get_num(data->vdev,
> +                                                     vq_config.index);
> +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num_max);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
> +            ret = 0;
> +        }
> +        break;
> +    default:
> +        ret = -EOPNOTSUPP;
> +        break;
> +    }
> +    return ret;
> +}
> +
> +static int virtio_ccw_device_init(VirtioCcwData *dev, VirtIODevice *vdev)
> +{
> +    unsigned int cssid = 0;
> +    unsigned int ssid = 0;
> +    unsigned int schid;
> +    unsigned int devno;
> +    bool have_devno = false;
> +    bool found = false;
> +    SubchDev *sch;
> +    int ret;
> +    int num;
> +
> +    sch = g_malloc0(sizeof(SubchDev));
> +
> +    sch->driver_data = dev;
> +    dev->sch = sch;
> +
> +    dev->vdev = vdev;
> +    dev->indicators = 0;
> +
> +    /* Initialize subchannel structure. */
> +    qemu_mutex_init(&sch->mutex);
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    sch->orb = NULL;
> +    /*
> +     * 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)) {
> +                ret = -EINVAL;
> +                error_report("Invalid cssid or ssid: cssid %x, ssid %x",
> +                             cssid, ssid);
> +                goto out_err;
> +            }
> +            /* Enforce use of virtual cssid. */
> +            if (cssid != VIRTUAL_CSSID) {
> +                ret = -EINVAL;
> +                error_report("cssid %x not valid for virtio devices", cssid);
> +                goto out_err;
> +            }
> +            if (css_devno_used(cssid, ssid, devno)) {
> +                ret = -EEXIST;
> +                error_report("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 {
> +            ret = -EINVAL;
> +            error_report("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) {
> +            ret = -ENODEV;
> +            error_report("No free subchannel found for %x.%x.%04x", cssid, ssid,
> +                         devno);
> +            goto out_err;
> +        }
> +    } 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) {
> +                            ret = -ENODEV;
> +                            error_report("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) {
> +            ret = -ENODEV;
> +            error_report("Virtual channel subsystem is full!");
> +            goto out_err;
> +        }
> +    }
> +
> +    /* Build initial schib. */
> +    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
> +
> +    sch->ccw_cb = virtio_ccw_cb;
> +
> +    /* Build senseid data. */
> +    memset(&sch->id, 0, sizeof(SenseId));
> +    sch->id.reserved = 0xff;
> +    sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
> +    sch->id.cu_model = dev->vdev->device_id;
> +
> +    virtio_bind_device(vdev, &virtio_ccw_bindings, dev);
> +    /* Only the first 32 feature bits are used. */
> +    dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]);
> +
> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
> +                     &sch->curr_status, dev->qdev.hotplugged, 1, 1);
> +    return 0;
> +
> +out_err:
> +    dev->sch = NULL;
> +    g_free(sch);
> +    return ret;
> +}
> +
> +static int virtio_ccw_exit(VirtioCcwData *dev)
> +{
> +    SubchDev *sch = dev->sch;
> +
> +    if (sch) {
> +        css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
> +        g_free(sch);
> +    }
> +    dev->indicators = 0;
> +    return 0;
> +}
> +
> +static int virtio_ccw_net_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_net_exit(VirtioCcwData *dev)
> +{
> +    virtio_net_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_blk_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_blk_init((DeviceState *)dev, &dev->blk);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_blk_exit(VirtioCcwData *dev)
> +{
> +    virtio_blk_exit(dev->vdev);
> +    blockdev_mark_auto_del(dev->blk.conf.bs);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_serial_init(VirtioCcwData *dev)
> +{
> +    VirtioCcwBus *bus;
> +    VirtIODevice *vdev;
> +    int r;
> +
> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
> +
> +    vdev = virtio_serial_init((DeviceState *)dev, &dev->serial);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    r = virtio_ccw_device_init(dev, vdev);
> +    if (!r) {
> +        bus->console = dev;
> +    }
> +
> +    return r;
> +}
> +
> +static int virtio_ccw_serial_exit(VirtioCcwData *dev)
> +{
> +    VirtioCcwBus *bus;
> +
> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
> +    bus->console = NULL;
> +    virtio_serial_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_balloon_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_balloon_init((DeviceState *)dev);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_balloon_exit(VirtioCcwData *dev)
> +{
> +    virtio_balloon_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_scsi_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_scsi_exit(VirtioCcwData *dev)
> +{
> +    virtio_scsi_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus)
> +{
> +    return bus->console;
> +}
> +
> +static void virtio_ccw_notify(void *opaque, uint16_t vector)
> +{
> +    VirtioCcwData *dev = opaque;
> +    SubchDev *sch = dev->sch;
> +    uint64_t indicators;
> +
> +    if (vector >= VIRTIO_PCI_QUEUE_MAX) {
> +        return;
> +    }
> +
> +    qemu_mutex_lock(&sch->mutex);

Why do you need this lock?

> +    indicators = ldq_phys(dev->indicators);
> +    set_bit(vector, &indicators);
> +    stq_phys(dev->indicators, indicators);
> +
> +    css_conditional_io_interrupt(sch);
> +
> +    qemu_mutex_unlock(&sch->mutex);
> +}
> +
> +static unsigned virtio_ccw_get_features(void *opaque)
> +{
> +    VirtioCcwData *dev = opaque;
> +
> +    /* Only the first 32 feature bits are used. */
> +    return dev->host_features[0];
> +}
> +
> +/**************** Virtio-ccw Bus Device Descriptions *******************/
> +
> +static const VirtIOBindings virtio_ccw_bindings = {
> +    .notify = virtio_ccw_notify,
> +    .get_features = virtio_ccw_get_features,
> +};
> +
> +static Property virtio_ccw_net_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_NIC_PROPERTIES(VirtioCcwData, nic),
> +    DEFINE_PROP_UINT32("x-txtimer", VirtioCcwData,
> +                       net.txtimer, TX_TIMER_INTERVAL),
> +    DEFINE_PROP_INT32("x-txburst", VirtioCcwData,
> +                      net.txburst, TX_BURST),
> +    DEFINE_PROP_STRING("tx", VirtioCcwData, net.tx),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_net_init;
> +    k->exit = virtio_ccw_net_exit;
> +    dc->props = virtio_ccw_net_properties;
> +}
> +
> +static TypeInfo virtio_ccw_net = {
> +    .name          = "virtio-net-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_net_class_init,
> +};
> +
> +static Property virtio_ccw_blk_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_BLOCK_PROPERTIES(VirtioCcwData, blk.conf),
> +    DEFINE_PROP_STRING("serial", VirtioCcwData, blk.serial),
> +#ifdef __linux__
> +    DEFINE_PROP_BIT("scsi", VirtioCcwData, blk.scsi, 0, true),
> +#endif
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_blk_init;
> +    k->exit = virtio_ccw_blk_exit;
> +    dc->props = virtio_ccw_blk_properties;
> +}
> +
> +static TypeInfo virtio_ccw_blk = {
> +    .name          = "virtio-blk-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_blk_class_init,
> +};
> +
> +static Property virtio_ccw_serial_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_PROP_UINT32("max_ports", VirtioCcwData, serial.max_virtserial_ports,
> +                       31),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_serial_init;
> +    k->exit = virtio_ccw_serial_exit;
> +    dc->props = virtio_ccw_serial_properties;
> +}
> +
> +static TypeInfo virtio_ccw_serial = {
> +    .name          = "virtio-serial-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_serial_class_init,
> +};
> +
> +static Property virtio_ccw_balloon_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_balloon_init;
> +    k->exit = virtio_ccw_balloon_exit;
> +    dc->props = virtio_ccw_balloon_properties;
> +}
> +
> +static TypeInfo virtio_ccw_balloon = {
> +    .name          = "virtio-balloon-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_balloon_class_init,
> +};
> +
> +static Property virtio_ccw_scsi_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwData, host_features[0], scsi),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_scsi_init;
> +    k->exit = virtio_ccw_scsi_exit;
> +    dc->props = virtio_ccw_scsi_properties;
> +}
> +
> +static TypeInfo virtio_ccw_scsi = {
> +    .name          = "virtio-scsi-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_scsi_class_init,
> +};
> +
> +static int virtio_ccw_busdev_init(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
> +
> +    return _info->init(_dev);
> +}
> +
> +static int virtio_ccw_busdev_exit(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
> +
> +    return _info->exit(_dev);
> +}
> +
> +static int virtio_ccw_busdev_unplug(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    SubchDev *sch = _dev->sch;
> +
> +    /*
> +     * 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. */
> +    qemu_mutex_lock(&sch->mutex);

Why do you need this lock?


Alex

> +    sch->curr_status.pmcw.ena = 0;
> +    sch->curr_status.pmcw.dnv = 0;
> +    qemu_mutex_unlock(&sch->mutex);
> +
> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
> +                     &sch->curr_status, 1, 0, 1);
> +
> +    object_unparent(OBJECT(dev));
> +    qdev_free(dev);
> +    return 0;
> +}
> +
> +static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->init = virtio_ccw_busdev_init;
> +    dc->exit = virtio_ccw_busdev_exit;
> +    dc->unplug = virtio_ccw_busdev_unplug;
> +    dc->bus_type = TYPE_VIRTIO_CCW_BUS;
> +
> +}
> +
> +static TypeInfo virtio_ccw_device_info = {
> +    .name = TYPE_VIRTIO_CCW_DEVICE,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init = virtio_ccw_device_class_init,
> +    .class_size = sizeof(VirtIOCCWDeviceClass),
> +    .abstract = true,
> +};
> +
> +/***************** Virtio-ccw Bus Bridge Device ********************/
> +/* Only required to have the virtio bus as child in the system bus */
> +
> +static int virtio_ccw_bridge_init(SysBusDevice *dev)
> +{
> +    /* nothing */
> +    return 0;
> +}
> +
> +static void virtio_ccw_bridge_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_bridge_init;
> +    dc->no_user = 1;
> +}
> +
> +static TypeInfo virtio_ccw_bridge_info = {
> +    .name          = "virtio-ccw-bridge",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SysBusDevice),
> +    .class_init    = virtio_ccw_bridge_class_init,
> +};
> +
> +static void virtio_ccw_register(void)
> +{
> +    type_register_static(&virtio_ccw_bus_info);
> +    type_register_static(&virtio_ccw_device_info);
> +    type_register_static(&virtio_ccw_serial);
> +    type_register_static(&virtio_ccw_blk);
> +    type_register_static(&virtio_ccw_net);
> +    type_register_static(&virtio_ccw_balloon);
> +    type_register_static(&virtio_ccw_scsi);
> +    type_register_static(&virtio_ccw_bridge_info);
> +}
> +type_init(virtio_ccw_register);
> diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
> new file mode 100644
> index 0000000..8125acf
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.h
> @@ -0,0 +1,79 @@
> +/*
> + * virtio ccw target 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.
> + */
> +
> +#include <hw/virtio-blk.h>
> +#include <hw/virtio-net.h>
> +#include <hw/virtio-serial.h>
> +#include <hw/virtio-scsi.h>
> +
> +#define VIRTUAL_CSSID 0xfe
> +
> +#define VIRTIO_CCW_CU_TYPE 0x3832
> +#define VIRTIO_CCW_CHPID_TYPE 0x32
> +
> +#define CCW_CMD_SET_VQ       0x13
> +#define CCW_CMD_VDEV_RESET   0x33
> +#define CCW_CMD_READ_FEAT    0x12
> +#define CCW_CMD_WRITE_FEAT   0x11
> +#define CCW_CMD_READ_CONF    0x22
> +#define CCW_CMD_WRITE_CONF   0x21
> +#define CCW_CMD_WRITE_STATUS 0x31
> +#define CCW_CMD_SET_IND      0x43
> +#define CCW_CMD_READ_VQ_CONF 0x32
> +
> +#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
> +#define VIRTIO_CCW_DEVICE(obj) \
> +     OBJECT_CHECK(VirtioCcwData, (obj), TYPE_VIRTIO_CCW_DEVICE)
> +#define VIRTIO_CCW_DEVICE_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE)
> +#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE)
> +
> +#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus"
> +#define VIRTIO_CCW_BUS(obj) \
> +     OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS)
> +
> +typedef struct VirtioCcwData VirtioCcwData;
> +
> +typedef struct VirtIOCCWDeviceClass {
> +    DeviceClass qdev;
> +    int (*init)(VirtioCcwData *dev);
> +    int (*exit)(VirtioCcwData *dev);
> +} VirtIOCCWDeviceClass;
> +
> +/* Change here if we want to support more feature bits. */
> +#define VIRTIO_CCW_FEATURE_SIZE 1
> +
> +struct VirtioCcwData {
> +    DeviceState qdev;
> +    SubchDev *sch;
> +    VirtIODevice *vdev;
> +    char *bus_id;
> +    VirtIOBlkConf blk;
> +    NICConf nic;
> +    uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
> +    virtio_serial_conf serial;
> +    virtio_net_conf net;
> +    VirtIOSCSIConf scsi;
> +    /* Guest provided values: */
> +    target_phys_addr_t indicators;
> +};
> +
> +/* virtio-ccw bus type */
> +typedef struct VirtioCcwBus {
> +    BusState bus;
> +    VirtioCcwData *console;
> +} VirtioCcwBus;
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void);
> +void virtio_ccw_device_update_status(SubchDev *sch);
> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus);
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
> diff --git a/vl.c b/vl.c
> index 7c577fa..2b8cae6 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -289,6 +289,7 @@ static struct {
>     { .driver = "scsi-cd",              .flag = &default_cdrom     },
>     { .driver = "virtio-serial-pci",    .flag = &default_virtcon   },
>     { .driver = "virtio-serial-s390",   .flag = &default_virtcon   },
> +    { .driver = "virtio-serial-ccw",    .flag = &default_virtcon   },
>     { .driver = "virtio-serial",        .flag = &default_virtcon   },
>     { .driver = "VGA",                  .flag = &default_vga       },
>     { .driver = "isa-vga",              .flag = &default_vga       },
> -- 
> 1.7.11.5
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM Cornelia Huck
@ 2012-09-19 16:22   ` Alexander Graf
  0 siblings, 0 replies; 17+ messages in thread
From: Alexander Graf @ 2012-09-19 16:22 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky


On 04.09.2012, at 17:13, Cornelia Huck wrote:

> Provide css support for the !KVM case as well.

As mentioned in my previous reply to the kvm side of this, I don't see any reason why we should split the code at such a high level. Why can't KVM and !KVM share the same code? If we run into performance issues, we can still think about moving the CCW subsystem into the kernel.

> 
> This includes the following:
> - Handling of instruction intercepts for I/O instructions.
> - Extended channel subsystem functions, like monitoring.
> - Support for injecting I/O interrupts and machine checks.
> 
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
> 
> Changes v1->v2:
> - coding style
> 
> ---
> hw/s390x/css.c           | 749 ++++++++++++++++++++++++++++++++++++++++++++++-
> hw/s390x/css.h           |  26 ++
> target-s390x/cpu.h       | 157 +++++++++-
> target-s390x/helper.c    | 140 +++++++++
> target-s390x/ioinst.c    | 696 +++++++++++++++++++++++++++++++++++++++++++
> target-s390x/ioinst.h    |  35 ++-
> target-s390x/kvm.c       | 164 ++++++++++-
> target-s390x/op_helper.c |  22 +-
> 8 files changed, 1961 insertions(+), 28 deletions(-)
> 
> diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> index a671e28..3aab586 100644
> --- a/hw/s390x/css.c
> +++ b/hw/s390x/css.c
> @@ -17,6 +17,12 @@
> #include "cpu.h"
> #include "ioinst.h"
> #include "css.h"
> +#include "virtio-ccw.h"
> +
> +typedef struct CrwContainer {
> +    CRW crw;
> +    QTAILQ_ENTRY(CrwContainer) sibling;
> +} CrwContainer;
> 
> typedef struct ChpInfo {
>     uint8_t in_use;
> @@ -35,6 +41,13 @@ typedef struct CssImage {
> } CssImage;
> 
> typedef struct ChannelSubSys {
> +    QTAILQ_HEAD(, CrwContainer) pending_crws;
> +    bool do_crw_mchk;
> +    bool crws_lost;
> +    uint8_t max_cssid;
> +    uint8_t max_ssid;
> +    bool chnmon_active;
> +    uint64_t chnmon_area;
>     CssImage *css[MAX_CSSID + 1];
>     uint8_t default_cssid;
> } ChannelSubSys;
> @@ -60,6 +73,76 @@ int css_create_css_image(uint8_t cssid, bool default_image)
>     return 0;
> }
> 
> +static void css_write_phys_pmcw(uint32_t addr, PMCW *pmcw)
> +{
> +    int i;
> +    uint32_t offset = 0;
> +    struct copy_pmcw {
> +        uint32_t intparm;
> +        uint16_t flags;
> +        uint16_t devno;
> +        uint8_t lpm;
> +        uint8_t pnom;
> +        uint8_t lpum;
> +        uint8_t pim;
> +        uint16_t mbi;
> +        uint8_t pom;
> +        uint8_t pam;
> +        uint8_t chpid[8];
> +        uint32_t chars;
> +    } *copy;
> +
> +    copy = (struct copy_pmcw *)pmcw;
> +    stl_phys(addr + offset, copy->intparm);
> +    offset += sizeof(copy->intparm);
> +    stw_phys(addr + offset, copy->flags);
> +    offset += sizeof(copy->flags);
> +    stw_phys(addr + offset, copy->devno);
> +    offset += sizeof(copy->devno);
> +    stb_phys(addr + offset, copy->lpm);
> +    offset += sizeof(copy->lpm);
> +    stb_phys(addr + offset, copy->pnom);
> +    offset += sizeof(copy->pnom);
> +    stb_phys(addr + offset, copy->lpum);
> +    offset += sizeof(copy->lpum);
> +    stb_phys(addr + offset, copy->pim);
> +    offset += sizeof(copy->pim);
> +    stw_phys(addr + offset, copy->mbi);
> +    offset += sizeof(copy->mbi);
> +    stb_phys(addr + offset, copy->pom);
> +    offset += sizeof(copy->pom);
> +    stb_phys(addr + offset, copy->pam);
> +    offset += sizeof(copy->pam);
> +    for (i = 0; i < 8; i++) {
> +        stb_phys(addr + offset, copy->chpid[i]);
> +        offset += sizeof(copy->chpid[i]);
> +    }
> +    stl_phys(addr + offset, copy->chars);

You're probably better off copying everything in one go, either through cpu_physical_memory_rw or cpu_physical_memory_map + cpu_physical_memory_unmap. Beware of endianness though :).

> +}
> +
> +static void css_write_phys_scsw(uint32_t addr, SCSW *scsw)
> +{
> +    uint32_t offset = 0;
> +    struct copy_scsw {
> +        uint32_t flags;
> +        uint32_t cpa;
> +        uint8_t dstat;
> +        uint8_t cstat;
> +        uint16_t count;
> +    } *copy;
> +
> +    copy = (struct copy_scsw *)scsw;
> +    stl_phys(addr + offset, copy->flags);
> +    offset += sizeof(copy->flags);
> +    stl_phys(addr + offset, copy->cpa);
> +    offset += sizeof(copy->cpa);
> +    stb_phys(addr + offset, copy->dstat);
> +    offset += sizeof(copy->dstat);
> +    stb_phys(addr + offset, copy->cstat);
> +    offset += sizeof(copy->cstat);
> +    stw_phys(addr + offset, copy->count);
> +}
> +
> static void css_inject_io_interrupt(SubchDev *sch, uint8_t func)
> {
>     s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
> @@ -375,6 +458,543 @@ int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
>     return 0;
> }
> 
> +/*
> + * This function should run asynchronously to the I/O instructions in order
> + * to match the implementation on real machines. For this simple virtual
> + * css it is fine to run the I/O work synchronously instead since it won't
> + * call out to real hardware.
> + * Note: This is only used in the !KVM case.
> + */
> +static void do_subchannel_work(SubchDev *sch)
> +{
> +
> +    SCSW *s = &sch->curr_status.scsw;
> +    uint8_t func;
> +
> +    if (s->fctl & SCSW_FCTL_CLEAR_FUNC) {
> +        func = CSS_DO_CSCH_SIMPLE;
> +    } else if (s->fctl & SCSW_FCTL_HALT_FUNC) {
> +        func = CSS_DO_HSCH_SIMPLE;
> +    } else if (s->fctl & SCSW_FCTL_START_FUNC) {
> +        func = CSS_DO_SSCH_SIMPLE;
> +    } else {
> +        /* Cannot happen. */
> +        return;
> +    }
> +    css_handle_sch_io((sch->cssid << 24) | (1 << 29) | (sch->ssid << 16) |
> +                      (1 << 16) | sch->schid,
> +                      func, 0, NULL, NULL);
> +}
> +
> +/* The various css_do_<instr> functions are only hit when KVM is not active. */
> +
> +int css_do_stsch(SubchDev *sch, uint32_t addr)
> +{
> +    int i;
> +    uint32_t offset = 0;
> +
> +    qemu_mutex_lock(&sch->mutex);

Locks in QEMU code are usually wrong ;).

> +    /* Use current status. */
> +    css_write_phys_pmcw(addr, &sch->curr_status.pmcw);
> +    offset += sizeof(PMCW);
> +    css_write_phys_scsw(addr + offset, &sch->curr_status.scsw);
> +    offset += sizeof(SCSW);
> +    stq_phys(addr + offset, sch->curr_status.mba);
> +    offset += sizeof(sch->curr_status.mba);
> +    for (i = 0; i < 4; i++) {
> +        stb_phys(addr + offset, sch->curr_status.mda[i]);
> +        offset += sizeof(sch->curr_status.mda[i]);
> +    }
> +    qemu_mutex_unlock(&sch->mutex);
> +    return 0;
> +}
> +
> +int css_do_msch(SubchDev *sch, SCHIB *schib)
> +{
> +    SCSW *s = &sch->curr_status.scsw;
> +    PMCW *p = &sch->curr_status.pmcw;
> +    int ret;
> +
> +    qemu_mutex_lock(&sch->mutex);
> +
> +    if (!sch->curr_status.pmcw.dnv) {
> +        ret = 0;
> +        goto out;
> +    }
> +
> +    if (s->stctl & SCSW_STCTL_STATUS_PEND) {
> +        ret = -EINPROGRESS;
> +        goto out;
> +    }
> +
> +    if (s->fctl &
> +        (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) {
> +        ret = -EBUSY;
> +        goto out;
> +    }
> +
> +    /* Only update the program-modifiable fields. */
> +    p->ena = schib->pmcw.ena;
> +    p->intparm = schib->pmcw.intparm;
> +    p->isc = schib->pmcw.isc;
> +    p->mp = schib->pmcw.mp;
> +    p->lpm = schib->pmcw.lpm;
> +    p->pom = schib->pmcw.pom;
> +    p->lm = schib->pmcw.lm;
> +    p->csense = schib->pmcw.csense;
> +
> +    p->mme = schib->pmcw.mme;
> +    p->mbi = schib->pmcw.mbi;
> +    p->mbfc = schib->pmcw.mbfc;
> +    sch->curr_status.mba = schib->mba;
> +
> +    ret = 0;
> +
> +out:
> +    qemu_mutex_unlock(&sch->mutex);
> +    return ret;
> +}
> +
> +int css_do_xsch(SubchDev *sch)
> +{
> +    SCSW *s = &sch->curr_status.scsw;
> +    PMCW *p = &sch->curr_status.pmcw;
> +    int ret;
> +
> +    qemu_mutex_lock(&sch->mutex);
> +
> +    if (!p->dnv || !p->ena) {
> +        ret = -ENODEV;
> +        goto out;
> +    }
> +
> +    if (!s->fctl || (s->fctl != SCSW_FCTL_START_FUNC) ||
> +        (!(s->actl &
> +           (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) ||
> +        (s->actl & SCSW_ACTL_SUBCH_ACTIVE)) {
> +        ret = -EINPROGRESS;
> +        goto out;
> +    }
> +
> +    if (s->stctl != 0) {
> +        ret = -EBUSY;
> +        goto out;
> +    }
> +
> +    /* Cancel the current operation. */
> +    s->fctl &= ~SCSW_FCTL_START_FUNC;
> +    s->actl &= ~(SCSW_ACTL_RESUME_PEND|SCSW_ACTL_START_PEND|SCSW_ACTL_SUSP);
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    sch->orb = NULL;
> +    s->dstat = 0;
> +    s->cstat = 0;
> +    ret = 0;
> +
> +out:
> +    qemu_mutex_unlock(&sch->mutex);
> +    return ret;
> +}
> +
> +int css_do_csch(SubchDev *sch)
> +{
> +    SCSW *s = &sch->curr_status.scsw;
> +    PMCW *p = &sch->curr_status.pmcw;
> +    int ret;
> +
> +    qemu_mutex_lock(&sch->mutex);
> +
> +    if (!p->dnv || !p->ena) {
> +        ret = -ENODEV;
> +        goto out;
> +    }
> +
> +    /* Trigger the clear function. */
> +    s->fctl = SCSW_FCTL_CLEAR_FUNC;
> +    s->actl = SCSW_ACTL_CLEAR_PEND;
> +
> +    do_subchannel_work(sch);
> +    ret = 0;
> +
> +out:
> +    qemu_mutex_unlock(&sch->mutex);
> +    return ret;
> +}
> +
> +int css_do_hsch(SubchDev *sch)
> +{
> +    SCSW *s = &sch->curr_status.scsw;
> +    PMCW *p = &sch->curr_status.pmcw;
> +    int ret;
> +
> +    qemu_mutex_lock(&sch->mutex);
> +
> +    if (!p->dnv || !p->ena) {
> +        ret = -ENODEV;
> +        goto out;
> +    }
> +
> +    if ((s->stctl == SCSW_STCTL_STATUS_PEND) ||
> +        (s->stctl & (SCSW_STCTL_PRIMARY |
> +                     SCSW_STCTL_SECONDARY |
> +                     SCSW_STCTL_ALERT))) {
> +        ret = -EINPROGRESS;
> +        goto out;
> +    }
> +
> +    if (s->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
> +        ret = -EBUSY;
> +        goto out;
> +    }
> +
> +    /* Trigger the halt function. */
> +    s->fctl |= SCSW_FCTL_HALT_FUNC;
> +    s->fctl &= ~SCSW_FCTL_START_FUNC;
> +    if ((s->actl == (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) &&
> +        (s->stctl == SCSW_STCTL_INTERMEDIATE)) {
> +        s->stctl &= ~SCSW_STCTL_STATUS_PEND;
> +    }
> +    s->actl |= SCSW_ACTL_HALT_PEND;
> +
> +    do_subchannel_work(sch);
> +    ret = 0;
> +
> +out:
> +    qemu_mutex_unlock(&sch->mutex);
> +    return ret;
> +}
> +
> +static void css_update_chnmon(SubchDev *sch)
> +{
> +    if (!sch->curr_status.pmcw.mme) {
> +        /* Not active. */
> +        return;
> +    }
> +    if (sch->curr_status.pmcw.mbfc) {
> +        /* Format 1, per-subchannel area. */
> +        struct cmbe *cmbe;
> +
> +        cmbe = qemu_get_ram_ptr(sch->curr_status.mba);

get_ram_ptr shouldn't be used. Better use cpu_physical_memory_map and unmap.


Alex

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
@ 2012-09-19 16:24   ` Alexander Graf
  2012-09-20 14:27   ` Anthony Liguori
  1 sibling, 0 replies; 17+ messages in thread
From: Alexander Graf @ 2012-09-19 16:24 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky


On 04.09.2012, at 17:13, Cornelia Huck wrote:

> This patch enables using both virtio-xxx-s390 and virtio-xxx-ccw
> by making the alias lookup code verify that a driver is actually
> registered.
> 
> (Only included in order to allow testing of virtio-ccw; should be
> replaced by cleaning up the virtio bus model.)

Phew. That explains how the overlapping aliases work for you. We really need to come up with something more clever here :)


Alex

> 
> Not-signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
> blockdev.c        |  6 +---
> hw/qdev-monitor.c | 85 +++++++++++++++++++++++++++++++++----------------------
> vl.c              |  6 +---
> 3 files changed, 53 insertions(+), 44 deletions(-)
> 
> diff --git a/blockdev.c b/blockdev.c
> index 7c83baa..a7c39b6 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -560,11 +560,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
>     case IF_VIRTIO:
>         /* add virtio block device */
>         opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
> -        if (arch_type == QEMU_ARCH_S390X) {
> -            qemu_opt_set(opts, "driver", "virtio-blk-s390");
> -        } else {
> -            qemu_opt_set(opts, "driver", "virtio-blk-pci");
> -        }
> +        qemu_opt_set(opts, "driver", "virtio-blk");
>         qemu_opt_set(opts, "drive", dinfo->id);
>         if (devaddr)
>             qemu_opt_set(opts, "addr", devaddr);
> diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
> index 92b7c59..9245a1e 100644
> --- a/hw/qdev-monitor.c
> +++ b/hw/qdev-monitor.c
> @@ -118,9 +118,53 @@ static int set_property(const char *name, const char *value, void *opaque)
>     return 0;
> }
> 
> -static const char *find_typename_by_alias(const char *alias)
> +static BusState *qbus_find_recursive(BusState *bus, const char *name,
> +                                     const char *bus_typename)
> +{
> +    BusChild *kid;
> +    BusState *child, *ret;
> +    int match = 1;
> +
> +    if (name && (strcmp(bus->name, name) != 0)) {
> +        match = 0;
> +    }
> +    if (bus_typename &&
> +        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
> +        match = 0;
> +    }
> +    if (match) {
> +        return bus;
> +    }
> +
> +    QTAILQ_FOREACH(kid, &bus->children, sibling) {
> +        DeviceState *dev = kid->child;
> +        QLIST_FOREACH(child, &dev->child_bus, sibling) {
> +            ret = qbus_find_recursive(child, name, bus_typename);
> +            if (ret) {
> +                return ret;
> +            }
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static bool qdev_verify_bus(DeviceClass *dc)
> +{
> +    BusState *bus;
> +
> +    if (dc) {
> +        bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
> +        if (bus) {
> +            return true;
> +        }
> +    }
> +    return false;
> +}
> +
> +static const char *find_typename_by_alias(const char *alias, bool check_bus)
> {
>     int i;
> +    ObjectClass *oc;
> 
>     for (i = 0; qdev_alias_table[i].alias; i++) {
>         if (qdev_alias_table[i].arch_mask &&
> @@ -129,7 +173,10 @@ static const char *find_typename_by_alias(const char *alias)
>         }
> 
>         if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
> -            return qdev_alias_table[i].typename;
> +            oc = object_class_by_name(qdev_alias_table[i].typename);
> +            if (oc && (!check_bus || qdev_verify_bus(DEVICE_CLASS(oc)))) {
> +                return qdev_alias_table[i].typename;
> +            }
>         }
>     }
> 
> @@ -155,7 +202,7 @@ int qdev_device_help(QemuOpts *opts)
> 
>     klass = object_class_by_name(driver);
>     if (!klass) {
> -        const char *typename = find_typename_by_alias(driver);
> +        const char *typename = find_typename_by_alias(driver, false);
> 
>         if (typename) {
>             driver = typename;
> @@ -283,36 +330,6 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem)
>     return NULL;
> }
> 
> -static BusState *qbus_find_recursive(BusState *bus, const char *name,
> -                                     const char *bus_typename)
> -{
> -    BusChild *kid;
> -    BusState *child, *ret;
> -    int match = 1;
> -
> -    if (name && (strcmp(bus->name, name) != 0)) {
> -        match = 0;
> -    }
> -    if (bus_typename &&
> -        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
> -        match = 0;
> -    }
> -    if (match) {
> -        return bus;
> -    }
> -
> -    QTAILQ_FOREACH(kid, &bus->children, sibling) {
> -        DeviceState *dev = kid->child;
> -        QLIST_FOREACH(child, &dev->child_bus, sibling) {
> -            ret = qbus_find_recursive(child, name, bus_typename);
> -            if (ret) {
> -                return ret;
> -            }
> -        }
> -    }
> -    return NULL;
> -}
> -
> static BusState *qbus_find(const char *path)
> {
>     DeviceState *dev;
> @@ -417,7 +434,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
>     /* find driver */
>     obj = object_class_by_name(driver);
>     if (!obj) {
> -        const char *typename = find_typename_by_alias(driver);
> +        const char *typename = find_typename_by_alias(driver, true);
> 
>         if (typename) {
>             driver = typename;
> diff --git a/vl.c b/vl.c
> index 2b8cae6..788a536 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2113,11 +2113,7 @@ static int virtcon_parse(const char *devname)
>     }
> 
>     bus_opts = qemu_opts_create(device, NULL, 0, NULL);
> -    if (arch_type == QEMU_ARCH_S390X) {
> -        qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
> -    } else {
> -        qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
> -    } 
> +    qemu_opt_set(bus_opts, "driver", "virtio-serial");
> 
>     dev_opts = qemu_opts_create(device, NULL, 0, NULL);
>     qemu_opt_set(dev_opts, "driver", "virtconsole");
> -- 
> 1.7.11.5
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-19 16:12   ` Alexander Graf
@ 2012-09-20  7:39     ` Jens Freimann
  0 siblings, 0 replies; 17+ messages in thread
From: Jens Freimann @ 2012-09-20  7:39 UTC (permalink / raw)
  To: Alexander Graf
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Christian Borntraeger, Avi Kivity, Cornelia Huck,
	Martin Schwidefsky

On Wed, Sep 19, 2012 at 06:12:55PM +0200, Alexander Graf wrote:
> Just a really quick glimpse. This patch is huge :).
> 
> On 04.09.2012, at 17:13, Cornelia Huck wrote:
> 
> > Add a new virtio transport that uses channel commands to perform
> > virtio operations.
> > 
> > Add a new machine type s390-ccw that uses this virtio-ccw transport
> > and make it the default machine for s390.
> > 
> > Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> > ---
> > 
> > Changes v1->v2:
> > - update to virtio-ccw interface changes
> > 
> > ---
> > hw/qdev-monitor.c      |   5 +
> > hw/s390-virtio.c       | 277 ++++++++++++----
> > hw/s390x/Makefile.objs |   1 +
> > hw/s390x/css.c         |  45 +++
> > hw/s390x/css.h         |   3 +
> > hw/s390x/virtio-ccw.c  | 875 +++++++++++++++++++++++++++++++++++++++++++++++++
> > hw/s390x/virtio-ccw.h  |  79 +++++
> > vl.c                   |   1 +
> > 8 files changed, 1215 insertions(+), 71 deletions(-)
> > create mode 100644 hw/s390x/virtio-ccw.c
> > create mode 100644 hw/s390x/virtio-ccw.h
> > 
> > diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
> > index 33b7f79..92b7c59 100644
> > --- a/hw/qdev-monitor.c
> > +++ b/hw/qdev-monitor.c
> > @@ -42,6 +42,11 @@ static const QDevAlias qdev_alias_table[] = {
> >     { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
> >     { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
> >     { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
> > +    { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
> > +    { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
> > +    { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
> > +    { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
> > +    { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
> 
> How does this work? We want to default to virtio-xxx-pci on !s390, to virtio-xxx-s390 on the legacy machine and to virtio-xxx-ccw for the s390-ccw machine, right?

See patch 5. As you said, we need to find a nice way to solve this. 

> > [...]  
> > +
> > +static void virtio_ccw_notify(void *opaque, uint16_t vector)
> > +{
> > +    VirtioCcwData *dev = opaque;
> > +    SubchDev *sch = dev->sch;
> > +    uint64_t indicators;
> > +
> > +    if (vector >= VIRTIO_PCI_QUEUE_MAX) {
> > +        return;
> > +    }
> > +
> > +    qemu_mutex_lock(&sch->mutex);
> 
> Why do you need this lock?

The locks are probably a leftover from an earlier version where Conny used
a separate thread for subchannel work. We will double check if the lock would
be needed when the big QEMU lock is gone and, if yes, leave a comment in the code 
as a reminder.

Jens

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
  2012-09-19 16:12   ` Alexander Graf
@ 2012-09-20 14:24   ` Anthony Liguori
  2012-09-20 14:50     ` Alexander Graf
  2012-12-18  8:45   ` Paolo Bonzini
  2 siblings, 1 reply; 17+ messages in thread
From: Anthony Liguori @ 2012-09-20 14:24 UTC (permalink / raw)
  To: Cornelia Huck, KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Marcelo Tosatti, Sebastian Ott, Rusty Russell,
	Heiko Carstens, Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

Cornelia Huck <cornelia.huck@de.ibm.com> writes:

> Add a new virtio transport that uses channel commands to perform
> virtio operations.
>
> Add a new machine type s390-ccw that uses this virtio-ccw transport
> and make it the default machine for s390.
>
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
>
> Changes v1->v2:
> - update to virtio-ccw interface changes
>
> ---
>  hw/qdev-monitor.c      |   5 +
>  hw/s390-virtio.c       | 277 ++++++++++++----
>  hw/s390x/Makefile.objs |   1 +
>  hw/s390x/css.c         |  45 +++
>  hw/s390x/css.h         |   3 +
>  hw/s390x/virtio-ccw.c  | 875 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/virtio-ccw.h  |  79 +++++
>  vl.c                   |   1 +
>  8 files changed, 1215 insertions(+), 71 deletions(-)
>  create mode 100644 hw/s390x/virtio-ccw.c
>  create mode 100644 hw/s390x/virtio-ccw.h
>
> diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
> index 33b7f79..92b7c59 100644
> --- a/hw/qdev-monitor.c
> +++ b/hw/qdev-monitor.c
> @@ -42,6 +42,11 @@ static const QDevAlias qdev_alias_table[] = {
>      { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
>      { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
>      { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
> +    { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
> +    { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
> +    { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
> +    { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
> +    { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
>      { "lsi53c895a", "lsi" },
>      { "ich9-ahci", "ahci" },

Please don't add aliases.  That's just an ugly hack to maintain compatibility.

>      { }
> diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
> index 47eed35..2509291 100644
> --- a/hw/s390-virtio.c
> +++ b/hw/s390-virtio.c
> @@ -30,8 +30,11 @@
>  #include "hw/sysbus.h"
>  #include "kvm.h"
>  #include "exec-memory.h"
> +#include "qemu-thread.h"
>  
>  #include "hw/s390-virtio-bus.h"
> +#include "hw/s390x/css.h"
> +#include "hw/s390x/virtio-ccw.h"
>  
>  //#define DEBUG_S390
>  
> @@ -46,6 +49,7 @@
>  #define KVM_S390_VIRTIO_NOTIFY          0
>  #define KVM_S390_VIRTIO_RESET           1
>  #define KVM_S390_VIRTIO_SET_STATUS      2
> +#define KVM_S390_VIRTIO_CCW_NOTIFY      3
>  
>  #define KERN_IMAGE_START                0x010000UL
>  #define KERN_PARM_AREA                  0x010480UL
> @@ -62,6 +66,7 @@
>  
>  static VirtIOS390Bus *s390_bus;
>  static S390CPU **ipi_states;
> +VirtioCcwBus *ccw_bus;
>  
>  S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
>  {
> @@ -75,15 +80,21 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
>  int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
>  {
>      int r = 0, i;
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
>  
>      dprintf("KVM hypercall: %ld\n", hypercall);
>      switch (hypercall) {
>      case KVM_S390_VIRTIO_NOTIFY:
>          if (mem > ram_size) {
> -            VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
> -                                                               mem, &i);
> -            if (dev) {
> -                virtio_queue_notify(dev->vdev, i);
> +            if (s390_bus) {
> +                VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
> +                                                                   mem, &i);
> +                if (dev) {
> +                    virtio_queue_notify(dev->vdev, i);
> +                } else {
> +                    r = -EINVAL;
> +                }
>              } else {
>                  r = -EINVAL;
>              }
> @@ -92,28 +103,49 @@ int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
>          }
>          break;
>      case KVM_S390_VIRTIO_RESET:
> -    {
> -        VirtIOS390Device *dev;
> -
> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
> -        virtio_reset(dev->vdev);
> -        stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
> -        s390_virtio_device_sync(dev);
> -        s390_virtio_reset_idx(dev);
> +        if (s390_bus) {
> +            VirtIOS390Device *dev;
> +
> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
> +            virtio_reset(dev->vdev);
> +            stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
> +            s390_virtio_device_sync(dev);
> +            s390_virtio_reset_idx(dev);
> +        } else {
> +            r = -EINVAL;
> +        }
>          break;
> -    }
>      case KVM_S390_VIRTIO_SET_STATUS:
> -    {
> -        VirtIOS390Device *dev;
> +        if (s390_bus) {
> +            VirtIOS390Device *dev;
>  
> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
> -        if (dev) {
> -            s390_virtio_device_update_status(dev);
> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
> +            if (dev) {
> +                s390_virtio_device_update_status(dev);
> +            } else {
> +                r = -EINVAL;
> +            }
>          } else {
>              r = -EINVAL;
>          }
>          break;
> -    }
> +    case KVM_S390_VIRTIO_CCW_NOTIFY:
> +        if (ccw_bus) {
> +            if (ioinst_disassemble_sch_ident(env->regs[2], &m, &cssid, &ssid,
> +                                             &schid)) {
> +                r = -EINVAL;
> +            } else {
> +                sch = css_find_subch(m, cssid, ssid, schid);
> +                if (sch) {
> +                    virtio_queue_notify(virtio_ccw_get_vdev(sch), env->regs[3]);
> +                } else {
> +                    r = -EINVAL;
> +                }
> +            }
> +         } else {
> +             r = -EINVAL;
> +         }
> +         break;
>      default:
>          r = -EINVAL;
>          break;
> @@ -150,58 +182,12 @@ unsigned s390_del_running_cpu(CPUS390XState *env)
>      return s390_running_cpus;
>  }
>  
> -/* PC hardware initialisation */
> -static void s390_init(ram_addr_t my_ram_size,
> -                      const char *boot_device,
> -                      const char *kernel_filename,
> -                      const char *kernel_cmdline,
> -                      const char *initrd_filename,
> -                      const char *cpu_model)
> +static CPUS390XState *s390_init_cpus(const char *cpu_model,
> +                                     uint8_t *storage_keys)
>  {
>      CPUS390XState *env = NULL;
> -    MemoryRegion *sysmem = get_system_memory();
> -    MemoryRegion *ram = g_new(MemoryRegion, 1);
> -    ram_addr_t kernel_size = 0;
> -    ram_addr_t initrd_offset;
> -    ram_addr_t initrd_size = 0;
> -    int shift = 0;
> -    uint8_t *storage_keys;
> -    void *virtio_region;
> -    target_phys_addr_t virtio_region_len;
> -    target_phys_addr_t virtio_region_start;
>      int i;
>  
> -    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> -       guests > 64GB can be specified in 2MB steps etc. */
> -    while ((my_ram_size >> (20 + shift)) > 65535) {
> -        shift++;
> -    }
> -    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> -
> -    /* lets propagate the changed ram size into the global variable. */
> -    ram_size = my_ram_size;
> -
> -    /* get a BUS */
> -    s390_bus = s390_virtio_bus_init(&my_ram_size);
> -
> -    /* allocate RAM */
> -    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> -    vmstate_register_ram_global(ram);
> -    memory_region_add_subregion(sysmem, 0, ram);
> -
> -    /* clear virtio region */
> -    virtio_region_len = my_ram_size - ram_size;
> -    virtio_region_start = ram_size;
> -    virtio_region = cpu_physical_memory_map(virtio_region_start,
> -                                            &virtio_region_len, true);
> -    memset(virtio_region, 0, virtio_region_len);
> -    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
> -                              virtio_region_len);
> -
> -    /* allocate storage keys */
> -    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> -
> -    /* init CPUs */
>      if (cpu_model == NULL) {
>          cpu_model = "host";
>      }
> @@ -222,6 +208,17 @@ static void s390_init(ram_addr_t my_ram_size,
>          tmp_env->exception_index = EXCP_HLT;
>          tmp_env->storage_keys = storage_keys;
>      }
> +    return env;
> +}
> +
> +static void s390_set_up_kernel(CPUS390XState *env,
> +                               const char *kernel_filename,
> +                               const char *kernel_cmdline,
> +                               const char *initrd_filename)
> +{
> +    ram_addr_t kernel_size = 0;
> +    ram_addr_t initrd_offset;
> +    ram_addr_t initrd_size = 0;
>  
>      /* One CPU has to run */
>      s390_add_running_cpu(env);
> @@ -294,8 +291,13 @@ static void s390_init(ram_addr_t my_ram_size,
>                 strlen(kernel_cmdline) + 1);
>      }
>  
> -    /* Create VirtIO network adapters */
> -    for(i = 0; i < nb_nics; i++) {
> +}
> +
> +static void s390_create_virtio_net(BusState *bus, const char *name)
> +{
> +    int i;
> +
> +    for (i = 0; i < nb_nics; i++) {
>          NICInfo *nd = &nd_table[i];
>          DeviceState *dev;
>  
> @@ -308,7 +310,7 @@ static void s390_init(ram_addr_t my_ram_size,
>              exit(1);
>          }
>  
> -        dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
> +        dev = qdev_create(bus, name);
>          qdev_set_nic_properties(dev, nd);
>          qdev_init_nofail(dev);
>      }
> @@ -329,6 +331,63 @@ static void s390_init(ram_addr_t my_ram_size,
>      }
>  }
>  
> +/* PC hardware initialisation */
> +static void s390_init(ram_addr_t my_ram_size,
> +                      const char *boot_device,
> +                      const char *kernel_filename,
> +                      const char *kernel_cmdline,
> +                      const char *initrd_filename,
> +                      const char *cpu_model)
> +{
> +    CPUS390XState *env = NULL;
> +    MemoryRegion *sysmem = get_system_memory();
> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
> +    int shift = 0;
> +    uint8_t *storage_keys;
> +    void *virtio_region;
> +    target_phys_addr_t virtio_region_len;
> +    target_phys_addr_t virtio_region_start;
> +
> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> +       guests > 64GB can be specified in 2MB steps etc. */
> +    while ((my_ram_size >> (20 + shift)) > 65535) {
> +        shift++;
> +    }
> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> +
> +    /* lets propagate the changed ram size into the global variable. */
> +    ram_size = my_ram_size;
> +
> +    /* get a BUS */
> +    s390_bus = s390_virtio_bus_init(&my_ram_size);
> +
> +    /* allocate RAM */
> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> +    vmstate_register_ram_global(ram);
> +    memory_region_add_subregion(sysmem, 0, ram);
> +
> +    /* clear virtio region */
> +    virtio_region_len = my_ram_size - ram_size;
> +    virtio_region_start = ram_size;
> +    virtio_region = cpu_physical_memory_map(virtio_region_start,
> +                                            &virtio_region_len, true);
> +    memset(virtio_region, 0, virtio_region_len);
> +    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
> +                              virtio_region_len);
> +
> +    /* allocate storage keys */
> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> +
> +    /* init CPUs */
> +    env = s390_init_cpus(cpu_model, storage_keys);
> +
> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
> +
> +    /* Create VirtIO network adapters */
> +    s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
> +
> +}
> +
>  static QEMUMachine s390_machine = {
>      .name = "s390-virtio",
>      .alias = "s390",
> @@ -341,7 +400,6 @@ static QEMUMachine s390_machine = {
>      .no_sdcard = 1,
>      .use_virtcon = 1,
>      .max_cpus = 255,
> -    .is_default = 1,
>  };
>  
>  static void s390_machine_init(void)
> @@ -350,3 +408,80 @@ static void s390_machine_init(void)
>  }
>  
>  machine_init(s390_machine_init);

Alex et al.

Now that we're doing virtio-ccw for s390, any reason to keep around the
old virtio transport?

Can we at least schedule it for removal?

> +
> +static void ccw_init(ram_addr_t my_ram_size,
> +                     const char *boot_device,
> +                     const char *kernel_filename,
> +                     const char *kernel_cmdline,
> +                     const char *initrd_filename,
> +                     const char *cpu_model)
> +{
> +    CPUS390XState *env = NULL;
> +    MemoryRegion *sysmem = get_system_memory();
> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
> +    int shift = 0;
> +    uint8_t *storage_keys;
> +    int ret;
> +
> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
> +       guests > 64GB can be specified in 2MB steps etc. */
> +    while ((my_ram_size >> (20 + shift)) > 65535) {
> +        shift++;
> +    }
> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
> +
> +    /* lets propagate the changed ram size into the global variable. */
> +    ram_size = my_ram_size;
> +
> +    /* get a BUS */
> +    ccw_bus = virtio_ccw_bus_init();
> +
> +    /* allocate RAM */
> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
> +    vmstate_register_ram_global(ram);
> +    memory_region_add_subregion(sysmem, 0, ram);
> +
> +    /* allocate storage keys */
> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
> +
> +    /* init CPUs */
> +    env = s390_init_cpus(cpu_model, storage_keys);
> +
> +    kvm_s390_enable_css_support(env);
> +
> +    /*
> +     * Create virtual css and set it as default so that non mcss-e
> +     * enabled guests only see virtio devices.
> +     */
> +    ret = css_create_css_image(VIRTUAL_CSSID, true);
> +    assert(ret == 0);
> +
> +
> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
> +
> +    /* Create VirtIO network adapters */
> +    s390_create_virtio_net((BusState *)ccw_bus, "virtio-net-ccw");
> +
> +}
> +
> +static QEMUMachine ccw_machine = {
> +    .name = "s390-ccw-virtio",
> +    .alias = "s390-ccw",
> +    .desc = "VirtIO-ccw based S390 machine",
> +    .init = ccw_init,
> +    .no_cdrom = 1,
> +    .no_floppy = 1,
> +    .no_serial = 1,
> +    .no_parallel = 1,
> +    .no_sdcard = 1,
> +    .use_virtcon = 1,
> +    .max_cpus = 255,
> +    .is_default = 1,
> +};
> +
> +static void ccw_machine_init(void)
> +{
> +    qemu_register_machine(&ccw_machine);
> +}
> +
> +machine_init(ccw_machine_init);
> diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
> index 93b41fb..e4c3d6f 100644
> --- a/hw/s390x/Makefile.objs
> +++ b/hw/s390x/Makefile.objs
> @@ -2,3 +2,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o
>  
>  obj-y := $(addprefix ../,$(obj-y))
>  obj-y += css.o
> +obj-y += virtio-ccw.o
> diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> index b9b6e48..a671e28 100644
> --- a/hw/s390x/css.c
> +++ b/hw/s390x/css.c
> @@ -441,6 +441,51 @@ 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];
>  }
>  
> +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
> +{
> +    if (!channel_subsys->css[cssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys->css[cssid]->sch_set[ssid]) {
> +        return false;
> +    }
> +
> +    return !!test_bit(devno,
> +                      channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
> +}
> +
> +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno,
> +                      SubchDev *sch)
> +{
> +    CssImage *css;
> +    SubchSet *s_set;
> +
> +    if (!channel_subsys->css[cssid]) {
> +        fprintf(stderr,
> +                "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
> +                __func__, cssid, ssid, schid);
> +       return;
> +   }
> +    css = channel_subsys->css[cssid];
> +
> +   if (!css->sch_set[ssid]) {
> +        size_t set_size = sizeof(SubchSet) +
> +            BITS_TO_LONGS(MAX_SCHID + 1) * 2;
> +        css->sch_set[ssid] = g_malloc0(set_size);
> +    }
> +    s_set = css->sch_set[ssid];
> +
> +    s_set->sch[schid] = sch;
> +    if (sch) {
> +        set_bit(schid, s_set->schids_used);
> +        set_bit(devno, s_set->devnos_used);
> +    } else {
> +        clear_bit(schid, s_set->schids_used);
> +        clear_bit(schid, s_set->devnos_used);
> +    }
> +}
> +
> +
>  bool css_present(uint8_t cssid)
>  {
>      return (channel_subsys->css[cssid] != NULL);
> diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> index f3590eb..6876633 100644
> --- a/hw/s390x/css.h
> +++ b/hw/s390x/css.h
> @@ -53,6 +53,9 @@ struct SubchDev {
>  };
>  
>  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);
>  void css_reset(void);
>  void css_reset_sch(SubchDev *sch);
> diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> new file mode 100644
> index 0000000..28be1a7
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.c
> @@ -0,0 +1,875 @@
> +/*
> + * virtio ccw target implementation
> + *
> + * 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.
> + */
> +
> +#include <hw/hw.h>

#include "hw.h"

> +#include "block.h"
> +#include "blockdev.h"
> +#include "sysemu.h"
> +#include "net.h"
> +#include "monitor.h"
> +#include "qemu-thread.h"
> +#include "hw/virtio.h"
> +#include "hw/virtio-serial.h"
> +#include "hw/virtio-net.h"
> +#include "hw/sysbus.h"
> +#include "bitops.h"
> +
> +#include "ioinst.h"
> +#include "css.h"
> +#include "virtio-ccw.h"
> +
> +static const TypeInfo virtio_ccw_bus_info = {
> +    .name = TYPE_VIRTIO_CCW_BUS,
> +    .parent = TYPE_BUS,
> +    .instance_size = sizeof(VirtioCcwBus),
> +};
> +
> +static const VirtIOBindings virtio_ccw_bindings;
> +
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> +{
> +    VirtIODevice *vdev = NULL;
> +
> +    if (sch->driver_data) {
> +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> +    }
> +    return vdev;
> +}
> +
> +static void virtio_ccw_reset_subchannels(void *opaque)
> +{
> +    VirtioCcwBus *bus = opaque;
> +    BusChild *kid;
> +    VirtioCcwData *data;
> +
> +    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
> +        data = (VirtioCcwData *)kid->child;
> +        virtio_reset(data->vdev);
> +        css_reset_sch(data->sch);
> +    }
> +    css_reset();
> +}
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void)
> +{
> +    VirtioCcwBus *cbus;
> +    BusState *bus;
> +    DeviceState *dev;
> +
> +    /* Create bridge device */
> +    dev = qdev_create(NULL, "virtio-ccw-bridge");
> +    qdev_init_nofail(dev);
> +
> +    /* Create bus on bridge device */
> +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
> +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
> +
> +    /* Enable hotplugging */
> +    bus->allow_hotplug = 1;
> +
> +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
> +    return cbus;
> +}
> +
> +/* Communication blocks used by several channel commands. */
> +typedef struct VqInfoBlock {
> +    uint64_t queue;
> +    uint32_t align;
> +    uint16_t index;
> +    uint16_t num;
> +} QEMU_PACKED VqInfoBlock;
> +
> +typedef struct VqConfigBlock {
> +    uint16_t index;
> +    uint16_t num_max;
> +} QEMU_PACKED VqConfigBlock;
> +
> +typedef struct VirtioFeatDesc {
> +    uint32_t features;
> +    uint8_t index;
> +} QEMU_PACKED VirtioFeatDesc;
> +
> +/* Specify where the virtqueues for the subchannel are in guest memory. */
> +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
> +                              uint16_t index, uint16_t num)
> +{
> +    VirtioCcwData *data = sch->driver_data;
> +
> +    if (index > VIRTIO_PCI_QUEUE_MAX) {
> +        return -EINVAL;
> +    }
> +
> +    /* Current code in virtio.c relies on 4K alignment. */
> +    if (align != 4096) {
> +        return -EINVAL;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    virtio_queue_set_addr(data->vdev, index, addr);
> +    if (!addr) {
> +        virtio_queue_set_vector(data->vdev, index, 0);
> +    } else {
> +        /* Fail if we don't have a big enough queue. */
> +        /* TODO: Add interface to handle vring.num changing */
> +        if (virtio_queue_get_num(data->vdev, index) > num) {
> +            return -EINVAL;
> +        }
> +        virtio_queue_set_vector(data->vdev, index, index);
> +    }
> +    return 0;
> +}
> +
> +static int virtio_ccw_cb(SubchDev *sch, CCW1 *ccw)
> +{
> +    int ret;
> +    VqInfoBlock info;
> +    uint8_t status;
> +    VirtioFeatDesc features;
> +    void *config;
> +    uint64_t *indicators;
> +    VqConfigBlock vq_config;
> +    VirtioCcwData *data = sch->driver_data;
> +    bool check_len;
> +    int len;
> +
> +    if (!ccw) {
> +        return -EIO;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
> +
> +    /* Look at the command. */
> +    switch (ccw->cmd_code) {
> +    case CCW_CMD_SET_VQ:
> +        if (check_len) {
> +            if (ccw->count != sizeof(info)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(info)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;

This is an interesting check.  But I don't think we want to spread
qemu_get_ram_ptr() around so perhaps you can introduce a
qemu_is_ram_addr_valid() call?

> +        } else {
> +            info.queue = ldq_phys(ccw->cda);
> +            info.align = ldl_phys(ccw->cda + sizeof(info.queue));
> +            info.index = lduw_phys(ccw->cda + sizeof(info.queue)
> +                                   + sizeof(info.align));
> +            info.num = lduw_phys(ccw->cda + sizeof(info.queue)
> +                                 + sizeof(info.align)
> +                                 + sizeof(info.index));
> +            ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index,
> +                                     info.num);
> +            sch->curr_status.scsw.count = 0;
> +        }
> +        break;
> +    case CCW_CMD_VDEV_RESET:
> +        virtio_reset(data->vdev);
> +        ret = 0;
> +        break;
> +    case CCW_CMD_READ_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
> +            if (features.index < ARRAY_SIZE(data->host_features)) {
> +                features.features = data->host_features[features.index];
> +            } else {
> +                /* Return zeroes if the guest supports more feature bits. */
> +                features.features = 0;
> +            }
> +            stw_le_phys(ccw->cda, features.features);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
> +            features.features = bswap32(lduw_phys(ccw->cda));
> +            if (features.index < ARRAY_SIZE(data->host_features)) {
> +                if (data->vdev->set_features) {
> +                    data->vdev->set_features(data->vdev, features.features);
> +                }
> +                data->vdev->guest_features = features.features;
> +            } else {
> +                /*
> +                 * If the guest supports more feature bits, assert that it
> +                 * passes us zeroes for those we don't support.
> +                 */
> +                if (features.features) {
> +                    fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n",
> +                            features.index, features.features);
> +                    /* XXX: do a unit check here? */

In general, we try to avoid guest initiated printf()s.  libvirt blindly
logs stdio from QEMU to the disk file system and a malicious guest
could, in theory, exhaust the host disk space.

> +                }
> +            }
> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_READ_CONF:
> +        if (check_len) {
> +            if (ccw->count > data->vdev->config_len) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, data->vdev->config_len);
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            data->vdev->get_config(data->vdev, data->vdev->config);
> +            cpu_physical_memory_write(ccw->cda, data->vdev->config, len);
> +            sch->curr_status.scsw.count = ccw->count - len;
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_CONF:
> +        if (check_len) {
> +            if (ccw->count > data->vdev->config_len) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, data->vdev->config_len);
> +        config = qemu_get_ram_ptr(ccw->cda);
> +        if (!config) {
> +            ret = -EFAULT;
> +        } else {
> +            memcpy(data->vdev->config, config, len);
> +            if (data->vdev->set_config) {
> +                data->vdev->set_config(data->vdev, data->vdev->config);
> +            }
> +            sch->curr_status.scsw.count = ccw->count - len;
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_STATUS:
> +        if (check_len) {
> +            if (ccw->count != sizeof(status)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(status)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            status = ldub_phys(ccw->cda);
> +            virtio_set_status(data->vdev, status);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(status);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_SET_IND:
> +        if (check_len) {
> +            if (ccw->count != sizeof(*indicators)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(*indicators)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        indicators = qemu_get_ram_ptr(ccw->cda);
> +        if (!indicators) {
> +            ret = -EFAULT;
> +        } else {
> +            data->indicators = ccw->cda;
> +            sch->curr_status.scsw.count = ccw->count - sizeof(*indicators);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_READ_VQ_CONF:
> +        if (check_len) {
> +            if (ccw->count != sizeof(vq_config)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(vq_config)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            vq_config.index = lduw_phys(ccw->cda);
> +            vq_config.num_max = virtio_queue_get_num(data->vdev,
> +                                                     vq_config.index);
> +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num_max);
> +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
> +            ret = 0;
> +        }
> +        break;
> +    default:
> +        ret = -EOPNOTSUPP;
> +        break;
> +    }
> +    return ret;
> +}
> +
> +static int virtio_ccw_device_init(VirtioCcwData *dev, VirtIODevice *vdev)
> +{
> +    unsigned int cssid = 0;
> +    unsigned int ssid = 0;
> +    unsigned int schid;
> +    unsigned int devno;
> +    bool have_devno = false;
> +    bool found = false;
> +    SubchDev *sch;
> +    int ret;
> +    int num;
> +
> +    sch = g_malloc0(sizeof(SubchDev));
> +
> +    sch->driver_data = dev;
> +    dev->sch = sch;
> +
> +    dev->vdev = vdev;
> +    dev->indicators = 0;
> +
> +    /* Initialize subchannel structure. */
> +    qemu_mutex_init(&sch->mutex);
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    sch->orb = NULL;
> +    /*
> +     * 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)) {
> +                ret = -EINVAL;
> +                error_report("Invalid cssid or ssid: cssid %x, ssid %x",
> +                             cssid, ssid);
> +                goto out_err;
> +            }
> +            /* Enforce use of virtual cssid. */
> +            if (cssid != VIRTUAL_CSSID) {
> +                ret = -EINVAL;
> +                error_report("cssid %x not valid for virtio devices", cssid);
> +                goto out_err;
> +            }
> +            if (css_devno_used(cssid, ssid, devno)) {
> +                ret = -EEXIST;
> +                error_report("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 {
> +            ret = -EINVAL;
> +            error_report("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) {
> +            ret = -ENODEV;
> +            error_report("No free subchannel found for %x.%x.%04x", cssid, ssid,
> +                         devno);
> +            goto out_err;
> +        }
> +    } 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) {
> +                            ret = -ENODEV;
> +                            error_report("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) {
> +            ret = -ENODEV;
> +            error_report("Virtual channel subsystem is full!");
> +            goto out_err;
> +        }
> +    }
> +
> +    /* Build initial schib. */
> +    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
> +
> +    sch->ccw_cb = virtio_ccw_cb;
> +
> +    /* Build senseid data. */
> +    memset(&sch->id, 0, sizeof(SenseId));
> +    sch->id.reserved = 0xff;
> +    sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
> +    sch->id.cu_model = dev->vdev->device_id;
> +
> +    virtio_bind_device(vdev, &virtio_ccw_bindings, dev);
> +    /* Only the first 32 feature bits are used. */
> +    dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]);
> +
> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
> +                     &sch->curr_status, dev->qdev.hotplugged, 1, 1);
> +    return 0;
> +
> +out_err:
> +    dev->sch = NULL;
> +    g_free(sch);
> +    return ret;
> +}
> +
> +static int virtio_ccw_exit(VirtioCcwData *dev)
> +{
> +    SubchDev *sch = dev->sch;
> +
> +    if (sch) {
> +        css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
> +        g_free(sch);
> +    }
> +    dev->indicators = 0;
> +    return 0;
> +}
> +
> +static int virtio_ccw_net_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_net_exit(VirtioCcwData *dev)
> +{
> +    virtio_net_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_blk_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_blk_init((DeviceState *)dev, &dev->blk);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_blk_exit(VirtioCcwData *dev)
> +{
> +    virtio_blk_exit(dev->vdev);
> +    blockdev_mark_auto_del(dev->blk.conf.bs);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_serial_init(VirtioCcwData *dev)
> +{
> +    VirtioCcwBus *bus;
> +    VirtIODevice *vdev;
> +    int r;
> +
> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
> +
> +    vdev = virtio_serial_init((DeviceState *)dev, &dev->serial);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    r = virtio_ccw_device_init(dev, vdev);
> +    if (!r) {
> +        bus->console = dev;
> +    }
> +
> +    return r;
> +}
> +
> +static int virtio_ccw_serial_exit(VirtioCcwData *dev)
> +{
> +    VirtioCcwBus *bus;
> +
> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
> +    bus->console = NULL;
> +    virtio_serial_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_balloon_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_balloon_init((DeviceState *)dev);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_balloon_exit(VirtioCcwData *dev)
> +{
> +    virtio_balloon_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +static int virtio_ccw_scsi_init(VirtioCcwData *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return virtio_ccw_device_init(dev, vdev);
> +}
> +
> +static int virtio_ccw_scsi_exit(VirtioCcwData *dev)
> +{
> +    virtio_scsi_exit(dev->vdev);
> +    return virtio_ccw_exit(dev);
> +}
> +
> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus)
> +{
> +    return bus->console;
> +}
> +
> +static void virtio_ccw_notify(void *opaque, uint16_t vector)
> +{
> +    VirtioCcwData *dev = opaque;
> +    SubchDev *sch = dev->sch;
> +    uint64_t indicators;
> +
> +    if (vector >= VIRTIO_PCI_QUEUE_MAX) {
> +        return;
> +    }
> +
> +    qemu_mutex_lock(&sch->mutex);
> +    indicators = ldq_phys(dev->indicators);
> +    set_bit(vector, &indicators);
> +    stq_phys(dev->indicators, indicators);
> +
> +    css_conditional_io_interrupt(sch);
> +
> +    qemu_mutex_unlock(&sch->mutex);

I don't understand the mutex here...

> +}
> +
> +static unsigned virtio_ccw_get_features(void *opaque)
> +{
> +    VirtioCcwData *dev = opaque;
> +
> +    /* Only the first 32 feature bits are used. */
> +    return dev->host_features[0];
> +}
> +
> +/**************** Virtio-ccw Bus Device Descriptions *******************/
> +
> +static const VirtIOBindings virtio_ccw_bindings = {
> +    .notify = virtio_ccw_notify,
> +    .get_features = virtio_ccw_get_features,
> +};
> +
> +static Property virtio_ccw_net_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_NIC_PROPERTIES(VirtioCcwData, nic),
> +    DEFINE_PROP_UINT32("x-txtimer", VirtioCcwData,
> +                       net.txtimer, TX_TIMER_INTERVAL),
> +    DEFINE_PROP_INT32("x-txburst", VirtioCcwData,
> +                      net.txburst, TX_BURST),
> +    DEFINE_PROP_STRING("tx", VirtioCcwData, net.tx),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_net_init;
> +    k->exit = virtio_ccw_net_exit;
> +    dc->props = virtio_ccw_net_properties;
> +}
> +
> +static TypeInfo virtio_ccw_net = {
> +    .name          = "virtio-net-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_net_class_init,
> +};
> +
> +static Property virtio_ccw_blk_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_BLOCK_PROPERTIES(VirtioCcwData, blk.conf),
> +    DEFINE_PROP_STRING("serial", VirtioCcwData, blk.serial),
> +#ifdef __linux__
> +    DEFINE_PROP_BIT("scsi", VirtioCcwData, blk.scsi, 0, true),
> +#endif
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_blk_init;
> +    k->exit = virtio_ccw_blk_exit;
> +    dc->props = virtio_ccw_blk_properties;
> +}
> +
> +static TypeInfo virtio_ccw_blk = {
> +    .name          = "virtio-blk-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_blk_class_init,
> +};
> +
> +static Property virtio_ccw_serial_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_PROP_UINT32("max_ports", VirtioCcwData, serial.max_virtserial_ports,
> +                       31),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_serial_init;
> +    k->exit = virtio_ccw_serial_exit;
> +    dc->props = virtio_ccw_serial_properties;
> +}
> +
> +static TypeInfo virtio_ccw_serial = {
> +    .name          = "virtio-serial-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_serial_class_init,
> +};
> +
> +static Property virtio_ccw_balloon_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_balloon_init;
> +    k->exit = virtio_ccw_balloon_exit;
> +    dc->props = virtio_ccw_balloon_properties;
> +}
> +
> +static TypeInfo virtio_ccw_balloon = {
> +    .name          = "virtio-balloon-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_balloon_class_init,
> +};
> +
> +static Property virtio_ccw_scsi_properties[] = {
> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
> +    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwData, host_features[0], scsi),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_scsi_init;
> +    k->exit = virtio_ccw_scsi_exit;
> +    dc->props = virtio_ccw_scsi_properties;
> +}
> +
> +static TypeInfo virtio_ccw_scsi = {
> +    .name          = "virtio-scsi-ccw",
> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init    = virtio_ccw_scsi_class_init,
> +};
> +
> +static int virtio_ccw_busdev_init(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
> +
> +    return _info->init(_dev);
> +}
> +
> +static int virtio_ccw_busdev_exit(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
> +
> +    return _info->exit(_dev);
> +}
> +
> +static int virtio_ccw_busdev_unplug(DeviceState *dev)
> +{
> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
> +    SubchDev *sch = _dev->sch;
> +
> +    /*
> +     * 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. */
> +    qemu_mutex_lock(&sch->mutex);
> +    sch->curr_status.pmcw.ena = 0;
> +    sch->curr_status.pmcw.dnv = 0;
> +    qemu_mutex_unlock(&sch->mutex);
> +
> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
> +                     &sch->curr_status, 1, 0, 1);
> +
> +    object_unparent(OBJECT(dev));
> +    qdev_free(dev);
> +    return 0;
> +}
> +
> +static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->init = virtio_ccw_busdev_init;
> +    dc->exit = virtio_ccw_busdev_exit;
> +    dc->unplug = virtio_ccw_busdev_unplug;
> +    dc->bus_type = TYPE_VIRTIO_CCW_BUS;
> +
> +}
> +
> +static TypeInfo virtio_ccw_device_info = {
> +    .name = TYPE_VIRTIO_CCW_DEVICE,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(VirtioCcwData),
> +    .class_init = virtio_ccw_device_class_init,
> +    .class_size = sizeof(VirtIOCCWDeviceClass),
> +    .abstract = true,
> +};
> +
> +/***************** Virtio-ccw Bus Bridge Device ********************/
> +/* Only required to have the virtio bus as child in the system bus */
> +
> +static int virtio_ccw_bridge_init(SysBusDevice *dev)
> +{
> +    /* nothing */
> +    return 0;
> +}
> +
> +static void virtio_ccw_bridge_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_ccw_bridge_init;
> +    dc->no_user = 1;
> +}
> +
> +static TypeInfo virtio_ccw_bridge_info = {
> +    .name          = "virtio-ccw-bridge",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SysBusDevice),
> +    .class_init    = virtio_ccw_bridge_class_init,
> +};
> +
> +static void virtio_ccw_register(void)
> +{
> +    type_register_static(&virtio_ccw_bus_info);
> +    type_register_static(&virtio_ccw_device_info);
> +    type_register_static(&virtio_ccw_serial);
> +    type_register_static(&virtio_ccw_blk);
> +    type_register_static(&virtio_ccw_net);
> +    type_register_static(&virtio_ccw_balloon);
> +    type_register_static(&virtio_ccw_scsi);
> +    type_register_static(&virtio_ccw_bridge_info);
> +}
> +type_init(virtio_ccw_register);
> diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
> new file mode 100644
> index 0000000..8125acf
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.h
> @@ -0,0 +1,79 @@
> +/*
> + * virtio ccw target 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.
> + */
> +
> +#include <hw/virtio-blk.h>
> +#include <hw/virtio-net.h>
> +#include <hw/virtio-serial.h>
> +#include <hw/virtio-scsi.h>
> +
> +#define VIRTUAL_CSSID 0xfe
> +
> +#define VIRTIO_CCW_CU_TYPE 0x3832
> +#define VIRTIO_CCW_CHPID_TYPE 0x32
> +
> +#define CCW_CMD_SET_VQ       0x13
> +#define CCW_CMD_VDEV_RESET   0x33
> +#define CCW_CMD_READ_FEAT    0x12
> +#define CCW_CMD_WRITE_FEAT   0x11
> +#define CCW_CMD_READ_CONF    0x22
> +#define CCW_CMD_WRITE_CONF   0x21
> +#define CCW_CMD_WRITE_STATUS 0x31
> +#define CCW_CMD_SET_IND      0x43
> +#define CCW_CMD_READ_VQ_CONF 0x32
> +
> +#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
> +#define VIRTIO_CCW_DEVICE(obj) \
> +     OBJECT_CHECK(VirtioCcwData, (obj), TYPE_VIRTIO_CCW_DEVICE)
> +#define VIRTIO_CCW_DEVICE_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE)
> +#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE)
> +
> +#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus"
> +#define VIRTIO_CCW_BUS(obj) \
> +     OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS)
> +
> +typedef struct VirtioCcwData VirtioCcwData;
> +
> +typedef struct VirtIOCCWDeviceClass {
> +    DeviceClass qdev;
> +    int (*init)(VirtioCcwData *dev);
> +    int (*exit)(VirtioCcwData *dev);
> +} VirtIOCCWDeviceClass;
> +
> +/* Change here if we want to support more feature bits. */
> +#define VIRTIO_CCW_FEATURE_SIZE 1
> +
> +struct VirtioCcwData {
> +    DeviceState qdev;
> +    SubchDev *sch;
> +    VirtIODevice *vdev;
> +    char *bus_id;
> +    VirtIOBlkConf blk;
> +    NICConf nic;
> +    uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
> +    virtio_serial_conf serial;
> +    virtio_net_conf net;
> +    VirtIOSCSIConf scsi;
> +    /* Guest provided values: */
> +    target_phys_addr_t indicators;
> +};
> +
> +/* virtio-ccw bus type */
> +typedef struct VirtioCcwBus {
> +    BusState bus;
> +    VirtioCcwData *console;
> +} VirtioCcwBus;
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void);
> +void virtio_ccw_device_update_status(SubchDev *sch);
> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus);
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
> diff --git a/vl.c b/vl.c
> index 7c577fa..2b8cae6 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -289,6 +289,7 @@ static struct {
>      { .driver = "scsi-cd",              .flag = &default_cdrom     },
>      { .driver = "virtio-serial-pci",    .flag = &default_virtcon   },
>      { .driver = "virtio-serial-s390",   .flag = &default_virtcon   },
> +    { .driver = "virtio-serial-ccw",    .flag = &default_virtcon   },
>      { .driver = "virtio-serial",        .flag = &default_virtcon   },
>      { .driver = "VGA",                  .flag = &default_vga       },
>      { .driver = "isa-vga",              .flag = &default_vga       },
> -- 
> 1.7.11.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
  2012-09-19 16:24   ` Alexander Graf
@ 2012-09-20 14:27   ` Anthony Liguori
  2012-10-09 14:39     ` Cornelia Huck
  1 sibling, 1 reply; 17+ messages in thread
From: Anthony Liguori @ 2012-09-20 14:27 UTC (permalink / raw)
  To: Cornelia Huck, KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Marcelo Tosatti, Sebastian Ott, Rusty Russell,
	Heiko Carstens, Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

Cornelia Huck <cornelia.huck@de.ibm.com> writes:

> This patch enables using both virtio-xxx-s390 and virtio-xxx-ccw
> by making the alias lookup code verify that a driver is actually
> registered.
>
> (Only included in order to allow testing of virtio-ccw; should be
> replaced by cleaning up the virtio bus model.)
>
> Not-signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>

No more aliases.  Just drop the whole thing.

Regards,

Anthony Liguori

> ---
>  blockdev.c        |  6 +---
>  hw/qdev-monitor.c | 85 +++++++++++++++++++++++++++++++++----------------------
>  vl.c              |  6 +---
>  3 files changed, 53 insertions(+), 44 deletions(-)
>
> diff --git a/blockdev.c b/blockdev.c
> index 7c83baa..a7c39b6 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -560,11 +560,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
>      case IF_VIRTIO:
>          /* add virtio block device */
>          opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
> -        if (arch_type == QEMU_ARCH_S390X) {
> -            qemu_opt_set(opts, "driver", "virtio-blk-s390");
> -        } else {
> -            qemu_opt_set(opts, "driver", "virtio-blk-pci");
> -        }
> +        qemu_opt_set(opts, "driver", "virtio-blk");
>          qemu_opt_set(opts, "drive", dinfo->id);
>          if (devaddr)
>              qemu_opt_set(opts, "addr", devaddr);
> diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
> index 92b7c59..9245a1e 100644
> --- a/hw/qdev-monitor.c
> +++ b/hw/qdev-monitor.c
> @@ -118,9 +118,53 @@ static int set_property(const char *name, const char *value, void *opaque)
>      return 0;
>  }
>  
> -static const char *find_typename_by_alias(const char *alias)
> +static BusState *qbus_find_recursive(BusState *bus, const char *name,
> +                                     const char *bus_typename)
> +{
> +    BusChild *kid;
> +    BusState *child, *ret;
> +    int match = 1;
> +
> +    if (name && (strcmp(bus->name, name) != 0)) {
> +        match = 0;
> +    }
> +    if (bus_typename &&
> +        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
> +        match = 0;
> +    }
> +    if (match) {
> +        return bus;
> +    }
> +
> +    QTAILQ_FOREACH(kid, &bus->children, sibling) {
> +        DeviceState *dev = kid->child;
> +        QLIST_FOREACH(child, &dev->child_bus, sibling) {
> +            ret = qbus_find_recursive(child, name, bus_typename);
> +            if (ret) {
> +                return ret;
> +            }
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static bool qdev_verify_bus(DeviceClass *dc)
> +{
> +    BusState *bus;
> +
> +    if (dc) {
> +        bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
> +        if (bus) {
> +            return true;
> +        }
> +    }
> +    return false;
> +}
> +
> +static const char *find_typename_by_alias(const char *alias, bool check_bus)
>  {
>      int i;
> +    ObjectClass *oc;
>  
>      for (i = 0; qdev_alias_table[i].alias; i++) {
>          if (qdev_alias_table[i].arch_mask &&
> @@ -129,7 +173,10 @@ static const char *find_typename_by_alias(const char *alias)
>          }
>  
>          if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
> -            return qdev_alias_table[i].typename;
> +            oc = object_class_by_name(qdev_alias_table[i].typename);
> +            if (oc && (!check_bus || qdev_verify_bus(DEVICE_CLASS(oc)))) {
> +                return qdev_alias_table[i].typename;
> +            }
>          }
>      }
>  
> @@ -155,7 +202,7 @@ int qdev_device_help(QemuOpts *opts)
>  
>      klass = object_class_by_name(driver);
>      if (!klass) {
> -        const char *typename = find_typename_by_alias(driver);
> +        const char *typename = find_typename_by_alias(driver, false);
>  
>          if (typename) {
>              driver = typename;
> @@ -283,36 +330,6 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem)
>      return NULL;
>  }
>  
> -static BusState *qbus_find_recursive(BusState *bus, const char *name,
> -                                     const char *bus_typename)
> -{
> -    BusChild *kid;
> -    BusState *child, *ret;
> -    int match = 1;
> -
> -    if (name && (strcmp(bus->name, name) != 0)) {
> -        match = 0;
> -    }
> -    if (bus_typename &&
> -        (strcmp(object_get_typename(OBJECT(bus)), bus_typename) != 0)) {
> -        match = 0;
> -    }
> -    if (match) {
> -        return bus;
> -    }
> -
> -    QTAILQ_FOREACH(kid, &bus->children, sibling) {
> -        DeviceState *dev = kid->child;
> -        QLIST_FOREACH(child, &dev->child_bus, sibling) {
> -            ret = qbus_find_recursive(child, name, bus_typename);
> -            if (ret) {
> -                return ret;
> -            }
> -        }
> -    }
> -    return NULL;
> -}
> -
>  static BusState *qbus_find(const char *path)
>  {
>      DeviceState *dev;
> @@ -417,7 +434,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
>      /* find driver */
>      obj = object_class_by_name(driver);
>      if (!obj) {
> -        const char *typename = find_typename_by_alias(driver);
> +        const char *typename = find_typename_by_alias(driver, true);
>  
>          if (typename) {
>              driver = typename;
> diff --git a/vl.c b/vl.c
> index 2b8cae6..788a536 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2113,11 +2113,7 @@ static int virtcon_parse(const char *devname)
>      }
>  
>      bus_opts = qemu_opts_create(device, NULL, 0, NULL);
> -    if (arch_type == QEMU_ARCH_S390X) {
> -        qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
> -    } else {
> -        qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
> -    } 
> +    qemu_opt_set(bus_opts, "driver", "virtio-serial");
>  
>      dev_opts = qemu_opts_create(device, NULL, 0, NULL);
>      qemu_opt_set(dev_opts, "driver", "virtconsole");
> -- 
> 1.7.11.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-20 14:24   ` Anthony Liguori
@ 2012-09-20 14:50     ` Alexander Graf
  0 siblings, 0 replies; 17+ messages in thread
From: Alexander Graf @ 2012-09-20 14:50 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: linux-s390, Rusty Russell, KVM, Carsten Otte, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Cornelia Huck,
	Martin Schwidefsky



On 20.09.2012, at 16:24, Anthony Liguori <aliguori@us.ibm.com> wrote:

> Cornelia Huck <cornelia.huck@de.ibm.com> writes:
> 
>> Add a new virtio transport that uses channel commands to perform
>> virtio operations.
>> 
>> Add a new machine type s390-ccw that uses this virtio-ccw transport
>> and make it the default machine for s390.
>> 
>> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
>> ---
>> 
>> Changes v1->v2:
>> - update to virtio-ccw interface changes
>> 
>> ---
>> hw/qdev-monitor.c      |   5 +
>> hw/s390-virtio.c       | 277 ++++++++++++----
>> hw/s390x/Makefile.objs |   1 +
>> hw/s390x/css.c         |  45 +++
>> hw/s390x/css.h         |   3 +
>> hw/s390x/virtio-ccw.c  | 875 +++++++++++++++++++++++++++++++++++++++++++++++++
>> hw/s390x/virtio-ccw.h  |  79 +++++
>> vl.c                   |   1 +
>> 8 files changed, 1215 insertions(+), 71 deletions(-)
>> create mode 100644 hw/s390x/virtio-ccw.c
>> create mode 100644 hw/s390x/virtio-ccw.h
>> 
>> diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
>> index 33b7f79..92b7c59 100644
>> --- a/hw/qdev-monitor.c
>> +++ b/hw/qdev-monitor.c
>> @@ -42,6 +42,11 @@ static const QDevAlias qdev_alias_table[] = {
>>     { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
>>     { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
>>     { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
>> +    { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
>> +    { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
>> +    { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
>> +    { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
>> +    { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
>>     { "lsi53c895a", "lsi" },
>>     { "ich9-ahci", "ahci" },
> 
> Please don't add aliases.  That's just an ugly hack to maintain compatibility.
> 
>>     { }
>> diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
>> index 47eed35..2509291 100644
>> --- a/hw/s390-virtio.c
>> +++ b/hw/s390-virtio.c
>> @@ -30,8 +30,11 @@
>> #include "hw/sysbus.h"
>> #include "kvm.h"
>> #include "exec-memory.h"
>> +#include "qemu-thread.h"
>> 
>> #include "hw/s390-virtio-bus.h"
>> +#include "hw/s390x/css.h"
>> +#include "hw/s390x/virtio-ccw.h"
>> 
>> //#define DEBUG_S390
>> 
>> @@ -46,6 +49,7 @@
>> #define KVM_S390_VIRTIO_NOTIFY          0
>> #define KVM_S390_VIRTIO_RESET           1
>> #define KVM_S390_VIRTIO_SET_STATUS      2
>> +#define KVM_S390_VIRTIO_CCW_NOTIFY      3
>> 
>> #define KERN_IMAGE_START                0x010000UL
>> #define KERN_PARM_AREA                  0x010480UL
>> @@ -62,6 +66,7 @@
>> 
>> static VirtIOS390Bus *s390_bus;
>> static S390CPU **ipi_states;
>> +VirtioCcwBus *ccw_bus;
>> 
>> S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
>> {
>> @@ -75,15 +80,21 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
>> int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
>> {
>>     int r = 0, i;
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> 
>>     dprintf("KVM hypercall: %ld\n", hypercall);
>>     switch (hypercall) {
>>     case KVM_S390_VIRTIO_NOTIFY:
>>         if (mem > ram_size) {
>> -            VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
>> -                                                               mem, &i);
>> -            if (dev) {
>> -                virtio_queue_notify(dev->vdev, i);
>> +            if (s390_bus) {
>> +                VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
>> +                                                                   mem, &i);
>> +                if (dev) {
>> +                    virtio_queue_notify(dev->vdev, i);
>> +                } else {
>> +                    r = -EINVAL;
>> +                }
>>             } else {
>>                 r = -EINVAL;
>>             }
>> @@ -92,28 +103,49 @@ int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
>>         }
>>         break;
>>     case KVM_S390_VIRTIO_RESET:
>> -    {
>> -        VirtIOS390Device *dev;
>> -
>> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
>> -        virtio_reset(dev->vdev);
>> -        stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
>> -        s390_virtio_device_sync(dev);
>> -        s390_virtio_reset_idx(dev);
>> +        if (s390_bus) {
>> +            VirtIOS390Device *dev;
>> +
>> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
>> +            virtio_reset(dev->vdev);
>> +            stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
>> +            s390_virtio_device_sync(dev);
>> +            s390_virtio_reset_idx(dev);
>> +        } else {
>> +            r = -EINVAL;
>> +        }
>>         break;
>> -    }
>>     case KVM_S390_VIRTIO_SET_STATUS:
>> -    {
>> -        VirtIOS390Device *dev;
>> +        if (s390_bus) {
>> +            VirtIOS390Device *dev;
>> 
>> -        dev = s390_virtio_bus_find_mem(s390_bus, mem);
>> -        if (dev) {
>> -            s390_virtio_device_update_status(dev);
>> +            dev = s390_virtio_bus_find_mem(s390_bus, mem);
>> +            if (dev) {
>> +                s390_virtio_device_update_status(dev);
>> +            } else {
>> +                r = -EINVAL;
>> +            }
>>         } else {
>>             r = -EINVAL;
>>         }
>>         break;
>> -    }
>> +    case KVM_S390_VIRTIO_CCW_NOTIFY:
>> +        if (ccw_bus) {
>> +            if (ioinst_disassemble_sch_ident(env->regs[2], &m, &cssid, &ssid,
>> +                                             &schid)) {
>> +                r = -EINVAL;
>> +            } else {
>> +                sch = css_find_subch(m, cssid, ssid, schid);
>> +                if (sch) {
>> +                    virtio_queue_notify(virtio_ccw_get_vdev(sch), env->regs[3]);
>> +                } else {
>> +                    r = -EINVAL;
>> +                }
>> +            }
>> +         } else {
>> +             r = -EINVAL;
>> +         }
>> +         break;
>>     default:
>>         r = -EINVAL;
>>         break;
>> @@ -150,58 +182,12 @@ unsigned s390_del_running_cpu(CPUS390XState *env)
>>     return s390_running_cpus;
>> }
>> 
>> -/* PC hardware initialisation */
>> -static void s390_init(ram_addr_t my_ram_size,
>> -                      const char *boot_device,
>> -                      const char *kernel_filename,
>> -                      const char *kernel_cmdline,
>> -                      const char *initrd_filename,
>> -                      const char *cpu_model)
>> +static CPUS390XState *s390_init_cpus(const char *cpu_model,
>> +                                     uint8_t *storage_keys)
>> {
>>     CPUS390XState *env = NULL;
>> -    MemoryRegion *sysmem = get_system_memory();
>> -    MemoryRegion *ram = g_new(MemoryRegion, 1);
>> -    ram_addr_t kernel_size = 0;
>> -    ram_addr_t initrd_offset;
>> -    ram_addr_t initrd_size = 0;
>> -    int shift = 0;
>> -    uint8_t *storage_keys;
>> -    void *virtio_region;
>> -    target_phys_addr_t virtio_region_len;
>> -    target_phys_addr_t virtio_region_start;
>>     int i;
>> 
>> -    /* s390x ram size detection needs a 16bit multiplier + an increment. So
>> -       guests > 64GB can be specified in 2MB steps etc. */
>> -    while ((my_ram_size >> (20 + shift)) > 65535) {
>> -        shift++;
>> -    }
>> -    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
>> -
>> -    /* lets propagate the changed ram size into the global variable. */
>> -    ram_size = my_ram_size;
>> -
>> -    /* get a BUS */
>> -    s390_bus = s390_virtio_bus_init(&my_ram_size);
>> -
>> -    /* allocate RAM */
>> -    memory_region_init_ram(ram, "s390.ram", my_ram_size);
>> -    vmstate_register_ram_global(ram);
>> -    memory_region_add_subregion(sysmem, 0, ram);
>> -
>> -    /* clear virtio region */
>> -    virtio_region_len = my_ram_size - ram_size;
>> -    virtio_region_start = ram_size;
>> -    virtio_region = cpu_physical_memory_map(virtio_region_start,
>> -                                            &virtio_region_len, true);
>> -    memset(virtio_region, 0, virtio_region_len);
>> -    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
>> -                              virtio_region_len);
>> -
>> -    /* allocate storage keys */
>> -    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
>> -
>> -    /* init CPUs */
>>     if (cpu_model == NULL) {
>>         cpu_model = "host";
>>     }
>> @@ -222,6 +208,17 @@ static void s390_init(ram_addr_t my_ram_size,
>>         tmp_env->exception_index = EXCP_HLT;
>>         tmp_env->storage_keys = storage_keys;
>>     }
>> +    return env;
>> +}
>> +
>> +static void s390_set_up_kernel(CPUS390XState *env,
>> +                               const char *kernel_filename,
>> +                               const char *kernel_cmdline,
>> +                               const char *initrd_filename)
>> +{
>> +    ram_addr_t kernel_size = 0;
>> +    ram_addr_t initrd_offset;
>> +    ram_addr_t initrd_size = 0;
>> 
>>     /* One CPU has to run */
>>     s390_add_running_cpu(env);
>> @@ -294,8 +291,13 @@ static void s390_init(ram_addr_t my_ram_size,
>>                strlen(kernel_cmdline) + 1);
>>     }
>> 
>> -    /* Create VirtIO network adapters */
>> -    for(i = 0; i < nb_nics; i++) {
>> +}
>> +
>> +static void s390_create_virtio_net(BusState *bus, const char *name)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < nb_nics; i++) {
>>         NICInfo *nd = &nd_table[i];
>>         DeviceState *dev;
>> 
>> @@ -308,7 +310,7 @@ static void s390_init(ram_addr_t my_ram_size,
>>             exit(1);
>>         }
>> 
>> -        dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
>> +        dev = qdev_create(bus, name);
>>         qdev_set_nic_properties(dev, nd);
>>         qdev_init_nofail(dev);
>>     }
>> @@ -329,6 +331,63 @@ static void s390_init(ram_addr_t my_ram_size,
>>     }
>> }
>> 
>> +/* PC hardware initialisation */
>> +static void s390_init(ram_addr_t my_ram_size,
>> +                      const char *boot_device,
>> +                      const char *kernel_filename,
>> +                      const char *kernel_cmdline,
>> +                      const char *initrd_filename,
>> +                      const char *cpu_model)
>> +{
>> +    CPUS390XState *env = NULL;
>> +    MemoryRegion *sysmem = get_system_memory();
>> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
>> +    int shift = 0;
>> +    uint8_t *storage_keys;
>> +    void *virtio_region;
>> +    target_phys_addr_t virtio_region_len;
>> +    target_phys_addr_t virtio_region_start;
>> +
>> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
>> +       guests > 64GB can be specified in 2MB steps etc. */
>> +    while ((my_ram_size >> (20 + shift)) > 65535) {
>> +        shift++;
>> +    }
>> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
>> +
>> +    /* lets propagate the changed ram size into the global variable. */
>> +    ram_size = my_ram_size;
>> +
>> +    /* get a BUS */
>> +    s390_bus = s390_virtio_bus_init(&my_ram_size);
>> +
>> +    /* allocate RAM */
>> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
>> +    vmstate_register_ram_global(ram);
>> +    memory_region_add_subregion(sysmem, 0, ram);
>> +
>> +    /* clear virtio region */
>> +    virtio_region_len = my_ram_size - ram_size;
>> +    virtio_region_start = ram_size;
>> +    virtio_region = cpu_physical_memory_map(virtio_region_start,
>> +                                            &virtio_region_len, true);
>> +    memset(virtio_region, 0, virtio_region_len);
>> +    cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
>> +                              virtio_region_len);
>> +
>> +    /* allocate storage keys */
>> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
>> +
>> +    /* init CPUs */
>> +    env = s390_init_cpus(cpu_model, storage_keys);
>> +
>> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
>> +
>> +    /* Create VirtIO network adapters */
>> +    s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
>> +
>> +}
>> +
>> static QEMUMachine s390_machine = {
>>     .name = "s390-virtio",
>>     .alias = "s390",
>> @@ -341,7 +400,6 @@ static QEMUMachine s390_machine = {
>>     .no_sdcard = 1,
>>     .use_virtcon = 1,
>>     .max_cpus = 255,
>> -    .is_default = 1,
>> };
>> 
>> static void s390_machine_init(void)
>> @@ -350,3 +408,80 @@ static void s390_machine_init(void)
>> }
>> 
>> machine_init(s390_machine_init);
> 
> Alex et al.
> 
> Now that we're doing virtio-ccw for s390, any reason to keep around the
> old virtio transport?
> 
> Can we at least schedule it for removal?

We can schedule it for removal, but that's still a few years out. Old kernels (read: all current distros) don't support the ccw machine yet.

So we should at least have virtio-ccw in Debian stable and the by-then current SLES and RHEL releases. Preferably version+1 even. Since those roadmaps are not public yet, I wouldn't dare to settle on a deprecation date yet.


Alex


> 
>> +
>> +static void ccw_init(ram_addr_t my_ram_size,
>> +                     const char *boot_device,
>> +                     const char *kernel_filename,
>> +                     const char *kernel_cmdline,
>> +                     const char *initrd_filename,
>> +                     const char *cpu_model)
>> +{
>> +    CPUS390XState *env = NULL;
>> +    MemoryRegion *sysmem = get_system_memory();
>> +    MemoryRegion *ram = g_new(MemoryRegion, 1);
>> +    int shift = 0;
>> +    uint8_t *storage_keys;
>> +    int ret;
>> +
>> +    /* s390x ram size detection needs a 16bit multiplier + an increment. So
>> +       guests > 64GB can be specified in 2MB steps etc. */
>> +    while ((my_ram_size >> (20 + shift)) > 65535) {
>> +        shift++;
>> +    }
>> +    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
>> +
>> +    /* lets propagate the changed ram size into the global variable. */
>> +    ram_size = my_ram_size;
>> +
>> +    /* get a BUS */
>> +    ccw_bus = virtio_ccw_bus_init();
>> +
>> +    /* allocate RAM */
>> +    memory_region_init_ram(ram, "s390.ram", my_ram_size);
>> +    vmstate_register_ram_global(ram);
>> +    memory_region_add_subregion(sysmem, 0, ram);
>> +
>> +    /* allocate storage keys */
>> +    storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
>> +
>> +    /* init CPUs */
>> +    env = s390_init_cpus(cpu_model, storage_keys);
>> +
>> +    kvm_s390_enable_css_support(env);
>> +
>> +    /*
>> +     * Create virtual css and set it as default so that non mcss-e
>> +     * enabled guests only see virtio devices.
>> +     */
>> +    ret = css_create_css_image(VIRTUAL_CSSID, true);
>> +    assert(ret == 0);
>> +
>> +
>> +    s390_set_up_kernel(env, kernel_filename, kernel_cmdline, initrd_filename);
>> +
>> +    /* Create VirtIO network adapters */
>> +    s390_create_virtio_net((BusState *)ccw_bus, "virtio-net-ccw");
>> +
>> +}
>> +
>> +static QEMUMachine ccw_machine = {
>> +    .name = "s390-ccw-virtio",
>> +    .alias = "s390-ccw",
>> +    .desc = "VirtIO-ccw based S390 machine",
>> +    .init = ccw_init,
>> +    .no_cdrom = 1,
>> +    .no_floppy = 1,
>> +    .no_serial = 1,
>> +    .no_parallel = 1,
>> +    .no_sdcard = 1,
>> +    .use_virtcon = 1,
>> +    .max_cpus = 255,
>> +    .is_default = 1,
>> +};
>> +
>> +static void ccw_machine_init(void)
>> +{
>> +    qemu_register_machine(&ccw_machine);
>> +}
>> +
>> +machine_init(ccw_machine_init);
>> diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
>> index 93b41fb..e4c3d6f 100644
>> --- a/hw/s390x/Makefile.objs
>> +++ b/hw/s390x/Makefile.objs
>> @@ -2,3 +2,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o
>> 
>> obj-y := $(addprefix ../,$(obj-y))
>> obj-y += css.o
>> +obj-y += virtio-ccw.o
>> diff --git a/hw/s390x/css.c b/hw/s390x/css.c
>> index b9b6e48..a671e28 100644
>> --- a/hw/s390x/css.c
>> +++ b/hw/s390x/css.c
>> @@ -441,6 +441,51 @@ 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];
>> }
>> 
>> +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
>> +{
>> +    if (!channel_subsys->css[cssid]) {
>> +        return false;
>> +    }
>> +    if (!channel_subsys->css[cssid]->sch_set[ssid]) {
>> +        return false;
>> +    }
>> +
>> +    return !!test_bit(devno,
>> +                      channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
>> +}
>> +
>> +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno,
>> +                      SubchDev *sch)
>> +{
>> +    CssImage *css;
>> +    SubchSet *s_set;
>> +
>> +    if (!channel_subsys->css[cssid]) {
>> +        fprintf(stderr,
>> +                "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
>> +                __func__, cssid, ssid, schid);
>> +       return;
>> +   }
>> +    css = channel_subsys->css[cssid];
>> +
>> +   if (!css->sch_set[ssid]) {
>> +        size_t set_size = sizeof(SubchSet) +
>> +            BITS_TO_LONGS(MAX_SCHID + 1) * 2;
>> +        css->sch_set[ssid] = g_malloc0(set_size);
>> +    }
>> +    s_set = css->sch_set[ssid];
>> +
>> +    s_set->sch[schid] = sch;
>> +    if (sch) {
>> +        set_bit(schid, s_set->schids_used);
>> +        set_bit(devno, s_set->devnos_used);
>> +    } else {
>> +        clear_bit(schid, s_set->schids_used);
>> +        clear_bit(schid, s_set->devnos_used);
>> +    }
>> +}
>> +
>> +
>> bool css_present(uint8_t cssid)
>> {
>>     return (channel_subsys->css[cssid] != NULL);
>> diff --git a/hw/s390x/css.h b/hw/s390x/css.h
>> index f3590eb..6876633 100644
>> --- a/hw/s390x/css.h
>> +++ b/hw/s390x/css.h
>> @@ -53,6 +53,9 @@ struct SubchDev {
>> };
>> 
>> 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);
>> void css_reset(void);
>> void css_reset_sch(SubchDev *sch);
>> diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
>> new file mode 100644
>> index 0000000..28be1a7
>> --- /dev/null
>> +++ b/hw/s390x/virtio-ccw.c
>> @@ -0,0 +1,875 @@
>> +/*
>> + * virtio ccw target implementation
>> + *
>> + * 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.
>> + */
>> +
>> +#include <hw/hw.h>
> 
> #include "hw.h"
> 
>> +#include "block.h"
>> +#include "blockdev.h"
>> +#include "sysemu.h"
>> +#include "net.h"
>> +#include "monitor.h"
>> +#include "qemu-thread.h"
>> +#include "hw/virtio.h"
>> +#include "hw/virtio-serial.h"
>> +#include "hw/virtio-net.h"
>> +#include "hw/sysbus.h"
>> +#include "bitops.h"
>> +
>> +#include "ioinst.h"
>> +#include "css.h"
>> +#include "virtio-ccw.h"
>> +
>> +static const TypeInfo virtio_ccw_bus_info = {
>> +    .name = TYPE_VIRTIO_CCW_BUS,
>> +    .parent = TYPE_BUS,
>> +    .instance_size = sizeof(VirtioCcwBus),
>> +};
>> +
>> +static const VirtIOBindings virtio_ccw_bindings;
>> +
>> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
>> +{
>> +    VirtIODevice *vdev = NULL;
>> +
>> +    if (sch->driver_data) {
>> +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
>> +    }
>> +    return vdev;
>> +}
>> +
>> +static void virtio_ccw_reset_subchannels(void *opaque)
>> +{
>> +    VirtioCcwBus *bus = opaque;
>> +    BusChild *kid;
>> +    VirtioCcwData *data;
>> +
>> +    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
>> +        data = (VirtioCcwData *)kid->child;
>> +        virtio_reset(data->vdev);
>> +        css_reset_sch(data->sch);
>> +    }
>> +    css_reset();
>> +}
>> +
>> +VirtioCcwBus *virtio_ccw_bus_init(void)
>> +{
>> +    VirtioCcwBus *cbus;
>> +    BusState *bus;
>> +    DeviceState *dev;
>> +
>> +    /* Create bridge device */
>> +    dev = qdev_create(NULL, "virtio-ccw-bridge");
>> +    qdev_init_nofail(dev);
>> +
>> +    /* Create bus on bridge device */
>> +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
>> +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
>> +
>> +    /* Enable hotplugging */
>> +    bus->allow_hotplug = 1;
>> +
>> +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
>> +    return cbus;
>> +}
>> +
>> +/* Communication blocks used by several channel commands. */
>> +typedef struct VqInfoBlock {
>> +    uint64_t queue;
>> +    uint32_t align;
>> +    uint16_t index;
>> +    uint16_t num;
>> +} QEMU_PACKED VqInfoBlock;
>> +
>> +typedef struct VqConfigBlock {
>> +    uint16_t index;
>> +    uint16_t num_max;
>> +} QEMU_PACKED VqConfigBlock;
>> +
>> +typedef struct VirtioFeatDesc {
>> +    uint32_t features;
>> +    uint8_t index;
>> +} QEMU_PACKED VirtioFeatDesc;
>> +
>> +/* Specify where the virtqueues for the subchannel are in guest memory. */
>> +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
>> +                              uint16_t index, uint16_t num)
>> +{
>> +    VirtioCcwData *data = sch->driver_data;
>> +
>> +    if (index > VIRTIO_PCI_QUEUE_MAX) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Current code in virtio.c relies on 4K alignment. */
>> +    if (align != 4096) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (!data) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    virtio_queue_set_addr(data->vdev, index, addr);
>> +    if (!addr) {
>> +        virtio_queue_set_vector(data->vdev, index, 0);
>> +    } else {
>> +        /* Fail if we don't have a big enough queue. */
>> +        /* TODO: Add interface to handle vring.num changing */
>> +        if (virtio_queue_get_num(data->vdev, index) > num) {
>> +            return -EINVAL;
>> +        }
>> +        virtio_queue_set_vector(data->vdev, index, index);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int virtio_ccw_cb(SubchDev *sch, CCW1 *ccw)
>> +{
>> +    int ret;
>> +    VqInfoBlock info;
>> +    uint8_t status;
>> +    VirtioFeatDesc features;
>> +    void *config;
>> +    uint64_t *indicators;
>> +    VqConfigBlock vq_config;
>> +    VirtioCcwData *data = sch->driver_data;
>> +    bool check_len;
>> +    int len;
>> +
>> +    if (!ccw) {
>> +        return -EIO;
>> +    }
>> +
>> +    if (!data) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
>> +
>> +    /* Look at the command. */
>> +    switch (ccw->cmd_code) {
>> +    case CCW_CMD_SET_VQ:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(info)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(info)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
> 
> This is an interesting check.  But I don't think we want to spread
> qemu_get_ram_ptr() around so perhaps you can introduce a
> qemu_is_ram_addr_valid() call?
> 
>> +        } else {
>> +            info.queue = ldq_phys(ccw->cda);
>> +            info.align = ldl_phys(ccw->cda + sizeof(info.queue));
>> +            info.index = lduw_phys(ccw->cda + sizeof(info.queue)
>> +                                   + sizeof(info.align));
>> +            info.num = lduw_phys(ccw->cda + sizeof(info.queue)
>> +                                 + sizeof(info.align)
>> +                                 + sizeof(info.index));
>> +            ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index,
>> +                                     info.num);
>> +            sch->curr_status.scsw.count = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_VDEV_RESET:
>> +        virtio_reset(data->vdev);
>> +        ret = 0;
>> +        break;
>> +    case CCW_CMD_READ_FEAT:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(features)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(features)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
>> +            if (features.index < ARRAY_SIZE(data->host_features)) {
>> +                features.features = data->host_features[features.index];
>> +            } else {
>> +                /* Return zeroes if the guest supports more feature bits. */
>> +                features.features = 0;
>> +            }
>> +            stw_le_phys(ccw->cda, features.features);
>> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_WRITE_FEAT:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(features)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(features)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            features.index = ldub_phys(ccw->cda + sizeof(features.features));
>> +            features.features = bswap32(lduw_phys(ccw->cda));
>> +            if (features.index < ARRAY_SIZE(data->host_features)) {
>> +                if (data->vdev->set_features) {
>> +                    data->vdev->set_features(data->vdev, features.features);
>> +                }
>> +                data->vdev->guest_features = features.features;
>> +            } else {
>> +                /*
>> +                 * If the guest supports more feature bits, assert that it
>> +                 * passes us zeroes for those we don't support.
>> +                 */
>> +                if (features.features) {
>> +                    fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n",
>> +                            features.index, features.features);
>> +                    /* XXX: do a unit check here? */
> 
> In general, we try to avoid guest initiated printf()s.  libvirt blindly
> logs stdio from QEMU to the disk file system and a malicious guest
> could, in theory, exhaust the host disk space.
> 
>> +                }
>> +            }
>> +            sch->curr_status.scsw.count = ccw->count - sizeof(features);
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_READ_CONF:
>> +        if (check_len) {
>> +            if (ccw->count > data->vdev->config_len) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        }
>> +        len = MIN(ccw->count, data->vdev->config_len);
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            data->vdev->get_config(data->vdev, data->vdev->config);
>> +            cpu_physical_memory_write(ccw->cda, data->vdev->config, len);
>> +            sch->curr_status.scsw.count = ccw->count - len;
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_WRITE_CONF:
>> +        if (check_len) {
>> +            if (ccw->count > data->vdev->config_len) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        }
>> +        len = MIN(ccw->count, data->vdev->config_len);
>> +        config = qemu_get_ram_ptr(ccw->cda);
>> +        if (!config) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            memcpy(data->vdev->config, config, len);
>> +            if (data->vdev->set_config) {
>> +                data->vdev->set_config(data->vdev, data->vdev->config);
>> +            }
>> +            sch->curr_status.scsw.count = ccw->count - len;
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_WRITE_STATUS:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(status)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(status)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            status = ldub_phys(ccw->cda);
>> +            virtio_set_status(data->vdev, status);
>> +            sch->curr_status.scsw.count = ccw->count - sizeof(status);
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_SET_IND:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(*indicators)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(*indicators)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        indicators = qemu_get_ram_ptr(ccw->cda);
>> +        if (!indicators) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            data->indicators = ccw->cda;
>> +            sch->curr_status.scsw.count = ccw->count - sizeof(*indicators);
>> +            ret = 0;
>> +        }
>> +        break;
>> +    case CCW_CMD_READ_VQ_CONF:
>> +        if (check_len) {
>> +            if (ccw->count != sizeof(vq_config)) {
>> +                ret = -EINVAL;
>> +                break;
>> +            }
>> +        } else if (ccw->count < sizeof(vq_config)) {
>> +            /* Can't execute command. */
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        if (!qemu_get_ram_ptr(ccw->cda)) {
>> +            ret = -EFAULT;
>> +        } else {
>> +            vq_config.index = lduw_phys(ccw->cda);
>> +            vq_config.num_max = virtio_queue_get_num(data->vdev,
>> +                                                     vq_config.index);
>> +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num_max);
>> +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
>> +            ret = 0;
>> +        }
>> +        break;
>> +    default:
>> +        ret = -EOPNOTSUPP;
>> +        break;
>> +    }
>> +    return ret;
>> +}
>> +
>> +static int virtio_ccw_device_init(VirtioCcwData *dev, VirtIODevice *vdev)
>> +{
>> +    unsigned int cssid = 0;
>> +    unsigned int ssid = 0;
>> +    unsigned int schid;
>> +    unsigned int devno;
>> +    bool have_devno = false;
>> +    bool found = false;
>> +    SubchDev *sch;
>> +    int ret;
>> +    int num;
>> +
>> +    sch = g_malloc0(sizeof(SubchDev));
>> +
>> +    sch->driver_data = dev;
>> +    dev->sch = sch;
>> +
>> +    dev->vdev = vdev;
>> +    dev->indicators = 0;
>> +
>> +    /* Initialize subchannel structure. */
>> +    qemu_mutex_init(&sch->mutex);
>> +    sch->channel_prog = NULL;
>> +    sch->last_cmd = NULL;
>> +    sch->orb = NULL;
>> +    /*
>> +     * 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)) {
>> +                ret = -EINVAL;
>> +                error_report("Invalid cssid or ssid: cssid %x, ssid %x",
>> +                             cssid, ssid);
>> +                goto out_err;
>> +            }
>> +            /* Enforce use of virtual cssid. */
>> +            if (cssid != VIRTUAL_CSSID) {
>> +                ret = -EINVAL;
>> +                error_report("cssid %x not valid for virtio devices", cssid);
>> +                goto out_err;
>> +            }
>> +            if (css_devno_used(cssid, ssid, devno)) {
>> +                ret = -EEXIST;
>> +                error_report("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 {
>> +            ret = -EINVAL;
>> +            error_report("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) {
>> +            ret = -ENODEV;
>> +            error_report("No free subchannel found for %x.%x.%04x", cssid, ssid,
>> +                         devno);
>> +            goto out_err;
>> +        }
>> +    } 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) {
>> +                            ret = -ENODEV;
>> +                            error_report("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) {
>> +            ret = -ENODEV;
>> +            error_report("Virtual channel subsystem is full!");
>> +            goto out_err;
>> +        }
>> +    }
>> +
>> +    /* Build initial schib. */
>> +    css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
>> +
>> +    sch->ccw_cb = virtio_ccw_cb;
>> +
>> +    /* Build senseid data. */
>> +    memset(&sch->id, 0, sizeof(SenseId));
>> +    sch->id.reserved = 0xff;
>> +    sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
>> +    sch->id.cu_model = dev->vdev->device_id;
>> +
>> +    virtio_bind_device(vdev, &virtio_ccw_bindings, dev);
>> +    /* Only the first 32 feature bits are used. */
>> +    dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]);
>> +
>> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
>> +                     &sch->curr_status, dev->qdev.hotplugged, 1, 1);
>> +    return 0;
>> +
>> +out_err:
>> +    dev->sch = NULL;
>> +    g_free(sch);
>> +    return ret;
>> +}
>> +
>> +static int virtio_ccw_exit(VirtioCcwData *dev)
>> +{
>> +    SubchDev *sch = dev->sch;
>> +
>> +    if (sch) {
>> +        css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
>> +        g_free(sch);
>> +    }
>> +    dev->indicators = 0;
>> +    return 0;
>> +}
>> +
>> +static int virtio_ccw_net_init(VirtioCcwData *dev)
>> +{
>> +    VirtIODevice *vdev;
>> +
>> +    vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
>> +    if (!vdev) {
>> +        return -1;
>> +    }
>> +
>> +    return virtio_ccw_device_init(dev, vdev);
>> +}
>> +
>> +static int virtio_ccw_net_exit(VirtioCcwData *dev)
>> +{
>> +    virtio_net_exit(dev->vdev);
>> +    return virtio_ccw_exit(dev);
>> +}
>> +
>> +static int virtio_ccw_blk_init(VirtioCcwData *dev)
>> +{
>> +    VirtIODevice *vdev;
>> +
>> +    vdev = virtio_blk_init((DeviceState *)dev, &dev->blk);
>> +    if (!vdev) {
>> +        return -1;
>> +    }
>> +
>> +    return virtio_ccw_device_init(dev, vdev);
>> +}
>> +
>> +static int virtio_ccw_blk_exit(VirtioCcwData *dev)
>> +{
>> +    virtio_blk_exit(dev->vdev);
>> +    blockdev_mark_auto_del(dev->blk.conf.bs);
>> +    return virtio_ccw_exit(dev);
>> +}
>> +
>> +static int virtio_ccw_serial_init(VirtioCcwData *dev)
>> +{
>> +    VirtioCcwBus *bus;
>> +    VirtIODevice *vdev;
>> +    int r;
>> +
>> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
>> +
>> +    vdev = virtio_serial_init((DeviceState *)dev, &dev->serial);
>> +    if (!vdev) {
>> +        return -1;
>> +    }
>> +
>> +    r = virtio_ccw_device_init(dev, vdev);
>> +    if (!r) {
>> +        bus->console = dev;
>> +    }
>> +
>> +    return r;
>> +}
>> +
>> +static int virtio_ccw_serial_exit(VirtioCcwData *dev)
>> +{
>> +    VirtioCcwBus *bus;
>> +
>> +    bus = DO_UPCAST(VirtioCcwBus, bus, dev->qdev.parent_bus);
>> +    bus->console = NULL;
>> +    virtio_serial_exit(dev->vdev);
>> +    return virtio_ccw_exit(dev);
>> +}
>> +
>> +static int virtio_ccw_balloon_init(VirtioCcwData *dev)
>> +{
>> +    VirtIODevice *vdev;
>> +
>> +    vdev = virtio_balloon_init((DeviceState *)dev);
>> +    if (!vdev) {
>> +        return -1;
>> +    }
>> +
>> +    return virtio_ccw_device_init(dev, vdev);
>> +}
>> +
>> +static int virtio_ccw_balloon_exit(VirtioCcwData *dev)
>> +{
>> +    virtio_balloon_exit(dev->vdev);
>> +    return virtio_ccw_exit(dev);
>> +}
>> +
>> +static int virtio_ccw_scsi_init(VirtioCcwData *dev)
>> +{
>> +    VirtIODevice *vdev;
>> +
>> +    vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
>> +    if (!vdev) {
>> +        return -1;
>> +    }
>> +
>> +    return virtio_ccw_device_init(dev, vdev);
>> +}
>> +
>> +static int virtio_ccw_scsi_exit(VirtioCcwData *dev)
>> +{
>> +    virtio_scsi_exit(dev->vdev);
>> +    return virtio_ccw_exit(dev);
>> +}
>> +
>> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus)
>> +{
>> +    return bus->console;
>> +}
>> +
>> +static void virtio_ccw_notify(void *opaque, uint16_t vector)
>> +{
>> +    VirtioCcwData *dev = opaque;
>> +    SubchDev *sch = dev->sch;
>> +    uint64_t indicators;
>> +
>> +    if (vector >= VIRTIO_PCI_QUEUE_MAX) {
>> +        return;
>> +    }
>> +
>> +    qemu_mutex_lock(&sch->mutex);
>> +    indicators = ldq_phys(dev->indicators);
>> +    set_bit(vector, &indicators);
>> +    stq_phys(dev->indicators, indicators);
>> +
>> +    css_conditional_io_interrupt(sch);
>> +
>> +    qemu_mutex_unlock(&sch->mutex);
> 
> I don't understand the mutex here...
> 
>> +}
>> +
>> +static unsigned virtio_ccw_get_features(void *opaque)
>> +{
>> +    VirtioCcwData *dev = opaque;
>> +
>> +    /* Only the first 32 feature bits are used. */
>> +    return dev->host_features[0];
>> +}
>> +
>> +/**************** Virtio-ccw Bus Device Descriptions *******************/
>> +
>> +static const VirtIOBindings virtio_ccw_bindings = {
>> +    .notify = virtio_ccw_notify,
>> +    .get_features = virtio_ccw_get_features,
>> +};
>> +
>> +static Property virtio_ccw_net_properties[] = {
>> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
>> +    DEFINE_NIC_PROPERTIES(VirtioCcwData, nic),
>> +    DEFINE_PROP_UINT32("x-txtimer", VirtioCcwData,
>> +                       net.txtimer, TX_TIMER_INTERVAL),
>> +    DEFINE_PROP_INT32("x-txburst", VirtioCcwData,
>> +                      net.txburst, TX_BURST),
>> +    DEFINE_PROP_STRING("tx", VirtioCcwData, net.tx),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_net_init;
>> +    k->exit = virtio_ccw_net_exit;
>> +    dc->props = virtio_ccw_net_properties;
>> +}
>> +
>> +static TypeInfo virtio_ccw_net = {
>> +    .name          = "virtio-net-ccw",
>> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init    = virtio_ccw_net_class_init,
>> +};
>> +
>> +static Property virtio_ccw_blk_properties[] = {
>> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
>> +    DEFINE_BLOCK_PROPERTIES(VirtioCcwData, blk.conf),
>> +    DEFINE_PROP_STRING("serial", VirtioCcwData, blk.serial),
>> +#ifdef __linux__
>> +    DEFINE_PROP_BIT("scsi", VirtioCcwData, blk.scsi, 0, true),
>> +#endif
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_blk_init;
>> +    k->exit = virtio_ccw_blk_exit;
>> +    dc->props = virtio_ccw_blk_properties;
>> +}
>> +
>> +static TypeInfo virtio_ccw_blk = {
>> +    .name          = "virtio-blk-ccw",
>> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init    = virtio_ccw_blk_class_init,
>> +};
>> +
>> +static Property virtio_ccw_serial_properties[] = {
>> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
>> +    DEFINE_PROP_UINT32("max_ports", VirtioCcwData, serial.max_virtserial_ports,
>> +                       31),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_serial_init;
>> +    k->exit = virtio_ccw_serial_exit;
>> +    dc->props = virtio_ccw_serial_properties;
>> +}
>> +
>> +static TypeInfo virtio_ccw_serial = {
>> +    .name          = "virtio-serial-ccw",
>> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init    = virtio_ccw_serial_class_init,
>> +};
>> +
>> +static Property virtio_ccw_balloon_properties[] = {
>> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_balloon_init;
>> +    k->exit = virtio_ccw_balloon_exit;
>> +    dc->props = virtio_ccw_balloon_properties;
>> +}
>> +
>> +static TypeInfo virtio_ccw_balloon = {
>> +    .name          = "virtio-balloon-ccw",
>> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init    = virtio_ccw_balloon_class_init,
>> +};
>> +
>> +static Property virtio_ccw_scsi_properties[] = {
>> +    DEFINE_PROP_STRING("devno", VirtioCcwData, bus_id),
>> +    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwData, host_features[0], scsi),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_scsi_init;
>> +    k->exit = virtio_ccw_scsi_exit;
>> +    dc->props = virtio_ccw_scsi_properties;
>> +}
>> +
>> +static TypeInfo virtio_ccw_scsi = {
>> +    .name          = "virtio-scsi-ccw",
>> +    .parent        = TYPE_VIRTIO_CCW_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init    = virtio_ccw_scsi_class_init,
>> +};
>> +
>> +static int virtio_ccw_busdev_init(DeviceState *dev)
>> +{
>> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
>> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
>> +
>> +    return _info->init(_dev);
>> +}
>> +
>> +static int virtio_ccw_busdev_exit(DeviceState *dev)
>> +{
>> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
>> +    VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
>> +
>> +    return _info->exit(_dev);
>> +}
>> +
>> +static int virtio_ccw_busdev_unplug(DeviceState *dev)
>> +{
>> +    VirtioCcwData *_dev = (VirtioCcwData *)dev;
>> +    SubchDev *sch = _dev->sch;
>> +
>> +    /*
>> +     * 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. */
>> +    qemu_mutex_lock(&sch->mutex);
>> +    sch->curr_status.pmcw.ena = 0;
>> +    sch->curr_status.pmcw.dnv = 0;
>> +    qemu_mutex_unlock(&sch->mutex);
>> +
>> +    s390_sch_hotplug(sch->cssid, sch->ssid, sch->schid, sch->devno,
>> +                     &sch->curr_status, 1, 0, 1);
>> +
>> +    object_unparent(OBJECT(dev));
>> +    qdev_free(dev);
>> +    return 0;
>> +}
>> +
>> +static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->init = virtio_ccw_busdev_init;
>> +    dc->exit = virtio_ccw_busdev_exit;
>> +    dc->unplug = virtio_ccw_busdev_unplug;
>> +    dc->bus_type = TYPE_VIRTIO_CCW_BUS;
>> +
>> +}
>> +
>> +static TypeInfo virtio_ccw_device_info = {
>> +    .name = TYPE_VIRTIO_CCW_DEVICE,
>> +    .parent = TYPE_DEVICE,
>> +    .instance_size = sizeof(VirtioCcwData),
>> +    .class_init = virtio_ccw_device_class_init,
>> +    .class_size = sizeof(VirtIOCCWDeviceClass),
>> +    .abstract = true,
>> +};
>> +
>> +/***************** Virtio-ccw Bus Bridge Device ********************/
>> +/* Only required to have the virtio bus as child in the system bus */
>> +
>> +static int virtio_ccw_bridge_init(SysBusDevice *dev)
>> +{
>> +    /* nothing */
>> +    return 0;
>> +}
>> +
>> +static void virtio_ccw_bridge_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init = virtio_ccw_bridge_init;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static TypeInfo virtio_ccw_bridge_info = {
>> +    .name          = "virtio-ccw-bridge",
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(SysBusDevice),
>> +    .class_init    = virtio_ccw_bridge_class_init,
>> +};
>> +
>> +static void virtio_ccw_register(void)
>> +{
>> +    type_register_static(&virtio_ccw_bus_info);
>> +    type_register_static(&virtio_ccw_device_info);
>> +    type_register_static(&virtio_ccw_serial);
>> +    type_register_static(&virtio_ccw_blk);
>> +    type_register_static(&virtio_ccw_net);
>> +    type_register_static(&virtio_ccw_balloon);
>> +    type_register_static(&virtio_ccw_scsi);
>> +    type_register_static(&virtio_ccw_bridge_info);
>> +}
>> +type_init(virtio_ccw_register);
>> diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
>> new file mode 100644
>> index 0000000..8125acf
>> --- /dev/null
>> +++ b/hw/s390x/virtio-ccw.h
>> @@ -0,0 +1,79 @@
>> +/*
>> + * virtio ccw target 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.
>> + */
>> +
>> +#include <hw/virtio-blk.h>
>> +#include <hw/virtio-net.h>
>> +#include <hw/virtio-serial.h>
>> +#include <hw/virtio-scsi.h>
>> +
>> +#define VIRTUAL_CSSID 0xfe
>> +
>> +#define VIRTIO_CCW_CU_TYPE 0x3832
>> +#define VIRTIO_CCW_CHPID_TYPE 0x32
>> +
>> +#define CCW_CMD_SET_VQ       0x13
>> +#define CCW_CMD_VDEV_RESET   0x33
>> +#define CCW_CMD_READ_FEAT    0x12
>> +#define CCW_CMD_WRITE_FEAT   0x11
>> +#define CCW_CMD_READ_CONF    0x22
>> +#define CCW_CMD_WRITE_CONF   0x21
>> +#define CCW_CMD_WRITE_STATUS 0x31
>> +#define CCW_CMD_SET_IND      0x43
>> +#define CCW_CMD_READ_VQ_CONF 0x32
>> +
>> +#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
>> +#define VIRTIO_CCW_DEVICE(obj) \
>> +     OBJECT_CHECK(VirtioCcwData, (obj), TYPE_VIRTIO_CCW_DEVICE)
>> +#define VIRTIO_CCW_DEVICE_CLASS(klass) \
>> +     OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE)
>> +#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \
>> +     OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE)
>> +
>> +#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus"
>> +#define VIRTIO_CCW_BUS(obj) \
>> +     OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS)
>> +
>> +typedef struct VirtioCcwData VirtioCcwData;
>> +
>> +typedef struct VirtIOCCWDeviceClass {
>> +    DeviceClass qdev;
>> +    int (*init)(VirtioCcwData *dev);
>> +    int (*exit)(VirtioCcwData *dev);
>> +} VirtIOCCWDeviceClass;
>> +
>> +/* Change here if we want to support more feature bits. */
>> +#define VIRTIO_CCW_FEATURE_SIZE 1
>> +
>> +struct VirtioCcwData {
>> +    DeviceState qdev;
>> +    SubchDev *sch;
>> +    VirtIODevice *vdev;
>> +    char *bus_id;
>> +    VirtIOBlkConf blk;
>> +    NICConf nic;
>> +    uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
>> +    virtio_serial_conf serial;
>> +    virtio_net_conf net;
>> +    VirtIOSCSIConf scsi;
>> +    /* Guest provided values: */
>> +    target_phys_addr_t indicators;
>> +};
>> +
>> +/* virtio-ccw bus type */
>> +typedef struct VirtioCcwBus {
>> +    BusState bus;
>> +    VirtioCcwData *console;
>> +} VirtioCcwBus;
>> +
>> +VirtioCcwBus *virtio_ccw_bus_init(void);
>> +void virtio_ccw_device_update_status(SubchDev *sch);
>> +VirtioCcwData *virtio_ccw_bus_console(VirtioCcwBus *bus);
>> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
>> diff --git a/vl.c b/vl.c
>> index 7c577fa..2b8cae6 100644
>> --- a/vl.c
>> +++ b/vl.c
>> @@ -289,6 +289,7 @@ static struct {
>>     { .driver = "scsi-cd",              .flag = &default_cdrom     },
>>     { .driver = "virtio-serial-pci",    .flag = &default_virtcon   },
>>     { .driver = "virtio-serial-s390",   .flag = &default_virtcon   },
>> +    { .driver = "virtio-serial-ccw",    .flag = &default_virtcon   },
>>     { .driver = "virtio-serial",        .flag = &default_virtcon   },
>>     { .driver = "VGA",                  .flag = &default_vga       },
>>     { .driver = "isa-vga",              .flag = &default_vga       },
>> -- 
>> 1.7.11.5
>> 
>> --
>> To unsubscribe from this list: send the line "unsubscribe kvm" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases.
  2012-09-20 14:27   ` Anthony Liguori
@ 2012-10-09 14:39     ` Cornelia Huck
  0 siblings, 0 replies; 17+ messages in thread
From: Cornelia Huck @ 2012-10-09 14:39 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: linux-s390, Marcelo Tosatti, KVM, Carsten Otte, Sebastian Ott,
	Rusty Russell, Heiko Carstens, qemu-devel, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

On Thu, 20 Sep 2012 09:27:00 -0500
Anthony Liguori <aliguori@us.ibm.com> wrote:

> Cornelia Huck <cornelia.huck@de.ibm.com> writes:
> 
> > This patch enables using both virtio-xxx-s390 and virtio-xxx-ccw
> > by making the alias lookup code verify that a driver is actually
> > registered.
> >
> > (Only included in order to allow testing of virtio-ccw; should be
> > replaced by cleaning up the virtio bus model.)
> >
> > Not-signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> 
> No more aliases.  Just drop the whole thing.

The whole alias stuff I put in there was just to be able to test
virtio-ccw. I agree that we should use the cleaned-up virtio driver
model that had been proposed for virtio-mmio earlier; I just want to
make sure the virtio-ccw interface is sane first.

> 
> Regards,
> 
> Anthony Liguori

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
  2012-09-19 16:12   ` Alexander Graf
  2012-09-20 14:24   ` Anthony Liguori
@ 2012-12-18  8:45   ` Paolo Bonzini
  2012-12-18 14:58     ` Cornelia Huck
  2 siblings, 1 reply; 17+ messages in thread
From: Paolo Bonzini @ 2012-12-18  8:45 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

Il 04/09/2012 17:13, Cornelia Huck ha scritto:
> +VirtioCcwBus *virtio_ccw_bus_init(void)
> +{
> +    VirtioCcwBus *cbus;
> +    BusState *bus;
> +    DeviceState *dev;
> +
> +    /* Create bridge device */
> +    dev = qdev_create(NULL, "virtio-ccw-bridge");
> +    qdev_init_nofail(dev);
> +
> +    /* Create bus on bridge device */
> +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
> +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
> +
> +    /* Enable hotplugging */
> +    bus->allow_hotplug = 1;
> +
> +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);

Please use qdev device-reset and bus-reset callbacks instead of this.

In particular, when writing the status you should call
qdev_reset_all(DEVICE(sch)), and whatever state should be reset will
have to be cleared by the device-reset callback of SubchDev, including
calling virtio_reset.

Everything else will be cleared instead by the bus-reset callback of
virtio-ccw-bus, similar to what you are doing in
virtio_ccw_reset_subchannels.

Paolo


> +    return cbus;
> +}

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-12-18  8:45   ` Paolo Bonzini
@ 2012-12-18 14:58     ` Cornelia Huck
  2012-12-18 15:01       ` Paolo Bonzini
  0 siblings, 1 reply; 17+ messages in thread
From: Cornelia Huck @ 2012-12-18 14:58 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Tue, 18 Dec 2012 09:45:27 +0100
Paolo Bonzini <pbonzini@redhat.com> wrote:

> Il 04/09/2012 17:13, Cornelia Huck ha scritto:
> > +VirtioCcwBus *virtio_ccw_bus_init(void)
> > +{
> > +    VirtioCcwBus *cbus;
> > +    BusState *bus;
> > +    DeviceState *dev;
> > +
> > +    /* Create bridge device */
> > +    dev = qdev_create(NULL, "virtio-ccw-bridge");
> > +    qdev_init_nofail(dev);
> > +
> > +    /* Create bus on bridge device */
> > +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
> > +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
> > +
> > +    /* Enable hotplugging */
> > +    bus->allow_hotplug = 1;
> > +
> > +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
> 
> Please use qdev device-reset and bus-reset callbacks instead of this.

Will do for the next version.
> 
> In particular, when writing the status you should call
> qdev_reset_all(DEVICE(sch)), and whatever state should be reset will
> have to be cleared by the device-reset callback of SubchDev, including
> calling virtio_reset.

With "writing the status" you mean "the guest sets the status to 0",
right?

> 
> Everything else will be cleared instead by the bus-reset callback of
> virtio-ccw-bus, similar to what you are doing in
> virtio_ccw_reset_subchannels.

Looking at the reset handler, css_reset() is a bit oddly placed, as it
doesn't really have anything to do with virtio-ccw; virtio-ccw is just
the only current creator of channel subsystem images. I'll try to come
up with a better model.

> 
> Paolo
> 
> 
> > +    return cbus;
> > +}
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport.
  2012-12-18 14:58     ` Cornelia Huck
@ 2012-12-18 15:01       ` Paolo Bonzini
  0 siblings, 0 replies; 17+ messages in thread
From: Paolo Bonzini @ 2012-12-18 15:01 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Martin Schwidefsky

Il 18/12/2012 15:58, Cornelia Huck ha scritto:
> On Tue, 18 Dec 2012 09:45:27 +0100
> Paolo Bonzini <pbonzini@redhat.com> wrote:
> 
>> Il 04/09/2012 17:13, Cornelia Huck ha scritto:
>>> +VirtioCcwBus *virtio_ccw_bus_init(void)
>>> +{
>>> +    VirtioCcwBus *cbus;
>>> +    BusState *bus;
>>> +    DeviceState *dev;
>>> +
>>> +    /* Create bridge device */
>>> +    dev = qdev_create(NULL, "virtio-ccw-bridge");
>>> +    qdev_init_nofail(dev);
>>> +
>>> +    /* Create bus on bridge device */
>>> +    bus = qbus_create(TYPE_VIRTIO_CCW_BUS, dev, "virtio-ccw");
>>> +    cbus = DO_UPCAST(VirtioCcwBus, bus, bus);
>>> +
>>> +    /* Enable hotplugging */
>>> +    bus->allow_hotplug = 1;
>>> +
>>> +    qemu_register_reset(virtio_ccw_reset_subchannels, cbus);
>>
>> Please use qdev device-reset and bus-reset callbacks instead of this.
> 
> Will do for the next version.
>>
>> In particular, when writing the status you should call
>> qdev_reset_all(DEVICE(sch)), and whatever state should be reset will
>> have to be cleared by the device-reset callback of SubchDev, including
>> calling virtio_reset.
> 
> With "writing the status" you mean "the guest sets the status to 0",
> right?

Yes.

Paolo

>> Everything else will be cleared instead by the bus-reset callback of
>> virtio-ccw-bus, similar to what you are doing in
>> virtio_ccw_reset_subchannels.
> 
> Looking at the reset handler, css_reset() is a bit oddly placed, as it
> doesn't really have anything to do with virtio-ccw; virtio-ccw is just
> the only current creator of channel subsystem images. I'll try to come
> up with a better model.
> 
>>
>> Paolo
>>
>>
>>> +    return cbus;
>>> +}
>>
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2012-12-18 15:02 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/5] Update headers for upcoming s390 changes Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/5] s390: Virtual channel subsystem support Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
2012-09-19 16:12   ` Alexander Graf
2012-09-20  7:39     ` Jens Freimann
2012-09-20 14:24   ` Anthony Liguori
2012-09-20 14:50     ` Alexander Graf
2012-12-18  8:45   ` Paolo Bonzini
2012-12-18 14:58     ` Cornelia Huck
2012-12-18 15:01       ` Paolo Bonzini
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/5] s390: Virtual channel subsystem support for !KVM Cornelia Huck
2012-09-19 16:22   ` Alexander Graf
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
2012-09-19 16:24   ` Alexander Graf
2012-09-20 14:27   ` Anthony Liguori
2012-10-09 14:39     ` Cornelia Huck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).