linux/arch/s390/kvm/intercept.c
<<
>>
Prefs
   1/*
   2 * in-kernel handling for sie intercepts
   3 *
   4 * Copyright IBM Corp. 2008, 2014
   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_host.h>
  15#include <linux/errno.h>
  16#include <linux/pagemap.h>
  17
  18#include <asm/kvm_host.h>
  19#include <asm/asm-offsets.h>
  20#include <asm/irq.h>
  21
  22#include "kvm-s390.h"
  23#include "gaccess.h"
  24#include "trace.h"
  25#include "trace-s390.h"
  26
  27
  28static const intercept_handler_t instruction_handlers[256] = {
  29        [0x01] = kvm_s390_handle_01,
  30        [0x82] = kvm_s390_handle_lpsw,
  31        [0x83] = kvm_s390_handle_diag,
  32        [0xae] = kvm_s390_handle_sigp,
  33        [0xb2] = kvm_s390_handle_b2,
  34        [0xb6] = kvm_s390_handle_stctl,
  35        [0xb7] = kvm_s390_handle_lctl,
  36        [0xb9] = kvm_s390_handle_b9,
  37        [0xe5] = kvm_s390_handle_e5,
  38        [0xeb] = kvm_s390_handle_eb,
  39};
  40
  41void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilc)
  42{
  43        struct kvm_s390_sie_block *sie_block = vcpu->arch.sie_block;
  44
  45        /* Use the length of the EXECUTE instruction if necessary */
  46        if (sie_block->icptstatus & 1) {
  47                ilc = (sie_block->icptstatus >> 4) & 0x6;
  48                if (!ilc)
  49                        ilc = 4;
  50        }
  51        sie_block->gpsw.addr = __rewind_psw(sie_block->gpsw, ilc);
  52}
  53
  54static int handle_noop(struct kvm_vcpu *vcpu)
  55{
  56        switch (vcpu->arch.sie_block->icptcode) {
  57        case 0x0:
  58                vcpu->stat.exit_null++;
  59                break;
  60        case 0x10:
  61                vcpu->stat.exit_external_request++;
  62                break;
  63        default:
  64                break; /* nothing */
  65        }
  66        return 0;
  67}
  68
  69static int handle_stop(struct kvm_vcpu *vcpu)
  70{
  71        struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
  72        int rc = 0;
  73        uint8_t flags, stop_pending;
  74
  75        vcpu->stat.exit_stop_request++;
  76
  77        /* delay the stop if any non-stop irq is pending */
  78        if (kvm_s390_vcpu_has_irq(vcpu, 1))
  79                return 0;
  80
  81        /* avoid races with the injection/SIGP STOP code */
  82        spin_lock(&li->lock);
  83        flags = li->irq.stop.flags;
  84        stop_pending = kvm_s390_is_stop_irq_pending(vcpu);
  85        spin_unlock(&li->lock);
  86
  87        trace_kvm_s390_stop_request(stop_pending, flags);
  88        if (!stop_pending)
  89                return 0;
  90
  91        if (flags & KVM_S390_STOP_FLAG_STORE_STATUS) {
  92                rc = kvm_s390_vcpu_store_status(vcpu,
  93                                                KVM_S390_STORE_STATUS_NOADDR);
  94                if (rc)
  95                        return rc;
  96        }
  97
  98        if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm))
  99                kvm_s390_vcpu_stop(vcpu);
 100        return -EOPNOTSUPP;
 101}
 102
 103static int handle_validity(struct kvm_vcpu *vcpu)
 104{
 105        int viwhy = vcpu->arch.sie_block->ipb >> 16;
 106
 107        vcpu->stat.exit_validity++;
 108        trace_kvm_s390_intercept_validity(vcpu, viwhy);
 109        WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
 110        return -EOPNOTSUPP;
 111}
 112
 113static int handle_instruction(struct kvm_vcpu *vcpu)
 114{
 115        intercept_handler_t handler;
 116
 117        vcpu->stat.exit_instruction++;
 118        trace_kvm_s390_intercept_instruction(vcpu,
 119                                             vcpu->arch.sie_block->ipa,
 120                                             vcpu->arch.sie_block->ipb);
 121        handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
 122        if (handler)
 123                return handler(vcpu);
 124        return -EOPNOTSUPP;
 125}
 126
 127static void __extract_prog_irq(struct kvm_vcpu *vcpu,
 128                               struct kvm_s390_pgm_info *pgm_info)
 129{
 130        memset(pgm_info, 0, sizeof(struct kvm_s390_pgm_info));
 131        pgm_info->code = vcpu->arch.sie_block->iprcc;
 132
 133        switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) {
 134        case PGM_AFX_TRANSLATION:
 135        case PGM_ASX_TRANSLATION:
 136        case PGM_EX_TRANSLATION:
 137        case PGM_LFX_TRANSLATION:
 138        case PGM_LSTE_SEQUENCE:
 139        case PGM_LSX_TRANSLATION:
 140        case PGM_LX_TRANSLATION:
 141        case PGM_PRIMARY_AUTHORITY:
 142        case PGM_SECONDARY_AUTHORITY:
 143        case PGM_SPACE_SWITCH:
 144                pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
 145                break;
 146        case PGM_ALEN_TRANSLATION:
 147        case PGM_ALE_SEQUENCE:
 148        case PGM_ASTE_INSTANCE:
 149        case PGM_ASTE_SEQUENCE:
 150        case PGM_ASTE_VALIDITY:
 151        case PGM_EXTENDED_AUTHORITY:
 152                pgm_info->exc_access_id = vcpu->arch.sie_block->eai;
 153                break;
 154        case PGM_ASCE_TYPE:
 155        case PGM_PAGE_TRANSLATION:
 156        case PGM_REGION_FIRST_TRANS:
 157        case PGM_REGION_SECOND_TRANS:
 158        case PGM_REGION_THIRD_TRANS:
 159        case PGM_SEGMENT_TRANSLATION:
 160                pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
 161                pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
 162                pgm_info->op_access_id  = vcpu->arch.sie_block->oai;
 163                break;
 164        case PGM_MONITOR:
 165                pgm_info->mon_class_nr = vcpu->arch.sie_block->mcn;
 166                pgm_info->mon_code = vcpu->arch.sie_block->tecmc;
 167                break;
 168        case PGM_DATA:
 169                pgm_info->data_exc_code = vcpu->arch.sie_block->dxc;
 170                break;
 171        case PGM_PROTECTION:
 172                pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
 173                pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
 174                break;
 175        default:
 176                break;
 177        }
 178
 179        if (vcpu->arch.sie_block->iprcc & PGM_PER) {
 180                pgm_info->per_code = vcpu->arch.sie_block->perc;
 181                pgm_info->per_atmid = vcpu->arch.sie_block->peratmid;
 182                pgm_info->per_address = vcpu->arch.sie_block->peraddr;
 183                pgm_info->per_access_id = vcpu->arch.sie_block->peraid;
 184        }
 185}
 186
 187/*
 188 * restore ITDB to program-interruption TDB in guest lowcore
 189 * and set TX abort indication if required
 190*/
 191static int handle_itdb(struct kvm_vcpu *vcpu)
 192{
 193        struct kvm_s390_itdb *itdb;
 194        int rc;
 195
 196        if (!IS_TE_ENABLED(vcpu) || !IS_ITDB_VALID(vcpu))
 197                return 0;
 198        if (current->thread.per_flags & PER_FLAG_NO_TE)
 199                return 0;
 200        itdb = (struct kvm_s390_itdb *)vcpu->arch.sie_block->itdba;
 201        rc = write_guest_lc(vcpu, __LC_PGM_TDB, itdb, sizeof(*itdb));
 202        if (rc)
 203                return rc;
 204        memset(itdb, 0, sizeof(*itdb));
 205
 206        return 0;
 207}
 208
 209#define per_event(vcpu) (vcpu->arch.sie_block->iprcc & PGM_PER)
 210
 211static int handle_prog(struct kvm_vcpu *vcpu)
 212{
 213        struct kvm_s390_pgm_info pgm_info;
 214        psw_t psw;
 215        int rc;
 216
 217        vcpu->stat.exit_program_interruption++;
 218
 219        if (guestdbg_enabled(vcpu) && per_event(vcpu)) {
 220                kvm_s390_handle_per_event(vcpu);
 221                /* the interrupt might have been filtered out completely */
 222                if (vcpu->arch.sie_block->iprcc == 0)
 223                        return 0;
 224        }
 225
 226        trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
 227        if (vcpu->arch.sie_block->iprcc == PGM_SPECIFICATION) {
 228                rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &psw, sizeof(psw_t));
 229                if (rc)
 230                        return rc;
 231                /* Avoid endless loops of specification exceptions */
 232                if (!is_valid_psw(&psw))
 233                        return -EOPNOTSUPP;
 234        }
 235        rc = handle_itdb(vcpu);
 236        if (rc)
 237                return rc;
 238
 239        __extract_prog_irq(vcpu, &pgm_info);
 240        return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
 241}
 242
 243static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
 244{
 245        int rc, rc2;
 246
 247        vcpu->stat.exit_instr_and_program++;
 248        rc = handle_instruction(vcpu);
 249        rc2 = handle_prog(vcpu);
 250
 251        if (rc == -EOPNOTSUPP)
 252                vcpu->arch.sie_block->icptcode = 0x04;
 253        if (rc)
 254                return rc;
 255        return rc2;
 256}
 257
 258/**
 259 * handle_external_interrupt - used for external interruption interceptions
 260 *
 261 * This interception only occurs if the CPUSTAT_EXT_INT bit was set, or if
 262 * the new PSW does not have external interrupts disabled. In the first case,
 263 * we've got to deliver the interrupt manually, and in the second case, we
 264 * drop to userspace to handle the situation there.
 265 */
 266static int handle_external_interrupt(struct kvm_vcpu *vcpu)
 267{
 268        u16 eic = vcpu->arch.sie_block->eic;
 269        struct kvm_s390_irq irq;
 270        psw_t newpsw;
 271        int rc;
 272
 273        vcpu->stat.exit_external_interrupt++;
 274
 275        rc = read_guest_lc(vcpu, __LC_EXT_NEW_PSW, &newpsw, sizeof(psw_t));
 276        if (rc)
 277                return rc;
 278        /* We can not handle clock comparator or timer interrupt with bad PSW */
 279        if ((eic == EXT_IRQ_CLK_COMP || eic == EXT_IRQ_CPU_TIMER) &&
 280            (newpsw.mask & PSW_MASK_EXT))
 281                return -EOPNOTSUPP;
 282
 283        switch (eic) {
 284        case EXT_IRQ_CLK_COMP:
 285                irq.type = KVM_S390_INT_CLOCK_COMP;
 286                break;
 287        case EXT_IRQ_CPU_TIMER:
 288                irq.type = KVM_S390_INT_CPU_TIMER;
 289                break;
 290        case EXT_IRQ_EXTERNAL_CALL:
 291                irq.type = KVM_S390_INT_EXTERNAL_CALL;
 292                irq.u.extcall.code = vcpu->arch.sie_block->extcpuaddr;
 293                rc = kvm_s390_inject_vcpu(vcpu, &irq);
 294                /* ignore if another external call is already pending */
 295                if (rc == -EBUSY)
 296                        return 0;
 297                return rc;
 298        default:
 299                return -EOPNOTSUPP;
 300        }
 301
 302        return kvm_s390_inject_vcpu(vcpu, &irq);
 303}
 304
 305/**
 306 * Handle MOVE PAGE partial execution interception.
 307 *
 308 * This interception can only happen for guests with DAT disabled and
 309 * addresses that are currently not mapped in the host. Thus we try to
 310 * set up the mappings for the corresponding user pages here (or throw
 311 * addressing exceptions in case of illegal guest addresses).
 312 */
 313static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
 314{
 315        unsigned long srcaddr, dstaddr;
 316        int reg1, reg2, rc;
 317
 318        kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
 319
 320        /* Make sure that the source is paged-in */
 321        rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg2],
 322                                     &srcaddr, 0);
 323        if (rc)
 324                return kvm_s390_inject_prog_cond(vcpu, rc);
 325        rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0);
 326        if (rc != 0)
 327                return rc;
 328
 329        /* Make sure that the destination is paged-in */
 330        rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg1],
 331                                     &dstaddr, 1);
 332        if (rc)
 333                return kvm_s390_inject_prog_cond(vcpu, rc);
 334        rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1);
 335        if (rc != 0)
 336                return rc;
 337
 338        kvm_s390_rewind_psw(vcpu, 4);
 339
 340        return 0;
 341}
 342
 343static int handle_partial_execution(struct kvm_vcpu *vcpu)
 344{
 345        if (vcpu->arch.sie_block->ipa == 0xb254)        /* MVPG */
 346                return handle_mvpg_pei(vcpu);
 347        if (vcpu->arch.sie_block->ipa >> 8 == 0xae)     /* SIGP */
 348                return kvm_s390_handle_sigp_pei(vcpu);
 349
 350        return -EOPNOTSUPP;
 351}
 352
 353static const intercept_handler_t intercept_funcs[] = {
 354        [0x00 >> 2] = handle_noop,
 355        [0x04 >> 2] = handle_instruction,
 356        [0x08 >> 2] = handle_prog,
 357        [0x0C >> 2] = handle_instruction_and_prog,
 358        [0x10 >> 2] = handle_noop,
 359        [0x14 >> 2] = handle_external_interrupt,
 360        [0x18 >> 2] = handle_noop,
 361        [0x1C >> 2] = kvm_s390_handle_wait,
 362        [0x20 >> 2] = handle_validity,
 363        [0x28 >> 2] = handle_stop,
 364        [0x38 >> 2] = handle_partial_execution,
 365};
 366
 367int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
 368{
 369        intercept_handler_t func;
 370        u8 code = vcpu->arch.sie_block->icptcode;
 371
 372        if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
 373                return -EOPNOTSUPP;
 374        func = intercept_funcs[code >> 2];
 375        if (func)
 376                return func(vcpu);
 377        return -EOPNOTSUPP;
 378}
 379