summaryrefslogtreecommitdiff
path: root/arch/s390/kvm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r--arch/s390/kvm/Kconfig1
-rw-r--r--arch/s390/kvm/Makefile2
-rw-r--r--arch/s390/kvm/diag.c34
-rw-r--r--arch/s390/kvm/gaccess.h243
-rw-r--r--arch/s390/kvm/intercept.c38
-rw-r--r--arch/s390/kvm/interrupt.c35
-rw-r--r--arch/s390/kvm/kvm-s390.c80
-rw-r--r--arch/s390/kvm/kvm-s390.h28
-rw-r--r--arch/s390/kvm/priv.c55
-rw-r--r--arch/s390/kvm/sie64a.S98
-rw-r--r--arch/s390/kvm/sigp.c96
11 files changed, 466 insertions, 244 deletions
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
index f66a1bdbb61d..a21634173a66 100644
--- a/arch/s390/kvm/Kconfig
+++ b/arch/s390/kvm/Kconfig
@@ -37,6 +37,5 @@ config KVM
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
source drivers/vhost/Kconfig
-source drivers/virtio/Kconfig
endif # VIRTUALIZATION
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index 860d26514c08..3975722bb19d 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 sie64a.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
obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c
index 9e4c84187cf5..8943e82cd4d9 100644
--- a/arch/s390/kvm/diag.c
+++ b/arch/s390/kvm/diag.c
@@ -1,7 +1,7 @@
/*
* diag.c - handling diagnose instructions
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2011
*
* 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)
@@ -15,6 +15,34 @@
#include <linux/kvm_host.h>
#include "kvm-s390.h"
+static int diag_release_pages(struct kvm_vcpu *vcpu)
+{
+ unsigned long start, end;
+ unsigned long prefix = vcpu->arch.sie_block->prefix;
+
+ start = vcpu->arch.guest_gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
+ end = vcpu->arch.guest_gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096;
+
+ if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end
+ || start < 2 * PAGE_SIZE)
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+ VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end);
+ vcpu->stat.diagnose_10++;
+
+ /* we checked for start > end above */
+ if (end < prefix || start >= prefix + 2 * PAGE_SIZE) {
+ gmap_discard(start, end, vcpu->arch.gmap);
+ } else {
+ if (start < prefix)
+ gmap_discard(start, prefix, vcpu->arch.gmap);
+ if (end >= prefix)
+ gmap_discard(prefix + 2 * PAGE_SIZE,
+ end, vcpu->arch.gmap);
+ }
+ return 0;
+}
+
static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
{
VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
@@ -42,7 +70,7 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
return -EOPNOTSUPP;
}
- atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
+ atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM;
vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL;
vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT;
@@ -57,6 +85,8 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16;
switch (code) {
+ case 0x10:
+ return diag_release_pages(vcpu);
case 0x44:
return __diag_time_slice_end(vcpu);
case 0x308:
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h
index 03c716a0f01f..c86f6ae43f76 100644
--- a/arch/s390/kvm/gaccess.h
+++ b/arch/s390/kvm/gaccess.h
@@ -1,5 +1,5 @@
/*
- * gaccess.h - access guest memory
+ * access.h - access guest memory
*
* Copyright IBM Corp. 2008,2009
*
@@ -22,20 +22,13 @@ static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
unsigned long guestaddr)
{
unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->arch.sie_block->gmsor;
- unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if (guestaddr < 2 * PAGE_SIZE)
guestaddr += prefix;
else if ((guestaddr >= prefix) && (guestaddr < prefix + 2 * PAGE_SIZE))
guestaddr -= prefix;
- if (guestaddr > memsize)
- return (void __user __force *) ERR_PTR(-EFAULT);
-
- guestaddr += origin;
-
- return (void __user *) guestaddr;
+ return (void __user *) gmap_fault(guestaddr, vcpu->arch.gmap);
}
static inline int get_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
@@ -141,11 +134,11 @@ static inline int put_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
unsigned long guestdest,
- const void *from, unsigned long n)
+ void *from, unsigned long n)
{
int rc;
unsigned long i;
- const u8 *data = from;
+ u8 *data = from;
for (i = 0; i < n; i++) {
rc = put_guest_u8(vcpu, guestdest++, *(data++));
@@ -155,12 +148,95 @@ static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
return 0;
}
+static inline int __copy_to_guest_fast(struct kvm_vcpu *vcpu,
+ unsigned long guestdest,
+ void *from, unsigned long n)
+{
+ int r;
+ void __user *uptr;
+ unsigned long size;
+
+ if (guestdest + n < guestdest)
+ return -EFAULT;
+
+ /* simple case: all within one segment table entry? */
+ if ((guestdest & PMD_MASK) == ((guestdest+n) & PMD_MASK)) {
+ uptr = (void __user *) gmap_fault(guestdest, vcpu->arch.gmap);
+
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
+
+ r = copy_to_user(uptr, from, n);
+
+ if (r)
+ r = -EFAULT;
+
+ goto out;
+ }
+
+ /* copy first segment */
+ uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
+
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
+
+ size = PMD_SIZE - (guestdest & ~PMD_MASK);
+
+ r = copy_to_user(uptr, from, size);
+
+ if (r) {
+ r = -EFAULT;
+ goto out;
+ }
+ from += size;
+ n -= size;
+ guestdest += size;
+
+ /* copy full segments */
+ while (n >= PMD_SIZE) {
+ uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
+
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
+
+ r = copy_to_user(uptr, from, PMD_SIZE);
+
+ if (r) {
+ r = -EFAULT;
+ goto out;
+ }
+ from += PMD_SIZE;
+ n -= PMD_SIZE;
+ guestdest += PMD_SIZE;
+ }
+
+ /* copy the tail segment */
+ if (n) {
+ uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
+
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
+
+ r = copy_to_user(uptr, from, n);
+
+ if (r)
+ r = -EFAULT;
+ }
+out:
+ return r;
+}
+
+static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
+ unsigned long guestdest,
+ void *from, unsigned long n)
+{
+ return __copy_to_guest_fast(vcpu, guestdest, from, n);
+}
+
static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
- const void *from, unsigned long n)
+ void *from, unsigned long n)
{
unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->arch.sie_block->gmsor;
- unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
goto slowpath;
@@ -177,15 +253,7 @@ static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
else if ((guestdest >= prefix) && (guestdest < prefix + 2 * PAGE_SIZE))
guestdest -= prefix;
- if (guestdest + n > memsize)
- return -EFAULT;
-
- if (guestdest + n < guestdest)
- return -EFAULT;
-
- guestdest += origin;
-
- return copy_to_user((void __user *) guestdest, from, n);
+ return __copy_to_guest_fast(vcpu, guestdest, from, n);
slowpath:
return __copy_to_guest_slow(vcpu, guestdest, from, n);
}
@@ -206,74 +274,113 @@ static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
return 0;
}
-static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
- unsigned long guestsrc, unsigned long n)
+static inline int __copy_from_guest_fast(struct kvm_vcpu *vcpu, void *to,
+ unsigned long guestsrc,
+ unsigned long n)
{
- unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->arch.sie_block->gmsor;
- unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
+ int r;
+ void __user *uptr;
+ unsigned long size;
- if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
- goto slowpath;
+ if (guestsrc + n < guestsrc)
+ return -EFAULT;
- if ((guestsrc < prefix) && (guestsrc + n > prefix))
- goto slowpath;
+ /* simple case: all within one segment table entry? */
+ if ((guestsrc & PMD_MASK) == ((guestsrc+n) & PMD_MASK)) {
+ uptr = (void __user *) gmap_fault(guestsrc, vcpu->arch.gmap);
- if ((guestsrc < prefix + 2 * PAGE_SIZE)
- && (guestsrc + n > prefix + 2 * PAGE_SIZE))
- goto slowpath;
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
- if (guestsrc < 2 * PAGE_SIZE)
- guestsrc += prefix;
- else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
- guestsrc -= prefix;
+ r = copy_from_user(to, uptr, n);
- if (guestsrc + n > memsize)
- return -EFAULT;
+ if (r)
+ r = -EFAULT;
- if (guestsrc + n < guestsrc)
- return -EFAULT;
+ goto out;
+ }
- guestsrc += origin;
+ /* copy first segment */
+ uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
- return copy_from_user(to, (void __user *) guestsrc, n);
-slowpath:
- return __copy_from_guest_slow(vcpu, to, guestsrc, n);
-}
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
-static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
- unsigned long guestdest,
- const void *from, unsigned long n)
-{
- unsigned long origin = vcpu->arch.sie_block->gmsor;
- unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
+ size = PMD_SIZE - (guestsrc & ~PMD_MASK);
- if (guestdest + n > memsize)
- return -EFAULT;
+ r = copy_from_user(to, uptr, size);
- if (guestdest + n < guestdest)
- return -EFAULT;
+ if (r) {
+ r = -EFAULT;
+ goto out;
+ }
+ to += size;
+ n -= size;
+ guestsrc += size;
+
+ /* copy full segments */
+ while (n >= PMD_SIZE) {
+ uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
+
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
+
+ r = copy_from_user(to, uptr, PMD_SIZE);
+
+ if (r) {
+ r = -EFAULT;
+ goto out;
+ }
+ to += PMD_SIZE;
+ n -= PMD_SIZE;
+ guestsrc += PMD_SIZE;
+ }
+
+ /* copy the tail segment */
+ if (n) {
+ uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
- guestdest += origin;
+ if (IS_ERR((void __force *) uptr))
+ return PTR_ERR((void __force *) uptr);
- return copy_to_user((void __user *) guestdest, from, n);
+ r = copy_from_user(to, uptr, n);
+
+ if (r)
+ r = -EFAULT;
+ }
+out:
+ return r;
}
static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
unsigned long guestsrc,
unsigned long n)
{
- unsigned long origin = vcpu->arch.sie_block->gmsor;
- unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
+ return __copy_from_guest_fast(vcpu, to, guestsrc, n);
+}
- if (guestsrc + n > memsize)
- return -EFAULT;
+static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
+ unsigned long guestsrc, unsigned long n)
+{
+ unsigned long prefix = vcpu->arch.sie_block->prefix;
- if (guestsrc + n < guestsrc)
- return -EFAULT;
+ if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
+ goto slowpath;
- guestsrc += origin;
+ if ((guestsrc < prefix) && (guestsrc + n > prefix))
+ goto slowpath;
+
+ if ((guestsrc < prefix + 2 * PAGE_SIZE)
+ && (guestsrc + n > prefix + 2 * PAGE_SIZE))
+ goto slowpath;
+
+ if (guestsrc < 2 * PAGE_SIZE)
+ guestsrc += prefix;
+ else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
+ guestsrc -= prefix;
- return copy_from_user(to, (void __user *) guestsrc, n);
+ return __copy_from_guest_fast(vcpu, to, guestsrc, n);
+slowpath:
+ return __copy_from_guest_slow(vcpu, to, guestsrc, n);
}
#endif
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index f7b6df45d8be..02434543eabb 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -105,6 +105,7 @@ static intercept_handler_t instruction_handlers[256] = {
[0xae] = kvm_s390_handle_sigp,
[0xb2] = kvm_s390_handle_b2,
[0xb7] = handle_lctl,
+ [0xe5] = kvm_s390_handle_e5,
[0xeb] = handle_lctlg,
};
@@ -131,7 +132,6 @@ static int handle_stop(struct kvm_vcpu *vcpu)
int rc = 0;
vcpu->stat.exit_stop_request++;
- atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
spin_lock_bh(&vcpu->arch.local_int.lock);
if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
@@ -148,6 +148,8 @@ static int handle_stop(struct kvm_vcpu *vcpu)
}
if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
+ atomic_set_mask(CPUSTAT_STOPPED,
+ &vcpu->arch.sie_block->cpuflags);
vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
rc = -EOPNOTSUPP;
@@ -159,22 +161,42 @@ static int handle_stop(struct kvm_vcpu *vcpu)
static int handle_validity(struct kvm_vcpu *vcpu)
{
+ unsigned long vmaddr;
int viwhy = vcpu->arch.sie_block->ipb >> 16;
int rc;
vcpu->stat.exit_validity++;
- if ((viwhy == 0x37) && (vcpu->arch.sie_block->prefix
- <= kvm_s390_vcpu_get_memsize(vcpu) - 2*PAGE_SIZE)) {
- rc = fault_in_pages_writeable((char __user *)
- vcpu->arch.sie_block->gmsor +
- vcpu->arch.sie_block->prefix,
- 2*PAGE_SIZE);
- if (rc)
+ if (viwhy == 0x37) {
+ vmaddr = gmap_fault(vcpu->arch.sie_block->prefix,
+ vcpu->arch.gmap);
+ if (IS_ERR_VALUE(vmaddr)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = fault_in_pages_writeable((char __user *) vmaddr,
+ PAGE_SIZE);
+ if (rc) {
+ /* user will receive sigsegv, exit to user */
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ vmaddr = gmap_fault(vcpu->arch.sie_block->prefix + PAGE_SIZE,
+ vcpu->arch.gmap);
+ if (IS_ERR_VALUE(vmaddr)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = fault_in_pages_writeable((char __user *) vmaddr,
+ PAGE_SIZE);
+ if (rc) {
/* user will receive sigsegv, exit to user */
rc = -EOPNOTSUPP;
+ goto out;
+ }
} else
rc = -EOPNOTSUPP;
+out:
if (rc)
VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d",
viwhy);
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 35c21bf910c5..278ee009ce65 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -38,6 +38,11 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
struct kvm_s390_interrupt_info *inti)
{
switch (inti->type) {
+ case KVM_S390_INT_EXTERNAL_CALL:
+ if (psw_extint_disabled(vcpu))
+ return 0;
+ if (vcpu->arch.sie_block->gcr[0] & 0x2000ul)
+ return 1;
case KVM_S390_INT_EMERGENCY:
if (psw_extint_disabled(vcpu))
return 0;
@@ -98,6 +103,7 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
struct kvm_s390_interrupt_info *inti)
{
switch (inti->type) {
+ case KVM_S390_INT_EXTERNAL_CALL:
case KVM_S390_INT_EMERGENCY:
case KVM_S390_INT_SERVICE:
case KVM_S390_INT_VIRTIO:
@@ -128,6 +134,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
if (rc == -EFAULT)
exception = 1;
+ rc = put_guest_u16(vcpu, __LC_CPU_ADDRESS, inti->emerg.code);
+ if (rc == -EFAULT)
+ exception = 1;
+
+ rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t));
+ if (rc == -EFAULT)
+ exception = 1;
+ break;
+
+ case KVM_S390_INT_EXTERNAL_CALL:
+ VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call");
+ vcpu->stat.deliver_external_call++;
+ rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x1202);
+ if (rc == -EFAULT)
+ exception = 1;
+
+ rc = put_guest_u16(vcpu, __LC_CPU_ADDRESS, inti->extcall.code);
+ if (rc == -EFAULT)
+ exception = 1;
+
rc = copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
&vcpu->arch.sie_block->gpsw, sizeof(psw_t));
if (rc == -EFAULT)
@@ -220,6 +252,7 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
offsetof(struct _lowcore, restart_psw), sizeof(psw_t));
if (rc == -EFAULT)
exception = 1;
+ atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
break;
case KVM_S390_PROGRAM_INT:
@@ -518,6 +551,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
break;
case KVM_S390_PROGRAM_INT:
case KVM_S390_SIGP_STOP:
+ case KVM_S390_INT_EXTERNAL_CALL:
case KVM_S390_INT_EMERGENCY:
default:
kfree(inti);
@@ -577,6 +611,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
break;
case KVM_S390_SIGP_STOP:
case KVM_S390_RESTART:
+ case KVM_S390_INT_EXTERNAL_CALL:
case KVM_S390_INT_EMERGENCY:
VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type);
inti->type = s390int->type;
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 67345ae7ce8d..d1c445732451 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -46,6 +46,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "instruction_lctlg", VCPU_STAT(instruction_lctlg) },
{ "instruction_lctl", VCPU_STAT(instruction_lctl) },
{ "deliver_emergency_signal", VCPU_STAT(deliver_emergency_signal) },
+ { "deliver_external_call", VCPU_STAT(deliver_external_call) },
{ "deliver_service_signal", VCPU_STAT(deliver_service_signal) },
{ "deliver_virtio_interrupt", VCPU_STAT(deliver_virtio_interrupt) },
{ "deliver_stop_signal", VCPU_STAT(deliver_stop_signal) },
@@ -62,12 +63,16 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "instruction_chsc", VCPU_STAT(instruction_chsc) },
{ "instruction_stsi", VCPU_STAT(instruction_stsi) },
{ "instruction_stfl", VCPU_STAT(instruction_stfl) },
+ { "instruction_tprot", VCPU_STAT(instruction_tprot) },
{ "instruction_sigp_sense", VCPU_STAT(instruction_sigp_sense) },
+ { "instruction_sigp_sense_running", VCPU_STAT(instruction_sigp_sense_running) },
+ { "instruction_sigp_external_call", VCPU_STAT(instruction_sigp_external_call) },
{ "instruction_sigp_emergency", VCPU_STAT(instruction_sigp_emergency) },
{ "instruction_sigp_stop", VCPU_STAT(instruction_sigp_stop) },
{ "instruction_sigp_set_arch", VCPU_STAT(instruction_sigp_arch) },
{ "instruction_sigp_set_prefix", VCPU_STAT(instruction_sigp_prefix) },
{ "instruction_sigp_restart", VCPU_STAT(instruction_sigp_restart) },
+ { "diagnose_10", VCPU_STAT(diagnose_10) },
{ "diagnose_44", VCPU_STAT(diagnose_44) },
{ NULL }
};
@@ -122,6 +127,8 @@ int kvm_dev_ioctl_check_extension(long ext)
switch (ext) {
case KVM_CAP_S390_PSW:
+ case KVM_CAP_S390_GMAP:
+ case KVM_CAP_SYNC_MMU:
r = 1;
break;
default:
@@ -173,6 +180,8 @@ int kvm_arch_init_vm(struct kvm *kvm)
if (rc)
goto out_err;
+ rc = -ENOMEM;
+
kvm->arch.sca = (struct sca_block *) get_zeroed_page(GFP_KERNEL);
if (!kvm->arch.sca)
goto out_err;
@@ -189,7 +198,13 @@ int kvm_arch_init_vm(struct kvm *kvm)
debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
VM_EVENT(kvm, 3, "%s", "vm created");
+ kvm->arch.gmap = gmap_alloc(current->mm);
+ if (!kvm->arch.gmap)
+ goto out_nogmap;
+
return 0;
+out_nogmap:
+ debug_unregister(kvm->arch.dbf);
out_nodbf:
free_page((unsigned long)(kvm->arch.sca));
out_err:
@@ -234,11 +249,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
kvm_free_vcpus(kvm);
free_page((unsigned long)(kvm->arch.sca));
debug_unregister(kvm->arch.dbf);
+ gmap_free(kvm->arch.gmap);
}
/* Section: vcpu related */
int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.gmap = vcpu->kvm->arch.gmap;
return 0;
}
@@ -254,10 +271,14 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
vcpu->arch.guest_fpregs.fpc &= FPC_VALID_MASK;
restore_fp_regs(&vcpu->arch.guest_fpregs);
restore_access_regs(vcpu->arch.guest_acrs);
+ gmap_enable(vcpu->arch.gmap);
+ atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
+ atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
+ gmap_disable(vcpu->arch.gmap);
save_fp_regs(&vcpu->arch.guest_fpregs);
save_access_regs(vcpu->arch.guest_acrs);
restore_fp_regs(&vcpu->arch.host_fpregs);
@@ -284,8 +305,9 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
- atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH);
- set_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests);
+ atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
+ CPUSTAT_SM |
+ CPUSTAT_STOPPED);
vcpu->arch.sie_block->ecb = 6;
vcpu->arch.sie_block->eca = 0xC1002001U;
vcpu->arch.sie_block->fac = (int) (long) facilities;
@@ -301,11 +323,17 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
unsigned int id)
{
- struct kvm_vcpu *vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL);
- int rc = -ENOMEM;
+ struct kvm_vcpu *vcpu;
+ int rc = -EINVAL;
+ if (id >= KVM_MAX_VCPUS)
+ goto out;
+
+ rc = -ENOMEM;
+
+ vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL);
if (!vcpu)
- goto out_nomem;
+ goto out;
vcpu->arch.sie_block = (struct kvm_s390_sie_block *)
get_zeroed_page(GFP_KERNEL);
@@ -341,7 +369,7 @@ out_free_sie_block:
free_page((unsigned long)(vcpu->arch.sie_block));
out_free_cpu:
kfree(vcpu);
-out_nomem:
+out:
return ERR_PTR(rc);
}
@@ -375,6 +403,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
{
memcpy(&vcpu->arch.guest_acrs, &sregs->acrs, sizeof(sregs->acrs));
memcpy(&vcpu->arch.sie_block->gcr, &sregs->crs, sizeof(sregs->crs));
+ restore_access_regs(vcpu->arch.guest_acrs);
return 0;
}
@@ -390,6 +419,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{
memcpy(&vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs));
vcpu->arch.guest_fpregs.fpc = fpu->fpc;
+ restore_fp_regs(&vcpu->arch.guest_fpregs);
return 0;
}
@@ -404,7 +434,7 @@ static int kvm_arch_vcpu_ioctl_set_initial_psw(struct kvm_vcpu *vcpu, psw_t psw)
{
int rc = 0;
- if (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_RUNNING)
+ if (!(atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_STOPPED))
rc = -EBUSY;
else {
vcpu->run->psw_mask = psw.mask;
@@ -474,21 +504,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
sigset_t sigsaved;
rerun_vcpu:
- if (vcpu->requests)
- if (test_and_clear_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
- kvm_s390_vcpu_set_mem(vcpu);
-
- /* verify, that memory has been registered */
- if (!vcpu->arch.sie_block->gmslm) {
- vcpu_put(vcpu);
- VCPU_EVENT(vcpu, 3, "%s", "no memory registered to run vcpu");
- return -EINVAL;
- }
-
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
- atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
+ atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
BUG_ON(vcpu->kvm->arch.float_int.local_int[vcpu->vcpu_id] == NULL);
@@ -545,7 +564,7 @@ rerun_vcpu:
return rc;
}
-static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, const void *from,
+static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, void *from,
unsigned long n, int prefix)
{
if (prefix)
@@ -562,7 +581,7 @@ static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, const void *from,
*/
int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
{
- const unsigned char archmode = 1;
+ unsigned char archmode = 1;
int prefix;
if (addr == KVM_S390_STORE_STATUS_NOADDR) {
@@ -680,10 +699,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
if (mem->guest_phys_addr)
return -EINVAL;
- if (mem->userspace_addr & (PAGE_SIZE - 1))
+ if (mem->userspace_addr & 0xffffful)
return -EINVAL;
- if (mem->memory_size & (PAGE_SIZE - 1))
+ if (mem->memory_size & 0xffffful)
return -EINVAL;
if (!user_alloc)
@@ -697,15 +716,14 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
struct kvm_memory_slot old,
int user_alloc)
{
- int i;
- struct kvm_vcpu *vcpu;
+ int rc;
- /* request update of sie control block for all available vcpus */
- kvm_for_each_vcpu(i, vcpu, kvm) {
- if (test_and_set_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
- continue;
- kvm_s390_inject_sigp_stop(vcpu, ACTION_RELOADVCPU_ON_STOP);
- }
+
+ rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr,
+ mem->guest_phys_addr, mem->memory_size);
+ if (rc)
+ printk(KERN_WARNING "kvm-s390: failed to commit memory region\n");
+ return;
}
void kvm_arch_flush_shadow(struct kvm *kvm)
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index a7b7586626db..99b0b7597115 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -58,35 +58,9 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
-static inline long kvm_s390_vcpu_get_memsize(struct kvm_vcpu *vcpu)
-{
- return vcpu->arch.sie_block->gmslm
- - vcpu->arch.sie_block->gmsor
- - VIRTIODESCSPACE + 1ul;
-}
-
-static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
-{
- int idx;
- struct kvm_memory_slot *mem;
- struct kvm_memslots *memslots;
-
- idx = srcu_read_lock(&vcpu->kvm->srcu);
- memslots = kvm_memslots(vcpu->kvm);
-
- mem = &memslots->memslots[0];
-
- vcpu->arch.sie_block->gmsor = mem->userspace_addr;
- vcpu->arch.sie_block->gmslm =
- mem->userspace_addr +
- (mem->npages << PAGE_SHIFT) +
- VIRTIODESCSPACE - 1ul;
-
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
-}
-
/* implemented in priv.c */
int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_e5(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 73c47bd95db3..d02638959922 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -326,3 +326,58 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
}
return -EOPNOTSUPP;
}
+
+static int handle_tprot(struct kvm_vcpu *vcpu)
+{
+ int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
+ int disp1 = (vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16;
+ int base2 = (vcpu->arch.sie_block->ipb & 0xf000) >> 12;
+ int disp2 = vcpu->arch.sie_block->ipb & 0x0fff;
+ u64 address1 = disp1 + base1 ? vcpu->arch.guest_gprs[base1] : 0;
+ u64 address2 = disp2 + base2 ? vcpu->arch.guest_gprs[base2] : 0;
+ struct vm_area_struct *vma;
+ unsigned long user_address;
+
+ vcpu->stat.instruction_tprot++;
+
+ /* we only handle the Linux memory detection case:
+ * access key == 0
+ * guest DAT == off
+ * everything else goes to userspace. */
+ if (address2 & 0xf0)
+ return -EOPNOTSUPP;
+ if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
+ return -EOPNOTSUPP;
+
+
+ /* we must resolve the address without holding the mmap semaphore.
+ * This is ok since the userspace hypervisor is not supposed to change
+ * the mapping while the guest queries the memory. Otherwise the guest
+ * might crash or get wrong info anyway. */
+ user_address = (unsigned long) __guestaddr_to_user(vcpu, address1);
+
+ down_read(&current->mm->mmap_sem);
+ vma = find_vma(current->mm, user_address);
+ if (!vma) {
+ up_read(&current->mm->mmap_sem);
+ return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+ }
+
+ vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
+ if (!(vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_READ))
+ vcpu->arch.sie_block->gpsw.mask |= (1ul << 44);
+ if (!(vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_READ))
+ vcpu->arch.sie_block->gpsw.mask |= (2ul << 44);
+
+ up_read(&current->mm->mmap_sem);
+ return 0;
+}
+
+int kvm_s390_handle_e5(struct kvm_vcpu *vcpu)
+{
+ /* For e5xx... instructions we only handle TPROT */
+ if ((vcpu->arch.sie_block->ipa & 0x00ff) == 0x01)
+ return handle_tprot(vcpu);
+ return -EOPNOTSUPP;
+}
+
diff --git a/arch/s390/kvm/sie64a.S b/arch/s390/kvm/sie64a.S
deleted file mode 100644
index 5faa1b1b23fa..000000000000
--- a/arch/s390/kvm/sie64a.S
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * sie64a.S - low level sie call
- *
- * Copyright IBM Corp. 2008,2010
- *
- * 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): Heiko Carstens <heiko.carstens@de.ibm.com>
- * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
- */
-
-#include <linux/errno.h>
-#include <asm/asm-offsets.h>
-#include <asm/setup.h>
-#include <asm/asm-offsets.h>
-#include <asm/ptrace.h>
-#include <asm/thread_info.h>
-
-_TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING)
-
-/*
- * offsets into stackframe
- * SP_ = offsets into stack sie64 is called with
- * SPI_ = offsets into irq stack
- */
-SP_GREGS = __SF_EMPTY
-SP_HOOK = __SF_EMPTY+8
-SP_GPP = __SF_EMPTY+16
-SPI_PSW = STACK_FRAME_OVERHEAD + __PT_PSW
-
-
- .macro SPP newpp
- tm __LC_MACHINE_FLAGS+6,0x20 # MACHINE_FLAG_SPP
- jz 0f
- .insn s,0xb2800000,\newpp
-0:
- .endm
-
-sie_irq_handler:
- SPP __LC_CMF_HPP # set host id
- larl %r2,sie_inst
- clg %r2,SPI_PSW+8(0,%r15) # intercepted sie
- jne 1f
- xc __LC_SIE_HOOK(8),__LC_SIE_HOOK
- lg %r2,__LC_THREAD_INFO # pointer thread_info struct
- tm __TI_flags+7(%r2),_TIF_EXIT_SIE
- jz 0f
- larl %r2,sie_exit # work pending, leave sie
- stg %r2,SPI_PSW+8(0,%r15)
- br %r14
-0: larl %r2,sie_reenter # re-enter with guest id
- stg %r2,SPI_PSW+8(0,%r15)
-1: br %r14
-
-/*
- * sie64a calling convention:
- * %r2 pointer to sie control block
- * %r3 guest register save area
- */
- .globl sie64a
-sie64a:
- stg %r3,SP_GREGS(%r15) # save guest register save area
- stmg %r6,%r14,__SF_GPRS(%r15) # save registers on entry
- lgr %r14,%r2 # pointer to sie control block
- larl %r5,sie_irq_handler
- stg %r2,SP_GPP(%r15)
- stg %r5,SP_HOOK(%r15) # save hook target
- lmg %r0,%r13,0(%r3) # load guest gprs 0-13
-sie_reenter:
- mvc __LC_SIE_HOOK(8),SP_HOOK(%r15)
- SPP SP_GPP(%r15) # set guest id
-sie_inst:
- sie 0(%r14)
- xc __LC_SIE_HOOK(8),__LC_SIE_HOOK
- SPP __LC_CMF_HPP # set host id
-sie_exit:
- lg %r14,SP_GREGS(%r15)
- stmg %r0,%r13,0(%r14) # save guest gprs 0-13
- lghi %r2,0
- lmg %r6,%r14,__SF_GPRS(%r15)
- br %r14
-
-sie_err:
- xc __LC_SIE_HOOK(8),__LC_SIE_HOOK
- SPP __LC_CMF_HPP # set host id
- lg %r14,SP_GREGS(%r15)
- stmg %r0,%r13,0(%r14) # save guest gprs 0-13
- lghi %r2,-EFAULT
- lmg %r6,%r14,__SF_GPRS(%r15)
- br %r14
-
- .section __ex_table,"a"
- .quad sie_inst,sie_err
- .quad sie_exit,sie_err
- .quad sie_reenter,sie_err
- .previous
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c
index 702276f5e2fa..0a7941d74bc6 100644
--- a/arch/s390/kvm/sigp.c
+++ b/arch/s390/kvm/sigp.c
@@ -31,9 +31,11 @@
#define SIGP_SET_PREFIX 0x0d
#define SIGP_STORE_STATUS_ADDR 0x0e
#define SIGP_SET_ARCH 0x12
+#define SIGP_SENSE_RUNNING 0x15
/* cpu status bits */
#define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL
+#define SIGP_STAT_NOT_RUNNING 0x00000400UL
#define SIGP_STAT_INCORRECT_STATE 0x00000200UL
#define SIGP_STAT_INVALID_PARAMETER 0x00000100UL
#define SIGP_STAT_EXT_CALL_PENDING 0x00000080UL
@@ -57,8 +59,8 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
spin_lock(&fi->lock);
if (fi->local_int[cpu_addr] == NULL)
rc = 3; /* not operational */
- else if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
- & CPUSTAT_RUNNING) {
+ else if (!(atomic_read(fi->local_int[cpu_addr]->cpuflags)
+ & CPUSTAT_STOPPED)) {
*reg &= 0xffffffff00000000UL;
rc = 1; /* status stored */
} else {
@@ -87,6 +89,7 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
return -ENOMEM;
inti->type = KVM_S390_INT_EMERGENCY;
+ inti->emerg.code = vcpu->vcpu_id;
spin_lock(&fi->lock);
li = fi->local_int[cpu_addr];
@@ -103,9 +106,47 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
wake_up_interruptible(&li->wq);
spin_unlock_bh(&li->lock);
rc = 0; /* order accepted */
+ VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
+unlock:
+ spin_unlock(&fi->lock);
+ return rc;
+}
+
+static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
+{
+ struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+ struct kvm_s390_local_interrupt *li;
+ struct kvm_s390_interrupt_info *inti;
+ int rc;
+
+ if (cpu_addr >= KVM_MAX_VCPUS)
+ return 3; /* not operational */
+
+ inti = kzalloc(sizeof(*inti), GFP_KERNEL);
+ if (!inti)
+ return -ENOMEM;
+
+ inti->type = KVM_S390_INT_EXTERNAL_CALL;
+ inti->extcall.code = vcpu->vcpu_id;
+
+ spin_lock(&fi->lock);
+ li = fi->local_int[cpu_addr];
+ if (li == NULL) {
+ rc = 3; /* not operational */
+ kfree(inti);
+ goto unlock;
+ }
+ spin_lock_bh(&li->lock);
+ list_add_tail(&inti->list, &li->list);
+ atomic_set(&li->active, 1);
+ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+ if (waitqueue_active(&li->wq))
+ wake_up_interruptible(&li->wq);
+ spin_unlock_bh(&li->lock);
+ rc = 0; /* order accepted */
+ VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr);
unlock:
spin_unlock(&fi->lock);
- VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
return rc;
}
@@ -189,10 +230,8 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
/* make sure that the new value is valid memory */
address = address & 0x7fffe000u;
- if ((copy_from_user(&tmp, (void __user *)
- (address + vcpu->arch.sie_block->gmsor) , 1)) ||
- (copy_from_user(&tmp, (void __user *)(address +
- vcpu->arch.sie_block->gmsor + PAGE_SIZE), 1))) {
+ if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
+ copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)) {
*reg |= SIGP_STAT_INVALID_PARAMETER;
return 1; /* invalid parameter */
}
@@ -214,7 +253,7 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
spin_lock_bh(&li->lock);
/* cpu must be in stopped state */
- if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) {
+ if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) {
rc = 1; /* incorrect state */
*reg &= SIGP_STAT_INCORRECT_STATE;
kfree(inti);
@@ -238,6 +277,38 @@ out_fi:
return rc;
}
+static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr,
+ unsigned long *reg)
+{
+ int rc;
+ struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+
+ if (cpu_addr >= KVM_MAX_VCPUS)
+ return 3; /* not operational */
+
+ spin_lock(&fi->lock);
+ if (fi->local_int[cpu_addr] == NULL)
+ rc = 3; /* not operational */
+ else {
+ if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
+ & CPUSTAT_RUNNING) {
+ /* running */
+ rc = 1;
+ } else {
+ /* not running */
+ *reg &= 0xffffffff00000000UL;
+ *reg |= SIGP_STAT_NOT_RUNNING;
+ rc = 0;
+ }
+ }
+ spin_unlock(&fi->lock);
+
+ VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr,
+ rc);
+
+ return rc;
+}
+
int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
{
int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
@@ -269,6 +340,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
rc = __sigp_sense(vcpu, cpu_addr,
&vcpu->arch.guest_gprs[r1]);
break;
+ case SIGP_EXTERNAL_CALL:
+ vcpu->stat.instruction_sigp_external_call++;
+ rc = __sigp_external_call(vcpu, cpu_addr);
+ break;
case SIGP_EMERGENCY:
vcpu->stat.instruction_sigp_emergency++;
rc = __sigp_emergency(vcpu, cpu_addr);
@@ -290,6 +365,11 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
rc = __sigp_set_prefix(vcpu, cpu_addr, parameter,
&vcpu->arch.guest_gprs[r1]);
break;
+ case SIGP_SENSE_RUNNING:
+ vcpu->stat.instruction_sigp_sense_running++;
+ rc = __sigp_sense_running(vcpu, cpu_addr,
+ &vcpu->arch.guest_gprs[r1]);
+ break;
case SIGP_RESTART:
vcpu->stat.instruction_sigp_restart++;
/* user space must know about restart */