linux/arch/s390/kvm/sigp.c
<<
>>
Prefs
   1/*
   2 * sigp.c - handlinge interprocessor communication
   3 *
   4 * Copyright IBM Corp. 2008,2009
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License (version 2 only)
   8 * as published by the Free Software Foundation.
   9 *
  10 *    Author(s): Carsten Otte <cotte@de.ibm.com>
  11 *               Christian Borntraeger <borntraeger@de.ibm.com>
  12 *               Christian Ehrhardt <ehrhardt@de.ibm.com>
  13 */
  14
  15#include <linux/kvm.h>
  16#include <linux/kvm_host.h>
  17#include <linux/slab.h>
  18#include "gaccess.h"
  19#include "kvm-s390.h"
  20
  21/* sigp order codes */
  22#define SIGP_SENSE             0x01
  23#define SIGP_EXTERNAL_CALL     0x02
  24#define SIGP_EMERGENCY         0x03
  25#define SIGP_START             0x04
  26#define SIGP_STOP              0x05
  27#define SIGP_RESTART           0x06
  28#define SIGP_STOP_STORE_STATUS 0x09
  29#define SIGP_INITIAL_CPU_RESET 0x0b
  30#define SIGP_CPU_RESET         0x0c
  31#define SIGP_SET_PREFIX        0x0d
  32#define SIGP_STORE_STATUS_ADDR 0x0e
  33#define SIGP_SET_ARCH          0x12
  34
  35/* cpu status bits */
  36#define SIGP_STAT_EQUIPMENT_CHECK   0x80000000UL
  37#define SIGP_STAT_INCORRECT_STATE   0x00000200UL
  38#define SIGP_STAT_INVALID_PARAMETER 0x00000100UL
  39#define SIGP_STAT_EXT_CALL_PENDING  0x00000080UL
  40#define SIGP_STAT_STOPPED           0x00000040UL
  41#define SIGP_STAT_OPERATOR_INTERV   0x00000020UL
  42#define SIGP_STAT_CHECK_STOP        0x00000010UL
  43#define SIGP_STAT_INOPERATIVE       0x00000004UL
  44#define SIGP_STAT_INVALID_ORDER     0x00000002UL
  45#define SIGP_STAT_RECEIVER_CHECK    0x00000001UL
  46
  47
  48static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
  49                        unsigned long *reg)
  50{
  51        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
  52        int rc;
  53
  54        if (cpu_addr >= KVM_MAX_VCPUS)
  55                return 3; /* not operational */
  56
  57        spin_lock(&fi->lock);
  58        if (fi->local_int[cpu_addr] == NULL)
  59                rc = 3; /* not operational */
  60        else if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
  61                 & CPUSTAT_RUNNING) {
  62                *reg &= 0xffffffff00000000UL;
  63                rc = 1; /* status stored */
  64        } else {
  65                *reg &= 0xffffffff00000000UL;
  66                *reg |= SIGP_STAT_STOPPED;
  67                rc = 1; /* status stored */
  68        }
  69        spin_unlock(&fi->lock);
  70
  71        VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc);
  72        return rc;
  73}
  74
  75static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
  76{
  77        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
  78        struct kvm_s390_local_interrupt *li;
  79        struct kvm_s390_interrupt_info *inti;
  80        int rc;
  81
  82        if (cpu_addr >= KVM_MAX_VCPUS)
  83                return 3; /* not operational */
  84
  85        inti = kzalloc(sizeof(*inti), GFP_KERNEL);
  86        if (!inti)
  87                return -ENOMEM;
  88
  89        inti->type = KVM_S390_INT_EMERGENCY;
  90
  91        spin_lock(&fi->lock);
  92        li = fi->local_int[cpu_addr];
  93        if (li == NULL) {
  94                rc = 3; /* not operational */
  95                kfree(inti);
  96                goto unlock;
  97        }
  98        spin_lock_bh(&li->lock);
  99        list_add_tail(&inti->list, &li->list);
 100        atomic_set(&li->active, 1);
 101        atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
 102        if (waitqueue_active(&li->wq))
 103                wake_up_interruptible(&li->wq);
 104        spin_unlock_bh(&li->lock);
 105        rc = 0; /* order accepted */
 106unlock:
 107        spin_unlock(&fi->lock);
 108        VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
 109        return rc;
 110}
 111
 112static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
 113{
 114        struct kvm_s390_interrupt_info *inti;
 115
 116        inti = kzalloc(sizeof(*inti), GFP_ATOMIC);
 117        if (!inti)
 118                return -ENOMEM;
 119        inti->type = KVM_S390_SIGP_STOP;
 120
 121        spin_lock_bh(&li->lock);
 122        list_add_tail(&inti->list, &li->list);
 123        atomic_set(&li->active, 1);
 124        atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags);
 125        li->action_bits |= action;
 126        if (waitqueue_active(&li->wq))
 127                wake_up_interruptible(&li->wq);
 128        spin_unlock_bh(&li->lock);
 129
 130        return 0; /* order accepted */
 131}
 132
 133static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
 134{
 135        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
 136        struct kvm_s390_local_interrupt *li;
 137        int rc;
 138
 139        if (cpu_addr >= KVM_MAX_VCPUS)
 140                return 3; /* not operational */
 141
 142        spin_lock(&fi->lock);
 143        li = fi->local_int[cpu_addr];
 144        if (li == NULL) {
 145                rc = 3; /* not operational */
 146                goto unlock;
 147        }
 148
 149        rc = __inject_sigp_stop(li, action);
 150
 151unlock:
 152        spin_unlock(&fi->lock);
 153        VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr);
 154        return rc;
 155}
 156
 157int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action)
 158{
 159        struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
 160        return __inject_sigp_stop(li, action);
 161}
 162
 163static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
 164{
 165        int rc;
 166
 167        switch (parameter & 0xff) {
 168        case 0:
 169                rc = 3; /* not operational */
 170                break;
 171        case 1:
 172        case 2:
 173                rc = 0; /* order accepted */
 174                break;
 175        default:
 176                rc = -EOPNOTSUPP;
 177        }
 178        return rc;
 179}
 180
 181static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
 182                             unsigned long *reg)
 183{
 184        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
 185        struct kvm_s390_local_interrupt *li = NULL;
 186        struct kvm_s390_interrupt_info *inti;
 187        int rc;
 188        u8 tmp;
 189
 190        /* make sure that the new value is valid memory */
 191        address = address & 0x7fffe000u;
 192        if ((copy_from_user(&tmp, (void __user *)
 193                (address + vcpu->arch.sie_block->gmsor) , 1)) ||
 194           (copy_from_user(&tmp, (void __user *)(address +
 195                        vcpu->arch.sie_block->gmsor + PAGE_SIZE), 1))) {
 196                *reg |= SIGP_STAT_INVALID_PARAMETER;
 197                return 1; /* invalid parameter */
 198        }
 199
 200        inti = kzalloc(sizeof(*inti), GFP_KERNEL);
 201        if (!inti)
 202                return 2; /* busy */
 203
 204        spin_lock(&fi->lock);
 205        if (cpu_addr < KVM_MAX_VCPUS)
 206                li = fi->local_int[cpu_addr];
 207
 208        if (li == NULL) {
 209                rc = 1; /* incorrect state */
 210                *reg &= SIGP_STAT_INCORRECT_STATE;
 211                kfree(inti);
 212                goto out_fi;
 213        }
 214
 215        spin_lock_bh(&li->lock);
 216        /* cpu must be in stopped state */
 217        if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) {
 218                rc = 1; /* incorrect state */
 219                *reg &= SIGP_STAT_INCORRECT_STATE;
 220                kfree(inti);
 221                goto out_li;
 222        }
 223
 224        inti->type = KVM_S390_SIGP_SET_PREFIX;
 225        inti->prefix.address = address;
 226
 227        list_add_tail(&inti->list, &li->list);
 228        atomic_set(&li->active, 1);
 229        if (waitqueue_active(&li->wq))
 230                wake_up_interruptible(&li->wq);
 231        rc = 0; /* order accepted */
 232
 233        VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address);
 234out_li:
 235        spin_unlock_bh(&li->lock);
 236out_fi:
 237        spin_unlock(&fi->lock);
 238        return rc;
 239}
 240
 241int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
 242{
 243        int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
 244        int r3 = vcpu->arch.sie_block->ipa & 0x000f;
 245        int base2 = vcpu->arch.sie_block->ipb >> 28;
 246        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
 247        u32 parameter;
 248        u16 cpu_addr = vcpu->arch.guest_gprs[r3];
 249        u8 order_code;
 250        int rc;
 251
 252        /* sigp in userspace can exit */
 253        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 254                return kvm_s390_inject_program_int(vcpu,
 255                                                   PGM_PRIVILEGED_OPERATION);
 256
 257        order_code = disp2;
 258        if (base2)
 259                order_code += vcpu->arch.guest_gprs[base2];
 260
 261        if (r1 % 2)
 262                parameter = vcpu->arch.guest_gprs[r1];
 263        else
 264                parameter = vcpu->arch.guest_gprs[r1 + 1];
 265
 266        switch (order_code) {
 267        case SIGP_SENSE:
 268                vcpu->stat.instruction_sigp_sense++;
 269                rc = __sigp_sense(vcpu, cpu_addr,
 270                                  &vcpu->arch.guest_gprs[r1]);
 271                break;
 272        case SIGP_EMERGENCY:
 273                vcpu->stat.instruction_sigp_emergency++;
 274                rc = __sigp_emergency(vcpu, cpu_addr);
 275                break;
 276        case SIGP_STOP:
 277                vcpu->stat.instruction_sigp_stop++;
 278                rc = __sigp_stop(vcpu, cpu_addr, ACTION_STOP_ON_STOP);
 279                break;
 280        case SIGP_STOP_STORE_STATUS:
 281                vcpu->stat.instruction_sigp_stop++;
 282                rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP);
 283                break;
 284        case SIGP_SET_ARCH:
 285                vcpu->stat.instruction_sigp_arch++;
 286                rc = __sigp_set_arch(vcpu, parameter);
 287                break;
 288        case SIGP_SET_PREFIX:
 289                vcpu->stat.instruction_sigp_prefix++;
 290                rc = __sigp_set_prefix(vcpu, cpu_addr, parameter,
 291                                       &vcpu->arch.guest_gprs[r1]);
 292                break;
 293        case SIGP_RESTART:
 294                vcpu->stat.instruction_sigp_restart++;
 295                /* user space must know about restart */
 296        default:
 297                return -EOPNOTSUPP;
 298        }
 299
 300        if (rc < 0)
 301                return rc;
 302
 303        vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 304        vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44;
 305        return 0;
 306}
 307