linux/arch/s390/kvm/priv.c
<<
>>
Prefs
   1/*
   2 * priv.c - handling privileged instructions
   3 *
   4 * Copyright IBM Corp. 2008
   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 */
  13
  14#include <linux/kvm.h>
  15#include <linux/errno.h>
  16#include <asm/current.h>
  17#include <asm/debug.h>
  18#include <asm/ebcdic.h>
  19#include <asm/sysinfo.h>
  20#include "gaccess.h"
  21#include "kvm-s390.h"
  22
  23static int handle_set_prefix(struct kvm_vcpu *vcpu)
  24{
  25        int base2 = vcpu->arch.sie_block->ipb >> 28;
  26        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
  27        u64 operand2;
  28        u32 address = 0;
  29        u8 tmp;
  30
  31        vcpu->stat.instruction_spx++;
  32
  33        operand2 = disp2;
  34        if (base2)
  35                operand2 += vcpu->arch.guest_gprs[base2];
  36
  37        /* must be word boundary */
  38        if (operand2 & 3) {
  39                kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  40                goto out;
  41        }
  42
  43        /* get the value */
  44        if (get_guest_u32(vcpu, operand2, &address)) {
  45                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
  46                goto out;
  47        }
  48
  49        address = address & 0x7fffe000u;
  50
  51        /* make sure that the new value is valid memory */
  52        if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
  53           (copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1))) {
  54                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
  55                goto out;
  56        }
  57
  58        vcpu->arch.sie_block->prefix = address;
  59        vcpu->arch.sie_block->ihcpu = 0xffff;
  60
  61        VCPU_EVENT(vcpu, 5, "setting prefix to %x", address);
  62out:
  63        return 0;
  64}
  65
  66static int handle_store_prefix(struct kvm_vcpu *vcpu)
  67{
  68        int base2 = vcpu->arch.sie_block->ipb >> 28;
  69        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
  70        u64 operand2;
  71        u32 address;
  72
  73        vcpu->stat.instruction_stpx++;
  74        operand2 = disp2;
  75        if (base2)
  76                operand2 += vcpu->arch.guest_gprs[base2];
  77
  78        /* must be word boundary */
  79        if (operand2 & 3) {
  80                kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  81                goto out;
  82        }
  83
  84        address = vcpu->arch.sie_block->prefix;
  85        address = address & 0x7fffe000u;
  86
  87        /* get the value */
  88        if (put_guest_u32(vcpu, operand2, address)) {
  89                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
  90                goto out;
  91        }
  92
  93        VCPU_EVENT(vcpu, 5, "storing prefix to %x", address);
  94out:
  95        return 0;
  96}
  97
  98static int handle_store_cpu_address(struct kvm_vcpu *vcpu)
  99{
 100        int base2 = vcpu->arch.sie_block->ipb >> 28;
 101        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
 102        u64 useraddr;
 103        int rc;
 104
 105        vcpu->stat.instruction_stap++;
 106        useraddr = disp2;
 107        if (base2)
 108                useraddr += vcpu->arch.guest_gprs[base2];
 109
 110        if (useraddr & 1) {
 111                kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 112                goto out;
 113        }
 114
 115        rc = put_guest_u16(vcpu, useraddr, vcpu->vcpu_id);
 116        if (rc == -EFAULT) {
 117                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 118                goto out;
 119        }
 120
 121        VCPU_EVENT(vcpu, 5, "storing cpu address to %lx", useraddr);
 122out:
 123        return 0;
 124}
 125
 126static int handle_skey(struct kvm_vcpu *vcpu)
 127{
 128        vcpu->stat.instruction_storage_key++;
 129        vcpu->arch.sie_block->gpsw.addr -= 4;
 130        VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation");
 131        return 0;
 132}
 133
 134static int handle_stsch(struct kvm_vcpu *vcpu)
 135{
 136        vcpu->stat.instruction_stsch++;
 137        VCPU_EVENT(vcpu, 4, "%s", "store subchannel - CC3");
 138        /* condition code 3 */
 139        vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 140        vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
 141        return 0;
 142}
 143
 144static int handle_chsc(struct kvm_vcpu *vcpu)
 145{
 146        vcpu->stat.instruction_chsc++;
 147        VCPU_EVENT(vcpu, 4, "%s", "channel subsystem call - CC3");
 148        /* condition code 3 */
 149        vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 150        vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
 151        return 0;
 152}
 153
 154static int handle_stfl(struct kvm_vcpu *vcpu)
 155{
 156        unsigned int facility_list = stfl();
 157        int rc;
 158
 159        vcpu->stat.instruction_stfl++;
 160        /* only pass the facility bits, which we can handle */
 161        facility_list &= 0xfe00fff3;
 162
 163        rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list),
 164                           &facility_list, sizeof(facility_list));
 165        if (rc == -EFAULT)
 166                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 167        else
 168                VCPU_EVENT(vcpu, 5, "store facility list value %x",
 169                           facility_list);
 170        return 0;
 171}
 172
 173static int handle_stidp(struct kvm_vcpu *vcpu)
 174{
 175        int base2 = vcpu->arch.sie_block->ipb >> 28;
 176        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
 177        u64 operand2;
 178        int rc;
 179
 180        vcpu->stat.instruction_stidp++;
 181        operand2 = disp2;
 182        if (base2)
 183                operand2 += vcpu->arch.guest_gprs[base2];
 184
 185        if (operand2 & 7) {
 186                kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 187                goto out;
 188        }
 189
 190        rc = put_guest_u64(vcpu, operand2, vcpu->arch.stidp_data);
 191        if (rc == -EFAULT) {
 192                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 193                goto out;
 194        }
 195
 196        VCPU_EVENT(vcpu, 5, "%s", "store cpu id");
 197out:
 198        return 0;
 199}
 200
 201static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem)
 202{
 203        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
 204        int cpus = 0;
 205        int n;
 206
 207        spin_lock_bh(&fi->lock);
 208        for (n = 0; n < KVM_MAX_VCPUS; n++)
 209                if (fi->local_int[n])
 210                        cpus++;
 211        spin_unlock_bh(&fi->lock);
 212
 213        /* deal with other level 3 hypervisors */
 214        if (stsi(mem, 3, 2, 2) == -ENOSYS)
 215                mem->count = 0;
 216        if (mem->count < 8)
 217                mem->count++;
 218        for (n = mem->count - 1; n > 0 ; n--)
 219                memcpy(&mem->vm[n], &mem->vm[n - 1], sizeof(mem->vm[0]));
 220
 221        mem->vm[0].cpus_total = cpus;
 222        mem->vm[0].cpus_configured = cpus;
 223        mem->vm[0].cpus_standby = 0;
 224        mem->vm[0].cpus_reserved = 0;
 225        mem->vm[0].caf = 1000;
 226        memcpy(mem->vm[0].name, "KVMguest", 8);
 227        ASCEBC(mem->vm[0].name, 8);
 228        memcpy(mem->vm[0].cpi, "KVM/Linux       ", 16);
 229        ASCEBC(mem->vm[0].cpi, 16);
 230}
 231
 232static int handle_stsi(struct kvm_vcpu *vcpu)
 233{
 234        int fc = (vcpu->arch.guest_gprs[0] & 0xf0000000) >> 28;
 235        int sel1 = vcpu->arch.guest_gprs[0] & 0xff;
 236        int sel2 = vcpu->arch.guest_gprs[1] & 0xffff;
 237        int base2 = vcpu->arch.sie_block->ipb >> 28;
 238        int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16);
 239        u64 operand2;
 240        unsigned long mem;
 241
 242        vcpu->stat.instruction_stsi++;
 243        VCPU_EVENT(vcpu, 4, "stsi: fc: %x sel1: %x sel2: %x", fc, sel1, sel2);
 244
 245        operand2 = disp2;
 246        if (base2)
 247                operand2 += vcpu->arch.guest_gprs[base2];
 248
 249        if (operand2 & 0xfff && fc > 0)
 250                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 251
 252        switch (fc) {
 253        case 0:
 254                vcpu->arch.guest_gprs[0] = 3 << 28;
 255                vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 256                return 0;
 257        case 1: /* same handling for 1 and 2 */
 258        case 2:
 259                mem = get_zeroed_page(GFP_KERNEL);
 260                if (!mem)
 261                        goto out_fail;
 262                if (stsi((void *) mem, fc, sel1, sel2) == -ENOSYS)
 263                        goto out_mem;
 264                break;
 265        case 3:
 266                if (sel1 != 2 || sel2 != 2)
 267                        goto out_fail;
 268                mem = get_zeroed_page(GFP_KERNEL);
 269                if (!mem)
 270                        goto out_fail;
 271                handle_stsi_3_2_2(vcpu, (void *) mem);
 272                break;
 273        default:
 274                goto out_fail;
 275        }
 276
 277        if (copy_to_guest_absolute(vcpu, operand2, (void *) mem, PAGE_SIZE)) {
 278                kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 279                goto out_mem;
 280        }
 281        free_page(mem);
 282        vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
 283        vcpu->arch.guest_gprs[0] = 0;
 284        return 0;
 285out_mem:
 286        free_page(mem);
 287out_fail:
 288        /* condition code 3 */
 289        vcpu->arch.sie_block->gpsw.mask |= 3ul << 44;
 290        return 0;
 291}
 292
 293static intercept_handler_t priv_handlers[256] = {
 294        [0x02] = handle_stidp,
 295        [0x10] = handle_set_prefix,
 296        [0x11] = handle_store_prefix,
 297        [0x12] = handle_store_cpu_address,
 298        [0x29] = handle_skey,
 299        [0x2a] = handle_skey,
 300        [0x2b] = handle_skey,
 301        [0x34] = handle_stsch,
 302        [0x5f] = handle_chsc,
 303        [0x7d] = handle_stsi,
 304        [0xb1] = handle_stfl,
 305};
 306
 307int kvm_s390_handle_priv(struct kvm_vcpu *vcpu)
 308{
 309        intercept_handler_t handler;
 310
 311        handler = priv_handlers[vcpu->arch.sie_block->ipa & 0x00ff];
 312        if (handler)
 313                return handler(vcpu);
 314        return -ENOTSUPP;
 315}
 316