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