linux/arch/s390/kvm/diag.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * handling diagnose instructions
   4 *
   5 * Copyright IBM Corp. 2008, 2020
   6 *
   7 *    Author(s): Carsten Otte <cotte@de.ibm.com>
   8 *               Christian Borntraeger <borntraeger@de.ibm.com>
   9 */
  10
  11#include <linux/kvm.h>
  12#include <linux/kvm_host.h>
  13#include <asm/gmap.h>
  14#include <asm/virtio-ccw.h>
  15#include "kvm-s390.h"
  16#include "trace.h"
  17#include "trace-s390.h"
  18#include "gaccess.h"
  19
  20static int diag_release_pages(struct kvm_vcpu *vcpu)
  21{
  22        unsigned long start, end;
  23        unsigned long prefix  = kvm_s390_get_prefix(vcpu);
  24
  25        start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
  26        end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + PAGE_SIZE;
  27        vcpu->stat.instruction_diagnose_10++;
  28
  29        if (start & ~PAGE_MASK || end & ~PAGE_MASK || start >= end
  30            || start < 2 * PAGE_SIZE)
  31                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  32
  33        VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end);
  34
  35        /*
  36         * We checked for start >= end above, so lets check for the
  37         * fast path (no prefix swap page involved)
  38         */
  39        if (end <= prefix || start >= prefix + 2 * PAGE_SIZE) {
  40                gmap_discard(vcpu->arch.gmap, start, end);
  41        } else {
  42                /*
  43                 * This is slow path.  gmap_discard will check for start
  44                 * so lets split this into before prefix, prefix, after
  45                 * prefix and let gmap_discard make some of these calls
  46                 * NOPs.
  47                 */
  48                gmap_discard(vcpu->arch.gmap, start, prefix);
  49                if (start <= prefix)
  50                        gmap_discard(vcpu->arch.gmap, 0, PAGE_SIZE);
  51                if (end > prefix + PAGE_SIZE)
  52                        gmap_discard(vcpu->arch.gmap, PAGE_SIZE, 2 * PAGE_SIZE);
  53                gmap_discard(vcpu->arch.gmap, prefix + 2 * PAGE_SIZE, end);
  54        }
  55        return 0;
  56}
  57
  58static int __diag_page_ref_service(struct kvm_vcpu *vcpu)
  59{
  60        struct prs_parm {
  61                u16 code;
  62                u16 subcode;
  63                u16 parm_len;
  64                u16 parm_version;
  65                u64 token_addr;
  66                u64 select_mask;
  67                u64 compare_mask;
  68                u64 zarch;
  69        };
  70        struct prs_parm parm;
  71        int rc;
  72        u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4;
  73        u16 ry = (vcpu->arch.sie_block->ipa & 0x0f);
  74
  75        VCPU_EVENT(vcpu, 3, "diag page reference parameter block at 0x%llx",
  76                   vcpu->run->s.regs.gprs[rx]);
  77        vcpu->stat.instruction_diagnose_258++;
  78        if (vcpu->run->s.regs.gprs[rx] & 7)
  79                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  80        rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], rx, &parm, sizeof(parm));
  81        if (rc)
  82                return kvm_s390_inject_prog_cond(vcpu, rc);
  83        if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258)
  84                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  85
  86        switch (parm.subcode) {
  87        case 0: /* TOKEN */
  88                VCPU_EVENT(vcpu, 3, "pageref token addr 0x%llx "
  89                           "select mask 0x%llx compare mask 0x%llx",
  90                           parm.token_addr, parm.select_mask, parm.compare_mask);
  91                if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) {
  92                        /*
  93                         * If the pagefault handshake is already activated,
  94                         * the token must not be changed.  We have to return
  95                         * decimal 8 instead, as mandated in SC24-6084.
  96                         */
  97                        vcpu->run->s.regs.gprs[ry] = 8;
  98                        return 0;
  99                }
 100
 101                if ((parm.compare_mask & parm.select_mask) != parm.compare_mask ||
 102                    parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL)
 103                        return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 104
 105                if (kvm_is_error_gpa(vcpu->kvm, parm.token_addr))
 106                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 107
 108                vcpu->arch.pfault_token = parm.token_addr;
 109                vcpu->arch.pfault_select = parm.select_mask;
 110                vcpu->arch.pfault_compare = parm.compare_mask;
 111                vcpu->run->s.regs.gprs[ry] = 0;
 112                rc = 0;
 113                break;
 114        case 1: /*
 115                 * CANCEL
 116                 * Specification allows to let already pending tokens survive
 117                 * the cancel, therefore to reduce code complexity, we assume
 118                 * all outstanding tokens are already pending.
 119                 */
 120                VCPU_EVENT(vcpu, 3, "pageref cancel addr 0x%llx", parm.token_addr);
 121                if (parm.token_addr || parm.select_mask ||
 122                    parm.compare_mask || parm.zarch)
 123                        return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 124
 125                vcpu->run->s.regs.gprs[ry] = 0;
 126                /*
 127                 * If the pfault handling was not established or is already
 128                 * canceled SC24-6084 requests to return decimal 4.
 129                 */
 130                if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID)
 131                        vcpu->run->s.regs.gprs[ry] = 4;
 132                else
 133                        vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
 134
 135                rc = 0;
 136                break;
 137        default:
 138                rc = -EOPNOTSUPP;
 139                break;
 140        }
 141
 142        return rc;
 143}
 144
 145static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
 146{
 147        VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
 148        vcpu->stat.instruction_diagnose_44++;
 149        kvm_vcpu_on_spin(vcpu, true);
 150        return 0;
 151}
 152
 153static int forward_cnt;
 154static unsigned long cur_slice;
 155
 156static int diag9c_forwarding_overrun(void)
 157{
 158        /* Reset the count on a new slice */
 159        if (time_after(jiffies, cur_slice)) {
 160                cur_slice = jiffies;
 161                forward_cnt = diag9c_forwarding_hz / HZ;
 162        }
 163        return forward_cnt-- <= 0 ? 1 : 0;
 164}
 165
 166static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
 167{
 168        struct kvm_vcpu *tcpu;
 169        int tid;
 170
 171        tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
 172        vcpu->stat.instruction_diagnose_9c++;
 173
 174        /* yield to self */
 175        if (tid == vcpu->vcpu_id)
 176                goto no_yield;
 177
 178        /* yield to invalid */
 179        tcpu = kvm_get_vcpu_by_id(vcpu->kvm, tid);
 180        if (!tcpu)
 181                goto no_yield;
 182
 183        /* target guest VCPU already running */
 184        if (READ_ONCE(tcpu->cpu) >= 0) {
 185                if (!diag9c_forwarding_hz || diag9c_forwarding_overrun())
 186                        goto no_yield;
 187
 188                /* target host CPU already running */
 189                if (!vcpu_is_preempted(tcpu->cpu))
 190                        goto no_yield;
 191                smp_yield_cpu(tcpu->cpu);
 192                VCPU_EVENT(vcpu, 5,
 193                           "diag time slice end directed to %d: yield forwarded",
 194                           tid);
 195                vcpu->stat.diag_9c_forward++;
 196                return 0;
 197        }
 198
 199        if (kvm_vcpu_yield_to(tcpu) <= 0)
 200                goto no_yield;
 201
 202        VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: done", tid);
 203        return 0;
 204no_yield:
 205        VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: ignored", tid);
 206        vcpu->stat.diag_9c_ignored++;
 207        return 0;
 208}
 209
 210static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
 211{
 212        unsigned int reg = vcpu->arch.sie_block->ipa & 0xf;
 213        unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff;
 214
 215        VCPU_EVENT(vcpu, 3, "diag ipl functions, subcode %lx", subcode);
 216        vcpu->stat.instruction_diagnose_308++;
 217        switch (subcode) {
 218        case 3:
 219                vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
 220                break;
 221        case 4:
 222                vcpu->run->s390_reset_flags = 0;
 223                break;
 224        default:
 225                return -EOPNOTSUPP;
 226        }
 227
 228        /*
 229         * no need to check the return value of vcpu_stop as it can only have
 230         * an error for protvirt, but protvirt means user cpu state
 231         */
 232        if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm))
 233                kvm_s390_vcpu_stop(vcpu);
 234        vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM;
 235        vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL;
 236        vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT;
 237        vcpu->run->exit_reason = KVM_EXIT_S390_RESET;
 238        VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx",
 239          vcpu->run->s390_reset_flags);
 240        trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags);
 241        return -EREMOTE;
 242}
 243
 244static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu)
 245{
 246        int ret;
 247
 248        vcpu->stat.instruction_diagnose_500++;
 249        /* No virtio-ccw notification? Get out quickly. */
 250        if (!vcpu->kvm->arch.css_support ||
 251            (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY))
 252                return -EOPNOTSUPP;
 253
 254        VCPU_EVENT(vcpu, 4, "diag 0x500 schid 0x%8.8x queue 0x%x cookie 0x%llx",
 255                            (u32) vcpu->run->s.regs.gprs[2],
 256                            (u32) vcpu->run->s.regs.gprs[3],
 257                            vcpu->run->s.regs.gprs[4]);
 258
 259        /*
 260         * The layout is as follows:
 261         * - gpr 2 contains the subchannel id (passed as addr)
 262         * - gpr 3 contains the virtqueue index (passed as datamatch)
 263         * - gpr 4 contains the index on the bus (optionally)
 264         */
 265        ret = kvm_io_bus_write_cookie(vcpu, KVM_VIRTIO_CCW_NOTIFY_BUS,
 266                                      vcpu->run->s.regs.gprs[2] & 0xffffffff,
 267                                      8, &vcpu->run->s.regs.gprs[3],
 268                                      vcpu->run->s.regs.gprs[4]);
 269
 270        /*
 271         * Return cookie in gpr 2, but don't overwrite the register if the
 272         * diagnose will be handled by userspace.
 273         */
 274        if (ret != -EOPNOTSUPP)
 275                vcpu->run->s.regs.gprs[2] = ret;
 276        /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */
 277        return ret < 0 ? ret : 0;
 278}
 279
 280int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
 281{
 282        int code = kvm_s390_get_base_disp_rs(vcpu, NULL) & 0xffff;
 283
 284        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 285                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 286
 287        trace_kvm_s390_handle_diag(vcpu, code);
 288        switch (code) {
 289        case 0x10:
 290                return diag_release_pages(vcpu);
 291        case 0x44:
 292                return __diag_time_slice_end(vcpu);
 293        case 0x9c:
 294                return __diag_time_slice_end_directed(vcpu);
 295        case 0x258:
 296                return __diag_page_ref_service(vcpu);
 297        case 0x308:
 298                return __diag_ipl_functions(vcpu);
 299        case 0x500:
 300                return __diag_virtio_hypercall(vcpu);
 301        default:
 302                vcpu->stat.instruction_diagnose_other++;
 303                return -EOPNOTSUPP;
 304        }
 305}
 306