All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] qemu: s390: virtual css and virtio-ccw
@ 2012-08-07 14:52 ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Avi Kivity, Marcelo Tosatti, Anthony Liguori, Rusty Russell,
	Christian Borntraeger, Carsten Otte, Alexander Graf,
	Heiko Carstens, Martin Schwidefsky, Sebastian Ott

Hi,

following are the qemu parts for supporting virtual channel I/O
and the new virtio-ccw transport.

Patch 1 pulls in the new interface definitions.

Patch 2 contains the ground work for supporting virtual subchannels
(kvm only).

Patch 3 implements the new virtio-ccw transport.

Patch 4 makes virtual subchannels available for !kvm as well. Note
that this patch is completely untested; it basically contains my
initial channel subsystem code before I moved most of it into the
kernel.

Patch 5 is a hack needed to make virtio-ccw work with the alias stuff
for now. The correct solution is to rework the virtio layering as
has been proposed for virtio-mmio (see
http://comments.gmane.org/gmane.comp.emulators.qemu/148224).

The patches still have some TODOs in them (but nothing major), and
likely some things need to be done differently. I hope, however,
that the architectural part is fine.

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                  |  268 ++++++---
 hw/s390x/Makefile.objs            |    2 +
 hw/s390x/css.c                    | 1202 +++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h                    |   89 +++
 hw/s390x/virtio-ccw.c             |  962 +++++++++++++++++++++++++++++
 hw/s390x/virtio-ccw.h             |   77 +++
 linux-headers/asm-s390/kvm.h      |    2 +-
 linux-headers/asm-s390/kvm_para.h |    2 +-
 linux-headers/linux/kvm.h         |   63 ++
 target-s390x/Makefile.objs        |    2 +-
 target-s390x/cpu.h                |  261 ++++++++
 target-s390x/helper.c             |  140 +++++
 target-s390x/ioinst.c             |  734 ++++++++++++++++++++++
 target-s390x/ioinst.h             |  206 +++++++
 target-s390x/kvm.c                |  265 +++++++-
 target-s390x/op_helper.c          |   22 +-
 vl.c                              |    7 +-
 19 files changed, 4260 insertions(+), 140 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 target-s390x/ioinst.c
 create mode 100644 target-s390x/ioinst.h

-- 
1.7.11.4

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

* [Qemu-devel] [RFC PATCH 0/5] qemu: s390: virtual css and virtio-ccw
@ 2012-08-07 14:52 ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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,

following are the qemu parts for supporting virtual channel I/O
and the new virtio-ccw transport.

Patch 1 pulls in the new interface definitions.

Patch 2 contains the ground work for supporting virtual subchannels
(kvm only).

Patch 3 implements the new virtio-ccw transport.

Patch 4 makes virtual subchannels available for !kvm as well. Note
that this patch is completely untested; it basically contains my
initial channel subsystem code before I moved most of it into the
kernel.

Patch 5 is a hack needed to make virtio-ccw work with the alias stuff
for now. The correct solution is to rework the virtio layering as
has been proposed for virtio-mmio (see
http://comments.gmane.org/gmane.comp.emulators.qemu/148224).

The patches still have some TODOs in them (but nothing major), and
likely some things need to be done differently. I hope, however,
that the architectural part is fine.

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                  |  268 ++++++---
 hw/s390x/Makefile.objs            |    2 +
 hw/s390x/css.c                    | 1202 +++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h                    |   89 +++
 hw/s390x/virtio-ccw.c             |  962 +++++++++++++++++++++++++++++
 hw/s390x/virtio-ccw.h             |   77 +++
 linux-headers/asm-s390/kvm.h      |    2 +-
 linux-headers/asm-s390/kvm_para.h |    2 +-
 linux-headers/linux/kvm.h         |   63 ++
 target-s390x/Makefile.objs        |    2 +-
 target-s390x/cpu.h                |  261 ++++++++
 target-s390x/helper.c             |  140 +++++
 target-s390x/ioinst.c             |  734 ++++++++++++++++++++++
 target-s390x/ioinst.h             |  206 +++++++
 target-s390x/kvm.c                |  265 +++++++-
 target-s390x/op_helper.c          |   22 +-
 vl.c                              |    7 +-
 19 files changed, 4260 insertions(+), 140 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 target-s390x/ioinst.c
 create mode 100644 target-s390x/ioinst.h

-- 
1.7.11.4

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

* [PATCH 1/5] Update headers for upcoming s390 changes.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Avi Kivity, Marcelo Tosatti, Anthony Liguori, Rusty Russell,
	Christian Borntraeger, Carsten Otte, Alexander Graf,
	Heiko Carstens, Martin Schwidefsky, Sebastian Ott

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 linux-headers/asm-s390/kvm.h      |  2 +-
 linux-headers/asm-s390/kvm_para.h |  2 +-
 linux-headers/linux/kvm.h         | 63 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h
index bdcbe0f..d25da59 100644
--- a/linux-headers/asm-s390/kvm.h
+++ b/linux-headers/asm-s390/kvm.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_KVM_S390_H
 #define __LINUX_KVM_S390_H
 /*
- * asm-s390/kvm.h - KVM s390 specific structures and definitions
+ * KVM s390 specific structures and definitions
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h
index 8e2dd67..870051f 100644
--- a/linux-headers/asm-s390/kvm_para.h
+++ b/linux-headers/asm-s390/kvm_para.h
@@ -1,5 +1,5 @@
 /*
- * asm-s390/kvm_para.h - definition for paravirtual devices on s390
+ * definition for paravirtual devices on s390
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 5a9d4e3..a813a1c 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -163,6 +163,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 +277,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 +403,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 +495,39 @@ 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;
+};
+
 #define KVMIO 0xAE
 
 /* machine type bits, to be used as argument to KVM_CREATE_VM */
@@ -617,6 +672,8 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_SIGNAL_MSI 77
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
+#define KVM_CAP_PPC_ALLOC_HTAB 80
+#define KVM_CAP_S390_CSS_SUPPORT 81
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -828,6 +885,12 @@ struct kvm_s390_ucas_mapping {
 #define KVM_SIGNAL_MSI            _IOW(KVMIO,  0xa5, struct kvm_msi)
 /* Available with KVM_CAP_PPC_GET_SMMU_INFO */
 #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)
 
 /*
  * ioctls for vcpu fds
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 1/5] Update headers for upcoming s390 changes.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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>
---
 linux-headers/asm-s390/kvm.h      |  2 +-
 linux-headers/asm-s390/kvm_para.h |  2 +-
 linux-headers/linux/kvm.h         | 63 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h
index bdcbe0f..d25da59 100644
--- a/linux-headers/asm-s390/kvm.h
+++ b/linux-headers/asm-s390/kvm.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_KVM_S390_H
 #define __LINUX_KVM_S390_H
 /*
- * asm-s390/kvm.h - KVM s390 specific structures and definitions
+ * KVM s390 specific structures and definitions
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h
index 8e2dd67..870051f 100644
--- a/linux-headers/asm-s390/kvm_para.h
+++ b/linux-headers/asm-s390/kvm_para.h
@@ -1,5 +1,5 @@
 /*
- * asm-s390/kvm_para.h - definition for paravirtual devices on s390
+ * definition for paravirtual devices on s390
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 5a9d4e3..a813a1c 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -163,6 +163,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 +277,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 +403,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 +495,39 @@ 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;
+};
+
 #define KVMIO 0xAE
 
 /* machine type bits, to be used as argument to KVM_CREATE_VM */
@@ -617,6 +672,8 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_SIGNAL_MSI 77
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
+#define KVM_CAP_PPC_ALLOC_HTAB 80
+#define KVM_CAP_S390_CSS_SUPPORT 81
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -828,6 +885,12 @@ struct kvm_s390_ucas_mapping {
 #define KVM_SIGNAL_MSI            _IOW(KVMIO,  0xa5, struct kvm_msi)
 /* Available with KVM_CAP_PPC_GET_SMMU_INFO */
 #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)
 
 /*
  * ioctls for vcpu fds
-- 
1.7.11.4

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

* [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Avi Kivity, Marcelo Tosatti, Anthony Liguori, Rusty Russell,
	Christian Borntraeger, Carsten Otte, Alexander Graf,
	Heiko Carstens, Martin Schwidefsky, Sebastian Ott

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>
---
 hw/s390x/Makefile.objs     |   1 +
 hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h             |  62 +++++++
 target-s390x/Makefile.objs |   2 +-
 target-s390x/cpu.h         | 108 +++++++++++
 target-s390x/ioinst.c      |  38 ++++
 target-s390x/ioinst.h      | 173 ++++++++++++++++++
 target-s390x/kvm.c         | 101 +++++++++++
 8 files changed, 924 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..7941c44
--- /dev/null
+++ b/hw/s390x/css.c
@@ -0,0 +1,440 @@
+/*
+ * 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 "kvm.h"
+#include "cpu.h"
+#include "ioinst.h"
+#include "css.h"
+
+struct chp_info {
+    uint8_t in_use;
+    uint8_t type;
+};
+
+static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
+
+static css_subch_cb_func css_subch_cb;
+
+int css_set_subch_cb(css_subch_cb_func func)
+{
+    if (func && css_subch_cb) {
+        return -EBUSY;
+    }
+    css_subch_cb = func;
+    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)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct 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)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct 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, struct 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)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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(struct pmcw));
+    switch (func) {
+    case CSS_DO_CSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct 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(struct 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(struct 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(struct 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)
+{
+    if (cssid > MAX_CSSID) {
+        return -EINVAL;
+    }
+    if (chpids[cssid][chpid].in_use) {
+        return -EEXIST;
+    }
+    chpids[cssid][chpid].in_use = 1;
+    chpids[cssid][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)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    int i;
+
+    memset(p, 0, sizeof(struct pmcw));
+    p->dnv = 1;
+    p->dev = sch->devno;
+    /* single path */
+    p->pim = 0x80;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->chpid[0] = chpid;
+    if (!chpids[sch->cssid][chpid].in_use) {
+        css_add_virtual_chpid(sch->cssid, chpid, type);
+    }
+
+    memset(s, 0, sizeof(struct 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)
+{
+    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
+}
+
+static void css_init(void)
+{
+    css_subch_cb = NULL;
+}
+machine_init(css_init);
+
+void css_reset_sch(SubchDev *sch)
+{
+    struct 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..b8a95cc
--- /dev/null
+++ b/hw/s390x/css.h
@@ -0,0 +1,62 @@
+/*
+ * 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 8
+
+struct senseid {
+    /* common part */
+    uint32_t  reserved:8;    /* always 0x'FF' */
+    uint32_t  cu_type:16;    /* control unit type */
+    uint32_t  cu_model:8;    /* control unit model */
+    uint32_t  dev_type:16;   /* device type */
+    uint32_t  dev_model:8;   /* device model */
+    uint32_t  unused:8;      /* padding byte */
+    /* extended part */
+    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
+};
+
+struct SubchDev {
+    /* channel-subsystem related things: */
+    uint8_t cssid;
+    uint8_t ssid;
+    uint16_t schid;
+    uint16_t devno;
+    struct schib curr_status;
+    uint8_t sense_data[32];
+    struct ccw1 *channel_prog;
+    struct ccw1 *last_cmd;
+    struct orb *orb;
+    QemuMutex mutex;
+    /* transport-provided data: */
+    int (*ccw_cb) (SubchDev *, struct ccw1 *);
+    struct senseid id;
+    void *driver_data;
+};
+
+typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid);
+int css_set_subch_cb(css_subch_cb_func func);
+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 262747f..cb05241 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
-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 c30ac3a..c09fa61 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -336,6 +336,23 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
 void cpu_lock(void);
 void cpu_unlock(void);
 
+typedef struct SubchDev SubchDev;
+struct 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);
+#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;
@@ -996,4 +1013,95 @@ 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);
+#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)
+{
+}
+#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");
+    }
+}
+
+#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..79628b4
--- /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) */
+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;
+};
+
+/* path management control word */
+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;
+};
+
+/* subchannel information block */
+struct schib {
+    struct pmcw pmcw;
+    struct scsw scsw;
+    uint64_t mba;
+    uint8_t mda[4];
+};
+
+/* interruption response block */
+struct irb {
+    struct scsw scsw;
+    uint32_t esw[5];
+    uint32_t ecw[8];
+    uint32_t emw[8];
+};
+
+/* 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) */
+struct ccw1 {
+    uint8_t cmd_code;
+    uint8_t flags;
+    uint16_t count;
+    uint32_t cda;
+};
+
+#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 47008c2..e4cde14 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;
 }
@@ -506,6 +509,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;
@@ -531,3 +541,94 @@ 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);
+    }
+}
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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>
---
 hw/s390x/Makefile.objs     |   1 +
 hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h             |  62 +++++++
 target-s390x/Makefile.objs |   2 +-
 target-s390x/cpu.h         | 108 +++++++++++
 target-s390x/ioinst.c      |  38 ++++
 target-s390x/ioinst.h      | 173 ++++++++++++++++++
 target-s390x/kvm.c         | 101 +++++++++++
 8 files changed, 924 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..7941c44
--- /dev/null
+++ b/hw/s390x/css.c
@@ -0,0 +1,440 @@
+/*
+ * 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 "kvm.h"
+#include "cpu.h"
+#include "ioinst.h"
+#include "css.h"
+
+struct chp_info {
+    uint8_t in_use;
+    uint8_t type;
+};
+
+static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
+
+static css_subch_cb_func css_subch_cb;
+
+int css_set_subch_cb(css_subch_cb_func func)
+{
+    if (func && css_subch_cb) {
+        return -EBUSY;
+    }
+    css_subch_cb = func;
+    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)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct 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)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct 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, struct 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)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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(struct pmcw));
+    switch (func) {
+    case CSS_DO_CSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct 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(struct 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(struct 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(struct 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)
+{
+    if (cssid > MAX_CSSID) {
+        return -EINVAL;
+    }
+    if (chpids[cssid][chpid].in_use) {
+        return -EEXIST;
+    }
+    chpids[cssid][chpid].in_use = 1;
+    chpids[cssid][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)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    int i;
+
+    memset(p, 0, sizeof(struct pmcw));
+    p->dnv = 1;
+    p->dev = sch->devno;
+    /* single path */
+    p->pim = 0x80;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->chpid[0] = chpid;
+    if (!chpids[sch->cssid][chpid].in_use) {
+        css_add_virtual_chpid(sch->cssid, chpid, type);
+    }
+
+    memset(s, 0, sizeof(struct 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)
+{
+    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
+}
+
+static void css_init(void)
+{
+    css_subch_cb = NULL;
+}
+machine_init(css_init);
+
+void css_reset_sch(SubchDev *sch)
+{
+    struct 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..b8a95cc
--- /dev/null
+++ b/hw/s390x/css.h
@@ -0,0 +1,62 @@
+/*
+ * 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 8
+
+struct senseid {
+    /* common part */
+    uint32_t  reserved:8;    /* always 0x'FF' */
+    uint32_t  cu_type:16;    /* control unit type */
+    uint32_t  cu_model:8;    /* control unit model */
+    uint32_t  dev_type:16;   /* device type */
+    uint32_t  dev_model:8;   /* device model */
+    uint32_t  unused:8;      /* padding byte */
+    /* extended part */
+    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
+};
+
+struct SubchDev {
+    /* channel-subsystem related things: */
+    uint8_t cssid;
+    uint8_t ssid;
+    uint16_t schid;
+    uint16_t devno;
+    struct schib curr_status;
+    uint8_t sense_data[32];
+    struct ccw1 *channel_prog;
+    struct ccw1 *last_cmd;
+    struct orb *orb;
+    QemuMutex mutex;
+    /* transport-provided data: */
+    int (*ccw_cb) (SubchDev *, struct ccw1 *);
+    struct senseid id;
+    void *driver_data;
+};
+
+typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid);
+int css_set_subch_cb(css_subch_cb_func func);
+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 262747f..cb05241 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
-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 c30ac3a..c09fa61 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -336,6 +336,23 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
 void cpu_lock(void);
 void cpu_unlock(void);
 
+typedef struct SubchDev SubchDev;
+struct 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);
+#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;
@@ -996,4 +1013,95 @@ 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);
+#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)
+{
+}
+#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");
+    }
+}
+
+#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..79628b4
--- /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) */
+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;
+};
+
+/* path management control word */
+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;
+};
+
+/* subchannel information block */
+struct schib {
+    struct pmcw pmcw;
+    struct scsw scsw;
+    uint64_t mba;
+    uint8_t mda[4];
+};
+
+/* interruption response block */
+struct irb {
+    struct scsw scsw;
+    uint32_t esw[5];
+    uint32_t ecw[8];
+    uint32_t emw[8];
+};
+
+/* 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) */
+struct ccw1 {
+    uint8_t cmd_code;
+    uint8_t flags;
+    uint16_t count;
+    uint32_t cda;
+};
+
+#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 47008c2..e4cde14 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;
 }
@@ -506,6 +509,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;
@@ -531,3 +541,94 @@ 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);
+    }
+}
-- 
1.7.11.4

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

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

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>
---
 hw/qdev-monitor.c      |   5 +
 hw/s390-virtio.c       | 268 ++++++++++----
 hw/s390x/Makefile.objs |   1 +
 hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/virtio-ccw.h  |  77 ++++
 vl.c                   |   1 +
 6 files changed, 1243 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 b22a37a..79f7e6b 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..b8bdf80 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,71 @@ 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;
+
+    /* 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);
+
+    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/virtio-ccw.c b/hw/s390x/virtio-ccw.c
new file mode 100644
index 0000000..8a90c3a
--- /dev/null
+++ b/hw/s390x/virtio-ccw.c
@@ -0,0 +1,962 @@
+/*
+ * 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 "../virtio.h"
+#include "../virtio-serial.h"
+#include "../virtio-net.h"
+#include "../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;
+
+typedef struct sch_entry {
+    SubchDev *sch;
+    QLIST_ENTRY(sch_entry) entry;
+} sch_entry;
+
+QLIST_HEAD(subch_list, sch_entry);
+
+typedef struct devno_entry {
+    uint16_t devno;
+    QLIST_ENTRY(devno_entry) entry;
+} devno_entry;
+
+QLIST_HEAD(devno_list, devno_entry);
+
+struct subch_set {
+    struct subch_list *s_list[256];
+    struct devno_list *d_list[256];
+};
+
+struct css_set {
+    struct subch_set *set[MAX_SSID + 1];
+};
+
+static struct css_set *channel_subsys[MAX_CSSID + 1];
+
+VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
+{
+    VirtIODevice *vdev = NULL;
+
+    if (sch->driver_data) {
+        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
+    }
+    return vdev;
+}
+
+static SubchDev *virtio_ccw_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid)
+{
+    struct sch_entry *sch_entry;
+    struct subch_list *list;
+    SubchDev *sch = NULL;
+    uint8_t real_cssid;
+
+    real_cssid = (!m && (cssid == 0)) ? VIRTUAL_CSSID : cssid;
+    if (!channel_subsys[real_cssid]) {
+        return NULL;
+    }
+    if (!channel_subsys[real_cssid]->set[ssid]) {
+        return NULL;
+    }
+    if (!channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8]) {
+        return NULL;
+    }
+
+    list = channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8];
+
+    QLIST_FOREACH(sch_entry, list, entry) {
+        if (sch_entry->sch->schid == schid) {
+            sch = sch_entry->sch;
+            break;
+        }
+    }
+
+    return sch;
+}
+
+static bool virtio_ccw_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
+{
+    struct devno_entry *devno_entry;
+    struct devno_list *list;
+    bool found = false;
+
+    if (!channel_subsys[cssid]) {
+        return false;
+    }
+    if (!channel_subsys[cssid]->set[ssid]) {
+        return false;
+    }
+    if (!channel_subsys[cssid]->set[ssid]->d_list[devno >> 8]) {
+        return false;
+    }
+
+    list = channel_subsys[cssid]->set[ssid]->d_list[devno >> 8];
+
+    QLIST_FOREACH(devno_entry, list, entry) {
+        if (devno_entry->devno == devno) {
+            found = true;
+            break;
+        }
+    }
+
+    return found;
+}
+
+static void virtio_ccw_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                                    uint16_t devno, SubchDev *sch)
+{
+    struct css_set *css;
+    struct subch_set *s_set;
+    struct subch_list *s_list;
+    struct devno_list *d_list;
+    struct sch_entry *sch_entry, *tmp;
+    struct devno_entry *devno_entry, *d_tmp;
+
+    if (!channel_subsys[cssid]) {
+        channel_subsys[cssid] = g_malloc0(sizeof(*channel_subsys[cssid]));
+    }
+    css = channel_subsys[cssid];
+
+    if (!css->set[ssid]) {
+        css->set[ssid] = g_malloc0(sizeof(*css->set[ssid]));
+    }
+    s_set = css->set[ssid];
+
+    if (!s_set->s_list[schid >> 8]) {
+        s_set->s_list[schid >> 8] =
+            g_malloc0(sizeof(*s_set->s_list[schid >> 8]));
+        QLIST_INIT(s_set->s_list[schid >> 8]);
+    }
+    s_list = s_set->s_list[schid >> 8];
+
+    if (!s_set->d_list[devno >> 8]) {
+        s_set->d_list[devno >> 8] =
+            g_malloc0(sizeof(*s_set->d_list[devno >> 8]));
+        QLIST_INIT(s_set->d_list[devno >> 8]);
+    }
+    d_list = s_set->d_list[devno >> 8];
+
+    if (sch) {
+        sch_entry = g_malloc0(sizeof(sch_entry));
+        sch_entry->sch = sch;
+        QLIST_INSERT_HEAD(s_list, sch_entry, entry);
+        devno_entry = g_malloc0(sizeof(devno_entry));
+        devno_entry->devno = devno;
+        QLIST_INSERT_HEAD(d_list, devno_entry, entry);
+    } else {
+        QLIST_FOREACH_SAFE(sch_entry, s_list, entry, tmp) {
+            if (sch_entry->sch->schid == schid) {
+                QLIST_REMOVE(sch_entry, entry);
+                g_free(sch_entry);
+                break;
+            }
+        }
+        QLIST_FOREACH_SAFE(devno_entry, d_list, entry, d_tmp) {
+            if (devno_entry->devno == devno) {
+                QLIST_REMOVE(devno_entry, entry);
+                g_free(devno_entry);
+                break;
+            }
+        }
+    }
+}
+
+VirtioCcwBus *virtio_ccw_bus_init(void)
+{
+    VirtioCcwBus *bus;
+    BusState *_bus;
+    DeviceState *dev;
+
+    css_set_subch_cb(virtio_ccw_find_subch);
+
+    /* 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");
+    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
+
+    /* Enable hotplugging */
+    _bus->allow_hotplug = 1;
+
+    return bus;
+}
+
+struct vq_info_block {
+    uint64_t queue;
+    uint16_t num;
+} QEMU_PACKED;
+
+struct vq_config_block {
+    uint16_t index;
+    uint16_t num;
+} QEMU_PACKED;
+
+/* Specify where the virtqueues for the subchannel are in guest memory. */
+static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint16_t num)
+{
+    VirtioCcwData *data = sch->driver_data;
+
+    if (num > VIRTIO_PCI_QUEUE_MAX) {
+        return -EINVAL;
+    }
+
+    if (!data) {
+        return -EINVAL;
+    }
+
+    virtio_queue_set_addr(data->vdev, num, addr);
+    if (!addr) {
+        virtio_queue_set_vector(data->vdev, num, 0);
+    } else {
+        virtio_queue_set_vector(data->vdev, num, num);
+    }
+    return 0;
+}
+
+static int virtio_ccw_cb(SubchDev *sch, struct ccw1 *ccw)
+{
+    int ret;
+    struct vq_info_block info;
+    uint8_t status;
+    uint32_t features;
+    void *config;
+    uint64_t *indicators;
+    struct vq_config_block 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.num = lduw_phys(ccw->cda + sizeof(info.queue));
+            ret = virtio_ccw_set_vqs(sch, info.queue, 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(data->host_features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(data->host_features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            stl_le_phys(ccw->cda, data->host_features);
+            sch->curr_status.scsw.count =
+                ccw->count - sizeof(data->host_features);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_WRITE_FEAT:
+        if (check_len) {
+            if (ccw->count != sizeof(data->vdev->guest_features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(data->vdev->guest_features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            features = bswap32(ldl_phys(ccw->cda));
+            if (data->vdev->set_features) {
+                data->vdev->set_features(data->vdev, features);
+            }
+            data->vdev->guest_features = features;
+            sch->curr_status.scsw.count =
+                ccw->count - sizeof(data->vdev->guest_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 = virtio_queue_get_num(data->vdev, vq_config.index);
+            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
+            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;
+                goto out_err;
+            }
+            /* Enforce use of virtual cssid. */
+            if (cssid != VIRTUAL_CSSID) {
+                ret = -EINVAL;
+                goto out_err;
+            }
+            if (virtio_ccw_devno_used(cssid, ssid, devno)) {
+                ret = -EEXIST;
+                goto out_err;
+            }
+            sch->cssid = cssid;
+            sch->ssid = ssid;
+            sch->devno = devno;
+            have_devno = true;
+        } else {
+            ret = -EINVAL;
+            goto out_err;
+        }
+    }
+
+    /* Find the next free id. */
+    if (have_devno) {
+        for (schid = 0; schid <= MAX_SCHID; schid++) {
+            if (!virtio_ccw_find_subch(1, cssid, ssid, schid)) {
+                sch->schid = schid;
+                virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            ret = -ENODEV;
+            goto out_err;
+        }
+    } else {
+        cssid = VIRTUAL_CSSID;
+        for (ssid = 0; ssid <= MAX_SSID; ssid++) {
+            for (schid = 0; schid <= MAX_SCHID; schid++) {
+                if (!virtio_ccw_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 (virtio_ccw_devno_used(cssid, ssid, devno)) {
+                        if (devno == MAX_SCHID) {
+                            devno = 0;
+                        } else if (devno == schid - 1) {
+                            ret = -ENODEV;
+                            goto out_err;
+                        } else {
+                            devno++;
+                        }
+                    }
+                    sch->devno = devno;
+                    virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
+                    found = true;
+                    break;
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        if (!found) {
+            ret = -ENODEV;
+            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(struct 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);
+    dev->host_features = vdev->get_features(vdev, dev->host_features);
+
+    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) {
+        virtio_ccw_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;
+
+    return dev->host_features;
+}
+
+void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus)
+{
+    BusChild *kid;
+    VirtioCcwData *data;
+
+    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+        data = (VirtioCcwData *)kid->child;
+        virtio_reset(data->vdev);
+        css_reset_sch(data->sch);
+    }
+}
+
+/**************** 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, 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..ee198d6
--- /dev/null
+++ b/hw/s390x/virtio-ccw.h
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+struct VirtioCcwData {
+    DeviceState qdev;
+    SubchDev *sch;
+    VirtIODevice *vdev;
+    char *bus_id;
+    VirtIOBlkConf blk;
+    NICConf nic;
+    uint32_t host_features;
+    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);
+void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus);
diff --git a/vl.c b/vl.c
index e71cb30..8d1f7f0 100644
--- a/vl.c
+++ b/vl.c
@@ -284,6 +284,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.4

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

* [Qemu-devel] [PATCH 3/5] s390: Add new channel I/O based virtio transport.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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>
---
 hw/qdev-monitor.c      |   5 +
 hw/s390-virtio.c       | 268 ++++++++++----
 hw/s390x/Makefile.objs |   1 +
 hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/virtio-ccw.h  |  77 ++++
 vl.c                   |   1 +
 6 files changed, 1243 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 b22a37a..79f7e6b 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..b8bdf80 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,71 @@ 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;
+
+    /* 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);
+
+    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/virtio-ccw.c b/hw/s390x/virtio-ccw.c
new file mode 100644
index 0000000..8a90c3a
--- /dev/null
+++ b/hw/s390x/virtio-ccw.c
@@ -0,0 +1,962 @@
+/*
+ * 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 "../virtio.h"
+#include "../virtio-serial.h"
+#include "../virtio-net.h"
+#include "../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;
+
+typedef struct sch_entry {
+    SubchDev *sch;
+    QLIST_ENTRY(sch_entry) entry;
+} sch_entry;
+
+QLIST_HEAD(subch_list, sch_entry);
+
+typedef struct devno_entry {
+    uint16_t devno;
+    QLIST_ENTRY(devno_entry) entry;
+} devno_entry;
+
+QLIST_HEAD(devno_list, devno_entry);
+
+struct subch_set {
+    struct subch_list *s_list[256];
+    struct devno_list *d_list[256];
+};
+
+struct css_set {
+    struct subch_set *set[MAX_SSID + 1];
+};
+
+static struct css_set *channel_subsys[MAX_CSSID + 1];
+
+VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
+{
+    VirtIODevice *vdev = NULL;
+
+    if (sch->driver_data) {
+        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
+    }
+    return vdev;
+}
+
+static SubchDev *virtio_ccw_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid)
+{
+    struct sch_entry *sch_entry;
+    struct subch_list *list;
+    SubchDev *sch = NULL;
+    uint8_t real_cssid;
+
+    real_cssid = (!m && (cssid == 0)) ? VIRTUAL_CSSID : cssid;
+    if (!channel_subsys[real_cssid]) {
+        return NULL;
+    }
+    if (!channel_subsys[real_cssid]->set[ssid]) {
+        return NULL;
+    }
+    if (!channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8]) {
+        return NULL;
+    }
+
+    list = channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8];
+
+    QLIST_FOREACH(sch_entry, list, entry) {
+        if (sch_entry->sch->schid == schid) {
+            sch = sch_entry->sch;
+            break;
+        }
+    }
+
+    return sch;
+}
+
+static bool virtio_ccw_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
+{
+    struct devno_entry *devno_entry;
+    struct devno_list *list;
+    bool found = false;
+
+    if (!channel_subsys[cssid]) {
+        return false;
+    }
+    if (!channel_subsys[cssid]->set[ssid]) {
+        return false;
+    }
+    if (!channel_subsys[cssid]->set[ssid]->d_list[devno >> 8]) {
+        return false;
+    }
+
+    list = channel_subsys[cssid]->set[ssid]->d_list[devno >> 8];
+
+    QLIST_FOREACH(devno_entry, list, entry) {
+        if (devno_entry->devno == devno) {
+            found = true;
+            break;
+        }
+    }
+
+    return found;
+}
+
+static void virtio_ccw_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                                    uint16_t devno, SubchDev *sch)
+{
+    struct css_set *css;
+    struct subch_set *s_set;
+    struct subch_list *s_list;
+    struct devno_list *d_list;
+    struct sch_entry *sch_entry, *tmp;
+    struct devno_entry *devno_entry, *d_tmp;
+
+    if (!channel_subsys[cssid]) {
+        channel_subsys[cssid] = g_malloc0(sizeof(*channel_subsys[cssid]));
+    }
+    css = channel_subsys[cssid];
+
+    if (!css->set[ssid]) {
+        css->set[ssid] = g_malloc0(sizeof(*css->set[ssid]));
+    }
+    s_set = css->set[ssid];
+
+    if (!s_set->s_list[schid >> 8]) {
+        s_set->s_list[schid >> 8] =
+            g_malloc0(sizeof(*s_set->s_list[schid >> 8]));
+        QLIST_INIT(s_set->s_list[schid >> 8]);
+    }
+    s_list = s_set->s_list[schid >> 8];
+
+    if (!s_set->d_list[devno >> 8]) {
+        s_set->d_list[devno >> 8] =
+            g_malloc0(sizeof(*s_set->d_list[devno >> 8]));
+        QLIST_INIT(s_set->d_list[devno >> 8]);
+    }
+    d_list = s_set->d_list[devno >> 8];
+
+    if (sch) {
+        sch_entry = g_malloc0(sizeof(sch_entry));
+        sch_entry->sch = sch;
+        QLIST_INSERT_HEAD(s_list, sch_entry, entry);
+        devno_entry = g_malloc0(sizeof(devno_entry));
+        devno_entry->devno = devno;
+        QLIST_INSERT_HEAD(d_list, devno_entry, entry);
+    } else {
+        QLIST_FOREACH_SAFE(sch_entry, s_list, entry, tmp) {
+            if (sch_entry->sch->schid == schid) {
+                QLIST_REMOVE(sch_entry, entry);
+                g_free(sch_entry);
+                break;
+            }
+        }
+        QLIST_FOREACH_SAFE(devno_entry, d_list, entry, d_tmp) {
+            if (devno_entry->devno == devno) {
+                QLIST_REMOVE(devno_entry, entry);
+                g_free(devno_entry);
+                break;
+            }
+        }
+    }
+}
+
+VirtioCcwBus *virtio_ccw_bus_init(void)
+{
+    VirtioCcwBus *bus;
+    BusState *_bus;
+    DeviceState *dev;
+
+    css_set_subch_cb(virtio_ccw_find_subch);
+
+    /* 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");
+    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
+
+    /* Enable hotplugging */
+    _bus->allow_hotplug = 1;
+
+    return bus;
+}
+
+struct vq_info_block {
+    uint64_t queue;
+    uint16_t num;
+} QEMU_PACKED;
+
+struct vq_config_block {
+    uint16_t index;
+    uint16_t num;
+} QEMU_PACKED;
+
+/* Specify where the virtqueues for the subchannel are in guest memory. */
+static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint16_t num)
+{
+    VirtioCcwData *data = sch->driver_data;
+
+    if (num > VIRTIO_PCI_QUEUE_MAX) {
+        return -EINVAL;
+    }
+
+    if (!data) {
+        return -EINVAL;
+    }
+
+    virtio_queue_set_addr(data->vdev, num, addr);
+    if (!addr) {
+        virtio_queue_set_vector(data->vdev, num, 0);
+    } else {
+        virtio_queue_set_vector(data->vdev, num, num);
+    }
+    return 0;
+}
+
+static int virtio_ccw_cb(SubchDev *sch, struct ccw1 *ccw)
+{
+    int ret;
+    struct vq_info_block info;
+    uint8_t status;
+    uint32_t features;
+    void *config;
+    uint64_t *indicators;
+    struct vq_config_block 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.num = lduw_phys(ccw->cda + sizeof(info.queue));
+            ret = virtio_ccw_set_vqs(sch, info.queue, 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(data->host_features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(data->host_features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            stl_le_phys(ccw->cda, data->host_features);
+            sch->curr_status.scsw.count =
+                ccw->count - sizeof(data->host_features);
+            ret = 0;
+        }
+        break;
+    case CCW_CMD_WRITE_FEAT:
+        if (check_len) {
+            if (ccw->count != sizeof(data->vdev->guest_features)) {
+                ret = -EINVAL;
+                break;
+            }
+        } else if (ccw->count < sizeof(data->vdev->guest_features)) {
+            /* Can't execute command. */
+            ret = -EINVAL;
+            break;
+        }
+        if (!qemu_get_ram_ptr(ccw->cda)) {
+            ret = -EFAULT;
+        } else {
+            features = bswap32(ldl_phys(ccw->cda));
+            if (data->vdev->set_features) {
+                data->vdev->set_features(data->vdev, features);
+            }
+            data->vdev->guest_features = features;
+            sch->curr_status.scsw.count =
+                ccw->count - sizeof(data->vdev->guest_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 = virtio_queue_get_num(data->vdev, vq_config.index);
+            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
+            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;
+                goto out_err;
+            }
+            /* Enforce use of virtual cssid. */
+            if (cssid != VIRTUAL_CSSID) {
+                ret = -EINVAL;
+                goto out_err;
+            }
+            if (virtio_ccw_devno_used(cssid, ssid, devno)) {
+                ret = -EEXIST;
+                goto out_err;
+            }
+            sch->cssid = cssid;
+            sch->ssid = ssid;
+            sch->devno = devno;
+            have_devno = true;
+        } else {
+            ret = -EINVAL;
+            goto out_err;
+        }
+    }
+
+    /* Find the next free id. */
+    if (have_devno) {
+        for (schid = 0; schid <= MAX_SCHID; schid++) {
+            if (!virtio_ccw_find_subch(1, cssid, ssid, schid)) {
+                sch->schid = schid;
+                virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            ret = -ENODEV;
+            goto out_err;
+        }
+    } else {
+        cssid = VIRTUAL_CSSID;
+        for (ssid = 0; ssid <= MAX_SSID; ssid++) {
+            for (schid = 0; schid <= MAX_SCHID; schid++) {
+                if (!virtio_ccw_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 (virtio_ccw_devno_used(cssid, ssid, devno)) {
+                        if (devno == MAX_SCHID) {
+                            devno = 0;
+                        } else if (devno == schid - 1) {
+                            ret = -ENODEV;
+                            goto out_err;
+                        } else {
+                            devno++;
+                        }
+                    }
+                    sch->devno = devno;
+                    virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
+                    found = true;
+                    break;
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        if (!found) {
+            ret = -ENODEV;
+            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(struct 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);
+    dev->host_features = vdev->get_features(vdev, dev->host_features);
+
+    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) {
+        virtio_ccw_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;
+
+    return dev->host_features;
+}
+
+void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus)
+{
+    BusChild *kid;
+    VirtioCcwData *data;
+
+    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+        data = (VirtioCcwData *)kid->child;
+        virtio_reset(data->vdev);
+        css_reset_sch(data->sch);
+    }
+}
+
+/**************** 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, 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..ee198d6
--- /dev/null
+++ b/hw/s390x/virtio-ccw.h
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+struct VirtioCcwData {
+    DeviceState qdev;
+    SubchDev *sch;
+    VirtIODevice *vdev;
+    char *bus_id;
+    VirtIOBlkConf blk;
+    NICConf nic;
+    uint32_t host_features;
+    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);
+void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus);
diff --git a/vl.c b/vl.c
index e71cb30..8d1f7f0 100644
--- a/vl.c
+++ b/vl.c
@@ -284,6 +284,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.4

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

* [PATCH 4/5] s390: Virtual channel subsystem support for !KVM.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Avi Kivity, Marcelo Tosatti, Anthony Liguori, Rusty Russell,
	Christian Borntraeger, Carsten Otte, Alexander Graf,
	Heiko Carstens, Martin Schwidefsky, Sebastian Ott

Provide css support for the !KVM case as well.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 hw/s390x/css.c           | 764 ++++++++++++++++++++++++++++++++++++++++++++++-
 hw/s390x/css.h           |  27 ++
 target-s390x/cpu.h       | 159 +++++++++-
 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, 1980 insertions(+), 27 deletions(-)

diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 7941c44..04728aa 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -16,6 +16,26 @@
 #include "cpu.h"
 #include "ioinst.h"
 #include "css.h"
+#include "virtio-ccw.h"
+
+typedef struct crw_container {
+    struct crw crw;
+    QTAILQ_ENTRY(crw_container) sibling;
+} crw_container;
+
+static QTAILQ_HEAD(crw_anchor, crw_container) pending_crws;
+
+static int do_crw_mchk;
+static int crws_lost;
+
+/*
+ * These variables control whether we actually show multiple channel
+ * subsystems or subchannel sets to the guest.
+ */
+static int max_cssid;
+static int max_ssid;
+
+static uint32_t global_schid[MAX_CSSID + 1][MAX_SSID + 1];
 
 struct chp_info {
     uint8_t in_use;
@@ -24,6 +44,9 @@ struct chp_info {
 
 static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
 
+static int chnmon_active;
+static uint64_t chnmon_area;
+
 static css_subch_cb_func css_subch_cb;
 
 int css_set_subch_cb(css_subch_cb_func func)
@@ -35,6 +58,76 @@ int css_set_subch_cb(css_subch_cb_func func)
     return 0;
 }
 
+static void css_write_phys_pmcw(uint32_t addr, struct 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, struct 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,
@@ -350,6 +443,552 @@ 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)
+{
+
+    struct 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(struct pmcw);
+    css_write_phys_scsw(addr + offset, &sch->curr_status.scsw);
+    offset += sizeof(struct 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, struct schib *schib)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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(chnmon_area + offset);
+        if (cmb) {
+            cmb->ssch_rsch_count++;
+        }
+    }
+}
+
+int css_do_ssch(SubchDev *sch, struct orb *orb)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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 (chnmon_active) {
+        css_update_chnmon(sch);
+    }
+    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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct pmcw *p = &sch->curr_status.pmcw;
+    uint8_t stctl;
+    uint8_t fctl;
+    uint8_t actl;
+    struct 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(struct irb));
+
+    /* Copy scsw from current status. */
+    memcpy(&irb.scsw, s, sizeof(struct 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(struct 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)
+{
+    struct crw_container *crw_cont;
+    int ret;
+
+    crw_cont = QTAILQ_FIRST(&pending_crws);
+    if (crw_cont) {
+        QTAILQ_REMOVE(&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);
+        do_crw_mchk = 1;
+        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];
+
+    if (m && !cssid) {
+            cssid = VIRTUAL_CSSID;
+    }
+    desc_size = 0;
+    for (i = f_chpid; i <= l_chpid; i++) {
+        if (chpids[cssid][i].in_use) {
+            if (rfmt == 0) {
+                words[0] = 0x80000000 | (chpids[cssid][i].type << 8) | i;
+                words[1] = 0;
+                memcpy(buf + desc_size, words, 8);
+                desc_size += 8;
+            } else if (rfmt == 1) {
+                words[0] = 0x80000000 | (chpids[cssid][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 && !chnmon_active) {
+        /* Enable measuring. */
+        chnmon_area = mbo;
+        chnmon_active = 1;
+    }
+    if (!update && chnmon_active) {
+        /* Disable measuring. */
+        chnmon_area = 0;
+        chnmon_active = 0;
+    }
+}
+
+int css_do_rsch(SubchDev *sch)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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 (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 > max_cssid) {
+        return -EINVAL;
+    }
+
+    if (!chpids[cssid][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, max_cssid > 0 ? 1 : 0, chpid);
+    if (max_cssid > 0) {
+        css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, cssid << 8);
+    }
+    return 0;
+}
+
+void css_update_sch_ids(uint8_t cssid, uint8_t ssid, uint16_t schid, int set)
+{
+    if (schid < global_schid[cssid][ssid]) {
+        return;
+    }
+
+    if (set && (schid > global_schid[cssid][ssid])) {
+        global_schid[cssid][ssid] = schid;
+    } else if (!set && (schid == global_schid[cssid][ssid])) {
+        /*
+         * Imprecise, but should not hurt in the big picture since
+         * (a) virtio_ccw will create big holes only on massive device
+         *     removal
+         * (b) global_schid is only used for stsch cc=3 handling anyway
+         */
+        global_schid[cssid][ssid]--;
+    } else if (schid != 0) {
+        fprintf(stderr, "CSS: suspicious %s call to %s(%x.%x.%04x)\n",
+                set ? "set" : "clear", __func__, cssid, ssid, schid);
+    }
+
+}
+
+int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return (cssid > MAX_CSSID ||
+            ssid > MAX_SSID ||
+            schid > global_schid[cssid][ssid]) ? 1 : 0;
+}
+
 static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
 {
     if (cssid > MAX_CSSID) {
@@ -396,9 +1035,117 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
     return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
 }
 
+bool css_present(uint8_t cssid)
+{
+    /* For now: */
+    return (cssid == VIRTUAL_CSSID);
+}
+
+void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
+{
+    struct crw_container *crw_cont;
+
+    /* TODO: Maybe use a static crw pool? */
+    crw_cont = g_try_malloc0(sizeof(struct crw_container));
+    if (!crw_cont) {
+        crws_lost = 1;
+        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 = crws_lost;
+    crws_lost = 0;
+
+    QTAILQ_INSERT_TAIL(&pending_crws, crw_cont, sibling);
+
+    if (do_crw_mchk) {
+        do_crw_mchk = 0;
+        /* 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;
+
+    if (add && !hotplugged) {
+        return;
+    }
+    guest_cssid = ((cssid == VIRTUAL_CSSID) && (max_cssid == 0)) ? 0 : cssid;
+    /*
+     * Only notify for higher subchannel sets/channel subsystems if the
+     * guest has enabled it.
+     */
+    if ((ssid > max_ssid) || (cssid > max_cssid) ||
+        ((max_cssid == 0) && (cssid != VIRTUAL_CSSID))) {
+        return;
+    }
+    css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI,
+                  ((max_ssid > 0) || (max_cssid > 0)) ? 1 : 0, schid);
+    if ((max_ssid > 0) || (max_cssid > 0)) {
+        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, (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, (max_cssid > 0) ? cssid : 0, ssid, schid, isc,
+                      intparm);
+    }
+}
+
+int css_enable_mcsse(void)
+{
+    max_cssid = MAX_CSSID;
+    return 0;
+}
+
+int css_enable_mss(void)
+{
+    max_ssid = MAX_SSID;
+    return 0;
+}
+
 static void css_init(void)
 {
     css_subch_cb = NULL;
+    QTAILQ_INIT(&pending_crws);
+    do_crw_mchk = 1;
+    max_cssid = 0;
+    max_ssid = 0;
+    chnmon_active = 0;
+    chnmon_area = 0;
 }
 machine_init(css_init);
 
@@ -436,5 +1183,20 @@ void css_reset_sch(SubchDev *sch)
 
 void css_reset(void)
 {
-    /* Nothing for now. */
+    struct crw_container *crw_cont;
+
+    /* Clean up monitoring. */
+    chnmon_active = 0;
+    chnmon_area = 0;
+
+    /* Clear pending CRWs. */
+    while ((crw_cont = QTAILQ_FIRST(&pending_crws))) {
+        QTAILQ_REMOVE(&pending_crws, crw_cont, sibling);
+        g_free(crw_cont);
+    }
+    do_crw_mchk = 1;
+
+    /* Reset maximum ids. */
+    max_cssid = 0;
+    max_ssid = 0;
 }
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index b8a95cc..d3efd5f 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -34,6 +34,31 @@ struct senseid {
     uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
 };
 
+/* 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;
@@ -58,5 +83,7 @@ int css_set_subch_cb(css_subch_cb_func func);
 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_update_sch_ids(uint8_t cssid, uint8_t ssid, uint16_t schid, int set);
+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 c09fa61..ca2f9bc 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
@@ -337,11 +360,30 @@ void cpu_lock(void);
 void cpu_unlock(void);
 
 typedef struct SubchDev SubchDev;
+struct schib;
 struct 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);
+int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid);
+int css_do_msch(SubchDev *sch, struct 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, struct 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,
                                        uint16_t schid)
@@ -351,6 +393,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 int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return 1;
+}
+static inline int css_do_msch(SubchDev *sch, struct 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, struct 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)
@@ -378,12 +484,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
@@ -1002,6 +1112,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) &&
@@ -1015,6 +1163,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,
@@ -1058,7 +1211,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);
     }
 }
 
@@ -1069,7 +1222,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);
     }
 }
 
@@ -1083,7 +1236,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..a5b610c 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(struct 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;
+    struct 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(struct 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;
+    struct 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)
+{
+    struct 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(struct 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;
+    struct 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;
+}
+
+struct chsc_req {
+    uint16_t len;
+    uint16_t command;
+    uint32_t param0;
+    uint32_t param1;
+    uint32_t param2;
+} QEMU_PACKED;
+
+struct chsc_resp {
+    uint16_t len;
+    uint16_t code;
+    uint32_t param;
+    char data[0];
+} QEMU_PACKED;
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+static void ioinst_handle_chsc_scpd(struct chsc_req *req, struct chsc_resp *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(struct chsc_req *req, struct chsc_resp *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(struct chsc_req *req, struct chsc_resp *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(struct chsc_resp *res)
+{
+    res->len = 8;
+    res->code = 0x0004;
+    res->param = 0;
+}
+
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
+{
+    struct chsc_req *req;
+    struct chsc_resp *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 79628b4..3c8981b 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 @@ struct ccw1 {
 #define SCSW_CSTAT_INTF_CTRL_CHK 0x02
 #define SCSW_CSTAT_CHAIN_CHECK   0x01
 
+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;
+};
+
+#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 e4cde14..17697b6 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
 
@@ -283,10 +303,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) {
@@ -294,8 +422,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;
     }
 
@@ -433,15 +569,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 7b72473..e1c368a 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -2336,18 +2336,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);
@@ -2358,6 +2351,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);
 }
 
 static void ext_interrupt(CPUS390XState *env, int type, uint32_t param,
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 4/5] s390: Virtual channel subsystem support for !KVM.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 hw/s390x/css.c           | 764 ++++++++++++++++++++++++++++++++++++++++++++++-
 hw/s390x/css.h           |  27 ++
 target-s390x/cpu.h       | 159 +++++++++-
 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, 1980 insertions(+), 27 deletions(-)

diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 7941c44..04728aa 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -16,6 +16,26 @@
 #include "cpu.h"
 #include "ioinst.h"
 #include "css.h"
+#include "virtio-ccw.h"
+
+typedef struct crw_container {
+    struct crw crw;
+    QTAILQ_ENTRY(crw_container) sibling;
+} crw_container;
+
+static QTAILQ_HEAD(crw_anchor, crw_container) pending_crws;
+
+static int do_crw_mchk;
+static int crws_lost;
+
+/*
+ * These variables control whether we actually show multiple channel
+ * subsystems or subchannel sets to the guest.
+ */
+static int max_cssid;
+static int max_ssid;
+
+static uint32_t global_schid[MAX_CSSID + 1][MAX_SSID + 1];
 
 struct chp_info {
     uint8_t in_use;
@@ -24,6 +44,9 @@ struct chp_info {
 
 static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
 
+static int chnmon_active;
+static uint64_t chnmon_area;
+
 static css_subch_cb_func css_subch_cb;
 
 int css_set_subch_cb(css_subch_cb_func func)
@@ -35,6 +58,76 @@ int css_set_subch_cb(css_subch_cb_func func)
     return 0;
 }
 
+static void css_write_phys_pmcw(uint32_t addr, struct 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, struct 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,
@@ -350,6 +443,552 @@ 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)
+{
+
+    struct 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(struct pmcw);
+    css_write_phys_scsw(addr + offset, &sch->curr_status.scsw);
+    offset += sizeof(struct 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, struct schib *schib)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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(chnmon_area + offset);
+        if (cmb) {
+            cmb->ssch_rsch_count++;
+        }
+    }
+}
+
+int css_do_ssch(SubchDev *sch, struct orb *orb)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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 (chnmon_active) {
+        css_update_chnmon(sch);
+    }
+    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)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct pmcw *p = &sch->curr_status.pmcw;
+    uint8_t stctl;
+    uint8_t fctl;
+    uint8_t actl;
+    struct 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(struct irb));
+
+    /* Copy scsw from current status. */
+    memcpy(&irb.scsw, s, sizeof(struct 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(struct 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)
+{
+    struct crw_container *crw_cont;
+    int ret;
+
+    crw_cont = QTAILQ_FIRST(&pending_crws);
+    if (crw_cont) {
+        QTAILQ_REMOVE(&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);
+        do_crw_mchk = 1;
+        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];
+
+    if (m && !cssid) {
+            cssid = VIRTUAL_CSSID;
+    }
+    desc_size = 0;
+    for (i = f_chpid; i <= l_chpid; i++) {
+        if (chpids[cssid][i].in_use) {
+            if (rfmt == 0) {
+                words[0] = 0x80000000 | (chpids[cssid][i].type << 8) | i;
+                words[1] = 0;
+                memcpy(buf + desc_size, words, 8);
+                desc_size += 8;
+            } else if (rfmt == 1) {
+                words[0] = 0x80000000 | (chpids[cssid][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 && !chnmon_active) {
+        /* Enable measuring. */
+        chnmon_area = mbo;
+        chnmon_active = 1;
+    }
+    if (!update && chnmon_active) {
+        /* Disable measuring. */
+        chnmon_area = 0;
+        chnmon_active = 0;
+    }
+}
+
+int css_do_rsch(SubchDev *sch)
+{
+    struct scsw *s = &sch->curr_status.scsw;
+    struct 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 (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 > max_cssid) {
+        return -EINVAL;
+    }
+
+    if (!chpids[cssid][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, max_cssid > 0 ? 1 : 0, chpid);
+    if (max_cssid > 0) {
+        css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, cssid << 8);
+    }
+    return 0;
+}
+
+void css_update_sch_ids(uint8_t cssid, uint8_t ssid, uint16_t schid, int set)
+{
+    if (schid < global_schid[cssid][ssid]) {
+        return;
+    }
+
+    if (set && (schid > global_schid[cssid][ssid])) {
+        global_schid[cssid][ssid] = schid;
+    } else if (!set && (schid == global_schid[cssid][ssid])) {
+        /*
+         * Imprecise, but should not hurt in the big picture since
+         * (a) virtio_ccw will create big holes only on massive device
+         *     removal
+         * (b) global_schid is only used for stsch cc=3 handling anyway
+         */
+        global_schid[cssid][ssid]--;
+    } else if (schid != 0) {
+        fprintf(stderr, "CSS: suspicious %s call to %s(%x.%x.%04x)\n",
+                set ? "set" : "clear", __func__, cssid, ssid, schid);
+    }
+
+}
+
+int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return (cssid > MAX_CSSID ||
+            ssid > MAX_SSID ||
+            schid > global_schid[cssid][ssid]) ? 1 : 0;
+}
+
 static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
 {
     if (cssid > MAX_CSSID) {
@@ -396,9 +1035,117 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
     return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
 }
 
+bool css_present(uint8_t cssid)
+{
+    /* For now: */
+    return (cssid == VIRTUAL_CSSID);
+}
+
+void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
+{
+    struct crw_container *crw_cont;
+
+    /* TODO: Maybe use a static crw pool? */
+    crw_cont = g_try_malloc0(sizeof(struct crw_container));
+    if (!crw_cont) {
+        crws_lost = 1;
+        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 = crws_lost;
+    crws_lost = 0;
+
+    QTAILQ_INSERT_TAIL(&pending_crws, crw_cont, sibling);
+
+    if (do_crw_mchk) {
+        do_crw_mchk = 0;
+        /* 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;
+
+    if (add && !hotplugged) {
+        return;
+    }
+    guest_cssid = ((cssid == VIRTUAL_CSSID) && (max_cssid == 0)) ? 0 : cssid;
+    /*
+     * Only notify for higher subchannel sets/channel subsystems if the
+     * guest has enabled it.
+     */
+    if ((ssid > max_ssid) || (cssid > max_cssid) ||
+        ((max_cssid == 0) && (cssid != VIRTUAL_CSSID))) {
+        return;
+    }
+    css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI,
+                  ((max_ssid > 0) || (max_cssid > 0)) ? 1 : 0, schid);
+    if ((max_ssid > 0) || (max_cssid > 0)) {
+        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, (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, (max_cssid > 0) ? cssid : 0, ssid, schid, isc,
+                      intparm);
+    }
+}
+
+int css_enable_mcsse(void)
+{
+    max_cssid = MAX_CSSID;
+    return 0;
+}
+
+int css_enable_mss(void)
+{
+    max_ssid = MAX_SSID;
+    return 0;
+}
+
 static void css_init(void)
 {
     css_subch_cb = NULL;
+    QTAILQ_INIT(&pending_crws);
+    do_crw_mchk = 1;
+    max_cssid = 0;
+    max_ssid = 0;
+    chnmon_active = 0;
+    chnmon_area = 0;
 }
 machine_init(css_init);
 
@@ -436,5 +1183,20 @@ void css_reset_sch(SubchDev *sch)
 
 void css_reset(void)
 {
-    /* Nothing for now. */
+    struct crw_container *crw_cont;
+
+    /* Clean up monitoring. */
+    chnmon_active = 0;
+    chnmon_area = 0;
+
+    /* Clear pending CRWs. */
+    while ((crw_cont = QTAILQ_FIRST(&pending_crws))) {
+        QTAILQ_REMOVE(&pending_crws, crw_cont, sibling);
+        g_free(crw_cont);
+    }
+    do_crw_mchk = 1;
+
+    /* Reset maximum ids. */
+    max_cssid = 0;
+    max_ssid = 0;
 }
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index b8a95cc..d3efd5f 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -34,6 +34,31 @@ struct senseid {
     uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
 };
 
+/* 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;
@@ -58,5 +83,7 @@ int css_set_subch_cb(css_subch_cb_func func);
 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_update_sch_ids(uint8_t cssid, uint8_t ssid, uint16_t schid, int set);
+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 c09fa61..ca2f9bc 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
@@ -337,11 +360,30 @@ void cpu_lock(void);
 void cpu_unlock(void);
 
 typedef struct SubchDev SubchDev;
+struct schib;
 struct 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);
+int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid);
+int css_do_msch(SubchDev *sch, struct 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, struct 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,
                                        uint16_t schid)
@@ -351,6 +393,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 int css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return 1;
+}
+static inline int css_do_msch(SubchDev *sch, struct 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, struct 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)
@@ -378,12 +484,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
@@ -1002,6 +1112,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) &&
@@ -1015,6 +1163,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,
@@ -1058,7 +1211,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);
     }
 }
 
@@ -1069,7 +1222,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);
     }
 }
 
@@ -1083,7 +1236,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..a5b610c 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(struct 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;
+    struct 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(struct 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;
+    struct 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)
+{
+    struct 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(struct 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;
+    struct 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;
+}
+
+struct chsc_req {
+    uint16_t len;
+    uint16_t command;
+    uint32_t param0;
+    uint32_t param1;
+    uint32_t param2;
+} QEMU_PACKED;
+
+struct chsc_resp {
+    uint16_t len;
+    uint16_t code;
+    uint32_t param;
+    char data[0];
+} QEMU_PACKED;
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+static void ioinst_handle_chsc_scpd(struct chsc_req *req, struct chsc_resp *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(struct chsc_req *req, struct chsc_resp *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(struct chsc_req *req, struct chsc_resp *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(struct chsc_resp *res)
+{
+    res->len = 8;
+    res->code = 0x0004;
+    res->param = 0;
+}
+
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
+{
+    struct chsc_req *req;
+    struct chsc_resp *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 79628b4..3c8981b 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 @@ struct ccw1 {
 #define SCSW_CSTAT_INTF_CTRL_CHK 0x02
 #define SCSW_CSTAT_CHAIN_CHECK   0x01
 
+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;
+};
+
+#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 e4cde14..17697b6 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
 
@@ -283,10 +303,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) {
@@ -294,8 +422,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;
     }
 
@@ -433,15 +569,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 7b72473..e1c368a 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -2336,18 +2336,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);
@@ -2358,6 +2351,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);
 }
 
 static void ext_interrupt(CPUS390XState *env, int type, uint32_t param,
-- 
1.7.11.4

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

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

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 8669142..7a8eb27 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -559,11 +559,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 79f7e6b..6178b83 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 8d1f7f0..f04eb18 100644
--- a/vl.c
+++ b/vl.c
@@ -2029,11 +2029,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.4

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

* [Qemu-devel] [PATCH 5/5] [HACK] Handle multiple virtio aliases.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-07 14:52 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 8669142..7a8eb27 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -559,11 +559,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 79f7e6b..6178b83 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 8d1f7f0..f04eb18 100644
--- a/vl.c
+++ b/vl.c
@@ -2029,11 +2029,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.4

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

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

On Tue, Aug 7, 2012 at 2:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> ---
>  hw/qdev-monitor.c      |   5 +
>  hw/s390-virtio.c       | 268 ++++++++++----
>  hw/s390x/Makefile.objs |   1 +
>  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/virtio-ccw.h  |  77 ++++
>  vl.c                   |   1 +
>  6 files changed, 1243 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 b22a37a..79f7e6b 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..b8bdf80 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,71 @@ 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;
> +
> +    /* 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);
> +
> +    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/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> new file mode 100644
> index 0000000..8a90c3a
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.c
> @@ -0,0 +1,962 @@
> +/*
> + * 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 "../virtio.h"
> +#include "../virtio-serial.h"
> +#include "../virtio-net.h"
> +#include "../sysbus.h"

"hw/virtio..." for the above

> +#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;
> +
> +typedef struct sch_entry {
> +    SubchDev *sch;
> +    QLIST_ENTRY(sch_entry) entry;
> +} sch_entry;

SubchEntry, see CODING_STYLE. Also other struct and typedef names below.

> +
> +QLIST_HEAD(subch_list, sch_entry);

static, but please put this to a structure that is passed around instead.

> +
> +typedef struct devno_entry {
> +    uint16_t devno;
> +    QLIST_ENTRY(devno_entry) entry;
> +} devno_entry;
> +
> +QLIST_HEAD(devno_list, devno_entry);

Ditto

> +
> +struct subch_set {
> +    struct subch_list *s_list[256];
> +    struct devno_list *d_list[256];
> +};
> +
> +struct css_set {
> +    struct subch_set *set[MAX_SSID + 1];
> +};
> +
> +static struct css_set *channel_subsys[MAX_CSSID + 1];
> +
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> +{
> +    VirtIODevice *vdev = NULL;
> +
> +    if (sch->driver_data) {
> +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> +    }
> +    return vdev;
> +}
> +
> +static SubchDev *virtio_ccw_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid)
> +{
> +    struct sch_entry *sch_entry;
> +    struct subch_list *list;
> +    SubchDev *sch = NULL;
> +    uint8_t real_cssid;
> +
> +    real_cssid = (!m && (cssid == 0)) ? VIRTUAL_CSSID : cssid;
> +    if (!channel_subsys[real_cssid]) {
> +        return NULL;
> +    }
> +    if (!channel_subsys[real_cssid]->set[ssid]) {
> +        return NULL;
> +    }
> +    if (!channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8]) {
> +        return NULL;
> +    }
> +
> +    list = channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8];
> +
> +    QLIST_FOREACH(sch_entry, list, entry) {
> +        if (sch_entry->sch->schid == schid) {
> +            sch = sch_entry->sch;
> +            break;
> +        }
> +    }
> +
> +    return sch;
> +}
> +
> +static bool virtio_ccw_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
> +{
> +    struct devno_entry *devno_entry;
> +    struct devno_list *list;
> +    bool found = false;
> +
> +    if (!channel_subsys[cssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys[cssid]->set[ssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys[cssid]->set[ssid]->d_list[devno >> 8]) {
> +        return false;
> +    }
> +
> +    list = channel_subsys[cssid]->set[ssid]->d_list[devno >> 8];
> +
> +    QLIST_FOREACH(devno_entry, list, entry) {
> +        if (devno_entry->devno == devno) {
> +            found = true;
> +            break;
> +        }
> +    }
> +
> +    return found;
> +}
> +
> +static void virtio_ccw_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                                    uint16_t devno, SubchDev *sch)
> +{
> +    struct css_set *css;
> +    struct subch_set *s_set;
> +    struct subch_list *s_list;
> +    struct devno_list *d_list;
> +    struct sch_entry *sch_entry, *tmp;
> +    struct devno_entry *devno_entry, *d_tmp;
> +
> +    if (!channel_subsys[cssid]) {
> +        channel_subsys[cssid] = g_malloc0(sizeof(*channel_subsys[cssid]));
> +    }
> +    css = channel_subsys[cssid];
> +
> +    if (!css->set[ssid]) {
> +        css->set[ssid] = g_malloc0(sizeof(*css->set[ssid]));
> +    }
> +    s_set = css->set[ssid];
> +
> +    if (!s_set->s_list[schid >> 8]) {
> +        s_set->s_list[schid >> 8] =
> +            g_malloc0(sizeof(*s_set->s_list[schid >> 8]));
> +        QLIST_INIT(s_set->s_list[schid >> 8]);
> +    }
> +    s_list = s_set->s_list[schid >> 8];
> +
> +    if (!s_set->d_list[devno >> 8]) {
> +        s_set->d_list[devno >> 8] =
> +            g_malloc0(sizeof(*s_set->d_list[devno >> 8]));
> +        QLIST_INIT(s_set->d_list[devno >> 8]);
> +    }
> +    d_list = s_set->d_list[devno >> 8];
> +
> +    if (sch) {
> +        sch_entry = g_malloc0(sizeof(sch_entry));
> +        sch_entry->sch = sch;
> +        QLIST_INSERT_HEAD(s_list, sch_entry, entry);
> +        devno_entry = g_malloc0(sizeof(devno_entry));
> +        devno_entry->devno = devno;
> +        QLIST_INSERT_HEAD(d_list, devno_entry, entry);
> +    } else {
> +        QLIST_FOREACH_SAFE(sch_entry, s_list, entry, tmp) {
> +            if (sch_entry->sch->schid == schid) {
> +                QLIST_REMOVE(sch_entry, entry);
> +                g_free(sch_entry);
> +                break;
> +            }
> +        }
> +        QLIST_FOREACH_SAFE(devno_entry, d_list, entry, d_tmp) {
> +            if (devno_entry->devno == devno) {
> +                QLIST_REMOVE(devno_entry, entry);
> +                g_free(devno_entry);
> +                break;
> +            }
> +        }
> +    }
> +}
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void)
> +{
> +    VirtioCcwBus *bus;
> +    BusState *_bus;

Please avoid identifiers with leading underscores.

> +    DeviceState *dev;
> +
> +    css_set_subch_cb(virtio_ccw_find_subch);
> +
> +    /* 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");
> +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
> +
> +    /* Enable hotplugging */
> +    _bus->allow_hotplug = 1;
> +
> +    return bus;
> +}
> +
> +struct vq_info_block {
> +    uint64_t queue;
> +    uint16_t num;
> +} QEMU_PACKED;
> +
> +struct vq_config_block {
> +    uint16_t index;
> +    uint16_t num;
> +} QEMU_PACKED;

Aren't these KVM structures? They should be defined in a KVM header
file file in linux-headers.

> +
> +/* Specify where the virtqueues for the subchannel are in guest memory. */
> +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint16_t num)
> +{
> +    VirtioCcwData *data = sch->driver_data;
> +
> +    if (num > VIRTIO_PCI_QUEUE_MAX) {
> +        return -EINVAL;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    virtio_queue_set_addr(data->vdev, num, addr);
> +    if (!addr) {
> +        virtio_queue_set_vector(data->vdev, num, 0);
> +    } else {
> +        virtio_queue_set_vector(data->vdev, num, num);
> +    }
> +    return 0;
> +}
> +
> +static int virtio_ccw_cb(SubchDev *sch, struct ccw1 *ccw)
> +{
> +    int ret;
> +    struct vq_info_block info;
> +    uint8_t status;
> +    uint32_t features;
> +    void *config;
> +    uint64_t *indicators;
> +    struct vq_config_block 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.num = lduw_phys(ccw->cda + sizeof(info.queue));
> +            ret = virtio_ccw_set_vqs(sch, info.queue, 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(data->host_features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(data->host_features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            stl_le_phys(ccw->cda, data->host_features);
> +            sch->curr_status.scsw.count =
> +                ccw->count - sizeof(data->host_features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(data->vdev->guest_features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(data->vdev->guest_features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features = bswap32(ldl_phys(ccw->cda));
> +            if (data->vdev->set_features) {
> +                data->vdev->set_features(data->vdev, features);
> +            }
> +            data->vdev->guest_features = features;
> +            sch->curr_status.scsw.count =
> +                ccw->count - sizeof(data->vdev->guest_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);

Please use cpu_physical_memory_read() (or DMA versions) instead of
this + memcpy().

> +        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);

lduw_{b,l}e_phys()

> +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
> +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);

stw_{b,l]e_phys(), likewise elsewhere.

> +            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;
> +                goto out_err;
> +            }
> +            /* Enforce use of virtual cssid. */
> +            if (cssid != VIRTUAL_CSSID) {
> +                ret = -EINVAL;
> +                goto out_err;
> +            }
> +            if (virtio_ccw_devno_used(cssid, ssid, devno)) {
> +                ret = -EEXIST;
> +                goto out_err;
> +            }
> +            sch->cssid = cssid;
> +            sch->ssid = ssid;
> +            sch->devno = devno;
> +            have_devno = true;
> +        } else {
> +            ret = -EINVAL;
> +            goto out_err;
> +        }
> +    }
> +
> +    /* Find the next free id. */
> +    if (have_devno) {
> +        for (schid = 0; schid <= MAX_SCHID; schid++) {
> +            if (!virtio_ccw_find_subch(1, cssid, ssid, schid)) {
> +                sch->schid = schid;
> +                virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
> +                found = true;
> +                break;
> +            }
> +        }
> +        if (!found) {
> +            ret = -ENODEV;
> +            goto out_err;
> +        }
> +    } else {
> +        cssid = VIRTUAL_CSSID;
> +        for (ssid = 0; ssid <= MAX_SSID; ssid++) {
> +            for (schid = 0; schid <= MAX_SCHID; schid++) {
> +                if (!virtio_ccw_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 (virtio_ccw_devno_used(cssid, ssid, devno)) {
> +                        if (devno == MAX_SCHID) {
> +                            devno = 0;
> +                        } else if (devno == schid - 1) {
> +                            ret = -ENODEV;
> +                            goto out_err;
> +                        } else {
> +                            devno++;
> +                        }
> +                    }
> +                    sch->devno = devno;
> +                    virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
> +                    found = true;
> +                    break;
> +                }
> +            }
> +            if (found) {
> +                break;
> +            }
> +        }
> +        if (!found) {
> +            ret = -ENODEV;
> +            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(struct 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);
> +    dev->host_features = vdev->get_features(vdev, dev->host_features);
> +
> +    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) {
> +        virtio_ccw_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;
> +
> +    return dev->host_features;
> +}
> +
> +void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus)
> +{
> +    BusChild *kid;
> +    VirtioCcwData *data;
> +
> +    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
> +        data = (VirtioCcwData *)kid->child;
> +        virtio_reset(data->vdev);
> +        css_reset_sch(data->sch);
> +    }
> +}
> +
> +/**************** 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, 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..ee198d6
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.h
> @@ -0,0 +1,77 @@
> +/*
> + * 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;
> +
> +struct VirtioCcwData {
> +    DeviceState qdev;
> +    SubchDev *sch;
> +    VirtIODevice *vdev;
> +    char *bus_id;
> +    VirtIOBlkConf blk;
> +    NICConf nic;
> +    uint32_t host_features;
> +    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);
> +void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus);
> diff --git a/vl.c b/vl.c
> index e71cb30..8d1f7f0 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -284,6 +284,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.4
>
>

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

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

On Tue, Aug 7, 2012 at 2:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> ---
>  hw/qdev-monitor.c      |   5 +
>  hw/s390-virtio.c       | 268 ++++++++++----
>  hw/s390x/Makefile.objs |   1 +
>  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/virtio-ccw.h  |  77 ++++
>  vl.c                   |   1 +
>  6 files changed, 1243 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 b22a37a..79f7e6b 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..b8bdf80 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,71 @@ 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;
> +
> +    /* 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);
> +
> +    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/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> new file mode 100644
> index 0000000..8a90c3a
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.c
> @@ -0,0 +1,962 @@
> +/*
> + * 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 "../virtio.h"
> +#include "../virtio-serial.h"
> +#include "../virtio-net.h"
> +#include "../sysbus.h"

"hw/virtio..." for the above

> +#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;
> +
> +typedef struct sch_entry {
> +    SubchDev *sch;
> +    QLIST_ENTRY(sch_entry) entry;
> +} sch_entry;

SubchEntry, see CODING_STYLE. Also other struct and typedef names below.

> +
> +QLIST_HEAD(subch_list, sch_entry);

static, but please put this to a structure that is passed around instead.

> +
> +typedef struct devno_entry {
> +    uint16_t devno;
> +    QLIST_ENTRY(devno_entry) entry;
> +} devno_entry;
> +
> +QLIST_HEAD(devno_list, devno_entry);

Ditto

> +
> +struct subch_set {
> +    struct subch_list *s_list[256];
> +    struct devno_list *d_list[256];
> +};
> +
> +struct css_set {
> +    struct subch_set *set[MAX_SSID + 1];
> +};
> +
> +static struct css_set *channel_subsys[MAX_CSSID + 1];
> +
> +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> +{
> +    VirtIODevice *vdev = NULL;
> +
> +    if (sch->driver_data) {
> +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> +    }
> +    return vdev;
> +}
> +
> +static SubchDev *virtio_ccw_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid)
> +{
> +    struct sch_entry *sch_entry;
> +    struct subch_list *list;
> +    SubchDev *sch = NULL;
> +    uint8_t real_cssid;
> +
> +    real_cssid = (!m && (cssid == 0)) ? VIRTUAL_CSSID : cssid;
> +    if (!channel_subsys[real_cssid]) {
> +        return NULL;
> +    }
> +    if (!channel_subsys[real_cssid]->set[ssid]) {
> +        return NULL;
> +    }
> +    if (!channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8]) {
> +        return NULL;
> +    }
> +
> +    list = channel_subsys[real_cssid]->set[ssid]->s_list[schid >> 8];
> +
> +    QLIST_FOREACH(sch_entry, list, entry) {
> +        if (sch_entry->sch->schid == schid) {
> +            sch = sch_entry->sch;
> +            break;
> +        }
> +    }
> +
> +    return sch;
> +}
> +
> +static bool virtio_ccw_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
> +{
> +    struct devno_entry *devno_entry;
> +    struct devno_list *list;
> +    bool found = false;
> +
> +    if (!channel_subsys[cssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys[cssid]->set[ssid]) {
> +        return false;
> +    }
> +    if (!channel_subsys[cssid]->set[ssid]->d_list[devno >> 8]) {
> +        return false;
> +    }
> +
> +    list = channel_subsys[cssid]->set[ssid]->d_list[devno >> 8];
> +
> +    QLIST_FOREACH(devno_entry, list, entry) {
> +        if (devno_entry->devno == devno) {
> +            found = true;
> +            break;
> +        }
> +    }
> +
> +    return found;
> +}
> +
> +static void virtio_ccw_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                                    uint16_t devno, SubchDev *sch)
> +{
> +    struct css_set *css;
> +    struct subch_set *s_set;
> +    struct subch_list *s_list;
> +    struct devno_list *d_list;
> +    struct sch_entry *sch_entry, *tmp;
> +    struct devno_entry *devno_entry, *d_tmp;
> +
> +    if (!channel_subsys[cssid]) {
> +        channel_subsys[cssid] = g_malloc0(sizeof(*channel_subsys[cssid]));
> +    }
> +    css = channel_subsys[cssid];
> +
> +    if (!css->set[ssid]) {
> +        css->set[ssid] = g_malloc0(sizeof(*css->set[ssid]));
> +    }
> +    s_set = css->set[ssid];
> +
> +    if (!s_set->s_list[schid >> 8]) {
> +        s_set->s_list[schid >> 8] =
> +            g_malloc0(sizeof(*s_set->s_list[schid >> 8]));
> +        QLIST_INIT(s_set->s_list[schid >> 8]);
> +    }
> +    s_list = s_set->s_list[schid >> 8];
> +
> +    if (!s_set->d_list[devno >> 8]) {
> +        s_set->d_list[devno >> 8] =
> +            g_malloc0(sizeof(*s_set->d_list[devno >> 8]));
> +        QLIST_INIT(s_set->d_list[devno >> 8]);
> +    }
> +    d_list = s_set->d_list[devno >> 8];
> +
> +    if (sch) {
> +        sch_entry = g_malloc0(sizeof(sch_entry));
> +        sch_entry->sch = sch;
> +        QLIST_INSERT_HEAD(s_list, sch_entry, entry);
> +        devno_entry = g_malloc0(sizeof(devno_entry));
> +        devno_entry->devno = devno;
> +        QLIST_INSERT_HEAD(d_list, devno_entry, entry);
> +    } else {
> +        QLIST_FOREACH_SAFE(sch_entry, s_list, entry, tmp) {
> +            if (sch_entry->sch->schid == schid) {
> +                QLIST_REMOVE(sch_entry, entry);
> +                g_free(sch_entry);
> +                break;
> +            }
> +        }
> +        QLIST_FOREACH_SAFE(devno_entry, d_list, entry, d_tmp) {
> +            if (devno_entry->devno == devno) {
> +                QLIST_REMOVE(devno_entry, entry);
> +                g_free(devno_entry);
> +                break;
> +            }
> +        }
> +    }
> +}
> +
> +VirtioCcwBus *virtio_ccw_bus_init(void)
> +{
> +    VirtioCcwBus *bus;
> +    BusState *_bus;

Please avoid identifiers with leading underscores.

> +    DeviceState *dev;
> +
> +    css_set_subch_cb(virtio_ccw_find_subch);
> +
> +    /* 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");
> +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
> +
> +    /* Enable hotplugging */
> +    _bus->allow_hotplug = 1;
> +
> +    return bus;
> +}
> +
> +struct vq_info_block {
> +    uint64_t queue;
> +    uint16_t num;
> +} QEMU_PACKED;
> +
> +struct vq_config_block {
> +    uint16_t index;
> +    uint16_t num;
> +} QEMU_PACKED;

Aren't these KVM structures? They should be defined in a KVM header
file file in linux-headers.

> +
> +/* Specify where the virtqueues for the subchannel are in guest memory. */
> +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint16_t num)
> +{
> +    VirtioCcwData *data = sch->driver_data;
> +
> +    if (num > VIRTIO_PCI_QUEUE_MAX) {
> +        return -EINVAL;
> +    }
> +
> +    if (!data) {
> +        return -EINVAL;
> +    }
> +
> +    virtio_queue_set_addr(data->vdev, num, addr);
> +    if (!addr) {
> +        virtio_queue_set_vector(data->vdev, num, 0);
> +    } else {
> +        virtio_queue_set_vector(data->vdev, num, num);
> +    }
> +    return 0;
> +}
> +
> +static int virtio_ccw_cb(SubchDev *sch, struct ccw1 *ccw)
> +{
> +    int ret;
> +    struct vq_info_block info;
> +    uint8_t status;
> +    uint32_t features;
> +    void *config;
> +    uint64_t *indicators;
> +    struct vq_config_block 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.num = lduw_phys(ccw->cda + sizeof(info.queue));
> +            ret = virtio_ccw_set_vqs(sch, info.queue, 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(data->host_features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(data->host_features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            stl_le_phys(ccw->cda, data->host_features);
> +            sch->curr_status.scsw.count =
> +                ccw->count - sizeof(data->host_features);
> +            ret = 0;
> +        }
> +        break;
> +    case CCW_CMD_WRITE_FEAT:
> +        if (check_len) {
> +            if (ccw->count != sizeof(data->vdev->guest_features)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        } else if (ccw->count < sizeof(data->vdev->guest_features)) {
> +            /* Can't execute command. */
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (!qemu_get_ram_ptr(ccw->cda)) {
> +            ret = -EFAULT;
> +        } else {
> +            features = bswap32(ldl_phys(ccw->cda));
> +            if (data->vdev->set_features) {
> +                data->vdev->set_features(data->vdev, features);
> +            }
> +            data->vdev->guest_features = features;
> +            sch->curr_status.scsw.count =
> +                ccw->count - sizeof(data->vdev->guest_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);

Please use cpu_physical_memory_read() (or DMA versions) instead of
this + memcpy().

> +        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);

lduw_{b,l}e_phys()

> +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
> +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);

stw_{b,l]e_phys(), likewise elsewhere.

> +            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;
> +                goto out_err;
> +            }
> +            /* Enforce use of virtual cssid. */
> +            if (cssid != VIRTUAL_CSSID) {
> +                ret = -EINVAL;
> +                goto out_err;
> +            }
> +            if (virtio_ccw_devno_used(cssid, ssid, devno)) {
> +                ret = -EEXIST;
> +                goto out_err;
> +            }
> +            sch->cssid = cssid;
> +            sch->ssid = ssid;
> +            sch->devno = devno;
> +            have_devno = true;
> +        } else {
> +            ret = -EINVAL;
> +            goto out_err;
> +        }
> +    }
> +
> +    /* Find the next free id. */
> +    if (have_devno) {
> +        for (schid = 0; schid <= MAX_SCHID; schid++) {
> +            if (!virtio_ccw_find_subch(1, cssid, ssid, schid)) {
> +                sch->schid = schid;
> +                virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
> +                found = true;
> +                break;
> +            }
> +        }
> +        if (!found) {
> +            ret = -ENODEV;
> +            goto out_err;
> +        }
> +    } else {
> +        cssid = VIRTUAL_CSSID;
> +        for (ssid = 0; ssid <= MAX_SSID; ssid++) {
> +            for (schid = 0; schid <= MAX_SCHID; schid++) {
> +                if (!virtio_ccw_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 (virtio_ccw_devno_used(cssid, ssid, devno)) {
> +                        if (devno == MAX_SCHID) {
> +                            devno = 0;
> +                        } else if (devno == schid - 1) {
> +                            ret = -ENODEV;
> +                            goto out_err;
> +                        } else {
> +                            devno++;
> +                        }
> +                    }
> +                    sch->devno = devno;
> +                    virtio_ccw_subch_assign(cssid, ssid, schid, devno, sch);
> +                    found = true;
> +                    break;
> +                }
> +            }
> +            if (found) {
> +                break;
> +            }
> +        }
> +        if (!found) {
> +            ret = -ENODEV;
> +            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(struct 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);
> +    dev->host_features = vdev->get_features(vdev, dev->host_features);
> +
> +    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) {
> +        virtio_ccw_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;
> +
> +    return dev->host_features;
> +}
> +
> +void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus)
> +{
> +    BusChild *kid;
> +    VirtioCcwData *data;
> +
> +    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
> +        data = (VirtioCcwData *)kid->child;
> +        virtio_reset(data->vdev);
> +        css_reset_sch(data->sch);
> +    }
> +}
> +
> +/**************** 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, 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..ee198d6
> --- /dev/null
> +++ b/hw/s390x/virtio-ccw.h
> @@ -0,0 +1,77 @@
> +/*
> + * 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;
> +
> +struct VirtioCcwData {
> +    DeviceState qdev;
> +    SubchDev *sch;
> +    VirtIODevice *vdev;
> +    char *bus_id;
> +    VirtIOBlkConf blk;
> +    NICConf nic;
> +    uint32_t host_features;
> +    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);
> +void virtio_ccw_reset_subchannels(struct VirtioCcwBus *bus);
> diff --git a/vl.c b/vl.c
> index e71cb30..8d1f7f0 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -284,6 +284,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.4
>
>

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 21:00     ` Blue Swirl
  -1 siblings, 0 replies; 42+ messages in thread
From: Blue Swirl @ 2012-08-07 21:00 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: KVM, linux-s390, qemu-devel, Carsten Otte, Anthony Liguori,
	Rusty Russell, Sebastian Ott, Marcelo Tosatti, Heiko Carstens,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Tue, Aug 7, 2012 at 2:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> 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>
> ---
>  hw/s390x/Makefile.objs     |   1 +
>  hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/css.h             |  62 +++++++
>  target-s390x/Makefile.objs |   2 +-
>  target-s390x/cpu.h         | 108 +++++++++++
>  target-s390x/ioinst.c      |  38 ++++
>  target-s390x/ioinst.h      | 173 ++++++++++++++++++
>  target-s390x/kvm.c         | 101 +++++++++++
>  8 files changed, 924 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..7941c44
> --- /dev/null
> +++ b/hw/s390x/css.c
> @@ -0,0 +1,440 @@
> +/*
> + * 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 "kvm.h"
> +#include "cpu.h"
> +#include "ioinst.h"
> +#include "css.h"
> +
> +struct chp_info {

CamelCase, please.

> +    uint8_t in_use;
> +    uint8_t type;
> +};
> +
> +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> +
> +static css_subch_cb_func css_subch_cb;

Probably these can be put to a container structure which can be passed around.

> +
> +int css_set_subch_cb(css_subch_cb_func func)
> +{
> +    if (func && css_subch_cb) {
> +        return -EBUSY;
> +    }
> +    css_subch_cb = func;
> +    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)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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, struct 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]);
> +        }

cpu_physical_memory_write()

> +        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)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    struct 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(struct pmcw));
> +    switch (func) {
> +    case CSS_DO_CSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct 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(struct 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(struct 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(struct 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)
> +{
> +    if (cssid > MAX_CSSID) {
> +        return -EINVAL;
> +    }
> +    if (chpids[cssid][chpid].in_use) {
> +        return -EEXIST;
> +    }
> +    chpids[cssid][chpid].in_use = 1;
> +    chpids[cssid][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)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int i;
> +
> +    memset(p, 0, sizeof(struct pmcw));
> +    p->dnv = 1;
> +    p->dev = sch->devno;
> +    /* single path */
> +    p->pim = 0x80;
> +    p->pom = 0xff;
> +    p->pam = 0x80;
> +    p->chpid[0] = chpid;
> +    if (!chpids[sch->cssid][chpid].in_use) {
> +        css_add_virtual_chpid(sch->cssid, chpid, type);
> +    }
> +
> +    memset(s, 0, sizeof(struct 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)
> +{
> +    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
> +}
> +
> +static void css_init(void)
> +{
> +    css_subch_cb = NULL;
> +}
> +machine_init(css_init);
> +
> +void css_reset_sch(SubchDev *sch)
> +{
> +    struct 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..b8a95cc
> --- /dev/null
> +++ b/hw/s390x/css.h
> @@ -0,0 +1,62 @@
> +/*
> + * 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 8
> +
> +struct senseid {

SenseID

> +    /* common part */
> +    uint32_t  reserved:8;    /* always 0x'FF' */

The standard syntax calls for 'unsigned' instead of uint32_t for bit
fields. But bit fields are not very well defined, it's better to avoid
them.

> +    uint32_t  cu_type:16;    /* control unit type */
> +    uint32_t  cu_model:8;    /* control unit model */
> +    uint32_t  dev_type:16;   /* device type */
> +    uint32_t  dev_model:8;   /* device model */
> +    uint32_t  unused:8;      /* padding byte */
> +    /* extended part */
> +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> +};
> +
> +struct SubchDev {
> +    /* channel-subsystem related things: */
> +    uint8_t cssid;
> +    uint8_t ssid;
> +    uint16_t schid;
> +    uint16_t devno;
> +    struct schib curr_status;
> +    uint8_t sense_data[32];
> +    struct ccw1 *channel_prog;
> +    struct ccw1 *last_cmd;
> +    struct orb *orb;
> +    QemuMutex mutex;
> +    /* transport-provided data: */
> +    int (*ccw_cb) (SubchDev *, struct ccw1 *);
> +    struct senseid id;
> +    void *driver_data;
> +};
> +
> +typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid);
> +int css_set_subch_cb(css_subch_cb_func func);
> +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 262747f..cb05241 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
> -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 c30ac3a..c09fa61 100644
> --- a/target-s390x/cpu.h
> +++ b/target-s390x/cpu.h
> @@ -336,6 +336,23 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>  void cpu_lock(void);
>  void cpu_unlock(void);
>
> +typedef struct SubchDev SubchDev;
> +struct 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);
> +#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;
> @@ -996,4 +1013,95 @@ 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);
> +#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)
> +{
> +}
> +#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");
> +    }
> +}
> +
> +#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..79628b4
> --- /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).

Is this a copy and if so, is the license of original Linux file also GPLv2+?

> + */
> +
> +/* subchannel status word (command mode only) */
> +struct scsw {

Please use more descriptive names instead of acronyms, for example SubChStatus.

> +    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;
> +};
> +
> +/* path management control word */
> +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;
> +};
> +
> +/* subchannel information block */
> +struct schib {
> +    struct pmcw pmcw;
> +    struct scsw scsw;
> +    uint64_t mba;
> +    uint8_t mda[4];
> +};
> +
> +/* interruption response block */
> +struct irb {
> +    struct scsw scsw;
> +    uint32_t esw[5];
> +    uint32_t ecw[8];
> +    uint32_t emw[8];
> +};
> +
> +/* 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) */
> +struct ccw1 {
> +    uint8_t cmd_code;
> +    uint8_t flags;
> +    uint16_t count;
> +    uint32_t cda;
> +};
> +
> +#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 47008c2..e4cde14 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;
>  }
> @@ -506,6 +509,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;
> @@ -531,3 +541,94 @@ 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);
> +    }
> +}
> --
> 1.7.11.4
>
>

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-07 21:00     ` Blue Swirl
  0 siblings, 0 replies; 42+ messages in thread
From: Blue Swirl @ 2012-08-07 21:00 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Tue, Aug 7, 2012 at 2:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> 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>
> ---
>  hw/s390x/Makefile.objs     |   1 +
>  hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/css.h             |  62 +++++++
>  target-s390x/Makefile.objs |   2 +-
>  target-s390x/cpu.h         | 108 +++++++++++
>  target-s390x/ioinst.c      |  38 ++++
>  target-s390x/ioinst.h      | 173 ++++++++++++++++++
>  target-s390x/kvm.c         | 101 +++++++++++
>  8 files changed, 924 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..7941c44
> --- /dev/null
> +++ b/hw/s390x/css.c
> @@ -0,0 +1,440 @@
> +/*
> + * 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 "kvm.h"
> +#include "cpu.h"
> +#include "ioinst.h"
> +#include "css.h"
> +
> +struct chp_info {

CamelCase, please.

> +    uint8_t in_use;
> +    uint8_t type;
> +};
> +
> +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> +
> +static css_subch_cb_func css_subch_cb;

Probably these can be put to a container structure which can be passed around.

> +
> +int css_set_subch_cb(css_subch_cb_func func)
> +{
> +    if (func && css_subch_cb) {
> +        return -EBUSY;
> +    }
> +    css_subch_cb = func;
> +    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)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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, struct 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]);
> +        }

cpu_physical_memory_write()

> +        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)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    struct 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(struct pmcw));
> +    switch (func) {
> +    case CSS_DO_CSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct 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(struct 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(struct 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(struct 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)
> +{
> +    if (cssid > MAX_CSSID) {
> +        return -EINVAL;
> +    }
> +    if (chpids[cssid][chpid].in_use) {
> +        return -EEXIST;
> +    }
> +    chpids[cssid][chpid].in_use = 1;
> +    chpids[cssid][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)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int i;
> +
> +    memset(p, 0, sizeof(struct pmcw));
> +    p->dnv = 1;
> +    p->dev = sch->devno;
> +    /* single path */
> +    p->pim = 0x80;
> +    p->pom = 0xff;
> +    p->pam = 0x80;
> +    p->chpid[0] = chpid;
> +    if (!chpids[sch->cssid][chpid].in_use) {
> +        css_add_virtual_chpid(sch->cssid, chpid, type);
> +    }
> +
> +    memset(s, 0, sizeof(struct 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)
> +{
> +    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
> +}
> +
> +static void css_init(void)
> +{
> +    css_subch_cb = NULL;
> +}
> +machine_init(css_init);
> +
> +void css_reset_sch(SubchDev *sch)
> +{
> +    struct 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..b8a95cc
> --- /dev/null
> +++ b/hw/s390x/css.h
> @@ -0,0 +1,62 @@
> +/*
> + * 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 8
> +
> +struct senseid {

SenseID

> +    /* common part */
> +    uint32_t  reserved:8;    /* always 0x'FF' */

The standard syntax calls for 'unsigned' instead of uint32_t for bit
fields. But bit fields are not very well defined, it's better to avoid
them.

> +    uint32_t  cu_type:16;    /* control unit type */
> +    uint32_t  cu_model:8;    /* control unit model */
> +    uint32_t  dev_type:16;   /* device type */
> +    uint32_t  dev_model:8;   /* device model */
> +    uint32_t  unused:8;      /* padding byte */
> +    /* extended part */
> +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> +};
> +
> +struct SubchDev {
> +    /* channel-subsystem related things: */
> +    uint8_t cssid;
> +    uint8_t ssid;
> +    uint16_t schid;
> +    uint16_t devno;
> +    struct schib curr_status;
> +    uint8_t sense_data[32];
> +    struct ccw1 *channel_prog;
> +    struct ccw1 *last_cmd;
> +    struct orb *orb;
> +    QemuMutex mutex;
> +    /* transport-provided data: */
> +    int (*ccw_cb) (SubchDev *, struct ccw1 *);
> +    struct senseid id;
> +    void *driver_data;
> +};
> +
> +typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid);
> +int css_set_subch_cb(css_subch_cb_func func);
> +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 262747f..cb05241 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
> -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 c30ac3a..c09fa61 100644
> --- a/target-s390x/cpu.h
> +++ b/target-s390x/cpu.h
> @@ -336,6 +336,23 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>  void cpu_lock(void);
>  void cpu_unlock(void);
>
> +typedef struct SubchDev SubchDev;
> +struct 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);
> +#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;
> @@ -996,4 +1013,95 @@ 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);
> +#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)
> +{
> +}
> +#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");
> +    }
> +}
> +
> +#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..79628b4
> --- /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).

Is this a copy and if so, is the license of original Linux file also GPLv2+?

> + */
> +
> +/* subchannel status word (command mode only) */
> +struct scsw {

Please use more descriptive names instead of acronyms, for example SubChStatus.

> +    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;
> +};
> +
> +/* path management control word */
> +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;
> +};
> +
> +/* subchannel information block */
> +struct schib {
> +    struct pmcw pmcw;
> +    struct scsw scsw;
> +    uint64_t mba;
> +    uint8_t mda[4];
> +};
> +
> +/* interruption response block */
> +struct irb {
> +    struct scsw scsw;
> +    uint32_t esw[5];
> +    uint32_t ecw[8];
> +    uint32_t emw[8];
> +};
> +
> +/* 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) */
> +struct ccw1 {
> +    uint8_t cmd_code;
> +    uint8_t flags;
> +    uint16_t count;
> +    uint32_t cda;
> +};
> +
> +#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 47008c2..e4cde14 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;
>  }
> @@ -506,6 +509,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;
> @@ -531,3 +541,94 @@ 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);
> +    }
> +}
> --
> 1.7.11.4
>
>

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

* Re: [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-07 21:00     ` Blue Swirl
@ 2012-08-08  8:17       ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-08  8:17 UTC (permalink / raw)
  To: Blue Swirl
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Tue, 7 Aug 2012 21:00:59 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> > new file mode 100644
> > index 0000000..7941c44
> > --- /dev/null
> > +++ b/hw/s390x/css.c
> > @@ -0,0 +1,440 @@
> > +/*
> > + * 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 "kvm.h"
> > +#include "cpu.h"
> > +#include "ioinst.h"
> > +#include "css.h"
> > +
> > +struct chp_info {
> 
> CamelCase, please.

OK.
> 
> > +    uint8_t in_use;
> > +    uint8_t type;
> > +};
> > +
> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> > +
> > +static css_subch_cb_func css_subch_cb;
> 
> Probably these can be put to a container structure which can be passed around.

Still trying to come up with a good model for that.

> 

> > +    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]);
> > +        }
> 
> cpu_physical_memory_write()

Hm, what's wrong with storing byte-by-byte?

> 
> > +        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;

> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> > new file mode 100644
> > index 0000000..b8a95cc
> > --- /dev/null
> > +++ b/hw/s390x/css.h
> > @@ -0,0 +1,62 @@
> > +/*
> > + * 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 8
> > +
> > +struct senseid {
> 
> SenseID

OK.
> 
> > +    /* common part */
> > +    uint32_t  reserved:8;    /* always 0x'FF' */
> 
> The standard syntax calls for 'unsigned' instead of uint32_t for bit
> fields. But bit fields are not very well defined, it's better to avoid
> them.

Well, the equivalent Linux structure also looks like that :) But I can
switch this to a uint8_t/uint16_t structure.

> 
> > +    uint32_t  cu_type:16;    /* control unit type */
> > +    uint32_t  cu_model:8;    /* control unit model */
> > +    uint32_t  dev_type:16;   /* device type */
> > +    uint32_t  dev_model:8;   /* device model */
> > +    uint32_t  unused:8;      /* padding byte */
> > +    /* extended part */
> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> > +};
> > +

> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
> > new file mode 100644
> > index 0000000..79628b4
> > --- /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).
> 
> Is this a copy and if so, is the license of original Linux file also GPLv2+?

It's not a verbatim copy.

> 
> > + */
> > +
> > +/* subchannel status word (command mode only) */
> > +struct scsw {
> 
> Please use more descriptive names instead of acronyms, for example SubChStatus.

I'd rather leave these at the well-known scsw, pmcw, etc. names. These
have been around for decades, and somebody familiar with channel I/O
will instantly know what a struct scsw is, but will need to look hard
at the code to figure out the meaning of SubChStatus.

> 
> > +    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;
> > +};

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-08  8:17       ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-08  8:17 UTC (permalink / raw)
  To: Blue Swirl
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Tue, 7 Aug 2012 21:00:59 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> > new file mode 100644
> > index 0000000..7941c44
> > --- /dev/null
> > +++ b/hw/s390x/css.c
> > @@ -0,0 +1,440 @@
> > +/*
> > + * 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 "kvm.h"
> > +#include "cpu.h"
> > +#include "ioinst.h"
> > +#include "css.h"
> > +
> > +struct chp_info {
> 
> CamelCase, please.

OK.
> 
> > +    uint8_t in_use;
> > +    uint8_t type;
> > +};
> > +
> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> > +
> > +static css_subch_cb_func css_subch_cb;
> 
> Probably these can be put to a container structure which can be passed around.

Still trying to come up with a good model for that.

> 

> > +    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]);
> > +        }
> 
> cpu_physical_memory_write()

Hm, what's wrong with storing byte-by-byte?

> 
> > +        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;

> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> > new file mode 100644
> > index 0000000..b8a95cc
> > --- /dev/null
> > +++ b/hw/s390x/css.h
> > @@ -0,0 +1,62 @@
> > +/*
> > + * 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 8
> > +
> > +struct senseid {
> 
> SenseID

OK.
> 
> > +    /* common part */
> > +    uint32_t  reserved:8;    /* always 0x'FF' */
> 
> The standard syntax calls for 'unsigned' instead of uint32_t for bit
> fields. But bit fields are not very well defined, it's better to avoid
> them.

Well, the equivalent Linux structure also looks like that :) But I can
switch this to a uint8_t/uint16_t structure.

> 
> > +    uint32_t  cu_type:16;    /* control unit type */
> > +    uint32_t  cu_model:8;    /* control unit model */
> > +    uint32_t  dev_type:16;   /* device type */
> > +    uint32_t  dev_model:8;   /* device model */
> > +    uint32_t  unused:8;      /* padding byte */
> > +    /* extended part */
> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> > +};
> > +

> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
> > new file mode 100644
> > index 0000000..79628b4
> > --- /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).
> 
> Is this a copy and if so, is the license of original Linux file also GPLv2+?

It's not a verbatim copy.

> 
> > + */
> > +
> > +/* subchannel status word (command mode only) */
> > +struct scsw {
> 
> Please use more descriptive names instead of acronyms, for example SubChStatus.

I'd rather leave these at the well-known scsw, pmcw, etc. names. These
have been around for decades, and somebody familiar with channel I/O
will instantly know what a struct scsw is, but will need to look hard
at the code to figure out the meaning of SubChStatus.

> 
> > +    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;
> > +};

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

* Re: [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
@ 2012-08-08  8:27     ` Peter Maydell
  -1 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2012-08-08  8:27 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> +static void sch_handle_clear_func(SubchDev *sch)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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;

This is unnecessary since we're going to set p->lpum to something else about
ten lines later, right?

> +    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;
> +
> +}

-- PMM

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-08  8:27     ` Peter Maydell
  0 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2012-08-08  8:27 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> +static void sch_handle_clear_func(SubchDev *sch)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct 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;

This is unnecessary since we're going to set p->lpum to something else about
ten lines later, right?

> +    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;
> +
> +}

-- PMM

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

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

On Tue, 7 Aug 2012 20:47:22 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > new file mode 100644
> > index 0000000..8a90c3a
> > --- /dev/null
> > +++ b/hw/s390x/virtio-ccw.c
> > @@ -0,0 +1,962 @@
> > +/*
> > + * 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 "../virtio.h"
> > +#include "../virtio-serial.h"
> > +#include "../virtio-net.h"
> > +#include "../sysbus.h"
> 
> "hw/virtio..." for the above

OK.
> 
> > +#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;
> > +
> > +typedef struct sch_entry {
> > +    SubchDev *sch;
> > +    QLIST_ENTRY(sch_entry) entry;
> > +} sch_entry;
> 
> SubchEntry, see CODING_STYLE. Also other struct and typedef names below.
> 
> > +
> > +QLIST_HEAD(subch_list, sch_entry);
> 
> static, but please put this to a structure that is passed around instead.
> 
> > +
> > +typedef struct devno_entry {
> > +    uint16_t devno;
> > +    QLIST_ENTRY(devno_entry) entry;
> > +} devno_entry;
> > +
> > +QLIST_HEAD(devno_list, devno_entry);
> 
> Ditto
> 
> > +
> > +struct subch_set {
> > +    struct subch_list *s_list[256];
> > +    struct devno_list *d_list[256];
> > +};
> > +
> > +struct css_set {
> > +    struct subch_set *set[MAX_SSID + 1];
> > +};
> > +
> > +static struct css_set *channel_subsys[MAX_CSSID + 1];

OK, will try to come up with some kind of structure for this and
CamelCasify it.

> > +
> > +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> > +{
> > +    VirtIODevice *vdev = NULL;
> > +
> > +    if (sch->driver_data) {
> > +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> > +    }
> > +    return vdev;
> > +}
> > +

> > +VirtioCcwBus *virtio_ccw_bus_init(void)
> > +{
> > +    VirtioCcwBus *bus;
> > +    BusState *_bus;
> 
> Please avoid identifiers with leading underscores.

OK.

> 
> > +    DeviceState *dev;
> > +
> > +    css_set_subch_cb(virtio_ccw_find_subch);
> > +
> > +    /* 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");
> > +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
> > +
> > +    /* Enable hotplugging */
> > +    _bus->allow_hotplug = 1;
> > +
> > +    return bus;
> > +}
> > +
> > +struct vq_info_block {
> > +    uint64_t queue;
> > +    uint16_t num;
> > +} QEMU_PACKED;
> > +
> > +struct vq_config_block {
> > +    uint16_t index;
> > +    uint16_t num;
> > +} QEMU_PACKED;
> 
> Aren't these KVM structures? They should be defined in a KVM header
> file file in linux-headers.

Not really, virtio-ccw isn't tied to kvm.

I see this more as command blocks that are specific to the "control
unit" - like something that would be defined in an attachment
specification for a classic s390 device (and in the virtio spec in this
case) and modeled as C structures here.

> 

> > +    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);
> 
> Please use cpu_physical_memory_read() (or DMA versions) instead of
> this + memcpy().

Will check.
> 
> > +        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_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);
> 
> lduw_{b,l}e_phys()
> 
> > +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
> > +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
> 
> stw_{b,l]e_phys(), likewise elsewhere.


Will check as well.
> 
> > +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
> > +            ret = 0;
> > +        }
> > +        break;
> > +    default:
> > +        ret = -EOPNOTSUPP;
> > +        break;
> > +    }
> > +    return ret;
> > +}
> > +

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

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

On Tue, 7 Aug 2012 20:47:22 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > new file mode 100644
> > index 0000000..8a90c3a
> > --- /dev/null
> > +++ b/hw/s390x/virtio-ccw.c
> > @@ -0,0 +1,962 @@
> > +/*
> > + * 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 "../virtio.h"
> > +#include "../virtio-serial.h"
> > +#include "../virtio-net.h"
> > +#include "../sysbus.h"
> 
> "hw/virtio..." for the above

OK.
> 
> > +#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;
> > +
> > +typedef struct sch_entry {
> > +    SubchDev *sch;
> > +    QLIST_ENTRY(sch_entry) entry;
> > +} sch_entry;
> 
> SubchEntry, see CODING_STYLE. Also other struct and typedef names below.
> 
> > +
> > +QLIST_HEAD(subch_list, sch_entry);
> 
> static, but please put this to a structure that is passed around instead.
> 
> > +
> > +typedef struct devno_entry {
> > +    uint16_t devno;
> > +    QLIST_ENTRY(devno_entry) entry;
> > +} devno_entry;
> > +
> > +QLIST_HEAD(devno_list, devno_entry);
> 
> Ditto
> 
> > +
> > +struct subch_set {
> > +    struct subch_list *s_list[256];
> > +    struct devno_list *d_list[256];
> > +};
> > +
> > +struct css_set {
> > +    struct subch_set *set[MAX_SSID + 1];
> > +};
> > +
> > +static struct css_set *channel_subsys[MAX_CSSID + 1];

OK, will try to come up with some kind of structure for this and
CamelCasify it.

> > +
> > +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
> > +{
> > +    VirtIODevice *vdev = NULL;
> > +
> > +    if (sch->driver_data) {
> > +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
> > +    }
> > +    return vdev;
> > +}
> > +

> > +VirtioCcwBus *virtio_ccw_bus_init(void)
> > +{
> > +    VirtioCcwBus *bus;
> > +    BusState *_bus;
> 
> Please avoid identifiers with leading underscores.

OK.

> 
> > +    DeviceState *dev;
> > +
> > +    css_set_subch_cb(virtio_ccw_find_subch);
> > +
> > +    /* 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");
> > +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
> > +
> > +    /* Enable hotplugging */
> > +    _bus->allow_hotplug = 1;
> > +
> > +    return bus;
> > +}
> > +
> > +struct vq_info_block {
> > +    uint64_t queue;
> > +    uint16_t num;
> > +} QEMU_PACKED;
> > +
> > +struct vq_config_block {
> > +    uint16_t index;
> > +    uint16_t num;
> > +} QEMU_PACKED;
> 
> Aren't these KVM structures? They should be defined in a KVM header
> file file in linux-headers.

Not really, virtio-ccw isn't tied to kvm.

I see this more as command blocks that are specific to the "control
unit" - like something that would be defined in an attachment
specification for a classic s390 device (and in the virtio spec in this
case) and modeled as C structures here.

> 

> > +    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);
> 
> Please use cpu_physical_memory_read() (or DMA versions) instead of
> this + memcpy().

Will check.
> 
> > +        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_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);
> 
> lduw_{b,l}e_phys()
> 
> > +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
> > +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
> 
> stw_{b,l]e_phys(), likewise elsewhere.


Will check as well.
> 
> > +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
> > +            ret = 0;
> > +        }
> > +        break;
> > +    default:
> > +        ret = -EOPNOTSUPP;
> > +        break;
> > +    }
> > +    return ret;
> > +}
> > +

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

* Re: [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-08  8:27     ` [Qemu-devel] " Peter Maydell
@ 2012-08-08  8:53       ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-08  8:53 UTC (permalink / raw)
  To: Peter Maydell
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Wed, 8 Aug 2012 09:27:32 +0100
Peter Maydell <peter.maydell@linaro.org> wrote:

> On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> > +static void sch_handle_clear_func(SubchDev *sch)
> > +{
> > +    struct pmcw *p = &sch->curr_status.pmcw;
> > +    struct 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;
> 
> This is unnecessary since we're going to set p->lpum to something else about
> ten lines later, right?

My intention here was following the description in the architecture
closely. In our case, 'attempting to issue the clear signal' will
always succeed, and so we will always set the lpum later on - on real
hardware, this may be different.

> 
> > +    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;
> > +
> > +}
> 
> -- PMM
> 

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-08  8:53       ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-08  8:53 UTC (permalink / raw)
  To: Peter Maydell
  Cc: linux-s390, Anthony Liguori, Marcelo Tosatti, KVM, Carsten Otte,
	Heiko Carstens, Rusty Russell, Sebastian Ott, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Wed, 8 Aug 2012 09:27:32 +0100
Peter Maydell <peter.maydell@linaro.org> wrote:

> On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> > +static void sch_handle_clear_func(SubchDev *sch)
> > +{
> > +    struct pmcw *p = &sch->curr_status.pmcw;
> > +    struct 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;
> 
> This is unnecessary since we're going to set p->lpum to something else about
> ten lines later, right?

My intention here was following the description in the architecture
closely. In our case, 'attempting to issue the clear signal' will
always succeed, and so we will always set the lpum later on - on real
hardware, this may be different.

> 
> > +    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;
> > +
> > +}
> 
> -- PMM
> 

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

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

On Wed, Aug 8, 2012 at 8:28 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Tue, 7 Aug 2012 20:47:22 +0000
> Blue Swirl <blauwirbel@gmail.com> wrote:
>
>
>> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
>> > new file mode 100644
>> > index 0000000..8a90c3a
>> > --- /dev/null
>> > +++ b/hw/s390x/virtio-ccw.c
>> > @@ -0,0 +1,962 @@
>> > +/*
>> > + * 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 "../virtio.h"
>> > +#include "../virtio-serial.h"
>> > +#include "../virtio-net.h"
>> > +#include "../sysbus.h"
>>
>> "hw/virtio..." for the above
>
> OK.
>>
>> > +#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;
>> > +
>> > +typedef struct sch_entry {
>> > +    SubchDev *sch;
>> > +    QLIST_ENTRY(sch_entry) entry;
>> > +} sch_entry;
>>
>> SubchEntry, see CODING_STYLE. Also other struct and typedef names below.
>>
>> > +
>> > +QLIST_HEAD(subch_list, sch_entry);
>>
>> static, but please put this to a structure that is passed around instead.
>>
>> > +
>> > +typedef struct devno_entry {
>> > +    uint16_t devno;
>> > +    QLIST_ENTRY(devno_entry) entry;
>> > +} devno_entry;
>> > +
>> > +QLIST_HEAD(devno_list, devno_entry);
>>
>> Ditto
>>
>> > +
>> > +struct subch_set {
>> > +    struct subch_list *s_list[256];
>> > +    struct devno_list *d_list[256];
>> > +};
>> > +
>> > +struct css_set {
>> > +    struct subch_set *set[MAX_SSID + 1];
>> > +};
>> > +
>> > +static struct css_set *channel_subsys[MAX_CSSID + 1];
>
> OK, will try to come up with some kind of structure for this and
> CamelCasify it.
>
>> > +
>> > +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
>> > +{
>> > +    VirtIODevice *vdev = NULL;
>> > +
>> > +    if (sch->driver_data) {
>> > +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
>> > +    }
>> > +    return vdev;
>> > +}
>> > +
>
>> > +VirtioCcwBus *virtio_ccw_bus_init(void)
>> > +{
>> > +    VirtioCcwBus *bus;
>> > +    BusState *_bus;
>>
>> Please avoid identifiers with leading underscores.
>
> OK.
>
>>
>> > +    DeviceState *dev;
>> > +
>> > +    css_set_subch_cb(virtio_ccw_find_subch);
>> > +
>> > +    /* 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");
>> > +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
>> > +
>> > +    /* Enable hotplugging */
>> > +    _bus->allow_hotplug = 1;
>> > +
>> > +    return bus;
>> > +}
>> > +
>> > +struct vq_info_block {
>> > +    uint64_t queue;
>> > +    uint16_t num;
>> > +} QEMU_PACKED;
>> > +
>> > +struct vq_config_block {
>> > +    uint16_t index;
>> > +    uint16_t num;
>> > +} QEMU_PACKED;
>>
>> Aren't these KVM structures? They should be defined in a KVM header
>> file file in linux-headers.
>
> Not really, virtio-ccw isn't tied to kvm.
>
> I see this more as command blocks that are specific to the "control
> unit" - like something that would be defined in an attachment
> specification for a classic s390 device (and in the virtio spec in this
> case) and modeled as C structures here.

OK. Then please use CamelCase for these too.

>
>>
>
>> > +    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);
>>
>> Please use cpu_physical_memory_read() (or DMA versions) instead of
>> this + memcpy().
>
> Will check.
>>
>> > +        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_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);
>>
>> lduw_{b,l}e_phys()
>>
>> > +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
>> > +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
>>
>> stw_{b,l]e_phys(), likewise elsewhere.
>
>
> Will check as well.
>>
>> > +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
>> > +            ret = 0;
>> > +        }
>> > +        break;
>> > +    default:
>> > +        ret = -EOPNOTSUPP;
>> > +        break;
>> > +    }
>> > +    return ret;
>> > +}
>> > +
>

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

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

On Wed, Aug 8, 2012 at 8:28 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Tue, 7 Aug 2012 20:47:22 +0000
> Blue Swirl <blauwirbel@gmail.com> wrote:
>
>
>> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
>> > new file mode 100644
>> > index 0000000..8a90c3a
>> > --- /dev/null
>> > +++ b/hw/s390x/virtio-ccw.c
>> > @@ -0,0 +1,962 @@
>> > +/*
>> > + * 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 "../virtio.h"
>> > +#include "../virtio-serial.h"
>> > +#include "../virtio-net.h"
>> > +#include "../sysbus.h"
>>
>> "hw/virtio..." for the above
>
> OK.
>>
>> > +#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;
>> > +
>> > +typedef struct sch_entry {
>> > +    SubchDev *sch;
>> > +    QLIST_ENTRY(sch_entry) entry;
>> > +} sch_entry;
>>
>> SubchEntry, see CODING_STYLE. Also other struct and typedef names below.
>>
>> > +
>> > +QLIST_HEAD(subch_list, sch_entry);
>>
>> static, but please put this to a structure that is passed around instead.
>>
>> > +
>> > +typedef struct devno_entry {
>> > +    uint16_t devno;
>> > +    QLIST_ENTRY(devno_entry) entry;
>> > +} devno_entry;
>> > +
>> > +QLIST_HEAD(devno_list, devno_entry);
>>
>> Ditto
>>
>> > +
>> > +struct subch_set {
>> > +    struct subch_list *s_list[256];
>> > +    struct devno_list *d_list[256];
>> > +};
>> > +
>> > +struct css_set {
>> > +    struct subch_set *set[MAX_SSID + 1];
>> > +};
>> > +
>> > +static struct css_set *channel_subsys[MAX_CSSID + 1];
>
> OK, will try to come up with some kind of structure for this and
> CamelCasify it.
>
>> > +
>> > +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
>> > +{
>> > +    VirtIODevice *vdev = NULL;
>> > +
>> > +    if (sch->driver_data) {
>> > +        vdev = ((VirtioCcwData *)sch->driver_data)->vdev;
>> > +    }
>> > +    return vdev;
>> > +}
>> > +
>
>> > +VirtioCcwBus *virtio_ccw_bus_init(void)
>> > +{
>> > +    VirtioCcwBus *bus;
>> > +    BusState *_bus;
>>
>> Please avoid identifiers with leading underscores.
>
> OK.
>
>>
>> > +    DeviceState *dev;
>> > +
>> > +    css_set_subch_cb(virtio_ccw_find_subch);
>> > +
>> > +    /* 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");
>> > +    bus = DO_UPCAST(VirtioCcwBus, bus, _bus);
>> > +
>> > +    /* Enable hotplugging */
>> > +    _bus->allow_hotplug = 1;
>> > +
>> > +    return bus;
>> > +}
>> > +
>> > +struct vq_info_block {
>> > +    uint64_t queue;
>> > +    uint16_t num;
>> > +} QEMU_PACKED;
>> > +
>> > +struct vq_config_block {
>> > +    uint16_t index;
>> > +    uint16_t num;
>> > +} QEMU_PACKED;
>>
>> Aren't these KVM structures? They should be defined in a KVM header
>> file file in linux-headers.
>
> Not really, virtio-ccw isn't tied to kvm.
>
> I see this more as command blocks that are specific to the "control
> unit" - like something that would be defined in an attachment
> specification for a classic s390 device (and in the virtio spec in this
> case) and modeled as C structures here.

OK. Then please use CamelCase for these too.

>
>>
>
>> > +    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);
>>
>> Please use cpu_physical_memory_read() (or DMA versions) instead of
>> this + memcpy().
>
> Will check.
>>
>> > +        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_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);
>>
>> lduw_{b,l}e_phys()
>>
>> > +            vq_config.num = virtio_queue_get_num(data->vdev, vq_config.index);
>> > +            stw_phys(ccw->cda + sizeof(vq_config.index), vq_config.num);
>>
>> stw_{b,l]e_phys(), likewise elsewhere.
>
>
> Will check as well.
>>
>> > +            sch->curr_status.scsw.count = ccw->count - sizeof(vq_config);
>> > +            ret = 0;
>> > +        }
>> > +        break;
>> > +    default:
>> > +        ret = -EOPNOTSUPP;
>> > +        break;
>> > +    }
>> > +    return ret;
>> > +}
>> > +
>

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-08  8:17       ` [Qemu-devel] " Cornelia Huck
@ 2012-08-08 19:16         ` Blue Swirl
  -1 siblings, 0 replies; 42+ messages in thread
From: Blue Swirl @ 2012-08-08 19:16 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: KVM, linux-s390, qemu-devel, Carsten Otte, Anthony Liguori,
	Rusty Russell, Sebastian Ott, Marcelo Tosatti, Heiko Carstens,
	Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Tue, 7 Aug 2012 21:00:59 +0000
> Blue Swirl <blauwirbel@gmail.com> wrote:
>
>
>> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
>> > new file mode 100644
>> > index 0000000..7941c44
>> > --- /dev/null
>> > +++ b/hw/s390x/css.c
>> > @@ -0,0 +1,440 @@
>> > +/*
>> > + * 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 "kvm.h"
>> > +#include "cpu.h"
>> > +#include "ioinst.h"
>> > +#include "css.h"
>> > +
>> > +struct chp_info {
>>
>> CamelCase, please.
>
> OK.
>>
>> > +    uint8_t in_use;
>> > +    uint8_t type;
>> > +};
>> > +
>> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
>> > +
>> > +static css_subch_cb_func css_subch_cb;
>>
>> Probably these can be put to a container structure which can be passed around.
>
> Still trying to come up with a good model for that.
>
>>
>
>> > +    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]);
>> > +        }
>>
>> cpu_physical_memory_write()
>
> Hm, what's wrong with storing byte-by-byte?

cpu_physical_memory_write() could be more optimal, for example resolve
guest addresses only once per page.

>
>>
>> > +        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;
>
>> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
>> > new file mode 100644
>> > index 0000000..b8a95cc
>> > --- /dev/null
>> > +++ b/hw/s390x/css.h
>> > @@ -0,0 +1,62 @@
>> > +/*
>> > + * 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 8
>> > +
>> > +struct senseid {
>>
>> SenseID
>
> OK.
>>
>> > +    /* common part */
>> > +    uint32_t  reserved:8;    /* always 0x'FF' */
>>
>> The standard syntax calls for 'unsigned' instead of uint32_t for bit
>> fields. But bit fields are not very well defined, it's better to avoid
>> them.
>
> Well, the equivalent Linux structure also looks like that :) But I can
> switch this to a uint8_t/uint16_t structure.
>
>>
>> > +    uint32_t  cu_type:16;    /* control unit type */
>> > +    uint32_t  cu_model:8;    /* control unit model */
>> > +    uint32_t  dev_type:16;   /* device type */
>> > +    uint32_t  dev_model:8;   /* device model */
>> > +    uint32_t  unused:8;      /* padding byte */
>> > +    /* extended part */
>> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
>> > +};
>> > +
>
>> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
>> > new file mode 100644
>> > index 0000000..79628b4
>> > --- /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).
>>
>> Is this a copy and if so, is the license of original Linux file also GPLv2+?
>
> It's not a verbatim copy.

But a copy of some sort? Can we use the original instead?

>
>>
>> > + */
>> > +
>> > +/* subchannel status word (command mode only) */
>> > +struct scsw {
>>
>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>
> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> have been around for decades, and somebody familiar with channel I/O
> will instantly know what a struct scsw is, but will need to look hard
> at the code to figure out the meaning of SubChStatus.

If they are well-known and have been around for so long time, are
there any suitable header files (with compatible licenses) where they
are defined which could be reused?

Otherwise, please follow CODING_STYLE.

>
>>
>> > +    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;
>> > +};
>

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

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

On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Tue, 7 Aug 2012 21:00:59 +0000
> Blue Swirl <blauwirbel@gmail.com> wrote:
>
>
>> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
>> > new file mode 100644
>> > index 0000000..7941c44
>> > --- /dev/null
>> > +++ b/hw/s390x/css.c
>> > @@ -0,0 +1,440 @@
>> > +/*
>> > + * 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 "kvm.h"
>> > +#include "cpu.h"
>> > +#include "ioinst.h"
>> > +#include "css.h"
>> > +
>> > +struct chp_info {
>>
>> CamelCase, please.
>
> OK.
>>
>> > +    uint8_t in_use;
>> > +    uint8_t type;
>> > +};
>> > +
>> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
>> > +
>> > +static css_subch_cb_func css_subch_cb;
>>
>> Probably these can be put to a container structure which can be passed around.
>
> Still trying to come up with a good model for that.
>
>>
>
>> > +    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]);
>> > +        }
>>
>> cpu_physical_memory_write()
>
> Hm, what's wrong with storing byte-by-byte?

cpu_physical_memory_write() could be more optimal, for example resolve
guest addresses only once per page.

>
>>
>> > +        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;
>
>> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
>> > new file mode 100644
>> > index 0000000..b8a95cc
>> > --- /dev/null
>> > +++ b/hw/s390x/css.h
>> > @@ -0,0 +1,62 @@
>> > +/*
>> > + * 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 8
>> > +
>> > +struct senseid {
>>
>> SenseID
>
> OK.
>>
>> > +    /* common part */
>> > +    uint32_t  reserved:8;    /* always 0x'FF' */
>>
>> The standard syntax calls for 'unsigned' instead of uint32_t for bit
>> fields. But bit fields are not very well defined, it's better to avoid
>> them.
>
> Well, the equivalent Linux structure also looks like that :) But I can
> switch this to a uint8_t/uint16_t structure.
>
>>
>> > +    uint32_t  cu_type:16;    /* control unit type */
>> > +    uint32_t  cu_model:8;    /* control unit model */
>> > +    uint32_t  dev_type:16;   /* device type */
>> > +    uint32_t  dev_model:8;   /* device model */
>> > +    uint32_t  unused:8;      /* padding byte */
>> > +    /* extended part */
>> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
>> > +};
>> > +
>
>> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
>> > new file mode 100644
>> > index 0000000..79628b4
>> > --- /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).
>>
>> Is this a copy and if so, is the license of original Linux file also GPLv2+?
>
> It's not a verbatim copy.

But a copy of some sort? Can we use the original instead?

>
>>
>> > + */
>> > +
>> > +/* subchannel status word (command mode only) */
>> > +struct scsw {
>>
>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>
> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> have been around for decades, and somebody familiar with channel I/O
> will instantly know what a struct scsw is, but will need to look hard
> at the code to figure out the meaning of SubChStatus.

If they are well-known and have been around for so long time, are
there any suitable header files (with compatible licenses) where they
are defined which could be reused?

Otherwise, please follow CODING_STYLE.

>
>>
>> > +    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;
>> > +};
>

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-08 19:16         ` Blue Swirl
@ 2012-08-08 19:34           ` Peter Maydell
  -1 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2012-08-08 19:34 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Cornelia Huck, linux-s390, Anthony Liguori, Marcelo Tosatti, KVM,
	Carsten Otte, Heiko Carstens, Rusty Russell, Sebastian Ott,
	qemu-devel, Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>> On Tue, 7 Aug 2012 21:00:59 +0000
>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>
>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>> have been around for decades, and somebody familiar with channel I/O
>> will instantly know what a struct scsw is, but will need to look hard
>> at the code to figure out the meaning of SubChStatus.
>
> If they are well-known and have been around for so long time, are
> there any suitable header files (with compatible licenses) where they
> are defined which could be reused?
>
> Otherwise, please follow CODING_STYLE.

I think we should follow CODING_STYLE for capitalisation issues
but generally if the device's documentation has standard abbreviations
for register names, structures, etc, etc we should use them. Often
this code has to be maintained later by somebody else who might not
be familiar with the general operation of the hardware and who is trying
to match up the code with whatever the data sheet says. Following the
naming used in the h/w docs makes that job easier.

(for instance I took the opportunity of making a bunch of structure
member names in target-arm line up with the ARM ARM names
as part of the refactoring that went on a while back.)

-- PMM

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-08 19:34           ` Peter Maydell
  0 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2012-08-08 19:34 UTC (permalink / raw)
  To: Blue Swirl
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity, Cornelia Huck,
	Martin Schwidefsky

On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>> On Tue, 7 Aug 2012 21:00:59 +0000
>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>
>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>> have been around for decades, and somebody familiar with channel I/O
>> will instantly know what a struct scsw is, but will need to look hard
>> at the code to figure out the meaning of SubChStatus.
>
> If they are well-known and have been around for so long time, are
> there any suitable header files (with compatible licenses) where they
> are defined which could be reused?
>
> Otherwise, please follow CODING_STYLE.

I think we should follow CODING_STYLE for capitalisation issues
but generally if the device's documentation has standard abbreviations
for register names, structures, etc, etc we should use them. Often
this code has to be maintained later by somebody else who might not
be familiar with the general operation of the hardware and who is trying
to match up the code with whatever the data sheet says. Following the
naming used in the h/w docs makes that job easier.

(for instance I took the opportunity of making a bunch of structure
member names in target-arm line up with the ARM ARM names
as part of the refactoring that went on a while back.)

-- PMM

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-08 19:34           ` Peter Maydell
@ 2012-08-08 19:39             ` Blue Swirl
  -1 siblings, 0 replies; 42+ messages in thread
From: Blue Swirl @ 2012-08-08 19:39 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Cornelia Huck, linux-s390, Anthony Liguori, Marcelo Tosatti, KVM,
	Carsten Otte, Heiko Carstens, Rusty Russell, Sebastian Ott,
	qemu-devel, Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
>> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>>> On Tue, 7 Aug 2012 21:00:59 +0000
>>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>>
>>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>>> have been around for decades, and somebody familiar with channel I/O
>>> will instantly know what a struct scsw is, but will need to look hard
>>> at the code to figure out the meaning of SubChStatus.
>>
>> If they are well-known and have been around for so long time, are
>> there any suitable header files (with compatible licenses) where they
>> are defined which could be reused?
>>
>> Otherwise, please follow CODING_STYLE.
>
> I think we should follow CODING_STYLE for capitalisation issues
> but generally if the device's documentation has standard abbreviations
> for register names, structures, etc, etc we should use them. Often
> this code has to be maintained later by somebody else who might not
> be familiar with the general operation of the hardware and who is trying
> to match up the code with whatever the data sheet says. Following the
> naming used in the h/w docs makes that job easier.

Yes. typedef struct SCSW {} SCSW; should be OK too.

>
> (for instance I took the opportunity of making a bunch of structure
> member names in target-arm line up with the ARM ARM names
> as part of the refactoring that went on a while back.)
>
> -- PMM

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-08 19:39             ` Blue Swirl
  0 siblings, 0 replies; 42+ messages in thread
From: Blue Swirl @ 2012-08-08 19:39 UTC (permalink / raw)
  To: Peter Maydell
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity, Cornelia Huck,
	Martin Schwidefsky

On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
>> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>>> On Tue, 7 Aug 2012 21:00:59 +0000
>>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>>
>>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>>> have been around for decades, and somebody familiar with channel I/O
>>> will instantly know what a struct scsw is, but will need to look hard
>>> at the code to figure out the meaning of SubChStatus.
>>
>> If they are well-known and have been around for so long time, are
>> there any suitable header files (with compatible licenses) where they
>> are defined which could be reused?
>>
>> Otherwise, please follow CODING_STYLE.
>
> I think we should follow CODING_STYLE for capitalisation issues
> but generally if the device's documentation has standard abbreviations
> for register names, structures, etc, etc we should use them. Often
> this code has to be maintained later by somebody else who might not
> be familiar with the general operation of the hardware and who is trying
> to match up the code with whatever the data sheet says. Following the
> naming used in the h/w docs makes that job easier.

Yes. typedef struct SCSW {} SCSW; should be OK too.

>
> (for instance I took the opportunity of making a bunch of structure
> member names in target-arm line up with the ARM ARM names
> as part of the refactoring that went on a while back.)
>
> -- PMM

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
  2012-08-08 19:39             ` Blue Swirl
@ 2012-08-09  7:19               ` Cornelia Huck
  -1 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-09  7:19 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Peter Maydell, linux-s390, Anthony Liguori, Marcelo Tosatti, KVM,
	Carsten Otte, Heiko Carstens, Rusty Russell, Sebastian Ott,
	qemu-devel, Alexander Graf, Christian Borntraeger, Avi Kivity,
	Martin Schwidefsky

On Wed, 8 Aug 2012 19:39:33 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:

> On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> > On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> >> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> >>> On Tue, 7 Aug 2012 21:00:59 +0000
> >>> Blue Swirl <blauwirbel@gmail.com> wrote:
> >>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
> >>>
> >>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> >>> have been around for decades, and somebody familiar with channel I/O
> >>> will instantly know what a struct scsw is, but will need to look hard
> >>> at the code to figure out the meaning of SubChStatus.
> >>
> >> If they are well-known and have been around for so long time, are
> >> there any suitable header files (with compatible licenses) where they
> >> are defined which could be reused?

There's the Linux headers, but they are not exported as this is not a
user space interface (on the guest side).

Otherwise, most code dealing with channel I/O is probably not written
in C ;)

> >>
> >> Otherwise, please follow CODING_STYLE.
> >
> > I think we should follow CODING_STYLE for capitalisation issues
> > but generally if the device's documentation has standard abbreviations
> > for register names, structures, etc, etc we should use them. Often
> > this code has to be maintained later by somebody else who might not
> > be familiar with the general operation of the hardware and who is trying
> > to match up the code with whatever the data sheet says. Following the
> > naming used in the h/w docs makes that job easier.
> 
> Yes. typedef struct SCSW {} SCSW; should be OK too.

Then I'll use something like that.

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

* Re: [Qemu-devel] [PATCH 2/5] s390: Virtual channel subsystem support.
@ 2012-08-09  7:19               ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-09  7:19 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Peter Maydell, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	linux-s390, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Alexander Graf, Christian Borntraeger, Avi Kivity, Sebastian Ott,
	Martin Schwidefsky

On Wed, 8 Aug 2012 19:39:33 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:

> On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> > On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> >> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> >>> On Tue, 7 Aug 2012 21:00:59 +0000
> >>> Blue Swirl <blauwirbel@gmail.com> wrote:
> >>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
> >>>
> >>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> >>> have been around for decades, and somebody familiar with channel I/O
> >>> will instantly know what a struct scsw is, but will need to look hard
> >>> at the code to figure out the meaning of SubChStatus.
> >>
> >> If they are well-known and have been around for so long time, are
> >> there any suitable header files (with compatible licenses) where they
> >> are defined which could be reused?

There's the Linux headers, but they are not exported as this is not a
user space interface (on the guest side).

Otherwise, most code dealing with channel I/O is probably not written
in C ;)

> >>
> >> Otherwise, please follow CODING_STYLE.
> >
> > I think we should follow CODING_STYLE for capitalisation issues
> > but generally if the device's documentation has standard abbreviations
> > for register names, structures, etc, etc we should use them. Often
> > this code has to be maintained later by somebody else who might not
> > be familiar with the general operation of the hardware and who is trying
> > to match up the code with whatever the data sheet says. Following the
> > naming used in the h/w docs makes that job easier.
> 
> Yes. typedef struct SCSW {} SCSW; should be OK too.

Then I'll use something like that.

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

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

On Wed, 8 Aug 2012 19:03:14 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> >> > +struct vq_info_block {
> >> > +    uint64_t queue;
> >> > +    uint16_t num;
> >> > +} QEMU_PACKED;
> >> > +
> >> > +struct vq_config_block {
> >> > +    uint16_t index;
> >> > +    uint16_t num;
> >> > +} QEMU_PACKED;
> >>
> >> Aren't these KVM structures? They should be defined in a KVM header
> >> file file in linux-headers.
> >
> > Not really, virtio-ccw isn't tied to kvm.
> >
> > I see this more as command blocks that are specific to the "control
> > unit" - like something that would be defined in an attachment
> > specification for a classic s390 device (and in the virtio spec in this
> > case) and modeled as C structures here.
> 
> OK. Then please use CamelCase for these too.
> 

OK.

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

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

On Wed, 8 Aug 2012 19:03:14 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> >> > +struct vq_info_block {
> >> > +    uint64_t queue;
> >> > +    uint16_t num;
> >> > +} QEMU_PACKED;
> >> > +
> >> > +struct vq_config_block {
> >> > +    uint16_t index;
> >> > +    uint16_t num;
> >> > +} QEMU_PACKED;
> >>
> >> Aren't these KVM structures? They should be defined in a KVM header
> >> file file in linux-headers.
> >
> > Not really, virtio-ccw isn't tied to kvm.
> >
> > I see this more as command blocks that are specific to the "control
> > unit" - like something that would be defined in an attachment
> > specification for a classic s390 device (and in the virtio spec in this
> > case) and modeled as C structures here.
> 
> OK. Then please use CamelCase for these too.
> 

OK.

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

* Re: [PATCH 3/5] s390: Add new channel I/O based virtio transport.
  2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
@ 2012-08-09 11:34     ` Stefan Hajnoczi
  -1 siblings, 0 replies; 42+ messages in thread
From: Stefan Hajnoczi @ 2012-08-09 11:34 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

On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> ---
>  hw/qdev-monitor.c      |   5 +
>  hw/s390-virtio.c       | 268 ++++++++++----
>  hw/s390x/Makefile.objs |   1 +
>  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/virtio-ccw.h  |  77 ++++
>  vl.c                   |   1 +
>  6 files changed, 1243 insertions(+), 71 deletions(-)
>  create mode 100644 hw/s390x/virtio-ccw.c
>  create mode 100644 hw/s390x/virtio-ccw.h

Is the virtqueue still using vring and assuming the hypervisor reaches
into guest memory?

Can existing ccw device types access memory directly (for some reason
I assumed ccw always copies or send messages)?

Stefan

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

* Re: [Qemu-devel] [PATCH 3/5] s390: Add new channel I/O based virtio transport.
@ 2012-08-09 11:34     ` Stefan Hajnoczi
  0 siblings, 0 replies; 42+ messages in thread
From: Stefan Hajnoczi @ 2012-08-09 11:34 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

On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> ---
>  hw/qdev-monitor.c      |   5 +
>  hw/s390-virtio.c       | 268 ++++++++++----
>  hw/s390x/Makefile.objs |   1 +
>  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/virtio-ccw.h  |  77 ++++
>  vl.c                   |   1 +
>  6 files changed, 1243 insertions(+), 71 deletions(-)
>  create mode 100644 hw/s390x/virtio-ccw.c
>  create mode 100644 hw/s390x/virtio-ccw.h

Is the virtqueue still using vring and assuming the hypervisor reaches
into guest memory?

Can existing ccw device types access memory directly (for some reason
I assumed ccw always copies or send messages)?

Stefan

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

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

On Thu, 9 Aug 2012 12:34:04 +0100
Stefan Hajnoczi <stefanha@gmail.com> wrote:

> On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> > ---
> >  hw/qdev-monitor.c      |   5 +
> >  hw/s390-virtio.c       | 268 ++++++++++----
> >  hw/s390x/Makefile.objs |   1 +
> >  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/s390x/virtio-ccw.h  |  77 ++++
> >  vl.c                   |   1 +
> >  6 files changed, 1243 insertions(+), 71 deletions(-)
> >  create mode 100644 hw/s390x/virtio-ccw.c
> >  create mode 100644 hw/s390x/virtio-ccw.h
> 
> Is the virtqueue still using vring and assuming the hypervisor reaches
> into guest memory?

The virtqueues are guest-allocated and their location is transmitted
via a control-type ccw to the host, which can then use it until
notified otherwise.

> 
> Can existing ccw device types access memory directly (for some reason
> I assumed ccw always copies or send messages)?

Not sure if I understand your question correctly, but read or write
type ccws specify a memory area where the hardware/hypervisor may write
to or read from. These accesses happen while the channel program is
running (any time between the ssch/rsch and ending status present at
the subchannel). The "specify an area that can be used by hardware and
os" approach exists as well; the closed thing to the virtio-ccw
approach is probably qdio.

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

* Re: [Qemu-devel] [PATCH 3/5] s390: Add new channel I/O based virtio transport.
@ 2012-08-09 12:12       ` Cornelia Huck
  0 siblings, 0 replies; 42+ messages in thread
From: Cornelia Huck @ 2012-08-09 12:12 UTC (permalink / raw)
  To: Stefan Hajnoczi
  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 Thu, 9 Aug 2012 12:34:04 +0100
Stefan Hajnoczi <stefanha@gmail.com> wrote:

> On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
> > ---
> >  hw/qdev-monitor.c      |   5 +
> >  hw/s390-virtio.c       | 268 ++++++++++----
> >  hw/s390x/Makefile.objs |   1 +
> >  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/s390x/virtio-ccw.h  |  77 ++++
> >  vl.c                   |   1 +
> >  6 files changed, 1243 insertions(+), 71 deletions(-)
> >  create mode 100644 hw/s390x/virtio-ccw.c
> >  create mode 100644 hw/s390x/virtio-ccw.h
> 
> Is the virtqueue still using vring and assuming the hypervisor reaches
> into guest memory?

The virtqueues are guest-allocated and their location is transmitted
via a control-type ccw to the host, which can then use it until
notified otherwise.

> 
> Can existing ccw device types access memory directly (for some reason
> I assumed ccw always copies or send messages)?

Not sure if I understand your question correctly, but read or write
type ccws specify a memory area where the hardware/hypervisor may write
to or read from. These accesses happen while the channel program is
running (any time between the ssch/rsch and ending status present at
the subchannel). The "specify an area that can be used by hardware and
os" approach exists as well; the closed thing to the virtio-ccw
approach is probably qdio.

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

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

On Thu, Aug 9, 2012 at 1:12 PM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Thu, 9 Aug 2012 12:34:04 +0100
> Stefan Hajnoczi <stefanha@gmail.com> wrote:
>
>> On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
>> > ---
>> >  hw/qdev-monitor.c      |   5 +
>> >  hw/s390-virtio.c       | 268 ++++++++++----
>> >  hw/s390x/Makefile.objs |   1 +
>> >  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >  hw/s390x/virtio-ccw.h  |  77 ++++
>> >  vl.c                   |   1 +
>> >  6 files changed, 1243 insertions(+), 71 deletions(-)
>> >  create mode 100644 hw/s390x/virtio-ccw.c
>> >  create mode 100644 hw/s390x/virtio-ccw.h
>>
>> Is the virtqueue still using vring and assuming the hypervisor reaches
>> into guest memory?
>
> The virtqueues are guest-allocated and their location is transmitted
> via a control-type ccw to the host, which can then use it until
> notified otherwise.
>
>>
>> Can existing ccw device types access memory directly (for some reason
>> I assumed ccw always copies or send messages)?
>
> Not sure if I understand your question correctly, but read or write
> type ccws specify a memory area where the hardware/hypervisor may write
> to or read from. These accesses happen while the channel program is
> running (any time between the ssch/rsch and ending status present at
> the subchannel). The "specify an area that can be used by hardware and
> os" approach exists as well; the closed thing to the virtio-ccw
> approach is probably qdio.

Okay, thanks!

Stefan

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

* Re: [Qemu-devel] [PATCH 3/5] s390: Add new channel I/O based virtio transport.
@ 2012-08-09 12:27         ` Stefan Hajnoczi
  0 siblings, 0 replies; 42+ messages in thread
From: Stefan Hajnoczi @ 2012-08-09 12:27 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

On Thu, Aug 9, 2012 at 1:12 PM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Thu, 9 Aug 2012 12:34:04 +0100
> Stefan Hajnoczi <stefanha@gmail.com> wrote:
>
>> On Tue, Aug 7, 2012 at 3:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> 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>
>> > ---
>> >  hw/qdev-monitor.c      |   5 +
>> >  hw/s390-virtio.c       | 268 ++++++++++----
>> >  hw/s390x/Makefile.objs |   1 +
>> >  hw/s390x/virtio-ccw.c  | 962 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >  hw/s390x/virtio-ccw.h  |  77 ++++
>> >  vl.c                   |   1 +
>> >  6 files changed, 1243 insertions(+), 71 deletions(-)
>> >  create mode 100644 hw/s390x/virtio-ccw.c
>> >  create mode 100644 hw/s390x/virtio-ccw.h
>>
>> Is the virtqueue still using vring and assuming the hypervisor reaches
>> into guest memory?
>
> The virtqueues are guest-allocated and their location is transmitted
> via a control-type ccw to the host, which can then use it until
> notified otherwise.
>
>>
>> Can existing ccw device types access memory directly (for some reason
>> I assumed ccw always copies or send messages)?
>
> Not sure if I understand your question correctly, but read or write
> type ccws specify a memory area where the hardware/hypervisor may write
> to or read from. These accesses happen while the channel program is
> running (any time between the ssch/rsch and ending status present at
> the subchannel). The "specify an area that can be used by hardware and
> os" approach exists as well; the closed thing to the virtio-ccw
> approach is probably qdio.

Okay, thanks!

Stefan

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

end of thread, other threads:[~2012-08-09 12:28 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-07 14:52 [RFC PATCH 0/5] qemu: s390: virtual css and virtio-ccw Cornelia Huck
2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 1/5] Update headers for upcoming s390 changes Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 2/5] s390: Virtual channel subsystem support Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 21:00   ` Blue Swirl
2012-08-07 21:00     ` Blue Swirl
2012-08-08  8:17     ` Cornelia Huck
2012-08-08  8:17       ` [Qemu-devel] " Cornelia Huck
2012-08-08 19:16       ` Blue Swirl
2012-08-08 19:16         ` Blue Swirl
2012-08-08 19:34         ` Peter Maydell
2012-08-08 19:34           ` Peter Maydell
2012-08-08 19:39           ` Blue Swirl
2012-08-08 19:39             ` Blue Swirl
2012-08-09  7:19             ` Cornelia Huck
2012-08-09  7:19               ` Cornelia Huck
2012-08-08  8:27   ` Peter Maydell
2012-08-08  8:27     ` [Qemu-devel] " Peter Maydell
2012-08-08  8:53     ` Cornelia Huck
2012-08-08  8:53       ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 3/5] s390: Add new channel I/O based virtio transport Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 20:47   ` Blue Swirl
2012-08-07 20:47     ` Blue Swirl
2012-08-08  8:28     ` Cornelia Huck
2012-08-08  8:28       ` Cornelia Huck
2012-08-08 19:03       ` Blue Swirl
2012-08-08 19:03         ` Blue Swirl
2012-08-09  7:21         ` Cornelia Huck
2012-08-09  7:21           ` Cornelia Huck
2012-08-09 11:34   ` Stefan Hajnoczi
2012-08-09 11:34     ` [Qemu-devel] " Stefan Hajnoczi
2012-08-09 12:12     ` Cornelia Huck
2012-08-09 12:12       ` [Qemu-devel] " Cornelia Huck
2012-08-09 12:27       ` Stefan Hajnoczi
2012-08-09 12:27         ` [Qemu-devel] " Stefan Hajnoczi
2012-08-07 14:52 ` [PATCH 4/5] s390: Virtual channel subsystem support for !KVM Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 5/5] [HACK] Handle multiple virtio aliases Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.