All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/7] s390: virtual css host support.
@ 2012-08-07 14:52 ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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,

the following patches implement support for a virtual channel
subsystem in the host (virtio-ccw on the host is handled by
user space).

Patches 1 and 2 add support for injecting I/O interrupts and
(some) machine checks via the already existing mechanisms.
The most important parts are those handling interrupt delivery.

Patch 3 prepares for in-kernel handling of I/O instructions.

Patches 4 and 5 make it possible for kvm to re-use some css-related
definitions already used by the s390 common I/O layer.

Patch 6 introduces the interface to dynamically enable capabilities
already in use on ppc to s390.

Patch 7 (the big one) adds in-kernel implementations of most I/O
instructions (leaving out some facilities that were too complex and
are not currently needed; these can convieniently be marked as
not supported). Some operations (which are executed asynchronically
on real hardware) are outsourced to user space. This in-kernel
channel subsystem support can be enabled by user space via a new
capability.

Cornelia Huck (7):
  s390/kvm: Support for I/O interrupts.
  s390/kvm: Add support for machine checks.
  s390/kvm: In-kernel handling of I/O instructions.
  s390: Move css limits from drivers/s390/cio/ to include/asm/.
  s390: Make some css-related structures usable by non-cio code.
  s390/kvm: Base infrastructure for enabling capabilities.
  s390/kvm: In-kernel channel subsystem support.

 Documentation/virtual/kvm/api.txt | 129 +++++-
 arch/s390/include/asm/cio.h       |   2 +
 arch/s390/include/asm/kvm_host.h  |  58 +++
 arch/s390/include/asm/orb.h       |  69 +++
 arch/s390/include/asm/schib.h     |  52 +++
 arch/s390/kvm/Makefile            |   2 +-
 arch/s390/kvm/css.c               | 945 ++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/intercept.c         |  22 +-
 arch/s390/kvm/interrupt.c         | 337 ++++++++++++--
 arch/s390/kvm/ioinst.c            | 797 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.c          |  61 +++
 arch/s390/kvm/kvm-s390.h          |  42 ++
 arch/s390/kvm/priv.c              | 194 +++++++-
 arch/s390/kvm/trace-s390.h        |  73 ++-
 arch/s390/kvm/trace.h             |  22 +
 drivers/s390/cio/cio.h            |  46 +-
 drivers/s390/cio/css.h            |   3 -
 drivers/s390/cio/io_sch.h         |   2 +-
 drivers/s390/cio/ioasm.h          |   2 +-
 drivers/s390/cio/orb.h            |  67 ---
 include/linux/kvm.h               |  60 +++
 include/trace/events/kvm.h        |   2 +-
 virt/kvm/kvm_main.c               |   3 +-
 23 files changed, 2816 insertions(+), 174 deletions(-)
 create mode 100644 arch/s390/include/asm/orb.h
 create mode 100644 arch/s390/include/asm/schib.h
 create mode 100644 arch/s390/kvm/css.c
 create mode 100644 arch/s390/kvm/ioinst.c
 delete mode 100644 drivers/s390/cio/orb.h

-- 
1.7.11.4

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

* [Qemu-devel] [RFC PATCH 0/7] s390: virtual css host support.
@ 2012-08-07 14:52 ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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,

the following patches implement support for a virtual channel
subsystem in the host (virtio-ccw on the host is handled by
user space).

Patches 1 and 2 add support for injecting I/O interrupts and
(some) machine checks via the already existing mechanisms.
The most important parts are those handling interrupt delivery.

Patch 3 prepares for in-kernel handling of I/O instructions.

Patches 4 and 5 make it possible for kvm to re-use some css-related
definitions already used by the s390 common I/O layer.

Patch 6 introduces the interface to dynamically enable capabilities
already in use on ppc to s390.

Patch 7 (the big one) adds in-kernel implementations of most I/O
instructions (leaving out some facilities that were too complex and
are not currently needed; these can convieniently be marked as
not supported). Some operations (which are executed asynchronically
on real hardware) are outsourced to user space. This in-kernel
channel subsystem support can be enabled by user space via a new
capability.

Cornelia Huck (7):
  s390/kvm: Support for I/O interrupts.
  s390/kvm: Add support for machine checks.
  s390/kvm: In-kernel handling of I/O instructions.
  s390: Move css limits from drivers/s390/cio/ to include/asm/.
  s390: Make some css-related structures usable by non-cio code.
  s390/kvm: Base infrastructure for enabling capabilities.
  s390/kvm: In-kernel channel subsystem support.

 Documentation/virtual/kvm/api.txt | 129 +++++-
 arch/s390/include/asm/cio.h       |   2 +
 arch/s390/include/asm/kvm_host.h  |  58 +++
 arch/s390/include/asm/orb.h       |  69 +++
 arch/s390/include/asm/schib.h     |  52 +++
 arch/s390/kvm/Makefile            |   2 +-
 arch/s390/kvm/css.c               | 945 ++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/intercept.c         |  22 +-
 arch/s390/kvm/interrupt.c         | 337 ++++++++++++--
 arch/s390/kvm/ioinst.c            | 797 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.c          |  61 +++
 arch/s390/kvm/kvm-s390.h          |  42 ++
 arch/s390/kvm/priv.c              | 194 +++++++-
 arch/s390/kvm/trace-s390.h        |  73 ++-
 arch/s390/kvm/trace.h             |  22 +
 drivers/s390/cio/cio.h            |  46 +-
 drivers/s390/cio/css.h            |   3 -
 drivers/s390/cio/io_sch.h         |   2 +-
 drivers/s390/cio/ioasm.h          |   2 +-
 drivers/s390/cio/orb.h            |  67 ---
 include/linux/kvm.h               |  60 +++
 include/trace/events/kvm.h        |   2 +-
 virt/kvm/kvm_main.c               |   3 +-
 23 files changed, 2816 insertions(+), 174 deletions(-)
 create mode 100644 arch/s390/include/asm/orb.h
 create mode 100644 arch/s390/include/asm/schib.h
 create mode 100644 arch/s390/kvm/css.c
 create mode 100644 arch/s390/kvm/ioinst.c
 delete mode 100644 drivers/s390/cio/orb.h

-- 
1.7.11.4

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

* [PATCH 1/7] s390/kvm: Support for I/O interrupts.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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 support for handling I/O interrupts (standard, subchannel-related
ones and rudimentary adapter interrupts).

The subchannel-identifying parameters are encoded into the interrupt
type.

I/O interrupts are floating, so they can't be injected on a specific
vcpu.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/kvm_host.h |   2 +
 arch/s390/kvm/interrupt.c        | 115 +++++++++++++++++++++++++++++++++++++--
 include/linux/kvm.h              |   6 ++
 3 files changed, 118 insertions(+), 5 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index b784154..e47f697 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -76,6 +76,7 @@ struct kvm_s390_sie_block {
 	__u64	epoch;			/* 0x0038 */
 	__u8	reserved40[4];		/* 0x0040 */
 #define LCTL_CR0	0x8000
+#define LCTL_CR6	0x0200
 	__u16   lctl;			/* 0x0044 */
 	__s16	icpua;			/* 0x0046 */
 	__u32	ictl;			/* 0x0048 */
@@ -127,6 +128,7 @@ struct kvm_vcpu_stat {
 	u32 deliver_prefix_signal;
 	u32 deliver_restart_signal;
 	u32 deliver_program_int;
+	u32 deliver_io_int;
 	u32 exit_wait_state;
 	u32 instruction_stidp;
 	u32 instruction_spx;
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 7556231..1dccfe7 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -21,11 +21,26 @@
 #include "gaccess.h"
 #include "trace-s390.h"
 
+#define IOINT_SCHID_MASK 0x0000ffff
+#define IOINT_SSID_MASK 0x00030000
+#define IOINT_CSSID_MASK 0x03fc0000
+#define IOINT_AI_MASK 0x04000000
+
+static int is_ioint(u64 type)
+{
+	return ((type & 0xfffe0000u) != 0xfffe0000u);
+}
+
 static int psw_extint_disabled(struct kvm_vcpu *vcpu)
 {
 	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
 }
 
+static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
+{
+	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
+}
+
 static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
 {
 	if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
@@ -68,7 +83,18 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
 	case KVM_S390_RESTART:
 		return 1;
 	default:
-		BUG();
+		if (is_ioint(inti->type)) {
+			if (psw_ioint_disabled(vcpu))
+				return 0;
+			if (vcpu->arch.sie_block->gcr[6] &
+			    inti->io.io_int_word)
+				return 1;
+			return 0;
+		} else {
+			printk(KERN_WARNING "illegal interrupt type %llx\n",
+			       inti->type);
+			BUG();
+		}
 	}
 	return 0;
 }
@@ -117,6 +143,13 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
 		__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
 		break;
 	default:
+		if (is_ioint(inti->type)) {
+			if (psw_ioint_disabled(vcpu))
+				__set_cpuflag(vcpu, CPUSTAT_IO_INT);
+			else
+				vcpu->arch.sie_block->lctl |= LCTL_CR6;
+			break;
+		}
 		BUG();
 	}
 }
@@ -298,7 +331,49 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 		break;
 
 	default:
-		BUG();
+		if (is_ioint(inti->type)) {
+			__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
+				inti->io.subchannel_nr;
+			__u64 param1 = ((__u64)inti->io.io_int_parm << 32) |
+				inti->io.io_int_word;
+			VCPU_EVENT(vcpu, 4,
+				   "interrupt: I/O %llx", inti->type);
+			vcpu->stat.deliver_io_int++;
+			trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+							 param0, param1);
+			rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID,
+					   inti->io.subchannel_id);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR,
+					   inti->io.subchannel_nr);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u32(vcpu, __LC_IO_INT_PARM,
+					   inti->io.io_int_parm);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u32(vcpu, __LC_IO_INT_WORD,
+					   inti->io.io_int_word);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW,
+					   &vcpu->arch.sie_block->gpsw,
+					   sizeof(psw_t));
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+					     __LC_IO_NEW_PSW, sizeof(psw_t));
+			if (rc == -EFAULT)
+				exception = 1;
+			break;
+		} else
+			BUG();
 	}
 	if (exception) {
 		printk("kvm: The guest lowcore is not mapped during interrupt "
@@ -547,7 +622,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 {
 	struct kvm_s390_local_interrupt *li;
 	struct kvm_s390_float_interrupt *fi;
-	struct kvm_s390_interrupt_info *inti;
+	struct kvm_s390_interrupt_info *inti, *iter;
 	int sigcpu;
 
 	inti = kzalloc(sizeof(*inti), GFP_KERNEL);
@@ -571,9 +646,26 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	case KVM_S390_SIGP_STOP:
 	case KVM_S390_INT_EXTERNAL_CALL:
 	case KVM_S390_INT_EMERGENCY:
-	default:
 		kfree(inti);
 		return -EINVAL;
+	default:
+		if (!is_ioint(s390int->type)) {
+			kfree(inti);
+			return -EINVAL;
+		}
+		if (s390int->type & IOINT_AI_MASK)
+			VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
+		else
+			VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x",
+				 s390int->type & IOINT_CSSID_MASK,
+				 s390int->type & IOINT_SSID_MASK,
+				 s390int->type & IOINT_SCHID_MASK);
+		inti->type = s390int->type;
+		inti->io.subchannel_id = s390int->parm >> 16;
+		inti->io.subchannel_nr = s390int->parm & 0x0000ffffu;
+		inti->io.io_int_parm = s390int->parm64 >> 32;
+		inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull;
+		break;
 	}
 	trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64,
 				 2);
@@ -581,7 +673,19 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	mutex_lock(&kvm->lock);
 	fi = &kvm->arch.float_int;
 	spin_lock(&fi->lock);
-	list_add_tail(&inti->list, &fi->list);
+	if (!is_ioint(inti->type))
+		list_add_tail(&inti->list, &fi->list);
+	else {
+		/* Keep I/O interrupts sorted in isc order. */
+		list_for_each_entry(iter, &fi->list, list) {
+			if (!is_ioint(iter->type))
+				continue;
+			if (iter->io.io_int_word <= inti->io.io_int_word)
+				continue;
+			break;
+		}
+		list_add_tail(&inti->list, &iter->list);
+	}
 	atomic_set(&fi->active, 1);
 	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
 	if (sigcpu == KVM_MAX_VCPUS) {
@@ -639,6 +743,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 	case KVM_S390_INT_VIRTIO:
 	case KVM_S390_INT_SERVICE:
 	default:
+		/* also includes IOINT */
 		kfree(inti);
 		return -EINVAL;
 	}
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 2ce09aa..31f52c6 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -392,6 +392,12 @@ struct kvm_s390_psw {
 #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;
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 1/7] s390/kvm: Support for I/O interrupts.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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 support for handling I/O interrupts (standard, subchannel-related
ones and rudimentary adapter interrupts).

The subchannel-identifying parameters are encoded into the interrupt
type.

I/O interrupts are floating, so they can't be injected on a specific
vcpu.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/kvm_host.h |   2 +
 arch/s390/kvm/interrupt.c        | 115 +++++++++++++++++++++++++++++++++++++--
 include/linux/kvm.h              |   6 ++
 3 files changed, 118 insertions(+), 5 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index b784154..e47f697 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -76,6 +76,7 @@ struct kvm_s390_sie_block {
 	__u64	epoch;			/* 0x0038 */
 	__u8	reserved40[4];		/* 0x0040 */
 #define LCTL_CR0	0x8000
+#define LCTL_CR6	0x0200
 	__u16   lctl;			/* 0x0044 */
 	__s16	icpua;			/* 0x0046 */
 	__u32	ictl;			/* 0x0048 */
@@ -127,6 +128,7 @@ struct kvm_vcpu_stat {
 	u32 deliver_prefix_signal;
 	u32 deliver_restart_signal;
 	u32 deliver_program_int;
+	u32 deliver_io_int;
 	u32 exit_wait_state;
 	u32 instruction_stidp;
 	u32 instruction_spx;
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 7556231..1dccfe7 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -21,11 +21,26 @@
 #include "gaccess.h"
 #include "trace-s390.h"
 
+#define IOINT_SCHID_MASK 0x0000ffff
+#define IOINT_SSID_MASK 0x00030000
+#define IOINT_CSSID_MASK 0x03fc0000
+#define IOINT_AI_MASK 0x04000000
+
+static int is_ioint(u64 type)
+{
+	return ((type & 0xfffe0000u) != 0xfffe0000u);
+}
+
 static int psw_extint_disabled(struct kvm_vcpu *vcpu)
 {
 	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
 }
 
+static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
+{
+	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
+}
+
 static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
 {
 	if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
@@ -68,7 +83,18 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
 	case KVM_S390_RESTART:
 		return 1;
 	default:
-		BUG();
+		if (is_ioint(inti->type)) {
+			if (psw_ioint_disabled(vcpu))
+				return 0;
+			if (vcpu->arch.sie_block->gcr[6] &
+			    inti->io.io_int_word)
+				return 1;
+			return 0;
+		} else {
+			printk(KERN_WARNING "illegal interrupt type %llx\n",
+			       inti->type);
+			BUG();
+		}
 	}
 	return 0;
 }
@@ -117,6 +143,13 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
 		__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
 		break;
 	default:
+		if (is_ioint(inti->type)) {
+			if (psw_ioint_disabled(vcpu))
+				__set_cpuflag(vcpu, CPUSTAT_IO_INT);
+			else
+				vcpu->arch.sie_block->lctl |= LCTL_CR6;
+			break;
+		}
 		BUG();
 	}
 }
@@ -298,7 +331,49 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 		break;
 
 	default:
-		BUG();
+		if (is_ioint(inti->type)) {
+			__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
+				inti->io.subchannel_nr;
+			__u64 param1 = ((__u64)inti->io.io_int_parm << 32) |
+				inti->io.io_int_word;
+			VCPU_EVENT(vcpu, 4,
+				   "interrupt: I/O %llx", inti->type);
+			vcpu->stat.deliver_io_int++;
+			trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+							 param0, param1);
+			rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID,
+					   inti->io.subchannel_id);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR,
+					   inti->io.subchannel_nr);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u32(vcpu, __LC_IO_INT_PARM,
+					   inti->io.io_int_parm);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = put_guest_u32(vcpu, __LC_IO_INT_WORD,
+					   inti->io.io_int_word);
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW,
+					   &vcpu->arch.sie_block->gpsw,
+					   sizeof(psw_t));
+			if (rc == -EFAULT)
+				exception = 1;
+
+			rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+					     __LC_IO_NEW_PSW, sizeof(psw_t));
+			if (rc == -EFAULT)
+				exception = 1;
+			break;
+		} else
+			BUG();
 	}
 	if (exception) {
 		printk("kvm: The guest lowcore is not mapped during interrupt "
@@ -547,7 +622,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 {
 	struct kvm_s390_local_interrupt *li;
 	struct kvm_s390_float_interrupt *fi;
-	struct kvm_s390_interrupt_info *inti;
+	struct kvm_s390_interrupt_info *inti, *iter;
 	int sigcpu;
 
 	inti = kzalloc(sizeof(*inti), GFP_KERNEL);
@@ -571,9 +646,26 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	case KVM_S390_SIGP_STOP:
 	case KVM_S390_INT_EXTERNAL_CALL:
 	case KVM_S390_INT_EMERGENCY:
-	default:
 		kfree(inti);
 		return -EINVAL;
+	default:
+		if (!is_ioint(s390int->type)) {
+			kfree(inti);
+			return -EINVAL;
+		}
+		if (s390int->type & IOINT_AI_MASK)
+			VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
+		else
+			VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x",
+				 s390int->type & IOINT_CSSID_MASK,
+				 s390int->type & IOINT_SSID_MASK,
+				 s390int->type & IOINT_SCHID_MASK);
+		inti->type = s390int->type;
+		inti->io.subchannel_id = s390int->parm >> 16;
+		inti->io.subchannel_nr = s390int->parm & 0x0000ffffu;
+		inti->io.io_int_parm = s390int->parm64 >> 32;
+		inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull;
+		break;
 	}
 	trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64,
 				 2);
@@ -581,7 +673,19 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	mutex_lock(&kvm->lock);
 	fi = &kvm->arch.float_int;
 	spin_lock(&fi->lock);
-	list_add_tail(&inti->list, &fi->list);
+	if (!is_ioint(inti->type))
+		list_add_tail(&inti->list, &fi->list);
+	else {
+		/* Keep I/O interrupts sorted in isc order. */
+		list_for_each_entry(iter, &fi->list, list) {
+			if (!is_ioint(iter->type))
+				continue;
+			if (iter->io.io_int_word <= inti->io.io_int_word)
+				continue;
+			break;
+		}
+		list_add_tail(&inti->list, &iter->list);
+	}
 	atomic_set(&fi->active, 1);
 	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
 	if (sigcpu == KVM_MAX_VCPUS) {
@@ -639,6 +743,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 	case KVM_S390_INT_VIRTIO:
 	case KVM_S390_INT_SERVICE:
 	default:
+		/* also includes IOINT */
 		kfree(inti);
 		return -EINVAL;
 	}
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 2ce09aa..31f52c6 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -392,6 +392,12 @@ struct kvm_s390_psw {
 #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;
-- 
1.7.11.4

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

* [PATCH 2/7] s390/kvm: Add support for machine checks.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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 support for injecting machine checks (only repressible
conditions for now).

This is a bit more involved than I/O interrupts, for these reasons:

- Machine checks come in both floating and cpu varieties.
- We don't have a bit for machine checks enabling, but have to use
  a roundabout approach with trapping PSW changing instructions and
  watching for opened machine checks.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/kvm_host.h |   8 +++
 arch/s390/kvm/intercept.c        |   2 +
 arch/s390/kvm/interrupt.c        | 111 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.h         |   3 +
 arch/s390/kvm/priv.c             | 133 +++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/trace-s390.h       |   6 +-
 include/linux/kvm.h              |   1 +
 7 files changed, 261 insertions(+), 3 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index e47f697..556774d 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -77,8 +77,10 @@ struct kvm_s390_sie_block {
 	__u8	reserved40[4];		/* 0x0040 */
 #define LCTL_CR0	0x8000
 #define LCTL_CR6	0x0200
+#define LCTL_CR14	0x0002
 	__u16   lctl;			/* 0x0044 */
 	__s16	icpua;			/* 0x0046 */
+#define ICTL_LPSW 0x02000000
 	__u32	ictl;			/* 0x0048 */
 	__u32	eca;			/* 0x004c */
 	__u8	icptcode;		/* 0x0050 */
@@ -189,6 +191,11 @@ struct kvm_s390_emerg_info {
 	__u16 code;
 };
 
+struct kvm_s390_mchk_info {
+	__u64 cr14;
+	__u64 mcic;
+};
+
 struct kvm_s390_interrupt_info {
 	struct list_head list;
 	u64	type;
@@ -199,6 +206,7 @@ struct kvm_s390_interrupt_info {
 		struct kvm_s390_emerg_info emerg;
 		struct kvm_s390_extcall_info extcall;
 		struct kvm_s390_prefix_info prefix;
+		struct kvm_s390_mchk_info mchk;
 	};
 };
 
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index 22798ec..ec1177f 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -106,10 +106,12 @@ static int handle_lctl(struct kvm_vcpu *vcpu)
 
 static intercept_handler_t instruction_handlers[256] = {
 	[0x01] = kvm_s390_handle_01,
+	[0x82] = kvm_s390_handle_lpsw,
 	[0x83] = kvm_s390_handle_diag,
 	[0xae] = kvm_s390_handle_sigp,
 	[0xb2] = kvm_s390_handle_b2,
 	[0xb7] = handle_lctl,
+	[0xb9] = kvm_s390_handle_b9,
 	[0xe5] = kvm_s390_handle_e5,
 	[0xeb] = handle_lctlg,
 };
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 1dccfe7..edc065f 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -41,6 +41,11 @@ static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
 	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
 }
 
+static int psw_mchk_disabled(struct kvm_vcpu *vcpu)
+{
+	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK);
+}
+
 static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
 {
 	if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
@@ -82,6 +87,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
 	case KVM_S390_SIGP_SET_PREFIX:
 	case KVM_S390_RESTART:
 		return 1;
+	case KVM_S390_MCHK:
+		if (psw_mchk_disabled(vcpu))
+			return 0;
+		if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14)
+			return 1;
+		return 0;
 	default:
 		if (is_ioint(inti->type)) {
 			if (psw_ioint_disabled(vcpu))
@@ -119,6 +130,7 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
 		CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
 		&vcpu->arch.sie_block->cpuflags);
 	vcpu->arch.sie_block->lctl = 0x0000;
+	vcpu->arch.sie_block->ictl &= ~ICTL_LPSW;
 }
 
 static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag)
@@ -142,6 +154,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
 	case KVM_S390_SIGP_STOP:
 		__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
 		break;
+	case KVM_S390_MCHK:
+		if (psw_mchk_disabled(vcpu))
+			vcpu->arch.sie_block->ictl |= ICTL_LPSW;
+		else
+			vcpu->arch.sie_block->lctl |= LCTL_CR14;
+		break;
 	default:
 		if (is_ioint(inti->type)) {
 			if (psw_ioint_disabled(vcpu))
@@ -330,6 +348,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 			exception = 1;
 		break;
 
+	case KVM_S390_MCHK:
+		VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx",
+			   inti->mchk.mcic);
+		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+						 inti->mchk.cr14,
+						 inti->mchk.mcic);
+		rc = kvm_s390_vcpu_store_status(vcpu,
+						KVM_S390_STORE_STATUS_PREFIXED);
+		if (rc == -EFAULT)
+			exception = 1;
+
+		rc = put_guest_u64(vcpu, __LC_MCCK_CODE, inti->mchk.mcic);
+		if (rc == -EFAULT)
+			exception = 1;
+		
+		rc = copy_to_guest(vcpu, __LC_MCK_OLD_PSW,
+				   &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+		if (rc == -EFAULT)
+			exception = 1;
+		
+		rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+				     __LC_MCK_NEW_PSW, sizeof(psw_t));
+		if (rc == -EFAULT)
+			exception = 1;
+		break;
+
 	default:
 		if (is_ioint(inti->type)) {
 			__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
@@ -595,6 +639,61 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 	}
 }
 
+void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
+{
+	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+	struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int;
+	struct kvm_s390_interrupt_info  *n, *inti = NULL;
+	int deliver;
+
+	__reset_intercept_indicators(vcpu);
+	if (atomic_read(&li->active)) {
+		do {
+			deliver = 0;
+			spin_lock_bh(&li->lock);
+			list_for_each_entry_safe(inti, n, &li->list, list) {
+				if ((inti->type == KVM_S390_MCHK) &&
+				    __interrupt_is_deliverable(vcpu, inti)) {
+					list_del(&inti->list);
+					deliver = 1;
+					break;
+				}
+				__set_intercept_indicator(vcpu, inti);
+			}
+			if (list_empty(&li->list))
+				atomic_set(&li->active, 0);
+			spin_unlock_bh(&li->lock);
+			if (deliver) {
+				__do_deliver_interrupt(vcpu, inti);
+				kfree(inti);
+			}
+		} while (deliver);
+	}
+
+	if (atomic_read(&fi->active)) {
+		do {
+			deliver = 0;
+			spin_lock(&fi->lock);
+			list_for_each_entry_safe(inti, n, &fi->list, list) {
+				if ((inti->type == KVM_S390_MCHK) &&
+				    __interrupt_is_deliverable(vcpu, inti)) {
+					list_del(&inti->list);
+					deliver = 1;
+					break;
+				}
+				__set_intercept_indicator(vcpu, inti);
+			}
+			if (list_empty(&fi->list))
+				atomic_set(&fi->active, 0);
+			spin_unlock(&fi->lock);
+			if (deliver) {
+				__do_deliver_interrupt(vcpu, inti);
+				kfree(inti);
+			}
+		} while (deliver);
+	}
+}
+
 int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
 {
 	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
@@ -648,6 +747,12 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	case KVM_S390_INT_EMERGENCY:
 		kfree(inti);
 		return -EINVAL;
+	case KVM_S390_MCHK:
+		VM_EVENT(kvm, 5, "inject: machine check parm64:%llx",
+			 s390int->parm64);
+		inti->type = s390int->type;
+		inti->mchk.mcic = s390int->parm64;
+		break;
 	default:
 		if (!is_ioint(s390int->type)) {
 			kfree(inti);
@@ -740,6 +845,12 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 		VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type);
 		inti->type = s390int->type;
 		break;
+	case KVM_S390_MCHK:
+		VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx",
+			   s390int->parm64);
+		inti->type = s390int->type;
+		inti->mchk.mcic = s390int->parm64;
+		break;
 	case KVM_S390_INT_VIRTIO:
 	case KVM_S390_INT_SERVICE:
 	default:
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index d75bc5e..b1e1cb6 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -69,6 +69,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer);
 void kvm_s390_tasklet(unsigned long parm);
 void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
+void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu);
 int kvm_s390_inject_vm(struct kvm *kvm,
 		struct kvm_s390_interrupt *s390int);
 int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
@@ -80,6 +81,8 @@ int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_01(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_b9(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index ed256fd..7e7263c 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -176,6 +176,101 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static void handle_new_psw(struct kvm_vcpu *vcpu)
+{
+	/* Check whether the new psw is enabled for machine checks. */
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK)
+		kvm_s390_deliver_pending_machine_checks(vcpu);
+}
+
+int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
+{
+	int base2 = vcpu->arch.sie_block->ipb >> 28;
+	int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
+	u64 addr;
+	u64 uninitialized_var(new_psw);
+
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+		return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+
+	addr = disp2;
+	if (base2)
+		addr += vcpu->run->s.regs.gprs[base2];
+
+	if (addr & 7) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	if (get_guest_u64(vcpu, addr, &new_psw)) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		goto out;
+	}
+
+	if (!(new_psw & 0x0008000000000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	vcpu->arch.sie_block->gpsw.mask = new_psw & 0xfff7ffff80000000;
+	vcpu->arch.sie_block->gpsw.addr = new_psw & 0x000000007fffffff;
+
+	if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe00000000) ||
+	    (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0x000000007ff00000)) ||
+	    ((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	     0x0000000100000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	handle_new_psw(vcpu);
+out:
+	return 0;
+}
+
+static int handle_lpswe(struct kvm_vcpu *vcpu)
+{
+	int base2 = vcpu->arch.sie_block->ipb >> 28;
+	int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
+	u64 addr;
+	u64 new_psw[2];
+
+	addr = disp2;
+	if (base2)
+		addr += vcpu->run->s.regs.gprs[base2];
+	
+	if (addr & 7) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	if (copy_from_guest(vcpu, new_psw, addr, sizeof(*new_psw))) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		goto out;
+	}
+
+	vcpu->arch.sie_block->gpsw.mask = new_psw[0];
+	vcpu->arch.sie_block->gpsw.addr = new_psw[1];
+
+	if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe7fffffff) ||
+	    (((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	      0x0000000010000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0xffffffff80000000)) ||
+	    (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0xfffffffffff00000)) ||
+	    ((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	     0x0000000100000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+	
+	handle_new_psw(vcpu);
+out:
+	return 0;
+}
+
 static int handle_stidp(struct kvm_vcpu *vcpu)
 {
 	int base2 = vcpu->arch.sie_block->ipb >> 28;
@@ -309,6 +404,7 @@ static intercept_handler_t priv_handlers[256] = {
 	[0x5f] = handle_chsc,
 	[0x7d] = handle_stsi,
 	[0xb1] = handle_stfl,
+	[0xb2] = handle_lpswe,
 };
 
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
@@ -333,6 +429,43 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 	return -EOPNOTSUPP;
 }
 
+static int handle_epsw(struct kvm_vcpu *vcpu)
+{
+	int reg1, reg2;
+
+	reg1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 24;
+	reg2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16;
+	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000;
+	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
+	if (reg2) {
+		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000;
+		vcpu->run->s.regs.gprs[reg2] |=
+			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffff;
+	}
+	return 0;
+}
+
+static intercept_handler_t b9_handlers[256] = {
+	[0x8d] = handle_epsw,
+};
+
+int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	/* This is handled just as for the B2 instructions. */
+	handler = b9_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
+	if (handler) {
+		if ((handler != handle_epsw) &&
+		    (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE))
+			return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+		else
+			return handler(vcpu);
+	}
+	return -EOPNOTSUPP;
+}
+
 static int handle_tprot(struct kvm_vcpu *vcpu)
 {
 	int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h
index 90fdf85..95fbc1a 100644
--- a/arch/s390/kvm/trace-s390.h
+++ b/arch/s390/kvm/trace-s390.h
@@ -141,13 +141,13 @@ TRACE_EVENT(kvm_s390_inject_vcpu,
  * Trace point for the actual delivery of interrupts.
  */
 TRACE_EVENT(kvm_s390_deliver_interrupt,
-	    TP_PROTO(unsigned int id, __u64 type, __u32 data0, __u64 data1),
+	    TP_PROTO(unsigned int id, __u64 type, __u64 data0, __u64 data1),
 	    TP_ARGS(id, type, data0, data1),
 
 	    TP_STRUCT__entry(
 		    __field(int, id)
 		    __field(__u32, inttype)
-		    __field(__u32, data0)
+		    __field(__u64, data0)
 		    __field(__u64, data1)
 		    ),
 
@@ -159,7 +159,7 @@ TRACE_EVENT(kvm_s390_deliver_interrupt,
 		    ),
 
 	    TP_printk("deliver interrupt (vcpu %d): type:%x (%s) "	\
-		      "data:%08x %016llx",
+		      "data:%08llx %016llx",
 		      __entry->id, __entry->inttype,
 		      __print_symbolic(__entry->inttype, kvm_s390_int_type),
 		      __entry->data0, __entry->data1)
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 31f52c6..e0c3d87 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -388,6 +388,7 @@ 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
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 2/7] s390/kvm: Add support for machine checks.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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 support for injecting machine checks (only repressible
conditions for now).

This is a bit more involved than I/O interrupts, for these reasons:

- Machine checks come in both floating and cpu varieties.
- We don't have a bit for machine checks enabling, but have to use
  a roundabout approach with trapping PSW changing instructions and
  watching for opened machine checks.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/kvm_host.h |   8 +++
 arch/s390/kvm/intercept.c        |   2 +
 arch/s390/kvm/interrupt.c        | 111 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.h         |   3 +
 arch/s390/kvm/priv.c             | 133 +++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/trace-s390.h       |   6 +-
 include/linux/kvm.h              |   1 +
 7 files changed, 261 insertions(+), 3 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index e47f697..556774d 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -77,8 +77,10 @@ struct kvm_s390_sie_block {
 	__u8	reserved40[4];		/* 0x0040 */
 #define LCTL_CR0	0x8000
 #define LCTL_CR6	0x0200
+#define LCTL_CR14	0x0002
 	__u16   lctl;			/* 0x0044 */
 	__s16	icpua;			/* 0x0046 */
+#define ICTL_LPSW 0x02000000
 	__u32	ictl;			/* 0x0048 */
 	__u32	eca;			/* 0x004c */
 	__u8	icptcode;		/* 0x0050 */
@@ -189,6 +191,11 @@ struct kvm_s390_emerg_info {
 	__u16 code;
 };
 
+struct kvm_s390_mchk_info {
+	__u64 cr14;
+	__u64 mcic;
+};
+
 struct kvm_s390_interrupt_info {
 	struct list_head list;
 	u64	type;
@@ -199,6 +206,7 @@ struct kvm_s390_interrupt_info {
 		struct kvm_s390_emerg_info emerg;
 		struct kvm_s390_extcall_info extcall;
 		struct kvm_s390_prefix_info prefix;
+		struct kvm_s390_mchk_info mchk;
 	};
 };
 
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index 22798ec..ec1177f 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -106,10 +106,12 @@ static int handle_lctl(struct kvm_vcpu *vcpu)
 
 static intercept_handler_t instruction_handlers[256] = {
 	[0x01] = kvm_s390_handle_01,
+	[0x82] = kvm_s390_handle_lpsw,
 	[0x83] = kvm_s390_handle_diag,
 	[0xae] = kvm_s390_handle_sigp,
 	[0xb2] = kvm_s390_handle_b2,
 	[0xb7] = handle_lctl,
+	[0xb9] = kvm_s390_handle_b9,
 	[0xe5] = kvm_s390_handle_e5,
 	[0xeb] = handle_lctlg,
 };
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 1dccfe7..edc065f 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -41,6 +41,11 @@ static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
 	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
 }
 
+static int psw_mchk_disabled(struct kvm_vcpu *vcpu)
+{
+	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK);
+}
+
 static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
 {
 	if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
@@ -82,6 +87,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
 	case KVM_S390_SIGP_SET_PREFIX:
 	case KVM_S390_RESTART:
 		return 1;
+	case KVM_S390_MCHK:
+		if (psw_mchk_disabled(vcpu))
+			return 0;
+		if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14)
+			return 1;
+		return 0;
 	default:
 		if (is_ioint(inti->type)) {
 			if (psw_ioint_disabled(vcpu))
@@ -119,6 +130,7 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
 		CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
 		&vcpu->arch.sie_block->cpuflags);
 	vcpu->arch.sie_block->lctl = 0x0000;
+	vcpu->arch.sie_block->ictl &= ~ICTL_LPSW;
 }
 
 static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag)
@@ -142,6 +154,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
 	case KVM_S390_SIGP_STOP:
 		__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
 		break;
+	case KVM_S390_MCHK:
+		if (psw_mchk_disabled(vcpu))
+			vcpu->arch.sie_block->ictl |= ICTL_LPSW;
+		else
+			vcpu->arch.sie_block->lctl |= LCTL_CR14;
+		break;
 	default:
 		if (is_ioint(inti->type)) {
 			if (psw_ioint_disabled(vcpu))
@@ -330,6 +348,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 			exception = 1;
 		break;
 
+	case KVM_S390_MCHK:
+		VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx",
+			   inti->mchk.mcic);
+		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+						 inti->mchk.cr14,
+						 inti->mchk.mcic);
+		rc = kvm_s390_vcpu_store_status(vcpu,
+						KVM_S390_STORE_STATUS_PREFIXED);
+		if (rc == -EFAULT)
+			exception = 1;
+
+		rc = put_guest_u64(vcpu, __LC_MCCK_CODE, inti->mchk.mcic);
+		if (rc == -EFAULT)
+			exception = 1;
+		
+		rc = copy_to_guest(vcpu, __LC_MCK_OLD_PSW,
+				   &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+		if (rc == -EFAULT)
+			exception = 1;
+		
+		rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+				     __LC_MCK_NEW_PSW, sizeof(psw_t));
+		if (rc == -EFAULT)
+			exception = 1;
+		break;
+
 	default:
 		if (is_ioint(inti->type)) {
 			__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
@@ -595,6 +639,61 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 	}
 }
 
+void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
+{
+	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+	struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int;
+	struct kvm_s390_interrupt_info  *n, *inti = NULL;
+	int deliver;
+
+	__reset_intercept_indicators(vcpu);
+	if (atomic_read(&li->active)) {
+		do {
+			deliver = 0;
+			spin_lock_bh(&li->lock);
+			list_for_each_entry_safe(inti, n, &li->list, list) {
+				if ((inti->type == KVM_S390_MCHK) &&
+				    __interrupt_is_deliverable(vcpu, inti)) {
+					list_del(&inti->list);
+					deliver = 1;
+					break;
+				}
+				__set_intercept_indicator(vcpu, inti);
+			}
+			if (list_empty(&li->list))
+				atomic_set(&li->active, 0);
+			spin_unlock_bh(&li->lock);
+			if (deliver) {
+				__do_deliver_interrupt(vcpu, inti);
+				kfree(inti);
+			}
+		} while (deliver);
+	}
+
+	if (atomic_read(&fi->active)) {
+		do {
+			deliver = 0;
+			spin_lock(&fi->lock);
+			list_for_each_entry_safe(inti, n, &fi->list, list) {
+				if ((inti->type == KVM_S390_MCHK) &&
+				    __interrupt_is_deliverable(vcpu, inti)) {
+					list_del(&inti->list);
+					deliver = 1;
+					break;
+				}
+				__set_intercept_indicator(vcpu, inti);
+			}
+			if (list_empty(&fi->list))
+				atomic_set(&fi->active, 0);
+			spin_unlock(&fi->lock);
+			if (deliver) {
+				__do_deliver_interrupt(vcpu, inti);
+				kfree(inti);
+			}
+		} while (deliver);
+	}
+}
+
 int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
 {
 	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
@@ -648,6 +747,12 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 	case KVM_S390_INT_EMERGENCY:
 		kfree(inti);
 		return -EINVAL;
+	case KVM_S390_MCHK:
+		VM_EVENT(kvm, 5, "inject: machine check parm64:%llx",
+			 s390int->parm64);
+		inti->type = s390int->type;
+		inti->mchk.mcic = s390int->parm64;
+		break;
 	default:
 		if (!is_ioint(s390int->type)) {
 			kfree(inti);
@@ -740,6 +845,12 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 		VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type);
 		inti->type = s390int->type;
 		break;
+	case KVM_S390_MCHK:
+		VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx",
+			   s390int->parm64);
+		inti->type = s390int->type;
+		inti->mchk.mcic = s390int->parm64;
+		break;
 	case KVM_S390_INT_VIRTIO:
 	case KVM_S390_INT_SERVICE:
 	default:
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index d75bc5e..b1e1cb6 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -69,6 +69,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer);
 void kvm_s390_tasklet(unsigned long parm);
 void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
+void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu);
 int kvm_s390_inject_vm(struct kvm *kvm,
 		struct kvm_s390_interrupt *s390int);
 int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
@@ -80,6 +81,8 @@ int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_01(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_b9(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index ed256fd..7e7263c 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -176,6 +176,101 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static void handle_new_psw(struct kvm_vcpu *vcpu)
+{
+	/* Check whether the new psw is enabled for machine checks. */
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK)
+		kvm_s390_deliver_pending_machine_checks(vcpu);
+}
+
+int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
+{
+	int base2 = vcpu->arch.sie_block->ipb >> 28;
+	int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
+	u64 addr;
+	u64 uninitialized_var(new_psw);
+
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+		return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+
+	addr = disp2;
+	if (base2)
+		addr += vcpu->run->s.regs.gprs[base2];
+
+	if (addr & 7) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	if (get_guest_u64(vcpu, addr, &new_psw)) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		goto out;
+	}
+
+	if (!(new_psw & 0x0008000000000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	vcpu->arch.sie_block->gpsw.mask = new_psw & 0xfff7ffff80000000;
+	vcpu->arch.sie_block->gpsw.addr = new_psw & 0x000000007fffffff;
+
+	if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe00000000) ||
+	    (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0x000000007ff00000)) ||
+	    ((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	     0x0000000100000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	handle_new_psw(vcpu);
+out:
+	return 0;
+}
+
+static int handle_lpswe(struct kvm_vcpu *vcpu)
+{
+	int base2 = vcpu->arch.sie_block->ipb >> 28;
+	int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
+	u64 addr;
+	u64 new_psw[2];
+
+	addr = disp2;
+	if (base2)
+		addr += vcpu->run->s.regs.gprs[base2];
+	
+	if (addr & 7) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+
+	if (copy_from_guest(vcpu, new_psw, addr, sizeof(*new_psw))) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		goto out;
+	}
+
+	vcpu->arch.sie_block->gpsw.mask = new_psw[0];
+	vcpu->arch.sie_block->gpsw.addr = new_psw[1];
+
+	if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe7fffffff) ||
+	    (((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	      0x0000000010000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0xffffffff80000000)) ||
+	    (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) &&
+	     (vcpu->arch.sie_block->gpsw.addr & 0xfffffffffff00000)) ||
+	    ((vcpu->arch.sie_block->gpsw.mask & 0x0000000110000000) ==
+	     0x0000000100000000)) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		goto out;
+	}
+	
+	handle_new_psw(vcpu);
+out:
+	return 0;
+}
+
 static int handle_stidp(struct kvm_vcpu *vcpu)
 {
 	int base2 = vcpu->arch.sie_block->ipb >> 28;
@@ -309,6 +404,7 @@ static intercept_handler_t priv_handlers[256] = {
 	[0x5f] = handle_chsc,
 	[0x7d] = handle_stsi,
 	[0xb1] = handle_stfl,
+	[0xb2] = handle_lpswe,
 };
 
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
@@ -333,6 +429,43 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 	return -EOPNOTSUPP;
 }
 
+static int handle_epsw(struct kvm_vcpu *vcpu)
+{
+	int reg1, reg2;
+
+	reg1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 24;
+	reg2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16;
+	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000;
+	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
+	if (reg2) {
+		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000;
+		vcpu->run->s.regs.gprs[reg2] |=
+			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffff;
+	}
+	return 0;
+}
+
+static intercept_handler_t b9_handlers[256] = {
+	[0x8d] = handle_epsw,
+};
+
+int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	/* This is handled just as for the B2 instructions. */
+	handler = b9_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
+	if (handler) {
+		if ((handler != handle_epsw) &&
+		    (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE))
+			return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+		else
+			return handler(vcpu);
+	}
+	return -EOPNOTSUPP;
+}
+
 static int handle_tprot(struct kvm_vcpu *vcpu)
 {
 	int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h
index 90fdf85..95fbc1a 100644
--- a/arch/s390/kvm/trace-s390.h
+++ b/arch/s390/kvm/trace-s390.h
@@ -141,13 +141,13 @@ TRACE_EVENT(kvm_s390_inject_vcpu,
  * Trace point for the actual delivery of interrupts.
  */
 TRACE_EVENT(kvm_s390_deliver_interrupt,
-	    TP_PROTO(unsigned int id, __u64 type, __u32 data0, __u64 data1),
+	    TP_PROTO(unsigned int id, __u64 type, __u64 data0, __u64 data1),
 	    TP_ARGS(id, type, data0, data1),
 
 	    TP_STRUCT__entry(
 		    __field(int, id)
 		    __field(__u32, inttype)
-		    __field(__u32, data0)
+		    __field(__u64, data0)
 		    __field(__u64, data1)
 		    ),
 
@@ -159,7 +159,7 @@ TRACE_EVENT(kvm_s390_deliver_interrupt,
 		    ),
 
 	    TP_printk("deliver interrupt (vcpu %d): type:%x (%s) "	\
-		      "data:%08x %016llx",
+		      "data:%08llx %016llx",
 		      __entry->id, __entry->inttype,
 		      __print_symbolic(__entry->inttype, kvm_s390_int_type),
 		      __entry->data0, __entry->data1)
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 31f52c6..e0c3d87 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -388,6 +388,7 @@ 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
-- 
1.7.11.4

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

* [PATCH 3/7] s390/kvm: In-kernel handling of I/O instructions.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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

Explicitely catch all channel I/O related instructions intercepts
in the kernel and set condition code 3 for them.

This paws the way for properly handling these instructions later
on.

Note: This is not architecture compliant (the previous code wasn't
either) since setting cc 3 is not the correct thing to do for some
of these instructions. For Linux guests, however, it still has the
intended effect of stopping css probing.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/kvm/intercept.c | 19 +++++++++++++---
 arch/s390/kvm/kvm-s390.h  |  1 +
 arch/s390/kvm/priv.c      | 56 +++++++++++++++++++++++++++++++++--------------
 3 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index ec1177f..754dc9e 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -33,8 +33,6 @@ static int handle_lctlg(struct kvm_vcpu *vcpu)
 	int reg, rc;
 
 	vcpu->stat.instruction_lctlg++;
-	if ((vcpu->arch.sie_block->ipb & 0xff) != 0x2f)
-		return -EOPNOTSUPP;
 
 	useraddr = disp2;
 	if (base2)
@@ -104,6 +102,21 @@ static int handle_lctl(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static intercept_handler_t eb_handlers[256] = {
+	[0x2f] = handle_lctlg,
+	[0x8a] = kvm_s390_handle_priv_eb,
+};
+
+static int handle_eb(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff];
+	if (handler)
+		return handler(vcpu);
+	return -EOPNOTSUPP;
+}
+
 static intercept_handler_t instruction_handlers[256] = {
 	[0x01] = kvm_s390_handle_01,
 	[0x82] = kvm_s390_handle_lpsw,
@@ -113,7 +126,7 @@ static intercept_handler_t instruction_handlers[256] = {
 	[0xb7] = handle_lctl,
 	[0xb9] = kvm_s390_handle_b9,
 	[0xe5] = kvm_s390_handle_e5,
-	[0xeb] = handle_lctlg,
+	[0xeb] = handle_eb,
 };
 
 static int handle_noop(struct kvm_vcpu *vcpu)
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index b1e1cb6..7f50229 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -83,6 +83,7 @@ int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_01(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 7e7263c..8b79a94 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -135,20 +135,9 @@ static int handle_skey(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-static int handle_stsch(struct kvm_vcpu *vcpu)
+static int handle_io_inst(struct kvm_vcpu *vcpu)
 {
-	vcpu->stat.instruction_stsch++;
-	VCPU_EVENT(vcpu, 4, "%s", "store subchannel - CC3");
-	/* condition code 3 */
-	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
-	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
-	return 0;
-}
-
-static int handle_chsc(struct kvm_vcpu *vcpu)
-{
-	vcpu->stat.instruction_chsc++;
-	VCPU_EVENT(vcpu, 4, "%s", "channel subsystem call - CC3");
+	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
 	/* condition code 3 */
 	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
@@ -392,7 +381,7 @@ out_fail:
 	return 0;
 }
 
-static intercept_handler_t priv_handlers[256] = {
+static intercept_handler_t b2_handlers[256] = {
 	[0x02] = handle_stidp,
 	[0x10] = handle_set_prefix,
 	[0x11] = handle_store_prefix,
@@ -400,8 +389,22 @@ static intercept_handler_t priv_handlers[256] = {
 	[0x29] = handle_skey,
 	[0x2a] = handle_skey,
 	[0x2b] = handle_skey,
-	[0x34] = handle_stsch,
-	[0x5f] = handle_chsc,
+	[0x30] = handle_io_inst,
+	[0x31] = handle_io_inst,
+	[0x32] = handle_io_inst,
+	[0x33] = handle_io_inst,
+	[0x34] = handle_io_inst,
+	[0x35] = handle_io_inst,
+	[0x36] = handle_io_inst,
+	[0x37] = handle_io_inst,
+	[0x38] = handle_io_inst,
+	[0x39] = handle_io_inst,
+	[0x3a] = handle_io_inst,
+	[0x3b] = handle_io_inst,
+	[0x3c] = handle_io_inst,
+	[0x5f] = handle_io_inst,
+	[0x74] = handle_io_inst,
+	[0x76] = handle_io_inst,
 	[0x7d] = handle_stsi,
 	[0xb1] = handle_stfl,
 	[0xb2] = handle_lpswe,
@@ -418,7 +421,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 	 * state bit and (a) handle the instruction or (b) send a code 2
 	 * program check.
 	 * Anything else goes to userspace.*/
-	handler = priv_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
+	handler = b2_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
 	if (handler) {
 		if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 			return kvm_s390_inject_program_int(vcpu,
@@ -447,6 +450,7 @@ static int handle_epsw(struct kvm_vcpu *vcpu)
 
 static intercept_handler_t b9_handlers[256] = {
 	[0x8d] = handle_epsw,
+	[0x9c] = handle_io_inst,
 };
 
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
@@ -466,6 +470,24 @@ int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
 	return -EOPNOTSUPP;
 }
 
+static intercept_handler_t eb_handlers[256] = {
+	[0x8a] = handle_io_inst,
+};
+
+int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	/* All eb instructions that end up here are privileged. */
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+		return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+	handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff];
+	if (handler)
+		return handler(vcpu);
+	return -EOPNOTSUPP;
+}
+
 static int handle_tprot(struct kvm_vcpu *vcpu)
 {
 	int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 3/7] s390/kvm: In-kernel handling of I/O instructions.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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

Explicitely catch all channel I/O related instructions intercepts
in the kernel and set condition code 3 for them.

This paws the way for properly handling these instructions later
on.

Note: This is not architecture compliant (the previous code wasn't
either) since setting cc 3 is not the correct thing to do for some
of these instructions. For Linux guests, however, it still has the
intended effect of stopping css probing.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/kvm/intercept.c | 19 +++++++++++++---
 arch/s390/kvm/kvm-s390.h  |  1 +
 arch/s390/kvm/priv.c      | 56 +++++++++++++++++++++++++++++++++--------------
 3 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index ec1177f..754dc9e 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -33,8 +33,6 @@ static int handle_lctlg(struct kvm_vcpu *vcpu)
 	int reg, rc;
 
 	vcpu->stat.instruction_lctlg++;
-	if ((vcpu->arch.sie_block->ipb & 0xff) != 0x2f)
-		return -EOPNOTSUPP;
 
 	useraddr = disp2;
 	if (base2)
@@ -104,6 +102,21 @@ static int handle_lctl(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static intercept_handler_t eb_handlers[256] = {
+	[0x2f] = handle_lctlg,
+	[0x8a] = kvm_s390_handle_priv_eb,
+};
+
+static int handle_eb(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff];
+	if (handler)
+		return handler(vcpu);
+	return -EOPNOTSUPP;
+}
+
 static intercept_handler_t instruction_handlers[256] = {
 	[0x01] = kvm_s390_handle_01,
 	[0x82] = kvm_s390_handle_lpsw,
@@ -113,7 +126,7 @@ static intercept_handler_t instruction_handlers[256] = {
 	[0xb7] = handle_lctl,
 	[0xb9] = kvm_s390_handle_b9,
 	[0xe5] = kvm_s390_handle_e5,
-	[0xeb] = handle_lctlg,
+	[0xeb] = handle_eb,
 };
 
 static int handle_noop(struct kvm_vcpu *vcpu)
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index b1e1cb6..7f50229 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -83,6 +83,7 @@ int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_01(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 7e7263c..8b79a94 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -135,20 +135,9 @@ static int handle_skey(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-static int handle_stsch(struct kvm_vcpu *vcpu)
+static int handle_io_inst(struct kvm_vcpu *vcpu)
 {
-	vcpu->stat.instruction_stsch++;
-	VCPU_EVENT(vcpu, 4, "%s", "store subchannel - CC3");
-	/* condition code 3 */
-	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
-	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
-	return 0;
-}
-
-static int handle_chsc(struct kvm_vcpu *vcpu)
-{
-	vcpu->stat.instruction_chsc++;
-	VCPU_EVENT(vcpu, 4, "%s", "channel subsystem call - CC3");
+	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
 	/* condition code 3 */
 	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
@@ -392,7 +381,7 @@ out_fail:
 	return 0;
 }
 
-static intercept_handler_t priv_handlers[256] = {
+static intercept_handler_t b2_handlers[256] = {
 	[0x02] = handle_stidp,
 	[0x10] = handle_set_prefix,
 	[0x11] = handle_store_prefix,
@@ -400,8 +389,22 @@ static intercept_handler_t priv_handlers[256] = {
 	[0x29] = handle_skey,
 	[0x2a] = handle_skey,
 	[0x2b] = handle_skey,
-	[0x34] = handle_stsch,
-	[0x5f] = handle_chsc,
+	[0x30] = handle_io_inst,
+	[0x31] = handle_io_inst,
+	[0x32] = handle_io_inst,
+	[0x33] = handle_io_inst,
+	[0x34] = handle_io_inst,
+	[0x35] = handle_io_inst,
+	[0x36] = handle_io_inst,
+	[0x37] = handle_io_inst,
+	[0x38] = handle_io_inst,
+	[0x39] = handle_io_inst,
+	[0x3a] = handle_io_inst,
+	[0x3b] = handle_io_inst,
+	[0x3c] = handle_io_inst,
+	[0x5f] = handle_io_inst,
+	[0x74] = handle_io_inst,
+	[0x76] = handle_io_inst,
 	[0x7d] = handle_stsi,
 	[0xb1] = handle_stfl,
 	[0xb2] = handle_lpswe,
@@ -418,7 +421,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 	 * state bit and (a) handle the instruction or (b) send a code 2
 	 * program check.
 	 * Anything else goes to userspace.*/
-	handler = priv_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
+	handler = b2_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
 	if (handler) {
 		if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 			return kvm_s390_inject_program_int(vcpu,
@@ -447,6 +450,7 @@ static int handle_epsw(struct kvm_vcpu *vcpu)
 
 static intercept_handler_t b9_handlers[256] = {
 	[0x8d] = handle_epsw,
+	[0x9c] = handle_io_inst,
 };
 
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
@@ -466,6 +470,24 @@ int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
 	return -EOPNOTSUPP;
 }
 
+static intercept_handler_t eb_handlers[256] = {
+	[0x8a] = handle_io_inst,
+};
+
+int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu)
+{
+	intercept_handler_t handler;
+
+	/* All eb instructions that end up here are privileged. */
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+		return kvm_s390_inject_program_int(vcpu,
+						   PGM_PRIVILEGED_OPERATION);
+	handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff];
+	if (handler)
+		return handler(vcpu);
+	return -EOPNOTSUPP;
+}
+
 static int handle_tprot(struct kvm_vcpu *vcpu)
 {
 	int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
-- 
1.7.11.4

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

* [PATCH 4/7] s390: Move css limits from drivers/s390/cio/ to include/asm/.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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

There's no need to keep __MAX_SUBCHANNEL and __MAX_SSID private to the
common I/O layer when __MAX_CSSID is usable by everybody.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/cio.h | 2 ++
 drivers/s390/cio/css.h      | 3 ---
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h
index 77043aa..9b6cc82 100644
--- a/arch/s390/include/asm/cio.h
+++ b/arch/s390/include/asm/cio.h
@@ -9,6 +9,8 @@
 
 #define LPM_ANYPATH 0xff
 #define __MAX_CSSID 0
+#define __MAX_SUBCHANNEL 65535
+#define __MAX_SSID 3
 
 #include <asm/scsw.h>
 
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 33bb4d8..4af3dfe 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -112,9 +112,6 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
 extern void css_reiterate_subchannels(void);
 void css_update_ssd_info(struct subchannel *sch);
 
-#define __MAX_SUBCHANNEL 65535
-#define __MAX_SSID 3
-
 struct channel_subsystem {
 	u8 cssid;
 	int valid;
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 4/7] s390: Move css limits from drivers/s390/cio/ to include/asm/.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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

There's no need to keep __MAX_SUBCHANNEL and __MAX_SSID private to the
common I/O layer when __MAX_CSSID is usable by everybody.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/cio.h | 2 ++
 drivers/s390/cio/css.h      | 3 ---
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h
index 77043aa..9b6cc82 100644
--- a/arch/s390/include/asm/cio.h
+++ b/arch/s390/include/asm/cio.h
@@ -9,6 +9,8 @@
 
 #define LPM_ANYPATH 0xff
 #define __MAX_CSSID 0
+#define __MAX_SUBCHANNEL 65535
+#define __MAX_SSID 3
 
 #include <asm/scsw.h>
 
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 33bb4d8..4af3dfe 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -112,9 +112,6 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
 extern void css_reiterate_subchannels(void);
 void css_update_ssd_info(struct subchannel *sch);
 
-#define __MAX_SUBCHANNEL 65535
-#define __MAX_SSID 3
-
 struct channel_subsystem {
 	u8 cssid;
 	int valid;
-- 
1.7.11.4

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

* [PATCH 5/7] s390: Make some css-related structures usable by non-cio code.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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

kvm will need to use some css-related structures (pmcw, schib, orb),
so let's move the definitions from drivers/s390/cio/ to include/asm/.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/orb.h   | 69 +++++++++++++++++++++++++++++++++++++++++++
 arch/s390/include/asm/schib.h | 52 ++++++++++++++++++++++++++++++++
 drivers/s390/cio/cio.h        | 46 +----------------------------
 drivers/s390/cio/io_sch.h     |  2 +-
 drivers/s390/cio/ioasm.h      |  2 +-
 drivers/s390/cio/orb.h        | 67 -----------------------------------------
 6 files changed, 124 insertions(+), 114 deletions(-)
 create mode 100644 arch/s390/include/asm/orb.h
 create mode 100644 arch/s390/include/asm/schib.h
 delete mode 100644 drivers/s390/cio/orb.h

diff --git a/arch/s390/include/asm/orb.h b/arch/s390/include/asm/orb.h
new file mode 100644
index 0000000..ca5d255
--- /dev/null
+++ b/arch/s390/include/asm/orb.h
@@ -0,0 +1,69 @@
+/*
+ * Orb related data structures.
+ *
+ * Copyright IBM Corp. 2007, 2011
+ *
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ *	      Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#ifndef S390_ORB_H
+#define S390_ORB_H
+
+#include <linux/types.h>
+
+/*
+ * Command-mode operation request block
+ */
+struct cmd_orb {
+	u32 intparm;	/* interruption parameter */
+	u32 key:4;	/* flags, like key, suspend control, etc. */
+	u32 spnd:1;	/* suspend control */
+	u32 res1:1;	/* reserved */
+	u32 mod:1;	/* modification control */
+	u32 sync:1;	/* synchronize control */
+	u32 fmt:1;	/* format control */
+	u32 pfch:1;	/* prefetch control */
+	u32 isic:1;	/* initial-status-interruption control */
+	u32 alcc:1;	/* address-limit-checking control */
+	u32 ssic:1;	/* suppress-suspended-interr. control */
+	u32 res2:1;	/* reserved */
+	u32 c64:1;	/* IDAW/QDIO 64 bit control  */
+	u32 i2k:1;	/* IDAW 2/4kB block size control */
+	u32 lpm:8;	/* logical path mask */
+	u32 ils:1;	/* incorrect length */
+	u32 zero:6;	/* reserved zeros */
+	u32 orbx:1;	/* ORB extension control */
+	u32 cpa;	/* channel program address */
+}  __packed __aligned(4);
+
+/*
+ * Transport-mode operation request block
+ */
+struct tm_orb {
+	u32 intparm;
+	u32 key:4;
+	u32:9;
+	u32 b:1;
+	u32:2;
+	u32 lpm:8;
+	u32:7;
+	u32 x:1;
+	u32 tcw;
+	u32 prio:8;
+	u32:8;
+	u32 rsvpgm:8;
+	u32:8;
+	u32:32;
+	u32:32;
+	u32:32;
+	u32:32;
+}  __packed __aligned(4);
+
+union orb {
+	struct cmd_orb cmd;
+	struct tm_orb tm;
+}  __packed __aligned(4);
+
+#endif /* S390_ORB_H */
diff --git a/arch/s390/include/asm/schib.h b/arch/s390/include/asm/schib.h
new file mode 100644
index 0000000..87d7403
--- /dev/null
+++ b/arch/s390/include/asm/schib.h
@@ -0,0 +1,52 @@
+#ifndef _ASM_S390_SCHIB_H_
+#define _ASM_S390_SCHIB_H_
+
+#include <asm/types.h>
+
+#include <asm/scsw.h>
+/*
+ * path management control word
+ */
+struct pmcw {
+	u32 intparm;		/* interruption parameter */
+	u32 qf:1;		/* qdio facility */
+	u32 w:1;
+	u32 isc:3;		/* interruption sublass */
+	u32 res5:3;		/* reserved zeros */
+	u32 ena:1;		/* enabled */
+	u32 lm:2;		/* limit mode */
+	u32 mme:2;		/* measurement-mode enable */
+	u32 mp:1;		/* multipath mode */
+	u32 tf:1;		/* timing facility */
+	u32 dnv:1;		/* device number valid */
+	u32 dev:16;		/* device number */
+	u8  lpm;		/* logical path mask */
+	u8  pnom;		/* path not operational mask */
+	u8  lpum;		/* last path used mask */
+	u8  pim;		/* path installed mask */
+	u16 mbi;		/* measurement-block index */
+	u8  pom;		/* path operational mask */
+	u8  pam;		/* path available mask */
+	u8  chpid[8];		/* CHPID 0-7 (if available) */
+	u32 unused1:8;		/* reserved zeros */
+	u32 st:3;		/* subchannel type */
+	u32 unused2:18;		/* reserved zeros */
+	u32 mbfc:1;		/* measurement block format control */
+	u32 xmwme:1;		/* extended measurement word mode enable */
+	u32 csense:1;		/* concurrent sense; can be enabled ...*/
+				/*  ... per MSCH, however, if facility */
+				/*  ... is not installed, this results */
+				/*  ... in an operand exception.       */
+} __packed;
+
+/*
+ * subchannel information block
+ */
+struct schib {
+	struct pmcw pmcw;	 /* path management control word */
+	union scsw scsw;	 /* subchannel status word */
+	__u64 mba;               /* measurement block address */
+	__u8 mda[4];		 /* model dependent area */
+} __packed __aligned(4);
+
+#endif /* _ASM_S390_SCHIB_H_ */
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 4a1ff5c..b6daa85 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -7,44 +7,10 @@
 #include <asm/chpid.h>
 #include <asm/cio.h>
 #include <asm/fcx.h>
+#include <asm/schib.h>
 #include <asm/schid.h>
 #include "chsc.h"
 
-/*
- * path management control word
- */
-struct pmcw {
-	u32 intparm;		/* interruption parameter */
-	u32 qf	 : 1;		/* qdio facility */
-	u32 w	 : 1;
-	u32 isc  : 3;		/* interruption sublass */
-	u32 res5 : 3;		/* reserved zeros */
-	u32 ena  : 1;		/* enabled */
-	u32 lm	 : 2;		/* limit mode */
-	u32 mme  : 2;		/* measurement-mode enable */
-	u32 mp	 : 1;		/* multipath mode */
-	u32 tf	 : 1;		/* timing facility */
-	u32 dnv  : 1;		/* device number valid */
-	u32 dev  : 16;		/* device number */
-	u8  lpm;		/* logical path mask */
-	u8  pnom;		/* path not operational mask */
-	u8  lpum;		/* last path used mask */
-	u8  pim;		/* path installed mask */
-	u16 mbi;		/* measurement-block index */
-	u8  pom;		/* path operational mask */
-	u8  pam;		/* path available mask */
-	u8  chpid[8];		/* CHPID 0-7 (if available) */
-	u32 unused1 : 8;	/* reserved zeros */
-	u32 st	    : 3;	/* subchannel type */
-	u32 unused2 : 18;	/* reserved zeros */
-	u32 mbfc    : 1;	/* measurement block format control */
-	u32 xmwme   : 1;	/* extended measurement word mode enable */
-	u32 csense  : 1;	/* concurrent sense; can be enabled ...*/
-				/*  ... per MSCH, however, if facility */
-				/*  ... is not installed, this results */
-				/*  ... in an operand exception.       */
-} __attribute__ ((packed));
-
 /* Target SCHIB configuration. */
 struct schib_config {
 	u64 mba;
@@ -59,16 +25,6 @@ struct schib_config {
 } __attribute__ ((packed));
 
 /*
- * subchannel information block
- */
-struct schib {
-	struct pmcw pmcw;	 /* path management control word */
-	union scsw scsw;	 /* subchannel status word */
-	__u64 mba;               /* measurement block address */
-	__u8 mda[4];		 /* model dependent area */
-} __attribute__ ((packed,aligned(4)));
-
-/*
  * When rescheduled, todo's with higher values will overwrite those
  * with lower values.
  */
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 76253df..39c4ef1 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -5,8 +5,8 @@
 #include <asm/schid.h>
 #include <asm/ccwdev.h>
 #include <asm/irq.h>
+#include <asm/orb.h>
 #include "css.h"
-#include "orb.h"
 
 struct io_subchannel_private {
 	union orb orb;		/* operation request block */
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 4d80fc6..e5aac8d 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -2,8 +2,8 @@
 #define S390_CIO_IOASM_H
 
 #include <asm/chpid.h>
+#include <asm/orb.h>
 #include <asm/schid.h>
-#include "orb.h"
 #include "cio.h"
 
 /*
diff --git a/drivers/s390/cio/orb.h b/drivers/s390/cio/orb.h
deleted file mode 100644
index 45a9865..0000000
--- a/drivers/s390/cio/orb.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Orb related data structures.
- *
- * Copyright IBM Corp. 2007, 2011
- *
- * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
- *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
- *	      Sebastian Ott <sebott@linux.vnet.ibm.com>
- */
-
-#ifndef S390_ORB_H
-#define S390_ORB_H
-
-/*
- * Command-mode operation request block
- */
-struct cmd_orb {
-	u32 intparm;	/* interruption parameter */
-	u32 key:4;	/* flags, like key, suspend control, etc. */
-	u32 spnd:1;	/* suspend control */
-	u32 res1:1;	/* reserved */
-	u32 mod:1;	/* modification control */
-	u32 sync:1;	/* synchronize control */
-	u32 fmt:1;	/* format control */
-	u32 pfch:1;	/* prefetch control */
-	u32 isic:1;	/* initial-status-interruption control */
-	u32 alcc:1;	/* address-limit-checking control */
-	u32 ssic:1;	/* suppress-suspended-interr. control */
-	u32 res2:1;	/* reserved */
-	u32 c64:1;	/* IDAW/QDIO 64 bit control  */
-	u32 i2k:1;	/* IDAW 2/4kB block size control */
-	u32 lpm:8;	/* logical path mask */
-	u32 ils:1;	/* incorrect length */
-	u32 zero:6;	/* reserved zeros */
-	u32 orbx:1;	/* ORB extension control */
-	u32 cpa;	/* channel program address */
-}  __packed __aligned(4);
-
-/*
- * Transport-mode operation request block
- */
-struct tm_orb {
-	u32 intparm;
-	u32 key:4;
-	u32:9;
-	u32 b:1;
-	u32:2;
-	u32 lpm:8;
-	u32:7;
-	u32 x:1;
-	u32 tcw;
-	u32 prio:8;
-	u32:8;
-	u32 rsvpgm:8;
-	u32:8;
-	u32:32;
-	u32:32;
-	u32:32;
-	u32:32;
-}  __packed __aligned(4);
-
-union orb {
-	struct cmd_orb cmd;
-	struct tm_orb tm;
-}  __packed __aligned(4);
-
-#endif /* S390_ORB_H */
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 5/7] s390: Make some css-related structures usable by non-cio code.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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

kvm will need to use some css-related structures (pmcw, schib, orb),
so let's move the definitions from drivers/s390/cio/ to include/asm/.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 arch/s390/include/asm/orb.h   | 69 +++++++++++++++++++++++++++++++++++++++++++
 arch/s390/include/asm/schib.h | 52 ++++++++++++++++++++++++++++++++
 drivers/s390/cio/cio.h        | 46 +----------------------------
 drivers/s390/cio/io_sch.h     |  2 +-
 drivers/s390/cio/ioasm.h      |  2 +-
 drivers/s390/cio/orb.h        | 67 -----------------------------------------
 6 files changed, 124 insertions(+), 114 deletions(-)
 create mode 100644 arch/s390/include/asm/orb.h
 create mode 100644 arch/s390/include/asm/schib.h
 delete mode 100644 drivers/s390/cio/orb.h

diff --git a/arch/s390/include/asm/orb.h b/arch/s390/include/asm/orb.h
new file mode 100644
index 0000000..ca5d255
--- /dev/null
+++ b/arch/s390/include/asm/orb.h
@@ -0,0 +1,69 @@
+/*
+ * Orb related data structures.
+ *
+ * Copyright IBM Corp. 2007, 2011
+ *
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ *	      Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#ifndef S390_ORB_H
+#define S390_ORB_H
+
+#include <linux/types.h>
+
+/*
+ * Command-mode operation request block
+ */
+struct cmd_orb {
+	u32 intparm;	/* interruption parameter */
+	u32 key:4;	/* flags, like key, suspend control, etc. */
+	u32 spnd:1;	/* suspend control */
+	u32 res1:1;	/* reserved */
+	u32 mod:1;	/* modification control */
+	u32 sync:1;	/* synchronize control */
+	u32 fmt:1;	/* format control */
+	u32 pfch:1;	/* prefetch control */
+	u32 isic:1;	/* initial-status-interruption control */
+	u32 alcc:1;	/* address-limit-checking control */
+	u32 ssic:1;	/* suppress-suspended-interr. control */
+	u32 res2:1;	/* reserved */
+	u32 c64:1;	/* IDAW/QDIO 64 bit control  */
+	u32 i2k:1;	/* IDAW 2/4kB block size control */
+	u32 lpm:8;	/* logical path mask */
+	u32 ils:1;	/* incorrect length */
+	u32 zero:6;	/* reserved zeros */
+	u32 orbx:1;	/* ORB extension control */
+	u32 cpa;	/* channel program address */
+}  __packed __aligned(4);
+
+/*
+ * Transport-mode operation request block
+ */
+struct tm_orb {
+	u32 intparm;
+	u32 key:4;
+	u32:9;
+	u32 b:1;
+	u32:2;
+	u32 lpm:8;
+	u32:7;
+	u32 x:1;
+	u32 tcw;
+	u32 prio:8;
+	u32:8;
+	u32 rsvpgm:8;
+	u32:8;
+	u32:32;
+	u32:32;
+	u32:32;
+	u32:32;
+}  __packed __aligned(4);
+
+union orb {
+	struct cmd_orb cmd;
+	struct tm_orb tm;
+}  __packed __aligned(4);
+
+#endif /* S390_ORB_H */
diff --git a/arch/s390/include/asm/schib.h b/arch/s390/include/asm/schib.h
new file mode 100644
index 0000000..87d7403
--- /dev/null
+++ b/arch/s390/include/asm/schib.h
@@ -0,0 +1,52 @@
+#ifndef _ASM_S390_SCHIB_H_
+#define _ASM_S390_SCHIB_H_
+
+#include <asm/types.h>
+
+#include <asm/scsw.h>
+/*
+ * path management control word
+ */
+struct pmcw {
+	u32 intparm;		/* interruption parameter */
+	u32 qf:1;		/* qdio facility */
+	u32 w:1;
+	u32 isc:3;		/* interruption sublass */
+	u32 res5:3;		/* reserved zeros */
+	u32 ena:1;		/* enabled */
+	u32 lm:2;		/* limit mode */
+	u32 mme:2;		/* measurement-mode enable */
+	u32 mp:1;		/* multipath mode */
+	u32 tf:1;		/* timing facility */
+	u32 dnv:1;		/* device number valid */
+	u32 dev:16;		/* device number */
+	u8  lpm;		/* logical path mask */
+	u8  pnom;		/* path not operational mask */
+	u8  lpum;		/* last path used mask */
+	u8  pim;		/* path installed mask */
+	u16 mbi;		/* measurement-block index */
+	u8  pom;		/* path operational mask */
+	u8  pam;		/* path available mask */
+	u8  chpid[8];		/* CHPID 0-7 (if available) */
+	u32 unused1:8;		/* reserved zeros */
+	u32 st:3;		/* subchannel type */
+	u32 unused2:18;		/* reserved zeros */
+	u32 mbfc:1;		/* measurement block format control */
+	u32 xmwme:1;		/* extended measurement word mode enable */
+	u32 csense:1;		/* concurrent sense; can be enabled ...*/
+				/*  ... per MSCH, however, if facility */
+				/*  ... is not installed, this results */
+				/*  ... in an operand exception.       */
+} __packed;
+
+/*
+ * subchannel information block
+ */
+struct schib {
+	struct pmcw pmcw;	 /* path management control word */
+	union scsw scsw;	 /* subchannel status word */
+	__u64 mba;               /* measurement block address */
+	__u8 mda[4];		 /* model dependent area */
+} __packed __aligned(4);
+
+#endif /* _ASM_S390_SCHIB_H_ */
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 4a1ff5c..b6daa85 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -7,44 +7,10 @@
 #include <asm/chpid.h>
 #include <asm/cio.h>
 #include <asm/fcx.h>
+#include <asm/schib.h>
 #include <asm/schid.h>
 #include "chsc.h"
 
-/*
- * path management control word
- */
-struct pmcw {
-	u32 intparm;		/* interruption parameter */
-	u32 qf	 : 1;		/* qdio facility */
-	u32 w	 : 1;
-	u32 isc  : 3;		/* interruption sublass */
-	u32 res5 : 3;		/* reserved zeros */
-	u32 ena  : 1;		/* enabled */
-	u32 lm	 : 2;		/* limit mode */
-	u32 mme  : 2;		/* measurement-mode enable */
-	u32 mp	 : 1;		/* multipath mode */
-	u32 tf	 : 1;		/* timing facility */
-	u32 dnv  : 1;		/* device number valid */
-	u32 dev  : 16;		/* device number */
-	u8  lpm;		/* logical path mask */
-	u8  pnom;		/* path not operational mask */
-	u8  lpum;		/* last path used mask */
-	u8  pim;		/* path installed mask */
-	u16 mbi;		/* measurement-block index */
-	u8  pom;		/* path operational mask */
-	u8  pam;		/* path available mask */
-	u8  chpid[8];		/* CHPID 0-7 (if available) */
-	u32 unused1 : 8;	/* reserved zeros */
-	u32 st	    : 3;	/* subchannel type */
-	u32 unused2 : 18;	/* reserved zeros */
-	u32 mbfc    : 1;	/* measurement block format control */
-	u32 xmwme   : 1;	/* extended measurement word mode enable */
-	u32 csense  : 1;	/* concurrent sense; can be enabled ...*/
-				/*  ... per MSCH, however, if facility */
-				/*  ... is not installed, this results */
-				/*  ... in an operand exception.       */
-} __attribute__ ((packed));
-
 /* Target SCHIB configuration. */
 struct schib_config {
 	u64 mba;
@@ -59,16 +25,6 @@ struct schib_config {
 } __attribute__ ((packed));
 
 /*
- * subchannel information block
- */
-struct schib {
-	struct pmcw pmcw;	 /* path management control word */
-	union scsw scsw;	 /* subchannel status word */
-	__u64 mba;               /* measurement block address */
-	__u8 mda[4];		 /* model dependent area */
-} __attribute__ ((packed,aligned(4)));
-
-/*
  * When rescheduled, todo's with higher values will overwrite those
  * with lower values.
  */
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 76253df..39c4ef1 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -5,8 +5,8 @@
 #include <asm/schid.h>
 #include <asm/ccwdev.h>
 #include <asm/irq.h>
+#include <asm/orb.h>
 #include "css.h"
-#include "orb.h"
 
 struct io_subchannel_private {
 	union orb orb;		/* operation request block */
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 4d80fc6..e5aac8d 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -2,8 +2,8 @@
 #define S390_CIO_IOASM_H
 
 #include <asm/chpid.h>
+#include <asm/orb.h>
 #include <asm/schid.h>
-#include "orb.h"
 #include "cio.h"
 
 /*
diff --git a/drivers/s390/cio/orb.h b/drivers/s390/cio/orb.h
deleted file mode 100644
index 45a9865..0000000
--- a/drivers/s390/cio/orb.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Orb related data structures.
- *
- * Copyright IBM Corp. 2007, 2011
- *
- * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
- *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
- *	      Sebastian Ott <sebott@linux.vnet.ibm.com>
- */
-
-#ifndef S390_ORB_H
-#define S390_ORB_H
-
-/*
- * Command-mode operation request block
- */
-struct cmd_orb {
-	u32 intparm;	/* interruption parameter */
-	u32 key:4;	/* flags, like key, suspend control, etc. */
-	u32 spnd:1;	/* suspend control */
-	u32 res1:1;	/* reserved */
-	u32 mod:1;	/* modification control */
-	u32 sync:1;	/* synchronize control */
-	u32 fmt:1;	/* format control */
-	u32 pfch:1;	/* prefetch control */
-	u32 isic:1;	/* initial-status-interruption control */
-	u32 alcc:1;	/* address-limit-checking control */
-	u32 ssic:1;	/* suppress-suspended-interr. control */
-	u32 res2:1;	/* reserved */
-	u32 c64:1;	/* IDAW/QDIO 64 bit control  */
-	u32 i2k:1;	/* IDAW 2/4kB block size control */
-	u32 lpm:8;	/* logical path mask */
-	u32 ils:1;	/* incorrect length */
-	u32 zero:6;	/* reserved zeros */
-	u32 orbx:1;	/* ORB extension control */
-	u32 cpa;	/* channel program address */
-}  __packed __aligned(4);
-
-/*
- * Transport-mode operation request block
- */
-struct tm_orb {
-	u32 intparm;
-	u32 key:4;
-	u32:9;
-	u32 b:1;
-	u32:2;
-	u32 lpm:8;
-	u32:7;
-	u32 x:1;
-	u32 tcw;
-	u32 prio:8;
-	u32:8;
-	u32 rsvpgm:8;
-	u32:8;
-	u32:32;
-	u32:32;
-	u32:32;
-	u32:32;
-}  __packed __aligned(4);
-
-union orb {
-	struct cmd_orb cmd;
-	struct tm_orb tm;
-}  __packed __aligned(4);
-
-#endif /* S390_ORB_H */
-- 
1.7.11.4

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

* [PATCH 6/7] s390/kvm: Base infrastructure for enabling capabilities.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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>
---
 Documentation/virtual/kvm/api.txt |  2 +-
 arch/s390/kvm/kvm-s390.c          | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index bf33aaa..140e7e2 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -909,7 +909,7 @@ documentation when it pops into existence).
 4.37 KVM_ENABLE_CAP
 
 Capability: KVM_CAP_ENABLE_CAP
-Architectures: ppc
+Architectures: ppc, s390
 Type: vcpu ioctl
 Parameters: struct kvm_enable_cap (in)
 Returns: 0 on success; -1 on error
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index e83df7f..4b0681c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -140,6 +140,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 #endif
 	case KVM_CAP_SYNC_REGS:
 	case KVM_CAP_ONE_REG:
+	case KVM_CAP_ENABLE_CAP:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
@@ -807,6 +808,22 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
 	return 0;
 }
 
+static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
+				     struct kvm_enable_cap *cap)
+{
+	int r;
+
+	if (cap->flags)
+		return -EINVAL;
+
+	switch (cap->cap) {
+	default:
+		r = -EINVAL;
+		break;
+	}
+	return r;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -893,6 +910,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 			r = 0;
 		break;
 	}
+	case KVM_ENABLE_CAP:
+	{
+		struct kvm_enable_cap cap;
+		r = -EFAULT;
+		if (copy_from_user(&cap, argp, sizeof(cap)))
+			break;
+		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 6/7] s390/kvm: Base infrastructure for enabling capabilities.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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>
---
 Documentation/virtual/kvm/api.txt |  2 +-
 arch/s390/kvm/kvm-s390.c          | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index bf33aaa..140e7e2 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -909,7 +909,7 @@ documentation when it pops into existence).
 4.37 KVM_ENABLE_CAP
 
 Capability: KVM_CAP_ENABLE_CAP
-Architectures: ppc
+Architectures: ppc, s390
 Type: vcpu ioctl
 Parameters: struct kvm_enable_cap (in)
 Returns: 0 on success; -1 on error
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index e83df7f..4b0681c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -140,6 +140,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 #endif
 	case KVM_CAP_SYNC_REGS:
 	case KVM_CAP_ONE_REG:
+	case KVM_CAP_ENABLE_CAP:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
@@ -807,6 +808,22 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
 	return 0;
 }
 
+static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
+				     struct kvm_enable_cap *cap)
+{
+	int r;
+
+	if (cap->flags)
+		return -EINVAL;
+
+	switch (cap->cap) {
+	default:
+		r = -EINVAL;
+		break;
+	}
+	return r;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -893,6 +910,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 			r = 0;
 		break;
 	}
+	case KVM_ENABLE_CAP:
+	{
+		struct kvm_enable_cap cap;
+		r = -EFAULT;
+		if (copy_from_user(&cap, argp, sizeof(cap)))
+			break;
+		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
-- 
1.7.11.4

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

* [PATCH 7/7] s390/kvm: In-kernel channel subsystem support.
  2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
@ 2012-08-07 14:52   ` Cornelia Huck
  -1 siblings, 0 replies; 16+ 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

Handle most support for channel I/O instructions in the kernel itself.

Only asynchronous functions (such as the start function) need to be
handled by userspace.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 Documentation/virtual/kvm/api.txt | 127 +++++
 arch/s390/include/asm/kvm_host.h  |  48 ++
 arch/s390/kvm/Makefile            |   2 +-
 arch/s390/kvm/css.c               | 945 ++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/intercept.c         |   1 +
 arch/s390/kvm/interrupt.c         | 147 ++++--
 arch/s390/kvm/ioinst.c            | 797 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.c          |  35 ++
 arch/s390/kvm/kvm-s390.h          |  38 ++
 arch/s390/kvm/priv.c              |   7 +-
 arch/s390/kvm/trace-s390.h        |  67 +++
 arch/s390/kvm/trace.h             |  22 +
 include/linux/kvm.h               |  53 +++
 include/trace/events/kvm.h        |   2 +-
 virt/kvm/kvm_main.c               |   3 +-
 15 files changed, 2247 insertions(+), 47 deletions(-)
 create mode 100644 arch/s390/kvm/css.c
 create mode 100644 arch/s390/kvm/ioinst.c

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 140e7e2..678b1d9 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1980,6 +1980,101 @@ return the hash table order in the parameter.  (If the guest is using
 the virtualized real-mode area (VRMA) facility, the kernel will
 re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.)
 
+4.77 KVM_S390_CSS_NOTIFY
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vcpu ioctl
+Parameters: struct kvm_css_notify (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl may be used by userspace to notify the kernel that the control
+blocks for a virtual subchannel should be updated and an I/O interrupt
+injected.
+
+It uses the following parameter block:
+
+/* 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;
+};
+
+cssid, ssid and schid specify the subchannel; scsw, pmcw and sense_data
+are the control blocks to be updated. If the notification is specified
+to be unsolicited, no new interrupt is generated if an interrupt is already
+pending for the subchannel; else an unsolicited interrupt is generated.
+
+The func parameter specifies the asynchronous function that is notified
+for (solicited interrupts only).
+
+This ioctl (like the other interrupt injection ioctls) is executed
+asynchronously to normal vcpu execution.
+
+4.78 KVM_S390_CCW_HOTPLUG
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vm ioctl
+Parameters: struct kvm_s390_sch_info (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl allows userspace to notify the kernel about addition or removal
+of subchannels.
+
+It uses the following data structure:
+
+/* 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;
+};
+
+cssid, ssid, schid and devno describe the subchannel. If the subchannel is
+being added, schib contains the initial subchannel information block for it.
+hotplugged (can only be 0 if add is !0) specifies whether the subchannel has
+been dynamically added or removed (as opposed to the initial machine setup,
+when no channel report words will be created). add specifies whether the
+subchannel is coming or going. virtual signifies whether this is a real or
+a purely virtual subchannel.
+
+4.79 KVM_S390_CHP_HOTPLUG
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vm ioctl
+Parameters: struct kvm_s390_chp_info (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl allows userspace to notify the kernel about addition or removal
+of a channel path.
+
+It uses the following structure:
+
+/* for KVM_S390_CHP_HOTPLUG */
+struct kvm_s390_chp_info {
+	__u8 cssid;
+	__u8 chpid;
+	__u8 type;
+	int add;
+	int virtual;
+};
+
+cssid and chpid specify the channel path, type the channel path type. add
+determines whether the path is coming or going, and virtual signifies
+whether this is a purely virtual or a real channel path.
 
 5. The kvm_run structure
 ------------------------
@@ -2195,6 +2290,24 @@ The possible hypercalls are defined in the Power Architecture Platform
 Requirements (PAPR) document available from www.power.org (free
 developer registration required to access it).
 
+		/* 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;
+
+s390 specific. Used for userspace processing of asynchronous subchannel
+functions.
+
 		/* Fix the size of the union. */
 		char padding[256];
 	};
@@ -2316,3 +2429,17 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
    where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
  - The tsize field of mas1 shall be set to 4K on TLB0, even though the
    hardware ignores this value for TLB0.
+
+6.4 KVM_CAP_S390_CSS_SUPPORT
+
+Architectures: s390
+Parameters: none
+Returns: 0 on success; -1 on error
+
+This capability enables in-kernel support for handling of channel I/O
+instructions like STORE SUBCHANNEL or CHANNEL SUBSYSTEM CALL.
+
+When this capability is enabled, KVM_EXIT_S390_SCH_IO can occur.
+
+When this capability is provided, the KVM_S390_CCW_HOTPLUG,
+KVM_S390_CHP_HOTPLUG and KVM_S390_CSS_NOTIFY ioctls are provided.
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 556774d..03e154b 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -17,13 +17,18 @@
 #include <linux/interrupt.h>
 #include <linux/kvm_host.h>
 #include <asm/debug.h>
+#include <asm/cio.h>
 #include <asm/cpu.h>
+#include <asm/crw.h>
 
 #define KVM_MAX_VCPUS 64
 #define KVM_MEMORY_SLOTS 32
 /* memory slots that does not exposed to userspace */
 #define KVM_PRIVATE_MEM_SLOTS 4
 
+#define VIRTUAL_CSSID 0xfe
+#define KVM_MAX_CSSID 0xfe /* 0xff is reserved */
+
 struct sca_entry {
 	atomic_t scn;
 	__u32	reserved;
@@ -174,6 +179,7 @@ struct kvm_s390_ext_info {
 #define PGM_ADDRESSING           0x05
 #define PGM_SPECIFICATION        0x06
 #define PGM_DATA                 0x07
+#define PGM_OPERAND              0x15
 
 struct kvm_s390_pgm_info {
 	__u16 code;
@@ -208,6 +214,7 @@ struct kvm_s390_interrupt_info {
 		struct kvm_s390_prefix_info prefix;
 		struct kvm_s390_mchk_info mchk;
 	};
+	int nondyn;
 };
 
 /* for local_interrupt.action_flags */
@@ -259,11 +266,52 @@ struct kvm_vm_stat {
 struct kvm_arch_memory_slot {
 };
 
+struct crw_container {
+	struct crw crw;
+	struct list_head sibling;
+};
+
+struct chp_info {
+	u8 in_use;
+	u8 type;
+};
+
+struct kvm_subch {
+	struct mutex lock;
+	u8 cssid;
+	u8 ssid;
+	u16 schid;
+	u16 devno;
+	u8 sense_data[32];
+	struct schib *curr_status;
+	struct kvm_s390_interrupt_info inti;
+};
+
+struct schid_info {
+	struct kvm_subch *schs[__MAX_SUBCHANNEL + 1];
+	unsigned long bm[0];
+};
+
+struct kvm_s390_css_data {
+	int max_cssid;
+	int max_ssid;
+	struct list_head pending_crws;
+	struct kvm_s390_interrupt_info crw_inti;
+	int do_crw_mchk;
+	int crws_lost;
+	struct schid_info *schids[KVM_MAX_CSSID + 1][__MAX_SSID + 1];
+	struct chp_info chpids[KVM_MAX_CSSID + 1][__MAX_CHPID + 1];
+	atomic_t chnmon_active;
+	u64 chnmon_area;
+};
+
 struct kvm_arch{
 	struct sca_block *sca;
 	debug_info_t *dbf;
 	struct kvm_s390_float_interrupt float_int;
 	struct gmap *gmap;
+	int css_support;
+	struct kvm_s390_css_data *css;
 };
 
 extern int sie64a(struct kvm_s390_sie_block *, u64 *);
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index 3975722..afcf71e 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -10,5 +10,5 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o)
 
 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
 
-kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o diag.o
+kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o diag.o ioinst.o css.o
 obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/s390/kvm/css.c b/arch/s390/kvm/css.c
new file mode 100644
index 0000000..36d26ce
--- /dev/null
+++ b/arch/s390/kvm/css.c
@@ -0,0 +1,945 @@
+/*
+ * Virtual channel subsystem support for kvm
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#include <linux/kvm.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <asm/cio.h>
+#include <asm/crw.h>
+#include <asm/schib.h>
+#include <asm/schid.h>
+#include <asm/scsw.h>
+#include "gaccess.h"
+#include "kvm-s390.h"
+#include "trace-s390.h"
+
+static void css_update_chnmon(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	if (!sch->curr_status->pmcw.mme)
+		/* Not active. */
+		return;
+
+	/*
+	 * The only field we want to update (ssch_rsch_count) is conveniently
+	 * located at the beginning of the measurement block.
+	 * For format 0, it is a 16 bit value; for format 1, a 32 bit value.
+	 */
+	if (sch->curr_status->pmcw.mbfc) {
+		/* Format 1, per-subchannel area. */
+		u32 uninitialized_var(count);
+
+		if (get_guest_u32(vcpu, sch->curr_status->mba, &count))
+			return;
+		count++;
+		put_guest_u32(vcpu, sch->curr_status->mba, count);
+	} else {
+		/* Format 0, global area. */
+		u64 target;
+		u16 uninitialized_var(count);
+
+		target = vcpu->kvm->arch.css->chnmon_area +
+			(sch->curr_status->pmcw.mbi << 5);
+		if (get_guest_u16(vcpu, target, &count))
+			return;
+		count++;
+		put_guest_u16(vcpu, target, count);
+	}
+}
+
+static int highest_schid(struct kvm *kvm, u8 cssid, u8 ssid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (!css->schids[cssid][ssid])
+		return 0;
+	return find_last_bit(css->schids[cssid][ssid]->bm,
+			     (__MAX_SUBCHANNEL + 1) / sizeof(unsigned long));
+}
+
+int css_schid_final(struct kvm *kvm, u8 cssid, u8 ssid, u16 schid)
+{
+	return (cssid > KVM_MAX_CSSID ||
+		ssid > __MAX_SSID ||
+		schid > highest_schid(kvm, cssid, ssid)) ? 1 : 0;
+}
+
+static int css_add_virtual_chpid(struct kvm *kvm, u8 cssid, u8 chpid, u8 type)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (cssid > KVM_MAX_CSSID)
+		return -EINVAL;
+
+	if (css->chpids[cssid][chpid].in_use)
+		return -EEXIST;
+
+	css->chpids[cssid][chpid].in_use = 1;
+	css->chpids[cssid][chpid].type = type;
+	return 0;
+}
+
+static int css_remove_virtual_chpid(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (cssid > KVM_MAX_CSSID)
+		return -EINVAL;
+
+	if (!css->chpids[cssid][chpid].in_use)
+		return -EINVAL;
+
+	css->chpids[cssid][chpid].in_use = 0;
+	return 0;
+}
+
+int css_chpid_in_use(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if ((cssid > KVM_MAX_CSSID) || (chpid > __MAX_CHPID))
+		return 0;
+	return css->chpids[cssid][chpid].in_use;
+}
+
+static int css_chpid_type(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if ((cssid > KVM_MAX_CSSID) || (chpid > __MAX_CHPID))
+		return 0;
+	return css->chpids[cssid][chpid].type;
+}
+
+int css_collect_chp_desc(struct kvm *kvm, u8 cssid, u8 f_chpid, u8 l_chpid,
+			 int rfmt, void *buf)
+{
+	int i, desc_size;
+	u32 words[8];
+
+	desc_size = 0;
+	for (i = f_chpid; i <= l_chpid; i++) {
+		if (!css_chpid_in_use(kvm, cssid, i))
+			continue;
+		if (rfmt == 0) {
+			words[0] = 0x80000000 |
+				(css_chpid_type(kvm, cssid, i) << 8) | i;
+			words[1] = 0;
+			memcpy(buf + desc_size, words, 8);
+			desc_size += 8;
+		} else if (rfmt == 1) {
+			words[0] = 0x80000000 |
+				(css_chpid_type(kvm, cssid, i) << 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;
+}
+
+struct kvm_subch *css_find_subch(struct kvm *kvm, u8 m, u8 cssid, u8 ssid,
+				 u16 schid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 real_cssid;
+
+	if (!m) {
+		if (cssid)
+			return NULL;
+		real_cssid = VIRTUAL_CSSID;
+	} else
+		real_cssid = cssid;
+	/* Don't bother for out of range values. */
+	if (css_schid_final(kvm, real_cssid, ssid, schid))
+		return NULL;
+	if (!css->schids[real_cssid][ssid])
+		return NULL;
+	if (!test_bit(schid, css->schids[real_cssid][ssid]->bm))
+		return NULL;
+	return css->schids[real_cssid][ssid]->schs[schid];
+}
+
+void css_queue_crw(struct kvm *kvm, u8 rsc, u8 erc, int chain, u16 rsid)
+{
+	struct crw_container *crw_cont;
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	int ret;
+
+	/* TODO: Maybe use a static crw pool? */
+	crw_cont = kzalloc(sizeof(struct crw_container), GFP_KERNEL);
+
+	mutex_lock(&kvm->lock);
+
+	if (!crw_cont) {
+		css->crws_lost = 1;
+		goto out;
+	}
+	crw_cont->crw.rsc = rsc;
+	crw_cont->crw.erc = erc;
+	crw_cont->crw.chn = chain;
+	crw_cont->crw.rsid = rsid;
+	crw_cont->crw.oflw = css->crws_lost;
+	css->crws_lost = 0;
+
+	list_add_tail(&crw_cont->sibling, &css->pending_crws);
+
+	if (css->do_crw_mchk) {
+		css->do_crw_mchk = 0;
+		ret = kvm_s390_inject_internal(kvm, &css->crw_inti);
+		if (ret)
+			css->do_crw_mchk = 1;
+	}
+out:
+	mutex_unlock(&kvm->lock);
+}
+
+int css_do_stcrw(struct kvm_vcpu *vcpu, u32 cda)
+{
+	struct crw_container *crw_cont;
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+	int ret;
+
+	mutex_lock(&vcpu->kvm->lock);
+	if (list_empty(&css->pending_crws)) {
+		u32 zeroes = 0;
+		/* List was empty, turn crw machine checks on again. */
+		if (copy_to_guest(vcpu, cda, &zeroes, sizeof(struct crw))) {
+			kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+			ret = -EIO;
+			goto out;
+		}
+		css->do_crw_mchk = 1;
+		ret = 1;
+		goto out;
+	}
+
+	crw_cont = container_of(css->pending_crws.next, struct crw_container,
+				sibling);
+	if (copy_to_guest(vcpu, cda, &crw_cont->crw, sizeof(struct crw))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		ret = -EIO;
+		goto out;
+	}
+	list_del(&crw_cont->sibling);
+	kfree(crw_cont);
+	ret = 0;
+out:
+	mutex_unlock(&vcpu->kvm->lock);
+	return ret;
+}
+
+void css_do_schm(struct kvm_vcpu *vcpu, u8 mbk, int update, int dct, u64 mbo)
+{
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+
+	/* dct is currently ignored (not really meaningful for our devices) */
+	/* TODO: Don't ignore mbk. */
+	if (update && !atomic_cmpxchg(&css->chnmon_active, 0, 1))
+		/* Enable measuring. */
+		css->chnmon_area = mbo;
+
+	if (!update && !atomic_cmpxchg(&css->chnmon_active, 1, 0))
+		/* Disable measuring. */
+		css->chnmon_area = 0;
+}
+
+int css_enable_mcsse(struct kvm *kvm)
+{
+	kvm->arch.css->max_cssid = KVM_MAX_CSSID;
+	return 0;
+}
+
+int css_enable_mss(struct kvm *kvm)
+{
+	kvm->arch.css->max_ssid = __MAX_SSID;
+	return 0;
+}
+
+int css_do_tpi(struct kvm_vcpu *vcpu, u32 addr, int lowcore)
+{
+	struct kvm_s390_interrupt_info *inti;
+
+	inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6]);
+	if (inti) {
+		if (!lowcore) {
+			put_guest_u16(vcpu, addr, inti->io.subchannel_id);
+			put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr);
+			put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm);
+		} else {
+			put_guest_u16(vcpu, addr + 184, inti->io.subchannel_id);
+			put_guest_u16(vcpu, addr + 186, inti->io.subchannel_nr);
+			put_guest_u32(vcpu, addr + 188, inti->io.io_int_parm);
+			put_guest_u32(vcpu, addr + 192, inti->io.io_int_word);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int css_do_msch(struct kvm_vcpu *vcpu, struct kvm_subch *sch,
+		struct schib *schib)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!sch->curr_status->pmcw.dnv) {
+		ret = 0;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (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;
+
+	/*
+	 * No need to exit to userspace since it will get the current state
+	 * with the next exit.
+	 */
+	ret = 0;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_xsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!scsw_fctl(s) || (scsw_fctl(s) != SCSW_FCTL_START_FUNC) ||
+	    (!(scsw_actl(s) & (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND |
+			       SCSW_ACTL_SUSPENDED))) ||
+	    (scsw_actl(s) & SCSW_ACTL_SCHACT)) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_stctl(s) != 0) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Cancel the current operation. */
+	s->cmd.fctl &= ~SCSW_FCTL_START_FUNC;
+	s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND |
+			 SCSW_ACTL_SUSPENDED);
+	s->cmd.dstat = 0;
+	s->cmd.cstat = 0;
+	/*
+	 * Let userspace update its state.
+	 * No hardware related structures need to be updated, since userspace
+	 * will get the current state with the next exit.
+	 */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_XSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_csch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* Trigger the clear function. */
+	s->cmd.fctl = SCSW_FCTL_CLEAR_FUNC;
+	s->cmd.actl = SCSW_ACTL_CLEAR_PEND;
+
+	/* Let userspace handle the clear function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_CSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_hsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if ((scsw_stctl(s) == SCSW_STCTL_STATUS_PEND) ||
+	    (scsw_stctl(s) & (SCSW_STCTL_PRIM_STATUS |
+			      SCSW_STCTL_SEC_STATUS |
+			      SCSW_STCTL_ALERT_STATUS))) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Trigger the halt function. */
+	s->cmd.fctl |= SCSW_FCTL_HALT_FUNC;
+	s->cmd.fctl &= ~SCSW_FCTL_START_FUNC;
+	if ((scsw_actl(s) == (SCSW_ACTL_SCHACT | SCSW_ACTL_DEVACT)) &&
+	    (scsw_stctl(s) == SCSW_STCTL_INTER_STATUS)) {
+		s->cmd.stctl &= ~SCSW_STCTL_STATUS_PEND;
+	}
+	s->cmd.actl |= SCSW_ACTL_HALT_PEND;
+
+	/* Let userspace handle the halt function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_HSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_ssch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u64 orb)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (SCSW_FCTL_START_FUNC |
+			    SCSW_FCTL_HALT_FUNC |
+			    SCSW_FCTL_CLEAR_FUNC)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* If monitoring is active, update counter. */
+	if (atomic_read(&vcpu->kvm->arch.css->chnmon_active))
+		css_update_chnmon(vcpu, sch);
+
+	/* Trigger the start function. */
+	s->cmd.fctl |= SCSW_FCTL_START_FUNC;
+	s->cmd.actl |= SCSW_ACTL_START_PEND;
+	s->cmd.pno = 0;
+
+	/* Let userspace handle the start function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_SSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	vcpu->run->s390_sch_io.orb = orb;
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_tsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, uint32_t addr)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	u8 stctl;
+	u8 fctl;
+	u8 actl;
+	struct irb irb;
+	int ret;
+	u32 *esw;
+
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	stctl = scsw_stctl(s);
+	fctl = scsw_fctl(s);
+	actl = scsw_actl(s);
+
+	memset(&irb, 0, sizeof(struct irb));
+
+	/* Copy scsw. */
+	memcpy(&irb.scsw, s, sizeof(union scsw));
+	esw = (u32 *)&irb.esw;
+	if (stctl & SCSW_STCTL_STATUS_PEND) {
+		if (scsw_cstat(s) & (SCHN_STAT_CHN_DATA_CHK |
+				     SCHN_STAT_CHN_CTRL_CHK |
+				     SCHN_STAT_INTF_CTRL_CHK)) {
+			irb.scsw.cmd.eswf = 1;
+			esw[0] = 0x04804000;
+		} else
+			esw[0] = 0x00800000;
+
+		/* If a unit check is pending, copy sense data. */
+		if ((scsw_dstat(s) & DEV_STAT_UNIT_CHECK) && p->csense) {
+			irb.scsw.cmd.eswf = 1;
+			irb.scsw.cmd.ectl = 1;
+			memcpy(irb.ecw, sch->sense_data,
+			       sizeof(sch->sense_data));
+			esw[1] = 0x02000000 | (sizeof(sch->sense_data) << 8);
+		}
+	}
+	if (copy_to_guest(vcpu, addr, &irb, sizeof(struct irb))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		ret = -EIO;
+		goto out;
+	}
+
+	/* Clear conditions on subchannel, if applicable. */
+	if (stctl & SCSW_STCTL_STATUS_PEND) {
+		s->cmd.stctl = 0;
+		if ((stctl != (SCSW_STCTL_INTER_STATUS |
+			       SCSW_STCTL_STATUS_PEND)) ||
+		    ((fctl & SCSW_FCTL_HALT_FUNC) &&
+		     (actl & SCSW_ACTL_SUSPENDED)))
+			s->cmd.fctl = 0;
+
+		if (stctl != (SCSW_STCTL_INTER_STATUS |
+			      SCSW_STCTL_STATUS_PEND)) {
+			s->cmd.pno = 0;
+			s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND |
+					 SCSW_ACTL_START_PEND |
+					 SCSW_ACTL_HALT_PEND |
+					 SCSW_ACTL_CLEAR_PEND |
+					 SCSW_ACTL_SUSPENDED);
+		} else {
+			if ((actl & SCSW_ACTL_SUSPENDED) &&
+			    (fctl & SCSW_FCTL_START_FUNC)) {
+				s->cmd.pno = 0;
+				if (fctl & SCSW_FCTL_HALT_FUNC)
+					s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND |
+							 SCSW_ACTL_START_PEND |
+							 SCSW_ACTL_HALT_PEND |
+							 SCSW_ACTL_CLEAR_PEND |
+							 SCSW_ACTL_SUSPENDED);
+				else
+					s->cmd.actl &= ~SCSW_ACTL_RESUME_PEND;
+			}
+			/* Clear a possible pending I/O interrupt. */
+			if (!list_empty(&sch->inti.list))
+				kvm_s390_dequeue_internal(vcpu->kvm, &sch->inti);
+		}
+		/* Clear pending sense data. */
+		if (p->csense)
+			memset(sch->sense_data, 0 , sizeof(sch->sense_data));
+	}
+
+	/*
+	 * No need to exit to userspace since it will get the current state
+	 * with the next exit.
+	 */
+	ret = (stctl & SCSW_STCTL_STATUS_PEND) ? -EBUSY : 0;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_rsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if ((scsw_fctl(s) != SCSW_FCTL_START_FUNC) ||
+	    (scsw_actl(s) & SCSW_ACTL_RESUME_PEND) ||
+	    (!(scsw_actl(s) & SCSW_ACTL_SUSPENDED))) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* If monitoring is active, update counter. */
+	if (atomic_read(&vcpu->kvm->arch.css->chnmon_active))
+		css_update_chnmon(vcpu, sch);
+
+	s->cmd.actl |= SCSW_ACTL_RESUME_PEND;
+	/* Let userspace handle the start function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_RSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int kvm_arch_vcpu_ioctl_css_notify(struct kvm_vcpu *vcpu,
+				   struct kvm_css_notify *notify)
+{
+	struct kvm_subch *sch;
+	int ret;
+
+	trace_kvm_s390_css_notify(notify->cssid, notify->ssid, notify->schid);
+	/* Userspace always gives us the real cssid. */
+	sch = css_find_subch(vcpu->kvm, 1, notify->cssid, notify->ssid,
+			     notify->schid);
+	if (!sch)
+		return -ENODEV;
+	mutex_lock(&sch->lock);
+	if (notify->unsolicited) {
+		/*
+		 * Userspace wants us to inject an unsolicited interrupt
+		 * iff the subchannel is not status pending.
+		 */
+		if (scsw_stctl(&sch->curr_status->scsw) &
+		    SCSW_STCTL_STATUS_PEND) {
+			ret = 0;
+			goto out;
+		}
+		sch->curr_status->scsw.cmd.stctl =
+			SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND;
+	} else {
+		/*
+		 * First, check whether any I/O instructions have been
+		 * issued in the mean time which would preclude normal
+		 * signalling as requested by the control block. This
+		 * might happen e.g. if the kernel accepted a csch while
+		 * the start function was in progress in user space.
+		 */
+		if (((notify->func == SCH_DO_SSCH) ||
+		     (notify->func == SCH_DO_RSCH)) &&
+		    (scsw_fctl(&sch->curr_status->scsw) != SCSW_FCTL_START_FUNC)) {
+			/*
+			 * xsch, hsch, or csch happened.
+			 * For the xsch case, no interrupt will be generated.
+			 * For the hsch/csch case, another notification will
+			 * happen.
+			 */
+			ret = 0;
+			goto out;
+		}
+		if ((notify->func == SCH_DO_HSCH) &&
+		    (scsw_fctl(&sch->curr_status->scsw) & SCSW_FCTL_CLEAR_FUNC)) {
+			/*
+			 * csch happened, and another notification will come
+			 * in later.
+			 */
+			ret = 0;
+			goto out;
+		}
+		/* Update internal status. */
+		memcpy(&sch->curr_status->scsw, &notify->scsw,
+		       sizeof(notify->scsw));
+		memcpy(&sch->curr_status->pmcw, &notify->pmcw,
+		       sizeof(notify->pmcw));
+		memcpy(sch->sense_data, notify->sense_data,
+		       sizeof(notify->sense_data));
+	}
+
+	/* Inject interrupt. */
+	sch->inti.type = (sch->cssid << 24) | (sch->ssid << 22) |
+		(sch->schid << 16);
+	sch->inti.io.subchannel_id = vcpu->kvm->arch.css->max_cssid > 0 ?
+		(sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1 :
+		(sch->ssid << 1) | 1;
+	sch->inti.io.subchannel_nr = sch->schid;
+	sch->inti.io.io_int_parm = sch->curr_status->pmcw.intparm;
+	sch->inti.io.io_int_word = (0x80 >> sch->curr_status->pmcw.isc) << 24;
+	BUG_ON(!list_empty(&sch->inti.list));
+	mutex_lock(&vcpu->kvm->lock);
+	ret = kvm_s390_inject_internal(vcpu->kvm, &sch->inti);
+	mutex_unlock(&vcpu->kvm->lock);
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+static int css_add_to_store(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	struct schid_info *info;
+	size_t schid_size;
+
+	if (!css->schids[sch->cssid][sch->ssid]) {
+		schid_size = sizeof(struct schid_info) +
+			    __BITOPS_WORDS(__MAX_SUBCHANNEL + 1) *
+			sizeof(unsigned long);
+		css->schids[sch->cssid][sch->ssid] = vmalloc(schid_size);
+		if (!css->schids[sch->cssid][sch->ssid])
+			return -ENOMEM;
+		memset(css->schids[sch->cssid][sch->ssid], 0, schid_size);
+	}
+	info = css->schids[sch->cssid][sch->ssid];
+	info->schs[sch->schid] = sch;
+	set_bit(sch->schid, info->bm);
+
+	return 0;
+}
+
+static int css_remove_from_store(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	struct schid_info *info;
+
+	info = css->schids[sch->cssid][sch->ssid];
+	if (!info)
+		return -EINVAL;
+	info->schs[sch->schid] = NULL;
+	clear_bit(sch->schid, info->bm);
+
+	return 0;
+}
+
+static int css_add_subchannel(struct kvm *kvm,
+			      struct kvm_s390_sch_info *sch_info)
+{
+	struct kvm_subch *sch;
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 guest_cssid;
+	bool no_crw;
+
+	/* Generate subchannel structure. */
+	sch = kzalloc(sizeof(*sch), GFP_KERNEL);
+	if (!sch)
+		return -ENOMEM;
+	sch->curr_status = kzalloc(sizeof(*sch->curr_status), GFP_KERNEL);
+	if (!sch->curr_status) {
+		kfree(sch);
+		return -ENOMEM;
+	}
+	mutex_init(&sch->lock);
+	sch->cssid = sch_info->cssid;
+	sch->ssid = sch_info->ssid;
+	sch->schid = sch_info->schid;
+	sch->devno = sch_info->devno;
+	memcpy(sch->curr_status, &sch_info->schib, sizeof(*sch->curr_status));
+	INIT_LIST_HEAD(&sch->inti.list);
+	sch->inti.nondyn = 1;
+	/* Add subchannel to store. */
+	css_add_to_store(kvm, sch);
+	if (!sch_info->hotplugged)
+		goto out;
+	/*
+	 * Generate add ccw.
+	 *
+	 * Only notify for higher subchannel sets/channel subsystems if the
+	 * guest has enabled it.
+	 */
+	guest_cssid = ((css->max_cssid == 0) && (sch->cssid == VIRTUAL_CSSID)) ?
+		0 : sch->cssid;
+	no_crw = (sch->ssid > css->max_ssid) ||
+		(guest_cssid > css->max_cssid) ||
+		((css->max_cssid == 0) && (sch->cssid != VIRTUAL_CSSID));
+	if (!no_crw) {
+		css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM,
+			      ((css->max_ssid > 0) || (css->max_cssid > 0)) ?
+			      1 : 0, sch->schid);
+		if ((css->max_ssid > 0) || (css->max_cssid > 0))
+			css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM, 0,
+				      (guest_cssid << 8) | (sch->ssid << 4));
+	}
+out:
+	return 0;
+}
+
+static int css_remove_subchannel(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 guest_cssid;
+	bool no_crw;
+
+	/* Make subchannel inaccessible. */
+	mutex_lock(&sch->lock);
+	/* Clear a possible pending I/O interrupt. */
+	if (!list_empty(&sch->inti.list))
+		kvm_s390_dequeue_internal(kvm, &sch->inti);
+	css_remove_from_store(kvm, sch);
+	mutex_unlock(&sch->lock);
+	/*
+	 * Generate removal ccw.
+	 *
+	 * Only notify for higher subchannel sets/channel subsystems if the
+	 * guest has enabled it.
+	 */
+	guest_cssid = ((css->max_cssid == 0) && (sch->cssid == VIRTUAL_CSSID)) ?
+		0 : sch->cssid;
+	no_crw = (sch->ssid > css->max_ssid) ||
+		(guest_cssid > css->max_cssid) ||
+		((css->max_cssid == 0) && (sch->cssid != VIRTUAL_CSSID));
+	if (!no_crw) {
+		css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM,
+			      ((css->max_ssid > 0) || (css->max_cssid > 0)) ?
+			      1 : 0, sch->schid);
+		if ((css->max_ssid > 0) || (css->max_cssid > 0))
+			css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM, 0,
+				      (guest_cssid << 8) | (sch->ssid << 4));
+	}
+	kfree(sch);
+	return 0;
+}
+
+int kvm_s390_process_ccw_hotplug(struct kvm *kvm,
+				 struct kvm_s390_sch_info *sch_info)
+{
+	struct kvm_subch *sch;
+
+	trace_kvm_s390_ccw_hotplug(sch_info->cssid, sch_info->ssid,
+				   sch_info->schid, sch_info->add);
+	/* We currently support only virtual subchannels. */
+	if (!sch_info->virtual)
+		return -EINVAL;
+
+	/* Virtual subchannels must be in the virtual css. */
+	if (sch_info->virtual && (sch_info->cssid != VIRTUAL_CSSID))
+		return -EINVAL;
+	/* Userspace always notifies with the real cssid. */
+	sch = css_find_subch(kvm, 1, sch_info->cssid, sch_info->ssid,
+			     sch_info->schid);
+	if (sch_info->add) {
+		/* New device. */
+		if (sch)
+			return -EINVAL;
+		return css_add_subchannel(kvm, sch_info);
+	} else {
+		/* Device gone. */
+		if (!sch)
+			return -EINVAL;
+		return css_remove_subchannel(kvm, sch);
+	}
+}
+
+int kvm_s390_process_chp_hotplug(struct kvm *kvm,
+				 struct kvm_s390_chp_info *chp_info)
+{
+	if (!chp_info->virtual)
+		/* Not supported for now. */
+		return -EINVAL;
+
+	/* Virtual channel paths must be in the virtual css. */
+	if (chp_info->virtual && (chp_info->cssid != VIRTUAL_CSSID))
+		return -EINVAL;
+	if (chp_info->add)
+		return css_add_virtual_chpid(kvm, chp_info->cssid,
+					     chp_info->chpid, chp_info->type);
+	else
+		return css_remove_virtual_chpid(kvm, chp_info->cssid,
+						chp_info->chpid);
+}
+
+int kvm_s390_enable_css(struct kvm *kvm)
+{
+	if (kvm->arch.css_support)
+		return 0;
+
+	kvm->arch.css = kzalloc(sizeof(*kvm->arch.css), GFP_KERNEL);
+	if (!kvm->arch.css)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&kvm->arch.css->pending_crws);
+	INIT_LIST_HEAD(&kvm->arch.css->crw_inti.list);
+	kvm->arch.css->crw_inti.type = KVM_S390_MCHK;
+	kvm->arch.css->crw_inti.mchk.mcic = 0x00400f1d40330000;
+	kvm->arch.css->crw_inti.mchk.cr14 = 1 << 28;
+	kvm->arch.css->crw_inti.nondyn = 1;
+	kvm->arch.css->do_crw_mchk = 1;
+	atomic_set(&kvm->arch.css->chnmon_active, 0);
+	kvm->arch.css_support = 1;
+	trace_kvm_s390_enable_kernel_css(kvm);
+	return 0;
+}
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index 754dc9e..9ab2efd 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -273,6 +273,7 @@ static const intercept_handler_t intercept_funcs[] = {
 	[0x0C >> 2] = handle_instruction_and_prog,
 	[0x10 >> 2] = handle_noop,
 	[0x14 >> 2] = handle_noop,
+	[0x18 >> 2] = handle_noop,
 	[0x1C >> 2] = kvm_s390_handle_wait,
 	[0x20 >> 2] = handle_validity,
 	[0x28 >> 2] = handle_stop,
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index edc065f..072828b 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -370,6 +370,7 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 		
 		rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
 				     __LC_MCK_NEW_PSW, sizeof(psw_t));
+
 		if (rc == -EFAULT)
 			exception = 1;
 		break;
@@ -596,7 +597,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_lock_bh(&li->lock);
 			list_for_each_entry_safe(inti, n, &li->list, list) {
 				if (__interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -607,7 +608,8 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_unlock_bh(&li->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -622,7 +624,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_lock(&fi->lock);
 			list_for_each_entry_safe(inti, n, &fi->list, list) {
 				if (__interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -633,7 +635,8 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_unlock(&fi->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -654,7 +657,7 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			list_for_each_entry_safe(inti, n, &li->list, list) {
 				if ((inti->type == KVM_S390_MCHK) &&
 				    __interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -665,7 +668,8 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			spin_unlock_bh(&li->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -677,7 +681,7 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			list_for_each_entry_safe(inti, n, &fi->list, list) {
 				if ((inti->type == KVM_S390_MCHK) &&
 				    __interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -688,7 +692,8 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			spin_unlock(&fi->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -716,14 +721,100 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
 	return 0;
 }
 
-int kvm_s390_inject_vm(struct kvm *kvm,
-		       struct kvm_s390_interrupt *s390int)
+int kvm_s390_inject_internal(struct kvm *kvm,
+			     struct kvm_s390_interrupt_info *inti)
 {
 	struct kvm_s390_local_interrupt *li;
 	struct kvm_s390_float_interrupt *fi;
-	struct kvm_s390_interrupt_info *inti, *iter;
+	struct kvm_s390_interrupt_info *iter;
 	int sigcpu;
 
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	if (!is_ioint(inti->type))
+		list_add_tail(&inti->list, &fi->list);
+	else {
+		/* Keep I/O interrupts sorted in isc order. */
+		list_for_each_entry(iter, &fi->list, list) {
+			if (!is_ioint(iter->type))
+				continue;
+			if (iter->io.io_int_word <= inti->io.io_int_word)
+				continue;
+			break;
+		}
+		list_add_tail(&inti->list, &iter->list);
+	}
+	atomic_set(&fi->active, 1);
+	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
+	if (sigcpu == KVM_MAX_VCPUS) {
+		do {
+			sigcpu = fi->next_rr_cpu++;
+			if (sigcpu == KVM_MAX_VCPUS)
+				sigcpu = fi->next_rr_cpu = 0;
+		} while (fi->local_int[sigcpu] == NULL);
+	}
+	li = fi->local_int[sigcpu];
+	spin_lock_bh(&li->lock);
+	atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+	if (waitqueue_active(&li->wq))
+		wake_up_interruptible(&li->wq);
+	spin_unlock_bh(&li->lock);
+	spin_unlock(&fi->lock);
+	return 0;
+}
+
+int kvm_s390_dequeue_internal(struct kvm *kvm,
+			      struct kvm_s390_interrupt_info *inti)
+{
+	struct kvm_s390_float_interrupt *fi;
+
+	if (!inti)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	list_del_init(&inti->list);
+	if (list_empty(&fi->list))
+		atomic_set(&fi->active, 0);
+	spin_unlock(&fi->lock);
+	mutex_unlock(&kvm->lock);
+	return 0;
+}
+
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, u64 cr6)
+{
+	struct kvm_s390_float_interrupt *fi;
+	struct kvm_s390_interrupt_info *inti, *iter;
+
+	mutex_lock(&kvm->lock);
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	inti = NULL;
+	list_for_each_entry(iter, &fi->list, list) {
+		if (!is_ioint(iter->type))
+			continue;
+		if ((cr6 & iter->io.io_int_word) == 0)
+			continue;
+		inti = iter;
+		break;
+	}
+	if (inti)
+		list_del_init(&inti->list);
+	if (list_empty(&fi->list))
+		atomic_set(&fi->active, 0);
+	spin_unlock(&fi->lock);
+	mutex_unlock(&kvm->lock);
+	return inti;
+}
+
+
+int kvm_s390_inject_vm(struct kvm *kvm,
+		       struct kvm_s390_interrupt *s390int)
+{
+	struct kvm_s390_interrupt_info *inti;
+	int rc;
+
 	inti = kzalloc(sizeof(*inti), GFP_KERNEL);
 	if (!inti)
 		return -ENOMEM;
@@ -776,39 +867,9 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 				 2);
 
 	mutex_lock(&kvm->lock);
-	fi = &kvm->arch.float_int;
-	spin_lock(&fi->lock);
-	if (!is_ioint(inti->type))
-		list_add_tail(&inti->list, &fi->list);
-	else {
-		/* Keep I/O interrupts sorted in isc order. */
-		list_for_each_entry(iter, &fi->list, list) {
-			if (!is_ioint(iter->type))
-				continue;
-			if (iter->io.io_int_word <= inti->io.io_int_word)
-				continue;
-			break;
-		}
-		list_add_tail(&inti->list, &iter->list);
-	}
-	atomic_set(&fi->active, 1);
-	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
-	if (sigcpu == KVM_MAX_VCPUS) {
-		do {
-			sigcpu = fi->next_rr_cpu++;
-			if (sigcpu == KVM_MAX_VCPUS)
-				sigcpu = fi->next_rr_cpu = 0;
-		} while (fi->local_int[sigcpu] == NULL);
-	}
-	li = fi->local_int[sigcpu];
-	spin_lock_bh(&li->lock);
-	atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
-	if (waitqueue_active(&li->wq))
-		wake_up_interruptible(&li->wq);
-	spin_unlock_bh(&li->lock);
-	spin_unlock(&fi->lock);
+	rc = kvm_s390_inject_internal(kvm, inti);
 	mutex_unlock(&kvm->lock);
-	return 0;
+	return rc;
 }
 
 int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
diff --git a/arch/s390/kvm/ioinst.c b/arch/s390/kvm/ioinst.c
new file mode 100644
index 0000000..29c4629
--- /dev/null
+++ b/arch/s390/kvm/ioinst.c
@@ -0,0 +1,797 @@
+/*
+ * Handling of channel I/O instructions for kvm
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#include <linux/kvm.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <asm/cio.h>
+#include <asm/crw.h>
+#include <asm/orb.h>
+#include <asm/schib.h>
+#include <asm/schid.h>
+#include <asm/scsw.h>
+#include "kvm-s390.h"
+#include "gaccess.h"
+#include "trace.h"
+
+#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_XSCH                       0x76
+
+static int ioinst_disassemble_sch_ident(u32 value, int *m, int *cssid, int *ssid,
+					int *schid)
+{
+	if (!(value & 0x00010000))
+		return -EINVAL;
+
+	if (!(value & 0x00080000)) {
+		if (value & 0xff000000)
+			return -EINVAL;
+		*m = 0;
+		*cssid = 0;
+	} else {
+		*m = 1;
+		*cssid = (value & 0xff000000) >> 24;
+	}
+	*ssid = (value & 0x00060000) >> 17;
+	*schid = value & 0x0000ffff;
+	return 0;
+}
+
+static int ioinst_handle_xsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("xsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_xsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_csch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("csch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_csch(vcpu, sch);
+
+	if (ret == -ENODEV) {
+		*cc = 3;
+	} else {
+		*cc = 0;
+	}
+	return ret;
+}
+
+static int ioinst_handle_hsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("hsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_hsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_schib_valid(struct schib *schib)
+{
+	if (schib->pmcw.res5 != 0)
+		return 0;
+
+	if ((schib->pmcw.unused1 != 0) || (schib->pmcw.unused2 != 0))
+		return 0;
+
+	/* Disallow extended measurements for now. */
+	if (schib->pmcw.xmwme)
+		return 0;
+
+	return 1;
+}
+
+static int ioinst_handle_msch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	struct schib schib;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (copy_from_guest(vcpu, &schib, addr, sizeof(struct schib))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	if (!ioinst_schib_valid(&schib)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("msch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_msch(vcpu, sch, &schib);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case 0:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_orb_valid(union orb *orb)
+{
+	if (orb->cmd.res2 != 0)
+		return 0;
+
+	if (orb->cmd.zero != 0)
+		return 0;
+
+	if ((orb->cmd.cpa & 0x80000000) != 0)
+		return 0;
+
+	return 1;
+}
+
+static int ioinst_handle_ssch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	union orb orb;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (copy_from_guest(vcpu, &orb, addr, sizeof(union orb))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	if (!ioinst_orb_valid(&orb)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("ssch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_ssch(vcpu, sch, addr);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_stcrw(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	int ret;
+	u32 addr;
+
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	ret = css_do_stcrw(vcpu, addr);
+	/* 0 - crw stored, 1 - zeroes stored */
+	if (ret >= 0) {
+		*cc = ret;
+		ret = 0;
+	}
+	return 0;
+}
+
+static int ioinst_handle_stsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	u32 addr;
+	int ret;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (addr & 3) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("stsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch) {
+		ret = copy_to_guest(vcpu, addr, sch->curr_status,
+				    sizeof(*sch->curr_status));
+		if (ret < 0)
+			kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		else
+			*cc = 0;
+	} else {
+		if (css_schid_final(vcpu->kvm, m ? cssid :
+				    (cssid ? cssid : VIRTUAL_CSSID),
+				    ssid, schid)) {
+			*cc = 3; /* No more subchannels in this css/ss */
+			ret = 0;
+		} else {
+			struct schib schib;
+
+			/* Store an empty schib. */
+			memset(&schib, 0, sizeof(struct schib));
+			ret = copy_to_guest(vcpu, addr, &schib, sizeof(schib));
+			if (ret < 0)
+				kvm_s390_inject_program_int(vcpu,
+							    PGM_ADDRESSING);
+			else
+				*cc = 0;
+		}
+	}
+	return ret;
+}
+
+static int ioinst_handle_tsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	trace_kvm_s390_handle_ioinst("tsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_tsch(vcpu, sch, addr);
+	/* 0 - status pending, 1 - not status pending */
+	switch (ret) {
+	case -EBUSY:
+		*cc = 0;
+		break;
+	case 0:
+		*cc = 1;
+		break;
+	case -ENODEV:
+		*cc = 3;
+		break;
+	}
+	return ret;
+}
+
+struct chsc_req {
+	u16 len;
+	u16 command;
+	u32 param0;
+	u32 param1;
+	u32 param2;
+} __attribute__((packed));
+
+struct chsc_resp {
+	u16 len;
+	u16 code;
+	u32 param;
+	char data[0];
+} __attribute__((packed));
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+static void ioinst_handle_chsc_scpd(struct kvm *kvm, struct chsc_req *req,
+				    struct chsc_resp *res)
+{
+	u16 resp_code;
+	int rfmt;
+	u16 cssid;
+	u8 f_chpid, l_chpid;
+	int desc_size;
+
+	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;
+	if (cssid != 0)
+		if (!(req->param0 & 0x20000000) || (cssid != VIRTUAL_CSSID)) {
+			resp_code = 0x0008;
+			goto out_err;
+		}
+
+	if ((cssid == 0) && (!(req->param0 & 0x20000000)))
+		cssid = VIRTUAL_CSSID;
+
+	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(kvm, 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;
+}
+
+/* For now, always the same characteristics. */
+static u32 general_chars[510] = { 0x03000000, 0x00059000, 0, };
+static u32 chsc_chars[508] = { 0x40000000, 0x00040000, 0, };
+
+static void ioinst_handle_chsc_scsc(struct kvm *kvm, struct chsc_req *req,
+				    struct chsc_resp *res)
+{
+	u8 cssid;
+	u16 resp_code;
+
+	if (req->param0 & 0x000f0000) {
+		resp_code = 0x0007;
+		goto out_err;
+	}
+	cssid = (req->param0 & 0x0000ff00) >> 8;
+	if (cssid != 0)
+		if (!(req->param0 & 0x20000000) || (cssid != VIRTUAL_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;
+
+	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_SC_MCSSE 0x0
+#define CHSC_SDA_SC_MSS 0x2
+
+static void ioinst_handle_chsc_sda(struct kvm *kvm, struct chsc_req *req,
+				   struct chsc_resp *res)
+{
+	u16 resp_code = 0x0001;
+	u16 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_SC_MCSSE:
+		ret = css_enable_mcsse(kvm);
+		if (ret == -EINVAL) {
+			resp_code = 0x0101;
+			goto out;
+		}
+		break;
+	case CHSC_SDA_SC_MSS:
+		ret = css_enable_mss(kvm);
+		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;
+}
+
+static int ioinst_handle_chsc(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	struct chsc_req *req;
+	struct chsc_resp *res;
+	u64 addr;
+	int reg;
+	int ret;
+
+	reg = (ipb >> 20) & 0x00f;
+	addr = vcpu->run->s.regs.gprs[reg];
+	if (addr & 0x0000000000000fff) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	req = (struct chsc_req *)get_zeroed_page(GFP_KERNEL);
+	if (!req)
+		return -EFAULT;
+	if (copy_from_guest(vcpu, req, addr, sizeof(*req))) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		return -EFAULT;
+	}
+	if ((req->len & 3) || (req->len < 16) || (req->len > 4088)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	res = (struct chsc_resp *)((unsigned long)req + req->len);
+	switch (req->command) {
+	case CHSC_SCSC:
+		ioinst_handle_chsc_scsc(vcpu->kvm, req, res);
+		break;
+	case CHSC_SCPD:
+		ioinst_handle_chsc_scpd(vcpu->kvm, req, res);
+		break;
+	case CHSC_SDA:
+		ioinst_handle_chsc_sda(vcpu->kvm, req, res);
+		break;
+	default:
+		ioinst_handle_chsc_unimplemented(res);
+		break;
+	}
+	ret = copy_to_guest(vcpu, addr + req->len, res, res->len);
+	if (ret < 0)
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+	else
+		*cc = 0;
+	free_page((unsigned long)req);
+	return ret;
+}
+
+static int ioinst_handle_tpi(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	u32 addr;
+	int lowcore;
+
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	lowcore = addr ? 0 : 1;
+	*cc = css_do_tpi(vcpu, addr, lowcore);
+	return 0;
+}
+
+static int ioinst_handle_schm(struct kvm_vcpu *vcpu, u64 reg1, u64 reg2,
+			      u32 ipb)
+{
+	u8 mbk;
+	int update;
+	int dct;
+
+	if (reg1 & 0x000000000ffffffc) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	mbk = (reg1 & 0x00000000f0000000) >> 28;
+	update = (reg1 & 0x0000000000000002) >> 1;
+	dct = reg1 & 0x0000000000000001;
+
+	if (update && (reg2 & 0x0000000000000fff)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	css_do_schm(vcpu, mbk, update, dct, update ? reg2 : 0);
+
+	return 0;
+}
+
+static int ioinst_handle_rsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("rsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_rsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EINVAL:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+
+}
+
+static int ioinst_handle_rchp(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	u8 cssid;
+	u8 chpid;
+	int ret;
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+
+	if (reg1 & 0xff00ff00) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	cssid = (reg1 >> 16) & 0xff;
+	chpid = reg1 & 0xff;
+
+	if (cssid > css->max_cssid) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		ret = -EIO;
+	} else if (!css_chpid_in_use(vcpu->kvm, cssid, chpid)) {
+		ret = 0;
+		*cc = 3;
+	} else {
+		/*
+		 * Since we only support virtual (i.e. not real) channel paths,
+		 * there's nothing left for us to do save signaling success.
+		 */
+		css_queue_crw(vcpu->kvm, CRW_RSC_CPATH, CRW_ERC_INIT,
+			      css->max_cssid > 0 ? 1 : 0, chpid);
+		if (css->max_cssid > 0)
+			css_queue_crw(vcpu->kvm, CRW_RSC_CPATH, CRW_ERC_INIT, 0,
+				      cssid << 8);
+		ret = 0;
+		*cc = 0;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_sal(struct kvm_vcpu *vcpu, u64 reg1)
+{
+	/* We do not provide address limit checking, so let's suppress it. */
+	if (reg1 & 0x000000008000ffff) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	return 0;
+}
+
+int kvm_css_instruction(struct kvm_vcpu *vcpu)
+{
+	int ret;
+	int cc;
+	int no_cc = 0;
+
+	if ((vcpu->arch.sie_block->ipa & 0xff00) != 0xb200)
+		/* Not handled for now. */
+		return -EOPNOTSUPP;
+
+	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
+	case PRIV_XSCH:
+		ret = ioinst_handle_xsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_CSCH:
+		ret = ioinst_handle_csch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_HSCH:
+		ret = ioinst_handle_hsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_MSCH:
+		ret = ioinst_handle_msch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_SSCH:
+		ret = ioinst_handle_ssch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_STCRW:
+		ret = ioinst_handle_stcrw(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_STSCH:
+		ret = ioinst_handle_stsch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					  vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_TSCH:
+		ret = ioinst_handle_tsch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_CHSC:
+		ret = ioinst_handle_chsc(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_TPI:
+		ret = ioinst_handle_tpi(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_SCHM:
+		no_cc = 1;
+		ret = ioinst_handle_schm(vcpu, vcpu->run->s.regs.gprs[1],
+					 vcpu->run->s.regs.gprs[2],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_RSCH:
+		ret = ioinst_handle_rsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_RCHP:
+		ret = ioinst_handle_rchp(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_STCPS:
+		/* We do not provide this instruction, it is suppressed. */
+		no_cc = 1;
+		ret = 0;
+		break;
+	case PRIV_SAL:
+		no_cc = 1;
+		ret = ioinst_handle_sal(vcpu, vcpu->run->s.regs.gprs[1]);
+		break;
+	default:
+		/* Give user space a go at this. */
+		return -EOPNOTSUPP;
+	}
+	if ((ret != -EFAULT) && (ret != -EIO) && (ret != -EREMOTE))
+		ret = 0;
+
+	if ((!ret || (ret == -EREMOTE)) && !no_cc) {
+		vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
+		vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44;
+	}
+
+	return (ret == -EREMOTE) ? ret : 0;
+}
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 4b0681c..5ee9e5e 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -141,6 +141,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_SYNC_REGS:
 	case KVM_CAP_ONE_REG:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_S390_CSS_SUPPORT:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
@@ -183,6 +184,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		r = kvm_s390_inject_vm(kvm, &s390int);
 		break;
 	}
+	case KVM_S390_CCW_HOTPLUG: {
+		struct kvm_s390_sch_info sch_info;
+
+		r = -EFAULT;
+		if (copy_from_user(&sch_info, argp, sizeof(sch_info)))
+			break;
+		r = kvm_s390_process_ccw_hotplug(kvm, &sch_info);
+		break;
+	}
+	case KVM_S390_CHP_HOTPLUG: {
+		struct kvm_s390_chp_info chp_info;
+
+		r = -EFAULT;
+		if (copy_from_user(&chp_info, argp, sizeof(chp_info)))
+			break;
+		r = kvm_s390_process_chp_hotplug(kvm, &chp_info);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
@@ -235,6 +254,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 		if (!kvm->arch.gmap)
 			goto out_nogmap;
 	}
+
+	kvm->arch.css_support = 0;
+
 	return 0;
 out_nogmap:
 	debug_unregister(kvm->arch.dbf);
@@ -657,6 +679,7 @@ rerun_vcpu:
 	case KVM_EXIT_INTR:
 	case KVM_EXIT_S390_RESET:
 	case KVM_EXIT_S390_UCONTROL:
+	case KVM_EXIT_S390_SCH_IO:
 		break;
 	default:
 		BUG();
@@ -817,6 +840,9 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_S390_CSS_SUPPORT:
+		r = kvm_s390_enable_css(vcpu->kvm);
+		break;
 	default:
 		r = -EINVAL;
 		break;
@@ -919,6 +945,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
 		break;
 	}
+	case KVM_S390_CSS_NOTIFY:
+	{
+		struct kvm_css_notify notify;
+		r = -EFAULT;
+		if (copy_from_user(&notify, argp, sizeof(notify)))
+			break;
+		r = kvm_arch_vcpu_ioctl_css_notify(vcpu, &notify);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 7f50229..8bf25f9 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -76,6 +76,11 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 		struct kvm_s390_interrupt *s390int);
 int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
 int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
+int kvm_s390_inject_internal(struct kvm *kvm,
+			     struct kvm_s390_interrupt_info *inti);
+int kvm_s390_dequeue_internal(struct kvm *kvm,
+			      struct kvm_s390_interrupt_info *inti);
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, u64 cr6);
 
 /* implemented in priv.c */
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
@@ -94,4 +99,37 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu,
 /* implemented in diag.c */
 int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
 
+/* implemented in ioinst.c */
+int kvm_css_instruction(struct kvm_vcpu *vcpu);
+
+/* implemented in css.c */
+struct schib;
+int kvm_arch_vcpu_ioctl_css_notify(struct kvm_vcpu *vcpu,
+				   struct kvm_css_notify *notify);
+int kvm_s390_process_ccw_hotplug(struct kvm *kvm,
+				 struct kvm_s390_sch_info *sch_info);
+int kvm_s390_process_chp_hotplug(struct kvm *kvm,
+				 struct kvm_s390_chp_info *chp_info);
+int kvm_s390_enable_css(struct kvm *kvm);
+struct kvm_subch *css_find_subch(struct kvm *kvm, u8 m, u8 cssid, u8 ssid,
+				 u16 schid);
+int css_do_stsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u32 addr);
+int css_schid_final(struct kvm *kvm, u8 cssid, u8 ssid, u16 schid);
+int css_do_msch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, struct schib *schib);
+int css_do_xsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_csch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_hsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_ssch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u64 orb);
+int css_do_tsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u32 addr);
+int css_do_stcrw(struct kvm_vcpu *vcpu, u32 addr);
+int css_do_tpi(struct kvm_vcpu *vcpu, u32 addr, int lowcore);
+int css_collect_chp_desc(struct kvm *kvm, u8 cssid, u8 f_chpid, u8 l_chpid,
+                         int rfmt, void *buf);
+void css_do_schm(struct kvm_vcpu *vcpu, u8 mbk, int update, int dct, uint64_t mbo);
+int css_enable_mcsse(struct kvm *kvm);
+int css_enable_mss(struct kvm *kvm);
+int css_do_rsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_rchp(struct kvm_vcpu *vcpu, u8 cssid, u8 chpid);
+int css_chpid_in_use(struct kvm *kvm, u8 cssid, u8 chpid);
+void css_queue_crw(struct kvm *kvm, u8 rsc, u8 erc, int chain, u16 rsid);
 #endif
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 8b79a94..8b128e4 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -138,7 +138,12 @@ static int handle_skey(struct kvm_vcpu *vcpu)
 static int handle_io_inst(struct kvm_vcpu *vcpu)
 {
 	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
-	/* condition code 3 */
+
+	if (vcpu->kvm->arch.css_support)
+		/* Use in-kernel css support. */
+		return kvm_css_instruction(vcpu);
+
+	/* Set cc 3 to stop guest issueing I/O instructions. */
 	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
 	return 0;
diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h
index 95fbc1a..6d2059e 100644
--- a/arch/s390/kvm/trace-s390.h
+++ b/arch/s390/kvm/trace-s390.h
@@ -203,6 +203,73 @@ TRACE_EVENT(kvm_s390_stop_request,
 		      __entry->action_bits)
 	);
 
+/*
+ * Trace point for enabling in-kernel channel subsystem support.
+ */
+TRACE_EVENT(kvm_s390_enable_kernel_css,
+	    TP_PROTO(void *kvm),
+	    TP_ARGS(kvm),
+
+	    TP_STRUCT__entry(
+		    __field(void *, kvm)
+		),
+
+	    TP_fast_assign(
+		    __entry->kvm = kvm;
+		),
+
+	    TP_printk("enabling in-kernel css support (kvm @ %p)\n",
+		    __entry->kvm)
+    );
+
+/*
+ * Trace point for user space subchannel I/O notification.
+ */
+TRACE_EVENT(kvm_s390_css_notify,
+	    TP_PROTO(u8 cssid, u8 ssid, u16 schid),
+	    TP_ARGS(cssid, ssid, schid),
+
+	    TP_STRUCT__entry(
+		    __field(u8, cssid)
+		    __field(u8, ssid)
+		    __field(u16, schid)
+		),
+
+	    TP_fast_assign(
+		    __entry->cssid = cssid;
+		    __entry->ssid = ssid;
+		    __entry->schid = schid;
+		),
+
+	    TP_printk("css notification for subchannel %x.%x.%04x\n",
+		      __entry->cssid, __entry->ssid, __entry->schid)
+    );
+
+/*
+ * Trace point for user space subchannel hotplug notification.
+ */
+TRACE_EVENT(kvm_s390_ccw_hotplug,
+	    TP_PROTO(u8 cssid, u8 ssid, u16 schid, int add),
+	    TP_ARGS(cssid, ssid, schid, add),
+
+	    TP_STRUCT__entry(
+		    __field(u8, cssid)
+		    __field(u8, ssid)
+		    __field(u16, schid)
+		    __field(int, add)
+		),
+
+	    TP_fast_assign(
+		    __entry->cssid = cssid;
+		    __entry->ssid = ssid;
+		    __entry->schid = schid;
+		    __entry->add = add;
+		),
+
+	    TP_printk("hotplug event for subchannel %x.%x.%04x (%s)\n",
+		      __entry->cssid, __entry->ssid, __entry->schid,
+		      __entry->add ? "attach" : "detach")
+    );
 
 #endif /* _TRACE_KVMS390_H */
 
diff --git a/arch/s390/kvm/trace.h b/arch/s390/kvm/trace.h
index 2b29e62..5f743f3 100644
--- a/arch/s390/kvm/trace.h
+++ b/arch/s390/kvm/trace.h
@@ -335,6 +335,28 @@ TRACE_EVENT(kvm_s390_handle_stsi,
 			   __entry->addr)
 	);
 
+TRACE_EVENT(kvm_s390_handle_ioinst,
+	    TP_PROTO(char *name, u8 cssid, u8 ssid, u16 schid),
+	    TP_ARGS(name, cssid, ssid, schid),
+
+	    TP_STRUCT__entry(
+		__field(char *, name)
+		__field(u8, cssid)
+		__field(u8, ssid)
+		__field(u16, schid)
+		),
+
+	    TP_fast_assign(
+		__entry->name = name;
+		__entry->cssid = cssid;
+		__entry->ssid = ssid;
+		__entry->schid = schid;
+		),
+
+	    TP_printk("I/O instruction %s (%x.%x.%04x)", __entry->name,
+		      __entry->cssid, __entry->ssid, __entry->schid)
+    );
+
 #endif /* _TRACE_KVM_H */
 
 /* This part must be outside protection */
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index e0c3d87..4c48923 100644
--- a/include/linux/kvm.h
+++ b/include/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];
 	};
@@ -480,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 */
@@ -625,6 +673,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
 #define KVM_CAP_PPC_ALLOC_HTAB 80
+#define KVM_CAP_S390_CSS_SUPPORT 81
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -838,6 +887,10 @@ struct kvm_s390_ucas_mapping {
 #define KVM_PPC_GET_SMMU_INFO	  _IOR(KVMIO,  0xa6, struct kvm_ppc_smmu_info)
 /* Available with KVM_CAP_PPC_ALLOC_HTAB */
 #define KVM_PPC_ALLOCATE_HTAB	  _IOWR(KVMIO, 0xa7, __u32)
+/* Available with KVM_CAP_S390_CSS_SUPPORT */
+#define KVM_S390_CSS_NOTIFY       _IOW(KVMIO, 0xae, struct kvm_css_notify)
+#define KVM_S390_CCW_HOTPLUG      _IOW(KVMIO, 0xab, struct kvm_s390_sch_info)
+#define KVM_S390_CHP_HOTPLUG      _IOW(KVMIO, 0xac, struct kvm_s390_chp_info)
 
 /*
  * ioctls for vcpu fds
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
index 7ef9e75..939ba8b 100644
--- a/include/trace/events/kvm.h
+++ b/include/trace/events/kvm.h
@@ -14,7 +14,7 @@
 	ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR),	\
 	ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
 	ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL),	\
-	ERSN(S390_UCONTROL)
+	ERSN(S390_UCONTROL), ERSN(S390_SCH_IO)
 
 TRACE_EVENT(kvm_userspace_exit,
 	    TP_PROTO(__u32 reason, int errno),
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index bcf973e..59c40aa 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1817,7 +1817,8 @@ static long kvm_vcpu_ioctl(struct file *filp,
 	 * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
 	 * so vcpu_load() would break it.
 	 */
-	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+	    ioctl == KVM_S390_CSS_NOTIFY)
 		return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif
 
-- 
1.7.11.4

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

* [Qemu-devel] [PATCH 7/7] s390/kvm: In-kernel channel subsystem support.
@ 2012-08-07 14:52   ` Cornelia Huck
  0 siblings, 0 replies; 16+ 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

Handle most support for channel I/O instructions in the kernel itself.

Only asynchronous functions (such as the start function) need to be
handled by userspace.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 Documentation/virtual/kvm/api.txt | 127 +++++
 arch/s390/include/asm/kvm_host.h  |  48 ++
 arch/s390/kvm/Makefile            |   2 +-
 arch/s390/kvm/css.c               | 945 ++++++++++++++++++++++++++++++++++++++
 arch/s390/kvm/intercept.c         |   1 +
 arch/s390/kvm/interrupt.c         | 147 ++++--
 arch/s390/kvm/ioinst.c            | 797 ++++++++++++++++++++++++++++++++
 arch/s390/kvm/kvm-s390.c          |  35 ++
 arch/s390/kvm/kvm-s390.h          |  38 ++
 arch/s390/kvm/priv.c              |   7 +-
 arch/s390/kvm/trace-s390.h        |  67 +++
 arch/s390/kvm/trace.h             |  22 +
 include/linux/kvm.h               |  53 +++
 include/trace/events/kvm.h        |   2 +-
 virt/kvm/kvm_main.c               |   3 +-
 15 files changed, 2247 insertions(+), 47 deletions(-)
 create mode 100644 arch/s390/kvm/css.c
 create mode 100644 arch/s390/kvm/ioinst.c

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 140e7e2..678b1d9 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1980,6 +1980,101 @@ return the hash table order in the parameter.  (If the guest is using
 the virtualized real-mode area (VRMA) facility, the kernel will
 re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.)
 
+4.77 KVM_S390_CSS_NOTIFY
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vcpu ioctl
+Parameters: struct kvm_css_notify (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl may be used by userspace to notify the kernel that the control
+blocks for a virtual subchannel should be updated and an I/O interrupt
+injected.
+
+It uses the following parameter block:
+
+/* 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;
+};
+
+cssid, ssid and schid specify the subchannel; scsw, pmcw and sense_data
+are the control blocks to be updated. If the notification is specified
+to be unsolicited, no new interrupt is generated if an interrupt is already
+pending for the subchannel; else an unsolicited interrupt is generated.
+
+The func parameter specifies the asynchronous function that is notified
+for (solicited interrupts only).
+
+This ioctl (like the other interrupt injection ioctls) is executed
+asynchronously to normal vcpu execution.
+
+4.78 KVM_S390_CCW_HOTPLUG
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vm ioctl
+Parameters: struct kvm_s390_sch_info (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl allows userspace to notify the kernel about addition or removal
+of subchannels.
+
+It uses the following data structure:
+
+/* 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;
+};
+
+cssid, ssid, schid and devno describe the subchannel. If the subchannel is
+being added, schib contains the initial subchannel information block for it.
+hotplugged (can only be 0 if add is !0) specifies whether the subchannel has
+been dynamically added or removed (as opposed to the initial machine setup,
+when no channel report words will be created). add specifies whether the
+subchannel is coming or going. virtual signifies whether this is a real or
+a purely virtual subchannel.
+
+4.79 KVM_S390_CHP_HOTPLUG
+
+Capability: KVM_CAP_S390_CSS_SUPPORT
+Architectures: s390
+Type: vm ioctl
+Parameters: struct kvm_s390_chp_info (in)
+Returns: 0 on success, negative value on failure
+
+This ioctl allows userspace to notify the kernel about addition or removal
+of a channel path.
+
+It uses the following structure:
+
+/* for KVM_S390_CHP_HOTPLUG */
+struct kvm_s390_chp_info {
+	__u8 cssid;
+	__u8 chpid;
+	__u8 type;
+	int add;
+	int virtual;
+};
+
+cssid and chpid specify the channel path, type the channel path type. add
+determines whether the path is coming or going, and virtual signifies
+whether this is a purely virtual or a real channel path.
 
 5. The kvm_run structure
 ------------------------
@@ -2195,6 +2290,24 @@ The possible hypercalls are defined in the Power Architecture Platform
 Requirements (PAPR) document available from www.power.org (free
 developer registration required to access it).
 
+		/* 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;
+
+s390 specific. Used for userspace processing of asynchronous subchannel
+functions.
+
 		/* Fix the size of the union. */
 		char padding[256];
 	};
@@ -2316,3 +2429,17 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
    where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
  - The tsize field of mas1 shall be set to 4K on TLB0, even though the
    hardware ignores this value for TLB0.
+
+6.4 KVM_CAP_S390_CSS_SUPPORT
+
+Architectures: s390
+Parameters: none
+Returns: 0 on success; -1 on error
+
+This capability enables in-kernel support for handling of channel I/O
+instructions like STORE SUBCHANNEL or CHANNEL SUBSYSTEM CALL.
+
+When this capability is enabled, KVM_EXIT_S390_SCH_IO can occur.
+
+When this capability is provided, the KVM_S390_CCW_HOTPLUG,
+KVM_S390_CHP_HOTPLUG and KVM_S390_CSS_NOTIFY ioctls are provided.
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 556774d..03e154b 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -17,13 +17,18 @@
 #include <linux/interrupt.h>
 #include <linux/kvm_host.h>
 #include <asm/debug.h>
+#include <asm/cio.h>
 #include <asm/cpu.h>
+#include <asm/crw.h>
 
 #define KVM_MAX_VCPUS 64
 #define KVM_MEMORY_SLOTS 32
 /* memory slots that does not exposed to userspace */
 #define KVM_PRIVATE_MEM_SLOTS 4
 
+#define VIRTUAL_CSSID 0xfe
+#define KVM_MAX_CSSID 0xfe /* 0xff is reserved */
+
 struct sca_entry {
 	atomic_t scn;
 	__u32	reserved;
@@ -174,6 +179,7 @@ struct kvm_s390_ext_info {
 #define PGM_ADDRESSING           0x05
 #define PGM_SPECIFICATION        0x06
 #define PGM_DATA                 0x07
+#define PGM_OPERAND              0x15
 
 struct kvm_s390_pgm_info {
 	__u16 code;
@@ -208,6 +214,7 @@ struct kvm_s390_interrupt_info {
 		struct kvm_s390_prefix_info prefix;
 		struct kvm_s390_mchk_info mchk;
 	};
+	int nondyn;
 };
 
 /* for local_interrupt.action_flags */
@@ -259,11 +266,52 @@ struct kvm_vm_stat {
 struct kvm_arch_memory_slot {
 };
 
+struct crw_container {
+	struct crw crw;
+	struct list_head sibling;
+};
+
+struct chp_info {
+	u8 in_use;
+	u8 type;
+};
+
+struct kvm_subch {
+	struct mutex lock;
+	u8 cssid;
+	u8 ssid;
+	u16 schid;
+	u16 devno;
+	u8 sense_data[32];
+	struct schib *curr_status;
+	struct kvm_s390_interrupt_info inti;
+};
+
+struct schid_info {
+	struct kvm_subch *schs[__MAX_SUBCHANNEL + 1];
+	unsigned long bm[0];
+};
+
+struct kvm_s390_css_data {
+	int max_cssid;
+	int max_ssid;
+	struct list_head pending_crws;
+	struct kvm_s390_interrupt_info crw_inti;
+	int do_crw_mchk;
+	int crws_lost;
+	struct schid_info *schids[KVM_MAX_CSSID + 1][__MAX_SSID + 1];
+	struct chp_info chpids[KVM_MAX_CSSID + 1][__MAX_CHPID + 1];
+	atomic_t chnmon_active;
+	u64 chnmon_area;
+};
+
 struct kvm_arch{
 	struct sca_block *sca;
 	debug_info_t *dbf;
 	struct kvm_s390_float_interrupt float_int;
 	struct gmap *gmap;
+	int css_support;
+	struct kvm_s390_css_data *css;
 };
 
 extern int sie64a(struct kvm_s390_sie_block *, u64 *);
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index 3975722..afcf71e 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -10,5 +10,5 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o)
 
 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
 
-kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o diag.o
+kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o diag.o ioinst.o css.o
 obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/s390/kvm/css.c b/arch/s390/kvm/css.c
new file mode 100644
index 0000000..36d26ce
--- /dev/null
+++ b/arch/s390/kvm/css.c
@@ -0,0 +1,945 @@
+/*
+ * Virtual channel subsystem support for kvm
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#include <linux/kvm.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <asm/cio.h>
+#include <asm/crw.h>
+#include <asm/schib.h>
+#include <asm/schid.h>
+#include <asm/scsw.h>
+#include "gaccess.h"
+#include "kvm-s390.h"
+#include "trace-s390.h"
+
+static void css_update_chnmon(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	if (!sch->curr_status->pmcw.mme)
+		/* Not active. */
+		return;
+
+	/*
+	 * The only field we want to update (ssch_rsch_count) is conveniently
+	 * located at the beginning of the measurement block.
+	 * For format 0, it is a 16 bit value; for format 1, a 32 bit value.
+	 */
+	if (sch->curr_status->pmcw.mbfc) {
+		/* Format 1, per-subchannel area. */
+		u32 uninitialized_var(count);
+
+		if (get_guest_u32(vcpu, sch->curr_status->mba, &count))
+			return;
+		count++;
+		put_guest_u32(vcpu, sch->curr_status->mba, count);
+	} else {
+		/* Format 0, global area. */
+		u64 target;
+		u16 uninitialized_var(count);
+
+		target = vcpu->kvm->arch.css->chnmon_area +
+			(sch->curr_status->pmcw.mbi << 5);
+		if (get_guest_u16(vcpu, target, &count))
+			return;
+		count++;
+		put_guest_u16(vcpu, target, count);
+	}
+}
+
+static int highest_schid(struct kvm *kvm, u8 cssid, u8 ssid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (!css->schids[cssid][ssid])
+		return 0;
+	return find_last_bit(css->schids[cssid][ssid]->bm,
+			     (__MAX_SUBCHANNEL + 1) / sizeof(unsigned long));
+}
+
+int css_schid_final(struct kvm *kvm, u8 cssid, u8 ssid, u16 schid)
+{
+	return (cssid > KVM_MAX_CSSID ||
+		ssid > __MAX_SSID ||
+		schid > highest_schid(kvm, cssid, ssid)) ? 1 : 0;
+}
+
+static int css_add_virtual_chpid(struct kvm *kvm, u8 cssid, u8 chpid, u8 type)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (cssid > KVM_MAX_CSSID)
+		return -EINVAL;
+
+	if (css->chpids[cssid][chpid].in_use)
+		return -EEXIST;
+
+	css->chpids[cssid][chpid].in_use = 1;
+	css->chpids[cssid][chpid].type = type;
+	return 0;
+}
+
+static int css_remove_virtual_chpid(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if (cssid > KVM_MAX_CSSID)
+		return -EINVAL;
+
+	if (!css->chpids[cssid][chpid].in_use)
+		return -EINVAL;
+
+	css->chpids[cssid][chpid].in_use = 0;
+	return 0;
+}
+
+int css_chpid_in_use(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if ((cssid > KVM_MAX_CSSID) || (chpid > __MAX_CHPID))
+		return 0;
+	return css->chpids[cssid][chpid].in_use;
+}
+
+static int css_chpid_type(struct kvm *kvm, u8 cssid, u8 chpid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+
+	if ((cssid > KVM_MAX_CSSID) || (chpid > __MAX_CHPID))
+		return 0;
+	return css->chpids[cssid][chpid].type;
+}
+
+int css_collect_chp_desc(struct kvm *kvm, u8 cssid, u8 f_chpid, u8 l_chpid,
+			 int rfmt, void *buf)
+{
+	int i, desc_size;
+	u32 words[8];
+
+	desc_size = 0;
+	for (i = f_chpid; i <= l_chpid; i++) {
+		if (!css_chpid_in_use(kvm, cssid, i))
+			continue;
+		if (rfmt == 0) {
+			words[0] = 0x80000000 |
+				(css_chpid_type(kvm, cssid, i) << 8) | i;
+			words[1] = 0;
+			memcpy(buf + desc_size, words, 8);
+			desc_size += 8;
+		} else if (rfmt == 1) {
+			words[0] = 0x80000000 |
+				(css_chpid_type(kvm, cssid, i) << 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;
+}
+
+struct kvm_subch *css_find_subch(struct kvm *kvm, u8 m, u8 cssid, u8 ssid,
+				 u16 schid)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 real_cssid;
+
+	if (!m) {
+		if (cssid)
+			return NULL;
+		real_cssid = VIRTUAL_CSSID;
+	} else
+		real_cssid = cssid;
+	/* Don't bother for out of range values. */
+	if (css_schid_final(kvm, real_cssid, ssid, schid))
+		return NULL;
+	if (!css->schids[real_cssid][ssid])
+		return NULL;
+	if (!test_bit(schid, css->schids[real_cssid][ssid]->bm))
+		return NULL;
+	return css->schids[real_cssid][ssid]->schs[schid];
+}
+
+void css_queue_crw(struct kvm *kvm, u8 rsc, u8 erc, int chain, u16 rsid)
+{
+	struct crw_container *crw_cont;
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	int ret;
+
+	/* TODO: Maybe use a static crw pool? */
+	crw_cont = kzalloc(sizeof(struct crw_container), GFP_KERNEL);
+
+	mutex_lock(&kvm->lock);
+
+	if (!crw_cont) {
+		css->crws_lost = 1;
+		goto out;
+	}
+	crw_cont->crw.rsc = rsc;
+	crw_cont->crw.erc = erc;
+	crw_cont->crw.chn = chain;
+	crw_cont->crw.rsid = rsid;
+	crw_cont->crw.oflw = css->crws_lost;
+	css->crws_lost = 0;
+
+	list_add_tail(&crw_cont->sibling, &css->pending_crws);
+
+	if (css->do_crw_mchk) {
+		css->do_crw_mchk = 0;
+		ret = kvm_s390_inject_internal(kvm, &css->crw_inti);
+		if (ret)
+			css->do_crw_mchk = 1;
+	}
+out:
+	mutex_unlock(&kvm->lock);
+}
+
+int css_do_stcrw(struct kvm_vcpu *vcpu, u32 cda)
+{
+	struct crw_container *crw_cont;
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+	int ret;
+
+	mutex_lock(&vcpu->kvm->lock);
+	if (list_empty(&css->pending_crws)) {
+		u32 zeroes = 0;
+		/* List was empty, turn crw machine checks on again. */
+		if (copy_to_guest(vcpu, cda, &zeroes, sizeof(struct crw))) {
+			kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+			ret = -EIO;
+			goto out;
+		}
+		css->do_crw_mchk = 1;
+		ret = 1;
+		goto out;
+	}
+
+	crw_cont = container_of(css->pending_crws.next, struct crw_container,
+				sibling);
+	if (copy_to_guest(vcpu, cda, &crw_cont->crw, sizeof(struct crw))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		ret = -EIO;
+		goto out;
+	}
+	list_del(&crw_cont->sibling);
+	kfree(crw_cont);
+	ret = 0;
+out:
+	mutex_unlock(&vcpu->kvm->lock);
+	return ret;
+}
+
+void css_do_schm(struct kvm_vcpu *vcpu, u8 mbk, int update, int dct, u64 mbo)
+{
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+
+	/* dct is currently ignored (not really meaningful for our devices) */
+	/* TODO: Don't ignore mbk. */
+	if (update && !atomic_cmpxchg(&css->chnmon_active, 0, 1))
+		/* Enable measuring. */
+		css->chnmon_area = mbo;
+
+	if (!update && !atomic_cmpxchg(&css->chnmon_active, 1, 0))
+		/* Disable measuring. */
+		css->chnmon_area = 0;
+}
+
+int css_enable_mcsse(struct kvm *kvm)
+{
+	kvm->arch.css->max_cssid = KVM_MAX_CSSID;
+	return 0;
+}
+
+int css_enable_mss(struct kvm *kvm)
+{
+	kvm->arch.css->max_ssid = __MAX_SSID;
+	return 0;
+}
+
+int css_do_tpi(struct kvm_vcpu *vcpu, u32 addr, int lowcore)
+{
+	struct kvm_s390_interrupt_info *inti;
+
+	inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6]);
+	if (inti) {
+		if (!lowcore) {
+			put_guest_u16(vcpu, addr, inti->io.subchannel_id);
+			put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr);
+			put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm);
+		} else {
+			put_guest_u16(vcpu, addr + 184, inti->io.subchannel_id);
+			put_guest_u16(vcpu, addr + 186, inti->io.subchannel_nr);
+			put_guest_u32(vcpu, addr + 188, inti->io.io_int_parm);
+			put_guest_u32(vcpu, addr + 192, inti->io.io_int_word);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int css_do_msch(struct kvm_vcpu *vcpu, struct kvm_subch *sch,
+		struct schib *schib)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!sch->curr_status->pmcw.dnv) {
+		ret = 0;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (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;
+
+	/*
+	 * No need to exit to userspace since it will get the current state
+	 * with the next exit.
+	 */
+	ret = 0;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_xsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!scsw_fctl(s) || (scsw_fctl(s) != SCSW_FCTL_START_FUNC) ||
+	    (!(scsw_actl(s) & (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND |
+			       SCSW_ACTL_SUSPENDED))) ||
+	    (scsw_actl(s) & SCSW_ACTL_SCHACT)) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_stctl(s) != 0) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Cancel the current operation. */
+	s->cmd.fctl &= ~SCSW_FCTL_START_FUNC;
+	s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND |
+			 SCSW_ACTL_SUSPENDED);
+	s->cmd.dstat = 0;
+	s->cmd.cstat = 0;
+	/*
+	 * Let userspace update its state.
+	 * No hardware related structures need to be updated, since userspace
+	 * will get the current state with the next exit.
+	 */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_XSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_csch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* Trigger the clear function. */
+	s->cmd.fctl = SCSW_FCTL_CLEAR_FUNC;
+	s->cmd.actl = SCSW_ACTL_CLEAR_PEND;
+
+	/* Let userspace handle the clear function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_CSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_hsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if ((scsw_stctl(s) == SCSW_STCTL_STATUS_PEND) ||
+	    (scsw_stctl(s) & (SCSW_STCTL_PRIM_STATUS |
+			      SCSW_STCTL_SEC_STATUS |
+			      SCSW_STCTL_ALERT_STATUS))) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Trigger the halt function. */
+	s->cmd.fctl |= SCSW_FCTL_HALT_FUNC;
+	s->cmd.fctl &= ~SCSW_FCTL_START_FUNC;
+	if ((scsw_actl(s) == (SCSW_ACTL_SCHACT | SCSW_ACTL_DEVACT)) &&
+	    (scsw_stctl(s) == SCSW_STCTL_INTER_STATUS)) {
+		s->cmd.stctl &= ~SCSW_STCTL_STATUS_PEND;
+	}
+	s->cmd.actl |= SCSW_ACTL_HALT_PEND;
+
+	/* Let userspace handle the halt function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_HSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_ssch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u64 orb)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if (scsw_fctl(s) & (SCSW_FCTL_START_FUNC |
+			    SCSW_FCTL_HALT_FUNC |
+			    SCSW_FCTL_CLEAR_FUNC)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* If monitoring is active, update counter. */
+	if (atomic_read(&vcpu->kvm->arch.css->chnmon_active))
+		css_update_chnmon(vcpu, sch);
+
+	/* Trigger the start function. */
+	s->cmd.fctl |= SCSW_FCTL_START_FUNC;
+	s->cmd.actl |= SCSW_ACTL_START_PEND;
+	s->cmd.pno = 0;
+
+	/* Let userspace handle the start function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_SSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	vcpu->run->s390_sch_io.orb = orb;
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_tsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, uint32_t addr)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	u8 stctl;
+	u8 fctl;
+	u8 actl;
+	struct irb irb;
+	int ret;
+	u32 *esw;
+
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	stctl = scsw_stctl(s);
+	fctl = scsw_fctl(s);
+	actl = scsw_actl(s);
+
+	memset(&irb, 0, sizeof(struct irb));
+
+	/* Copy scsw. */
+	memcpy(&irb.scsw, s, sizeof(union scsw));
+	esw = (u32 *)&irb.esw;
+	if (stctl & SCSW_STCTL_STATUS_PEND) {
+		if (scsw_cstat(s) & (SCHN_STAT_CHN_DATA_CHK |
+				     SCHN_STAT_CHN_CTRL_CHK |
+				     SCHN_STAT_INTF_CTRL_CHK)) {
+			irb.scsw.cmd.eswf = 1;
+			esw[0] = 0x04804000;
+		} else
+			esw[0] = 0x00800000;
+
+		/* If a unit check is pending, copy sense data. */
+		if ((scsw_dstat(s) & DEV_STAT_UNIT_CHECK) && p->csense) {
+			irb.scsw.cmd.eswf = 1;
+			irb.scsw.cmd.ectl = 1;
+			memcpy(irb.ecw, sch->sense_data,
+			       sizeof(sch->sense_data));
+			esw[1] = 0x02000000 | (sizeof(sch->sense_data) << 8);
+		}
+	}
+	if (copy_to_guest(vcpu, addr, &irb, sizeof(struct irb))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		ret = -EIO;
+		goto out;
+	}
+
+	/* Clear conditions on subchannel, if applicable. */
+	if (stctl & SCSW_STCTL_STATUS_PEND) {
+		s->cmd.stctl = 0;
+		if ((stctl != (SCSW_STCTL_INTER_STATUS |
+			       SCSW_STCTL_STATUS_PEND)) ||
+		    ((fctl & SCSW_FCTL_HALT_FUNC) &&
+		     (actl & SCSW_ACTL_SUSPENDED)))
+			s->cmd.fctl = 0;
+
+		if (stctl != (SCSW_STCTL_INTER_STATUS |
+			      SCSW_STCTL_STATUS_PEND)) {
+			s->cmd.pno = 0;
+			s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND |
+					 SCSW_ACTL_START_PEND |
+					 SCSW_ACTL_HALT_PEND |
+					 SCSW_ACTL_CLEAR_PEND |
+					 SCSW_ACTL_SUSPENDED);
+		} else {
+			if ((actl & SCSW_ACTL_SUSPENDED) &&
+			    (fctl & SCSW_FCTL_START_FUNC)) {
+				s->cmd.pno = 0;
+				if (fctl & SCSW_FCTL_HALT_FUNC)
+					s->cmd.actl &= ~(SCSW_ACTL_RESUME_PEND |
+							 SCSW_ACTL_START_PEND |
+							 SCSW_ACTL_HALT_PEND |
+							 SCSW_ACTL_CLEAR_PEND |
+							 SCSW_ACTL_SUSPENDED);
+				else
+					s->cmd.actl &= ~SCSW_ACTL_RESUME_PEND;
+			}
+			/* Clear a possible pending I/O interrupt. */
+			if (!list_empty(&sch->inti.list))
+				kvm_s390_dequeue_internal(vcpu->kvm, &sch->inti);
+		}
+		/* Clear pending sense data. */
+		if (p->csense)
+			memset(sch->sense_data, 0 , sizeof(sch->sense_data));
+	}
+
+	/*
+	 * No need to exit to userspace since it will get the current state
+	 * with the next exit.
+	 */
+	ret = (stctl & SCSW_STCTL_STATUS_PEND) ? -EBUSY : 0;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int css_do_rsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch)
+{
+	union scsw *s = &sch->curr_status->scsw;
+	struct pmcw *p = &sch->curr_status->pmcw;
+	int ret;
+
+	mutex_lock(&sch->lock);
+
+	if (!p->dnv || !p->ena) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (scsw_stctl(s) & SCSW_STCTL_STATUS_PEND) {
+		ret = -EINPROGRESS;
+		goto out;
+	}
+
+	if ((scsw_fctl(s) != SCSW_FCTL_START_FUNC) ||
+	    (scsw_actl(s) & SCSW_ACTL_RESUME_PEND) ||
+	    (!(scsw_actl(s) & SCSW_ACTL_SUSPENDED))) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* If monitoring is active, update counter. */
+	if (atomic_read(&vcpu->kvm->arch.css->chnmon_active))
+		css_update_chnmon(vcpu, sch);
+
+	s->cmd.actl |= SCSW_ACTL_RESUME_PEND;
+	/* Let userspace handle the start function. */
+	vcpu->run->exit_reason = KVM_EXIT_S390_SCH_IO;
+	vcpu->run->s390_sch_io.func = SCH_DO_RSCH;
+	vcpu->run->s390_sch_io.sch_id = (sch->cssid << 24) | (1 << 19) |
+		(sch->ssid << 17) | 1 << 16 | sch->schid;
+	memcpy(&vcpu->run->s390_sch_io.scsw, s, sizeof(*s));
+	memcpy(&vcpu->run->s390_sch_io.pmcw, p, sizeof(*p));
+	ret = -EREMOTE;
+
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+int kvm_arch_vcpu_ioctl_css_notify(struct kvm_vcpu *vcpu,
+				   struct kvm_css_notify *notify)
+{
+	struct kvm_subch *sch;
+	int ret;
+
+	trace_kvm_s390_css_notify(notify->cssid, notify->ssid, notify->schid);
+	/* Userspace always gives us the real cssid. */
+	sch = css_find_subch(vcpu->kvm, 1, notify->cssid, notify->ssid,
+			     notify->schid);
+	if (!sch)
+		return -ENODEV;
+	mutex_lock(&sch->lock);
+	if (notify->unsolicited) {
+		/*
+		 * Userspace wants us to inject an unsolicited interrupt
+		 * iff the subchannel is not status pending.
+		 */
+		if (scsw_stctl(&sch->curr_status->scsw) &
+		    SCSW_STCTL_STATUS_PEND) {
+			ret = 0;
+			goto out;
+		}
+		sch->curr_status->scsw.cmd.stctl =
+			SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND;
+	} else {
+		/*
+		 * First, check whether any I/O instructions have been
+		 * issued in the mean time which would preclude normal
+		 * signalling as requested by the control block. This
+		 * might happen e.g. if the kernel accepted a csch while
+		 * the start function was in progress in user space.
+		 */
+		if (((notify->func == SCH_DO_SSCH) ||
+		     (notify->func == SCH_DO_RSCH)) &&
+		    (scsw_fctl(&sch->curr_status->scsw) != SCSW_FCTL_START_FUNC)) {
+			/*
+			 * xsch, hsch, or csch happened.
+			 * For the xsch case, no interrupt will be generated.
+			 * For the hsch/csch case, another notification will
+			 * happen.
+			 */
+			ret = 0;
+			goto out;
+		}
+		if ((notify->func == SCH_DO_HSCH) &&
+		    (scsw_fctl(&sch->curr_status->scsw) & SCSW_FCTL_CLEAR_FUNC)) {
+			/*
+			 * csch happened, and another notification will come
+			 * in later.
+			 */
+			ret = 0;
+			goto out;
+		}
+		/* Update internal status. */
+		memcpy(&sch->curr_status->scsw, &notify->scsw,
+		       sizeof(notify->scsw));
+		memcpy(&sch->curr_status->pmcw, &notify->pmcw,
+		       sizeof(notify->pmcw));
+		memcpy(sch->sense_data, notify->sense_data,
+		       sizeof(notify->sense_data));
+	}
+
+	/* Inject interrupt. */
+	sch->inti.type = (sch->cssid << 24) | (sch->ssid << 22) |
+		(sch->schid << 16);
+	sch->inti.io.subchannel_id = vcpu->kvm->arch.css->max_cssid > 0 ?
+		(sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1 :
+		(sch->ssid << 1) | 1;
+	sch->inti.io.subchannel_nr = sch->schid;
+	sch->inti.io.io_int_parm = sch->curr_status->pmcw.intparm;
+	sch->inti.io.io_int_word = (0x80 >> sch->curr_status->pmcw.isc) << 24;
+	BUG_ON(!list_empty(&sch->inti.list));
+	mutex_lock(&vcpu->kvm->lock);
+	ret = kvm_s390_inject_internal(vcpu->kvm, &sch->inti);
+	mutex_unlock(&vcpu->kvm->lock);
+out:
+	mutex_unlock(&sch->lock);
+	return ret;
+}
+
+static int css_add_to_store(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	struct schid_info *info;
+	size_t schid_size;
+
+	if (!css->schids[sch->cssid][sch->ssid]) {
+		schid_size = sizeof(struct schid_info) +
+			    __BITOPS_WORDS(__MAX_SUBCHANNEL + 1) *
+			sizeof(unsigned long);
+		css->schids[sch->cssid][sch->ssid] = vmalloc(schid_size);
+		if (!css->schids[sch->cssid][sch->ssid])
+			return -ENOMEM;
+		memset(css->schids[sch->cssid][sch->ssid], 0, schid_size);
+	}
+	info = css->schids[sch->cssid][sch->ssid];
+	info->schs[sch->schid] = sch;
+	set_bit(sch->schid, info->bm);
+
+	return 0;
+}
+
+static int css_remove_from_store(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	struct schid_info *info;
+
+	info = css->schids[sch->cssid][sch->ssid];
+	if (!info)
+		return -EINVAL;
+	info->schs[sch->schid] = NULL;
+	clear_bit(sch->schid, info->bm);
+
+	return 0;
+}
+
+static int css_add_subchannel(struct kvm *kvm,
+			      struct kvm_s390_sch_info *sch_info)
+{
+	struct kvm_subch *sch;
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 guest_cssid;
+	bool no_crw;
+
+	/* Generate subchannel structure. */
+	sch = kzalloc(sizeof(*sch), GFP_KERNEL);
+	if (!sch)
+		return -ENOMEM;
+	sch->curr_status = kzalloc(sizeof(*sch->curr_status), GFP_KERNEL);
+	if (!sch->curr_status) {
+		kfree(sch);
+		return -ENOMEM;
+	}
+	mutex_init(&sch->lock);
+	sch->cssid = sch_info->cssid;
+	sch->ssid = sch_info->ssid;
+	sch->schid = sch_info->schid;
+	sch->devno = sch_info->devno;
+	memcpy(sch->curr_status, &sch_info->schib, sizeof(*sch->curr_status));
+	INIT_LIST_HEAD(&sch->inti.list);
+	sch->inti.nondyn = 1;
+	/* Add subchannel to store. */
+	css_add_to_store(kvm, sch);
+	if (!sch_info->hotplugged)
+		goto out;
+	/*
+	 * Generate add ccw.
+	 *
+	 * Only notify for higher subchannel sets/channel subsystems if the
+	 * guest has enabled it.
+	 */
+	guest_cssid = ((css->max_cssid == 0) && (sch->cssid == VIRTUAL_CSSID)) ?
+		0 : sch->cssid;
+	no_crw = (sch->ssid > css->max_ssid) ||
+		(guest_cssid > css->max_cssid) ||
+		((css->max_cssid == 0) && (sch->cssid != VIRTUAL_CSSID));
+	if (!no_crw) {
+		css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM,
+			      ((css->max_ssid > 0) || (css->max_cssid > 0)) ?
+			      1 : 0, sch->schid);
+		if ((css->max_ssid > 0) || (css->max_cssid > 0))
+			css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM, 0,
+				      (guest_cssid << 8) | (sch->ssid << 4));
+	}
+out:
+	return 0;
+}
+
+static int css_remove_subchannel(struct kvm *kvm, struct kvm_subch *sch)
+{
+	struct kvm_s390_css_data *css = kvm->arch.css;
+	u8 guest_cssid;
+	bool no_crw;
+
+	/* Make subchannel inaccessible. */
+	mutex_lock(&sch->lock);
+	/* Clear a possible pending I/O interrupt. */
+	if (!list_empty(&sch->inti.list))
+		kvm_s390_dequeue_internal(kvm, &sch->inti);
+	css_remove_from_store(kvm, sch);
+	mutex_unlock(&sch->lock);
+	/*
+	 * Generate removal ccw.
+	 *
+	 * Only notify for higher subchannel sets/channel subsystems if the
+	 * guest has enabled it.
+	 */
+	guest_cssid = ((css->max_cssid == 0) && (sch->cssid == VIRTUAL_CSSID)) ?
+		0 : sch->cssid;
+	no_crw = (sch->ssid > css->max_ssid) ||
+		(guest_cssid > css->max_cssid) ||
+		((css->max_cssid == 0) && (sch->cssid != VIRTUAL_CSSID));
+	if (!no_crw) {
+		css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM,
+			      ((css->max_ssid > 0) || (css->max_cssid > 0)) ?
+			      1 : 0, sch->schid);
+		if ((css->max_ssid > 0) || (css->max_cssid > 0))
+			css_queue_crw(kvm, CRW_RSC_SCH, CRW_ERC_IPARM, 0,
+				      (guest_cssid << 8) | (sch->ssid << 4));
+	}
+	kfree(sch);
+	return 0;
+}
+
+int kvm_s390_process_ccw_hotplug(struct kvm *kvm,
+				 struct kvm_s390_sch_info *sch_info)
+{
+	struct kvm_subch *sch;
+
+	trace_kvm_s390_ccw_hotplug(sch_info->cssid, sch_info->ssid,
+				   sch_info->schid, sch_info->add);
+	/* We currently support only virtual subchannels. */
+	if (!sch_info->virtual)
+		return -EINVAL;
+
+	/* Virtual subchannels must be in the virtual css. */
+	if (sch_info->virtual && (sch_info->cssid != VIRTUAL_CSSID))
+		return -EINVAL;
+	/* Userspace always notifies with the real cssid. */
+	sch = css_find_subch(kvm, 1, sch_info->cssid, sch_info->ssid,
+			     sch_info->schid);
+	if (sch_info->add) {
+		/* New device. */
+		if (sch)
+			return -EINVAL;
+		return css_add_subchannel(kvm, sch_info);
+	} else {
+		/* Device gone. */
+		if (!sch)
+			return -EINVAL;
+		return css_remove_subchannel(kvm, sch);
+	}
+}
+
+int kvm_s390_process_chp_hotplug(struct kvm *kvm,
+				 struct kvm_s390_chp_info *chp_info)
+{
+	if (!chp_info->virtual)
+		/* Not supported for now. */
+		return -EINVAL;
+
+	/* Virtual channel paths must be in the virtual css. */
+	if (chp_info->virtual && (chp_info->cssid != VIRTUAL_CSSID))
+		return -EINVAL;
+	if (chp_info->add)
+		return css_add_virtual_chpid(kvm, chp_info->cssid,
+					     chp_info->chpid, chp_info->type);
+	else
+		return css_remove_virtual_chpid(kvm, chp_info->cssid,
+						chp_info->chpid);
+}
+
+int kvm_s390_enable_css(struct kvm *kvm)
+{
+	if (kvm->arch.css_support)
+		return 0;
+
+	kvm->arch.css = kzalloc(sizeof(*kvm->arch.css), GFP_KERNEL);
+	if (!kvm->arch.css)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&kvm->arch.css->pending_crws);
+	INIT_LIST_HEAD(&kvm->arch.css->crw_inti.list);
+	kvm->arch.css->crw_inti.type = KVM_S390_MCHK;
+	kvm->arch.css->crw_inti.mchk.mcic = 0x00400f1d40330000;
+	kvm->arch.css->crw_inti.mchk.cr14 = 1 << 28;
+	kvm->arch.css->crw_inti.nondyn = 1;
+	kvm->arch.css->do_crw_mchk = 1;
+	atomic_set(&kvm->arch.css->chnmon_active, 0);
+	kvm->arch.css_support = 1;
+	trace_kvm_s390_enable_kernel_css(kvm);
+	return 0;
+}
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index 754dc9e..9ab2efd 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -273,6 +273,7 @@ static const intercept_handler_t intercept_funcs[] = {
 	[0x0C >> 2] = handle_instruction_and_prog,
 	[0x10 >> 2] = handle_noop,
 	[0x14 >> 2] = handle_noop,
+	[0x18 >> 2] = handle_noop,
 	[0x1C >> 2] = kvm_s390_handle_wait,
 	[0x20 >> 2] = handle_validity,
 	[0x28 >> 2] = handle_stop,
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index edc065f..072828b 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -370,6 +370,7 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
 		
 		rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
 				     __LC_MCK_NEW_PSW, sizeof(psw_t));
+
 		if (rc == -EFAULT)
 			exception = 1;
 		break;
@@ -596,7 +597,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_lock_bh(&li->lock);
 			list_for_each_entry_safe(inti, n, &li->list, list) {
 				if (__interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -607,7 +608,8 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_unlock_bh(&li->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -622,7 +624,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_lock(&fi->lock);
 			list_for_each_entry_safe(inti, n, &fi->list, list) {
 				if (__interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -633,7 +635,8 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
 			spin_unlock(&fi->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -654,7 +657,7 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			list_for_each_entry_safe(inti, n, &li->list, list) {
 				if ((inti->type == KVM_S390_MCHK) &&
 				    __interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -665,7 +668,8 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			spin_unlock_bh(&li->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -677,7 +681,7 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			list_for_each_entry_safe(inti, n, &fi->list, list) {
 				if ((inti->type == KVM_S390_MCHK) &&
 				    __interrupt_is_deliverable(vcpu, inti)) {
-					list_del(&inti->list);
+					list_del_init(&inti->list);
 					deliver = 1;
 					break;
 				}
@@ -688,7 +692,8 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
 			spin_unlock(&fi->lock);
 			if (deliver) {
 				__do_deliver_interrupt(vcpu, inti);
-				kfree(inti);
+				if (!inti->nondyn)
+					kfree(inti);
 			}
 		} while (deliver);
 	}
@@ -716,14 +721,100 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
 	return 0;
 }
 
-int kvm_s390_inject_vm(struct kvm *kvm,
-		       struct kvm_s390_interrupt *s390int)
+int kvm_s390_inject_internal(struct kvm *kvm,
+			     struct kvm_s390_interrupt_info *inti)
 {
 	struct kvm_s390_local_interrupt *li;
 	struct kvm_s390_float_interrupt *fi;
-	struct kvm_s390_interrupt_info *inti, *iter;
+	struct kvm_s390_interrupt_info *iter;
 	int sigcpu;
 
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	if (!is_ioint(inti->type))
+		list_add_tail(&inti->list, &fi->list);
+	else {
+		/* Keep I/O interrupts sorted in isc order. */
+		list_for_each_entry(iter, &fi->list, list) {
+			if (!is_ioint(iter->type))
+				continue;
+			if (iter->io.io_int_word <= inti->io.io_int_word)
+				continue;
+			break;
+		}
+		list_add_tail(&inti->list, &iter->list);
+	}
+	atomic_set(&fi->active, 1);
+	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
+	if (sigcpu == KVM_MAX_VCPUS) {
+		do {
+			sigcpu = fi->next_rr_cpu++;
+			if (sigcpu == KVM_MAX_VCPUS)
+				sigcpu = fi->next_rr_cpu = 0;
+		} while (fi->local_int[sigcpu] == NULL);
+	}
+	li = fi->local_int[sigcpu];
+	spin_lock_bh(&li->lock);
+	atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+	if (waitqueue_active(&li->wq))
+		wake_up_interruptible(&li->wq);
+	spin_unlock_bh(&li->lock);
+	spin_unlock(&fi->lock);
+	return 0;
+}
+
+int kvm_s390_dequeue_internal(struct kvm *kvm,
+			      struct kvm_s390_interrupt_info *inti)
+{
+	struct kvm_s390_float_interrupt *fi;
+
+	if (!inti)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	list_del_init(&inti->list);
+	if (list_empty(&fi->list))
+		atomic_set(&fi->active, 0);
+	spin_unlock(&fi->lock);
+	mutex_unlock(&kvm->lock);
+	return 0;
+}
+
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, u64 cr6)
+{
+	struct kvm_s390_float_interrupt *fi;
+	struct kvm_s390_interrupt_info *inti, *iter;
+
+	mutex_lock(&kvm->lock);
+	fi = &kvm->arch.float_int;
+	spin_lock(&fi->lock);
+	inti = NULL;
+	list_for_each_entry(iter, &fi->list, list) {
+		if (!is_ioint(iter->type))
+			continue;
+		if ((cr6 & iter->io.io_int_word) == 0)
+			continue;
+		inti = iter;
+		break;
+	}
+	if (inti)
+		list_del_init(&inti->list);
+	if (list_empty(&fi->list))
+		atomic_set(&fi->active, 0);
+	spin_unlock(&fi->lock);
+	mutex_unlock(&kvm->lock);
+	return inti;
+}
+
+
+int kvm_s390_inject_vm(struct kvm *kvm,
+		       struct kvm_s390_interrupt *s390int)
+{
+	struct kvm_s390_interrupt_info *inti;
+	int rc;
+
 	inti = kzalloc(sizeof(*inti), GFP_KERNEL);
 	if (!inti)
 		return -ENOMEM;
@@ -776,39 +867,9 @@ int kvm_s390_inject_vm(struct kvm *kvm,
 				 2);
 
 	mutex_lock(&kvm->lock);
-	fi = &kvm->arch.float_int;
-	spin_lock(&fi->lock);
-	if (!is_ioint(inti->type))
-		list_add_tail(&inti->list, &fi->list);
-	else {
-		/* Keep I/O interrupts sorted in isc order. */
-		list_for_each_entry(iter, &fi->list, list) {
-			if (!is_ioint(iter->type))
-				continue;
-			if (iter->io.io_int_word <= inti->io.io_int_word)
-				continue;
-			break;
-		}
-		list_add_tail(&inti->list, &iter->list);
-	}
-	atomic_set(&fi->active, 1);
-	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
-	if (sigcpu == KVM_MAX_VCPUS) {
-		do {
-			sigcpu = fi->next_rr_cpu++;
-			if (sigcpu == KVM_MAX_VCPUS)
-				sigcpu = fi->next_rr_cpu = 0;
-		} while (fi->local_int[sigcpu] == NULL);
-	}
-	li = fi->local_int[sigcpu];
-	spin_lock_bh(&li->lock);
-	atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
-	if (waitqueue_active(&li->wq))
-		wake_up_interruptible(&li->wq);
-	spin_unlock_bh(&li->lock);
-	spin_unlock(&fi->lock);
+	rc = kvm_s390_inject_internal(kvm, inti);
 	mutex_unlock(&kvm->lock);
-	return 0;
+	return rc;
 }
 
 int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
diff --git a/arch/s390/kvm/ioinst.c b/arch/s390/kvm/ioinst.c
new file mode 100644
index 0000000..29c4629
--- /dev/null
+++ b/arch/s390/kvm/ioinst.c
@@ -0,0 +1,797 @@
+/*
+ * Handling of channel I/O instructions for kvm
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#include <linux/kvm.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <asm/cio.h>
+#include <asm/crw.h>
+#include <asm/orb.h>
+#include <asm/schib.h>
+#include <asm/schid.h>
+#include <asm/scsw.h>
+#include "kvm-s390.h"
+#include "gaccess.h"
+#include "trace.h"
+
+#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_XSCH                       0x76
+
+static int ioinst_disassemble_sch_ident(u32 value, int *m, int *cssid, int *ssid,
+					int *schid)
+{
+	if (!(value & 0x00010000))
+		return -EINVAL;
+
+	if (!(value & 0x00080000)) {
+		if (value & 0xff000000)
+			return -EINVAL;
+		*m = 0;
+		*cssid = 0;
+	} else {
+		*m = 1;
+		*cssid = (value & 0xff000000) >> 24;
+	}
+	*ssid = (value & 0x00060000) >> 17;
+	*schid = value & 0x0000ffff;
+	return 0;
+}
+
+static int ioinst_handle_xsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("xsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_xsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_csch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("csch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_csch(vcpu, sch);
+
+	if (ret == -ENODEV) {
+		*cc = 3;
+	} else {
+		*cc = 0;
+	}
+	return ret;
+}
+
+static int ioinst_handle_hsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("hsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_hsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_schib_valid(struct schib *schib)
+{
+	if (schib->pmcw.res5 != 0)
+		return 0;
+
+	if ((schib->pmcw.unused1 != 0) || (schib->pmcw.unused2 != 0))
+		return 0;
+
+	/* Disallow extended measurements for now. */
+	if (schib->pmcw.xmwme)
+		return 0;
+
+	return 1;
+}
+
+static int ioinst_handle_msch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	struct schib schib;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (copy_from_guest(vcpu, &schib, addr, sizeof(struct schib))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	if (!ioinst_schib_valid(&schib)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("msch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_msch(vcpu, sch, &schib);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case 0:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_orb_valid(union orb *orb)
+{
+	if (orb->cmd.res2 != 0)
+		return 0;
+
+	if (orb->cmd.zero != 0)
+		return 0;
+
+	if ((orb->cmd.cpa & 0x80000000) != 0)
+		return 0;
+
+	return 1;
+}
+
+static int ioinst_handle_ssch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	union orb orb;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (copy_from_guest(vcpu, &orb, addr, sizeof(union orb))) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	if (!ioinst_orb_valid(&orb)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("ssch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_ssch(vcpu, sch, addr);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EBUSY:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_stcrw(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	int ret;
+	u32 addr;
+
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	ret = css_do_stcrw(vcpu, addr);
+	/* 0 - crw stored, 1 - zeroes stored */
+	if (ret >= 0) {
+		*cc = ret;
+		ret = 0;
+	}
+	return 0;
+}
+
+static int ioinst_handle_stsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	u32 addr;
+	int ret;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	if (addr & 3) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("stsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch) {
+		ret = copy_to_guest(vcpu, addr, sch->curr_status,
+				    sizeof(*sch->curr_status));
+		if (ret < 0)
+			kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		else
+			*cc = 0;
+	} else {
+		if (css_schid_final(vcpu->kvm, m ? cssid :
+				    (cssid ? cssid : VIRTUAL_CSSID),
+				    ssid, schid)) {
+			*cc = 3; /* No more subchannels in this css/ss */
+			ret = 0;
+		} else {
+			struct schib schib;
+
+			/* Store an empty schib. */
+			memset(&schib, 0, sizeof(struct schib));
+			ret = copy_to_guest(vcpu, addr, &schib, sizeof(schib));
+			if (ret < 0)
+				kvm_s390_inject_program_int(vcpu,
+							    PGM_ADDRESSING);
+			else
+				*cc = 0;
+		}
+	}
+	return ret;
+}
+
+static int ioinst_handle_tsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1, u32 ipb)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	u32 addr;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	trace_kvm_s390_handle_ioinst("tsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_tsch(vcpu, sch, addr);
+	/* 0 - status pending, 1 - not status pending */
+	switch (ret) {
+	case -EBUSY:
+		*cc = 0;
+		break;
+	case 0:
+		*cc = 1;
+		break;
+	case -ENODEV:
+		*cc = 3;
+		break;
+	}
+	return ret;
+}
+
+struct chsc_req {
+	u16 len;
+	u16 command;
+	u32 param0;
+	u32 param1;
+	u32 param2;
+} __attribute__((packed));
+
+struct chsc_resp {
+	u16 len;
+	u16 code;
+	u32 param;
+	char data[0];
+} __attribute__((packed));
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+static void ioinst_handle_chsc_scpd(struct kvm *kvm, struct chsc_req *req,
+				    struct chsc_resp *res)
+{
+	u16 resp_code;
+	int rfmt;
+	u16 cssid;
+	u8 f_chpid, l_chpid;
+	int desc_size;
+
+	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;
+	if (cssid != 0)
+		if (!(req->param0 & 0x20000000) || (cssid != VIRTUAL_CSSID)) {
+			resp_code = 0x0008;
+			goto out_err;
+		}
+
+	if ((cssid == 0) && (!(req->param0 & 0x20000000)))
+		cssid = VIRTUAL_CSSID;
+
+	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(kvm, 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;
+}
+
+/* For now, always the same characteristics. */
+static u32 general_chars[510] = { 0x03000000, 0x00059000, 0, };
+static u32 chsc_chars[508] = { 0x40000000, 0x00040000, 0, };
+
+static void ioinst_handle_chsc_scsc(struct kvm *kvm, struct chsc_req *req,
+				    struct chsc_resp *res)
+{
+	u8 cssid;
+	u16 resp_code;
+
+	if (req->param0 & 0x000f0000) {
+		resp_code = 0x0007;
+		goto out_err;
+	}
+	cssid = (req->param0 & 0x0000ff00) >> 8;
+	if (cssid != 0)
+		if (!(req->param0 & 0x20000000) || (cssid != VIRTUAL_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;
+
+	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_SC_MCSSE 0x0
+#define CHSC_SDA_SC_MSS 0x2
+
+static void ioinst_handle_chsc_sda(struct kvm *kvm, struct chsc_req *req,
+				   struct chsc_resp *res)
+{
+	u16 resp_code = 0x0001;
+	u16 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_SC_MCSSE:
+		ret = css_enable_mcsse(kvm);
+		if (ret == -EINVAL) {
+			resp_code = 0x0101;
+			goto out;
+		}
+		break;
+	case CHSC_SDA_SC_MSS:
+		ret = css_enable_mss(kvm);
+		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;
+}
+
+static int ioinst_handle_chsc(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	struct chsc_req *req;
+	struct chsc_resp *res;
+	u64 addr;
+	int reg;
+	int ret;
+
+	reg = (ipb >> 20) & 0x00f;
+	addr = vcpu->run->s.regs.gprs[reg];
+	if (addr & 0x0000000000000fff) {
+		kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+		return -EIO;
+	}
+	req = (struct chsc_req *)get_zeroed_page(GFP_KERNEL);
+	if (!req)
+		return -EFAULT;
+	if (copy_from_guest(vcpu, req, addr, sizeof(*req))) {
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+		return -EFAULT;
+	}
+	if ((req->len & 3) || (req->len < 16) || (req->len > 4088)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	res = (struct chsc_resp *)((unsigned long)req + req->len);
+	switch (req->command) {
+	case CHSC_SCSC:
+		ioinst_handle_chsc_scsc(vcpu->kvm, req, res);
+		break;
+	case CHSC_SCPD:
+		ioinst_handle_chsc_scpd(vcpu->kvm, req, res);
+		break;
+	case CHSC_SDA:
+		ioinst_handle_chsc_sda(vcpu->kvm, req, res);
+		break;
+	default:
+		ioinst_handle_chsc_unimplemented(res);
+		break;
+	}
+	ret = copy_to_guest(vcpu, addr + req->len, res, res->len);
+	if (ret < 0)
+		kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+	else
+		*cc = 0;
+	free_page((unsigned long)req);
+	return ret;
+}
+
+static int ioinst_handle_tpi(struct kvm_vcpu *vcpu, int *cc, u32 ipb)
+{
+	u32 addr;
+	int lowcore;
+
+	addr = ipb >> 28;
+	if (addr > 0)
+		addr = vcpu->run->s.regs.gprs[addr];
+
+	addr += (ipb & 0xfff0000) >> 16;
+	lowcore = addr ? 0 : 1;
+	*cc = css_do_tpi(vcpu, addr, lowcore);
+	return 0;
+}
+
+static int ioinst_handle_schm(struct kvm_vcpu *vcpu, u64 reg1, u64 reg2,
+			      u32 ipb)
+{
+	u8 mbk;
+	int update;
+	int dct;
+
+	if (reg1 & 0x000000000ffffffc) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	mbk = (reg1 & 0x00000000f0000000) >> 28;
+	update = (reg1 & 0x0000000000000002) >> 1;
+	dct = reg1 & 0x0000000000000001;
+
+	if (update && (reg2 & 0x0000000000000fff)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	css_do_schm(vcpu, mbk, update, dct, update ? reg2 : 0);
+
+	return 0;
+}
+
+static int ioinst_handle_rsch(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	int m, cssid, ssid, schid;
+	struct kvm_subch *sch;
+	int ret = -ENODEV;
+
+	if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	trace_kvm_s390_handle_ioinst("rsch", cssid, ssid, schid);
+	sch = css_find_subch(vcpu->kvm, m, cssid, ssid, schid);
+	if (sch)
+		ret = css_do_rsch(vcpu, sch);
+
+	switch (ret) {
+	case -ENODEV:
+		*cc = 3;
+		break;
+	case -EINVAL:
+		*cc = 2;
+		break;
+	case -EREMOTE:
+		*cc = 0;
+		break;
+	default:
+		*cc = 1;
+		break;
+	}
+
+	return ret;
+
+}
+
+static int ioinst_handle_rchp(struct kvm_vcpu *vcpu, int *cc, u64 reg1)
+{
+	u8 cssid;
+	u8 chpid;
+	int ret;
+	struct kvm_s390_css_data *css = vcpu->kvm->arch.css;
+
+	if (reg1 & 0xff00ff00) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+
+	cssid = (reg1 >> 16) & 0xff;
+	chpid = reg1 & 0xff;
+
+	if (cssid > css->max_cssid) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		ret = -EIO;
+	} else if (!css_chpid_in_use(vcpu->kvm, cssid, chpid)) {
+		ret = 0;
+		*cc = 3;
+	} else {
+		/*
+		 * Since we only support virtual (i.e. not real) channel paths,
+		 * there's nothing left for us to do save signaling success.
+		 */
+		css_queue_crw(vcpu->kvm, CRW_RSC_CPATH, CRW_ERC_INIT,
+			      css->max_cssid > 0 ? 1 : 0, chpid);
+		if (css->max_cssid > 0)
+			css_queue_crw(vcpu->kvm, CRW_RSC_CPATH, CRW_ERC_INIT, 0,
+				      cssid << 8);
+		ret = 0;
+		*cc = 0;
+	}
+
+	return ret;
+}
+
+static int ioinst_handle_sal(struct kvm_vcpu *vcpu, u64 reg1)
+{
+	/* We do not provide address limit checking, so let's suppress it. */
+	if (reg1 & 0x000000008000ffff) {
+		kvm_s390_inject_program_int(vcpu, PGM_OPERAND);
+		return -EIO;
+	}
+	return 0;
+}
+
+int kvm_css_instruction(struct kvm_vcpu *vcpu)
+{
+	int ret;
+	int cc;
+	int no_cc = 0;
+
+	if ((vcpu->arch.sie_block->ipa & 0xff00) != 0xb200)
+		/* Not handled for now. */
+		return -EOPNOTSUPP;
+
+	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
+	case PRIV_XSCH:
+		ret = ioinst_handle_xsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_CSCH:
+		ret = ioinst_handle_csch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_HSCH:
+		ret = ioinst_handle_hsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_MSCH:
+		ret = ioinst_handle_msch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_SSCH:
+		ret = ioinst_handle_ssch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_STCRW:
+		ret = ioinst_handle_stcrw(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_STSCH:
+		ret = ioinst_handle_stsch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					  vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_TSCH:
+		ret = ioinst_handle_tsch(vcpu, &cc, vcpu->run->s.regs.gprs[1],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_CHSC:
+		ret = ioinst_handle_chsc(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_TPI:
+		ret = ioinst_handle_tpi(vcpu, &cc, vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_SCHM:
+		no_cc = 1;
+		ret = ioinst_handle_schm(vcpu, vcpu->run->s.regs.gprs[1],
+					 vcpu->run->s.regs.gprs[2],
+					 vcpu->arch.sie_block->ipb);
+		break;
+	case PRIV_RSCH:
+		ret = ioinst_handle_rsch(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_RCHP:
+		ret = ioinst_handle_rchp(vcpu, &cc, vcpu->run->s.regs.gprs[1]);
+		break;
+	case PRIV_STCPS:
+		/* We do not provide this instruction, it is suppressed. */
+		no_cc = 1;
+		ret = 0;
+		break;
+	case PRIV_SAL:
+		no_cc = 1;
+		ret = ioinst_handle_sal(vcpu, vcpu->run->s.regs.gprs[1]);
+		break;
+	default:
+		/* Give user space a go at this. */
+		return -EOPNOTSUPP;
+	}
+	if ((ret != -EFAULT) && (ret != -EIO) && (ret != -EREMOTE))
+		ret = 0;
+
+	if ((!ret || (ret == -EREMOTE)) && !no_cc) {
+		vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
+		vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44;
+	}
+
+	return (ret == -EREMOTE) ? ret : 0;
+}
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 4b0681c..5ee9e5e 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -141,6 +141,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_SYNC_REGS:
 	case KVM_CAP_ONE_REG:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_S390_CSS_SUPPORT:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
@@ -183,6 +184,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		r = kvm_s390_inject_vm(kvm, &s390int);
 		break;
 	}
+	case KVM_S390_CCW_HOTPLUG: {
+		struct kvm_s390_sch_info sch_info;
+
+		r = -EFAULT;
+		if (copy_from_user(&sch_info, argp, sizeof(sch_info)))
+			break;
+		r = kvm_s390_process_ccw_hotplug(kvm, &sch_info);
+		break;
+	}
+	case KVM_S390_CHP_HOTPLUG: {
+		struct kvm_s390_chp_info chp_info;
+
+		r = -EFAULT;
+		if (copy_from_user(&chp_info, argp, sizeof(chp_info)))
+			break;
+		r = kvm_s390_process_chp_hotplug(kvm, &chp_info);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
@@ -235,6 +254,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 		if (!kvm->arch.gmap)
 			goto out_nogmap;
 	}
+
+	kvm->arch.css_support = 0;
+
 	return 0;
 out_nogmap:
 	debug_unregister(kvm->arch.dbf);
@@ -657,6 +679,7 @@ rerun_vcpu:
 	case KVM_EXIT_INTR:
 	case KVM_EXIT_S390_RESET:
 	case KVM_EXIT_S390_UCONTROL:
+	case KVM_EXIT_S390_SCH_IO:
 		break;
 	default:
 		BUG();
@@ -817,6 +840,9 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_S390_CSS_SUPPORT:
+		r = kvm_s390_enable_css(vcpu->kvm);
+		break;
 	default:
 		r = -EINVAL;
 		break;
@@ -919,6 +945,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
 		break;
 	}
+	case KVM_S390_CSS_NOTIFY:
+	{
+		struct kvm_css_notify notify;
+		r = -EFAULT;
+		if (copy_from_user(&notify, argp, sizeof(notify)))
+			break;
+		r = kvm_arch_vcpu_ioctl_css_notify(vcpu, &notify);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 7f50229..8bf25f9 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -76,6 +76,11 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 		struct kvm_s390_interrupt *s390int);
 int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
 int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
+int kvm_s390_inject_internal(struct kvm *kvm,
+			     struct kvm_s390_interrupt_info *inti);
+int kvm_s390_dequeue_internal(struct kvm *kvm,
+			      struct kvm_s390_interrupt_info *inti);
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, u64 cr6);
 
 /* implemented in priv.c */
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
@@ -94,4 +99,37 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu,
 /* implemented in diag.c */
 int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
 
+/* implemented in ioinst.c */
+int kvm_css_instruction(struct kvm_vcpu *vcpu);
+
+/* implemented in css.c */
+struct schib;
+int kvm_arch_vcpu_ioctl_css_notify(struct kvm_vcpu *vcpu,
+				   struct kvm_css_notify *notify);
+int kvm_s390_process_ccw_hotplug(struct kvm *kvm,
+				 struct kvm_s390_sch_info *sch_info);
+int kvm_s390_process_chp_hotplug(struct kvm *kvm,
+				 struct kvm_s390_chp_info *chp_info);
+int kvm_s390_enable_css(struct kvm *kvm);
+struct kvm_subch *css_find_subch(struct kvm *kvm, u8 m, u8 cssid, u8 ssid,
+				 u16 schid);
+int css_do_stsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u32 addr);
+int css_schid_final(struct kvm *kvm, u8 cssid, u8 ssid, u16 schid);
+int css_do_msch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, struct schib *schib);
+int css_do_xsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_csch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_hsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_ssch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u64 orb);
+int css_do_tsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch, u32 addr);
+int css_do_stcrw(struct kvm_vcpu *vcpu, u32 addr);
+int css_do_tpi(struct kvm_vcpu *vcpu, u32 addr, int lowcore);
+int css_collect_chp_desc(struct kvm *kvm, u8 cssid, u8 f_chpid, u8 l_chpid,
+                         int rfmt, void *buf);
+void css_do_schm(struct kvm_vcpu *vcpu, u8 mbk, int update, int dct, uint64_t mbo);
+int css_enable_mcsse(struct kvm *kvm);
+int css_enable_mss(struct kvm *kvm);
+int css_do_rsch(struct kvm_vcpu *vcpu, struct kvm_subch *sch);
+int css_do_rchp(struct kvm_vcpu *vcpu, u8 cssid, u8 chpid);
+int css_chpid_in_use(struct kvm *kvm, u8 cssid, u8 chpid);
+void css_queue_crw(struct kvm *kvm, u8 rsc, u8 erc, int chain, u16 rsid);
 #endif
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 8b79a94..8b128e4 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -138,7 +138,12 @@ static int handle_skey(struct kvm_vcpu *vcpu)
 static int handle_io_inst(struct kvm_vcpu *vcpu)
 {
 	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
-	/* condition code 3 */
+
+	if (vcpu->kvm->arch.css_support)
+		/* Use in-kernel css support. */
+		return kvm_css_instruction(vcpu);
+
+	/* Set cc 3 to stop guest issueing I/O instructions. */
 	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
 	return 0;
diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h
index 95fbc1a..6d2059e 100644
--- a/arch/s390/kvm/trace-s390.h
+++ b/arch/s390/kvm/trace-s390.h
@@ -203,6 +203,73 @@ TRACE_EVENT(kvm_s390_stop_request,
 		      __entry->action_bits)
 	);
 
+/*
+ * Trace point for enabling in-kernel channel subsystem support.
+ */
+TRACE_EVENT(kvm_s390_enable_kernel_css,
+	    TP_PROTO(void *kvm),
+	    TP_ARGS(kvm),
+
+	    TP_STRUCT__entry(
+		    __field(void *, kvm)
+		),
+
+	    TP_fast_assign(
+		    __entry->kvm = kvm;
+		),
+
+	    TP_printk("enabling in-kernel css support (kvm @ %p)\n",
+		    __entry->kvm)
+    );
+
+/*
+ * Trace point for user space subchannel I/O notification.
+ */
+TRACE_EVENT(kvm_s390_css_notify,
+	    TP_PROTO(u8 cssid, u8 ssid, u16 schid),
+	    TP_ARGS(cssid, ssid, schid),
+
+	    TP_STRUCT__entry(
+		    __field(u8, cssid)
+		    __field(u8, ssid)
+		    __field(u16, schid)
+		),
+
+	    TP_fast_assign(
+		    __entry->cssid = cssid;
+		    __entry->ssid = ssid;
+		    __entry->schid = schid;
+		),
+
+	    TP_printk("css notification for subchannel %x.%x.%04x\n",
+		      __entry->cssid, __entry->ssid, __entry->schid)
+    );
+
+/*
+ * Trace point for user space subchannel hotplug notification.
+ */
+TRACE_EVENT(kvm_s390_ccw_hotplug,
+	    TP_PROTO(u8 cssid, u8 ssid, u16 schid, int add),
+	    TP_ARGS(cssid, ssid, schid, add),
+
+	    TP_STRUCT__entry(
+		    __field(u8, cssid)
+		    __field(u8, ssid)
+		    __field(u16, schid)
+		    __field(int, add)
+		),
+
+	    TP_fast_assign(
+		    __entry->cssid = cssid;
+		    __entry->ssid = ssid;
+		    __entry->schid = schid;
+		    __entry->add = add;
+		),
+
+	    TP_printk("hotplug event for subchannel %x.%x.%04x (%s)\n",
+		      __entry->cssid, __entry->ssid, __entry->schid,
+		      __entry->add ? "attach" : "detach")
+    );
 
 #endif /* _TRACE_KVMS390_H */
 
diff --git a/arch/s390/kvm/trace.h b/arch/s390/kvm/trace.h
index 2b29e62..5f743f3 100644
--- a/arch/s390/kvm/trace.h
+++ b/arch/s390/kvm/trace.h
@@ -335,6 +335,28 @@ TRACE_EVENT(kvm_s390_handle_stsi,
 			   __entry->addr)
 	);
 
+TRACE_EVENT(kvm_s390_handle_ioinst,
+	    TP_PROTO(char *name, u8 cssid, u8 ssid, u16 schid),
+	    TP_ARGS(name, cssid, ssid, schid),
+
+	    TP_STRUCT__entry(
+		__field(char *, name)
+		__field(u8, cssid)
+		__field(u8, ssid)
+		__field(u16, schid)
+		),
+
+	    TP_fast_assign(
+		__entry->name = name;
+		__entry->cssid = cssid;
+		__entry->ssid = ssid;
+		__entry->schid = schid;
+		),
+
+	    TP_printk("I/O instruction %s (%x.%x.%04x)", __entry->name,
+		      __entry->cssid, __entry->ssid, __entry->schid)
+    );
+
 #endif /* _TRACE_KVM_H */
 
 /* This part must be outside protection */
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index e0c3d87..4c48923 100644
--- a/include/linux/kvm.h
+++ b/include/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];
 	};
@@ -480,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 */
@@ -625,6 +673,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
 #define KVM_CAP_PPC_ALLOC_HTAB 80
+#define KVM_CAP_S390_CSS_SUPPORT 81
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -838,6 +887,10 @@ struct kvm_s390_ucas_mapping {
 #define KVM_PPC_GET_SMMU_INFO	  _IOR(KVMIO,  0xa6, struct kvm_ppc_smmu_info)
 /* Available with KVM_CAP_PPC_ALLOC_HTAB */
 #define KVM_PPC_ALLOCATE_HTAB	  _IOWR(KVMIO, 0xa7, __u32)
+/* Available with KVM_CAP_S390_CSS_SUPPORT */
+#define KVM_S390_CSS_NOTIFY       _IOW(KVMIO, 0xae, struct kvm_css_notify)
+#define KVM_S390_CCW_HOTPLUG      _IOW(KVMIO, 0xab, struct kvm_s390_sch_info)
+#define KVM_S390_CHP_HOTPLUG      _IOW(KVMIO, 0xac, struct kvm_s390_chp_info)
 
 /*
  * ioctls for vcpu fds
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
index 7ef9e75..939ba8b 100644
--- a/include/trace/events/kvm.h
+++ b/include/trace/events/kvm.h
@@ -14,7 +14,7 @@
 	ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR),	\
 	ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
 	ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL),	\
-	ERSN(S390_UCONTROL)
+	ERSN(S390_UCONTROL), ERSN(S390_SCH_IO)
 
 TRACE_EVENT(kvm_userspace_exit,
 	    TP_PROTO(__u32 reason, int errno),
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index bcf973e..59c40aa 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1817,7 +1817,8 @@ static long kvm_vcpu_ioctl(struct file *filp,
 	 * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
 	 * so vcpu_load() would break it.
 	 */
-	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+	    ioctl == KVM_S390_CSS_NOTIFY)
 		return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif
 
-- 
1.7.11.4

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

end of thread, other threads:[~2012-08-07 14:53 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-07 14:52 [RFC PATCH 0/7] s390: virtual css host support Cornelia Huck
2012-08-07 14:52 ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 1/7] s390/kvm: Support for I/O interrupts Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 2/7] s390/kvm: Add support for machine checks Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 3/7] s390/kvm: In-kernel handling of I/O instructions Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 4/7] s390: Move css limits from drivers/s390/cio/ to include/asm/ Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 5/7] s390: Make some css-related structures usable by non-cio code Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 6/7] s390/kvm: Base infrastructure for enabling capabilities Cornelia Huck
2012-08-07 14:52   ` [Qemu-devel] " Cornelia Huck
2012-08-07 14:52 ` [PATCH 7/7] s390/kvm: In-kernel channel subsystem support 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.