include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / arch / s390 / kvm / sigp.c
index 5a55611..eff3c59 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * sigp.c - handlinge interprocessor communication
  *
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
  *
  * 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)
@@ -9,10 +9,12 @@
  *
  *    Author(s): Carsten Otte <cotte@de.ibm.com>
  *               Christian Borntraeger <borntraeger@de.ibm.com>
+ *               Christian Ehrhardt <ehrhardt@de.ibm.com>
  */
 
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
+#include <linux/slab.h>
 #include "gaccess.h"
 #include "kvm-s390.h"
 
@@ -43,7 +45,8 @@
 #define SIGP_STAT_RECEIVER_CHECK    0x00000001UL
 
 
-static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, u64 *reg)
+static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
+                       unsigned long *reg)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
        int rc;
@@ -51,7 +54,7 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, u64 *reg)
        if (cpu_addr >= KVM_MAX_VCPUS)
                return 3; /* not operational */
 
-       spin_lock_bh(&fi->lock);
+       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)
@@ -63,7 +66,7 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, u64 *reg)
                *reg |= SIGP_STAT_STOPPED;
                rc = 1; /* status stored */
        }
-       spin_unlock_bh(&fi->lock);
+       spin_unlock(&fi->lock);
 
        VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc);
        return rc;
@@ -85,7 +88,7 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
 
        inti->type = KVM_S390_INT_EMERGENCY;
 
-       spin_lock_bh(&fi->lock);
+       spin_lock(&fi->lock);
        li = fi->local_int[cpu_addr];
        if (li == NULL) {
                rc = 3; /* not operational */
@@ -101,59 +104,68 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
        spin_unlock_bh(&li->lock);
        rc = 0; /* order accepted */
 unlock:
-       spin_unlock_bh(&fi->lock);
+       spin_unlock(&fi->lock);
        VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
        return rc;
 }
 
-static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int store)
+static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
 {
-       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_SIGP_STOP;
 
-       spin_lock_bh(&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_STOP_INT, li->cpuflags);
-       if (store)
-               li->action_bits |= ACTION_STORE_ON_STOP;
-       li->action_bits |= ACTION_STOP_ON_STOP;
+       li->action_bits |= action;
        if (waitqueue_active(&li->wq))
                wake_up_interruptible(&li->wq);
        spin_unlock_bh(&li->lock);
-       rc = 0; /* order accepted */
+
+       return 0; /* order accepted */
+}
+
+static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
+{
+       struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+       struct kvm_s390_local_interrupt *li;
+       int rc;
+
+       if (cpu_addr >= KVM_MAX_VCPUS)
+               return 3; /* not operational */
+
+       spin_lock(&fi->lock);
+       li = fi->local_int[cpu_addr];
+       if (li == NULL) {
+               rc = 3; /* not operational */
+               goto unlock;
+       }
+
+       rc = __inject_sigp_stop(li, action);
+
 unlock:
-       spin_unlock_bh(&fi->lock);
+       spin_unlock(&fi->lock);
        VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr);
        return rc;
 }
 
+int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action)
+{
+       struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+       return __inject_sigp_stop(li, action);
+}
+
 static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
 {
        int rc;
 
        switch (parameter & 0xff) {
        case 0:
-               printk(KERN_WARNING "kvm: request to switch to ESA/390 mode"
-                                                       " not supported");
                rc = 3; /* not operational */
                break;
        case 1:
@@ -161,26 +173,26 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
                rc = 0; /* order accepted */
                break;
        default:
-               rc = -ENOTSUPP;
+               rc = -EOPNOTSUPP;
        }
        return rc;
 }
 
 static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
-                            u64 *reg)
+                            unsigned long *reg)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
-       struct kvm_s390_local_interrupt *li;
+       struct kvm_s390_local_interrupt *li = NULL;
        struct kvm_s390_interrupt_info *inti;
        int rc;
        u8 tmp;
 
        /* make sure that the new value is valid memory */
        address = address & 0x7fffe000u;
-       if ((copy_from_guest(vcpu, &tmp,
-               (u64) (address + vcpu->kvm->arch.guest_origin) , 1)) ||
-          (copy_from_guest(vcpu, &tmp, (u64) (address +
-                       vcpu->kvm->arch.guest_origin + PAGE_SIZE), 1))) {
+       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))) {
                *reg |= SIGP_STAT_INVALID_PARAMETER;
                return 1; /* invalid parameter */
        }
@@ -189,10 +201,11 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
        if (!inti)
                return 2; /* busy */
 
-       spin_lock_bh(&fi->lock);
-       li = fi->local_int[cpu_addr];
+       spin_lock(&fi->lock);
+       if (cpu_addr < KVM_MAX_VCPUS)
+               li = fi->local_int[cpu_addr];
 
-       if ((cpu_addr >= KVM_MAX_VCPUS) || (li == NULL)) {
+       if (li == NULL) {
                rc = 1; /* incorrect state */
                *reg &= SIGP_STAT_INCORRECT_STATE;
                kfree(inti);
@@ -221,7 +234,7 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
 out_li:
        spin_unlock_bh(&li->lock);
 out_fi:
-       spin_unlock_bh(&fi->lock);
+       spin_unlock(&fi->lock);
        return rc;
 }
 
@@ -236,6 +249,11 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
        u8 order_code;
        int rc;
 
+       /* sigp in userspace can exit */
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu,
+                                                  PGM_PRIVILEGED_OPERATION);
+
        order_code = disp2;
        if (base2)
                order_code += vcpu->arch.guest_gprs[base2];
@@ -257,11 +275,11 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
                break;
        case SIGP_STOP:
                vcpu->stat.instruction_sigp_stop++;
-               rc = __sigp_stop(vcpu, cpu_addr, 0);
+               rc = __sigp_stop(vcpu, cpu_addr, ACTION_STOP_ON_STOP);
                break;
        case SIGP_STOP_STORE_STATUS:
                vcpu->stat.instruction_sigp_stop++;
-               rc = __sigp_stop(vcpu, cpu_addr, 1);
+               rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP);
                break;
        case SIGP_SET_ARCH:
                vcpu->stat.instruction_sigp_arch++;
@@ -276,7 +294,7 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
                vcpu->stat.instruction_sigp_restart++;
                /* user space must know about restart */
        default:
-               return -ENOTSUPP;
+               return -EOPNOTSUPP;
        }
 
        if (rc < 0)