1e28acfeaSChristian Borntraeger /* 2a53c8fabSHeiko Carstens * handling diagnose instructions 3e28acfeaSChristian Borntraeger * 4388186bcSChristian Borntraeger * Copyright IBM Corp. 2008, 2011 5e28acfeaSChristian Borntraeger * 6e28acfeaSChristian Borntraeger * This program is free software; you can redistribute it and/or modify 7e28acfeaSChristian Borntraeger * it under the terms of the GNU General Public License (version 2 only) 8e28acfeaSChristian Borntraeger * as published by the Free Software Foundation. 9e28acfeaSChristian Borntraeger * 10e28acfeaSChristian Borntraeger * Author(s): Carsten Otte <cotte@de.ibm.com> 11e28acfeaSChristian Borntraeger * Christian Borntraeger <borntraeger@de.ibm.com> 12e28acfeaSChristian Borntraeger */ 13e28acfeaSChristian Borntraeger 14e28acfeaSChristian Borntraeger #include <linux/kvm.h> 15e28acfeaSChristian Borntraeger #include <linux/kvm_host.h> 1610ccaa1eSCornelia Huck #include <asm/virtio-ccw.h> 17e28acfeaSChristian Borntraeger #include "kvm-s390.h" 185786fffaSCornelia Huck #include "trace.h" 19ade38c31SCornelia Huck #include "trace-s390.h" 20*3c038e6bSDominik Dingel #include "gaccess.h" 21e28acfeaSChristian Borntraeger 22388186bcSChristian Borntraeger static int diag_release_pages(struct kvm_vcpu *vcpu) 23388186bcSChristian Borntraeger { 24388186bcSChristian Borntraeger unsigned long start, end; 25388186bcSChristian Borntraeger unsigned long prefix = vcpu->arch.sie_block->prefix; 26388186bcSChristian Borntraeger 275a32c1afSChristian Borntraeger start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 285a32c1afSChristian Borntraeger end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; 29388186bcSChristian Borntraeger 30388186bcSChristian Borntraeger if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end 31388186bcSChristian Borntraeger || start < 2 * PAGE_SIZE) 32388186bcSChristian Borntraeger return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 33388186bcSChristian Borntraeger 34388186bcSChristian Borntraeger VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); 35388186bcSChristian Borntraeger vcpu->stat.diagnose_10++; 36388186bcSChristian Borntraeger 37388186bcSChristian Borntraeger /* we checked for start > end above */ 38388186bcSChristian Borntraeger if (end < prefix || start >= prefix + 2 * PAGE_SIZE) { 39388186bcSChristian Borntraeger gmap_discard(start, end, vcpu->arch.gmap); 40388186bcSChristian Borntraeger } else { 41388186bcSChristian Borntraeger if (start < prefix) 42388186bcSChristian Borntraeger gmap_discard(start, prefix, vcpu->arch.gmap); 43388186bcSChristian Borntraeger if (end >= prefix) 44388186bcSChristian Borntraeger gmap_discard(prefix + 2 * PAGE_SIZE, 45388186bcSChristian Borntraeger end, vcpu->arch.gmap); 46388186bcSChristian Borntraeger } 47388186bcSChristian Borntraeger return 0; 48388186bcSChristian Borntraeger } 49388186bcSChristian Borntraeger 50*3c038e6bSDominik Dingel static int __diag_page_ref_service(struct kvm_vcpu *vcpu) 51*3c038e6bSDominik Dingel { 52*3c038e6bSDominik Dingel struct prs_parm { 53*3c038e6bSDominik Dingel u16 code; 54*3c038e6bSDominik Dingel u16 subcode; 55*3c038e6bSDominik Dingel u16 parm_len; 56*3c038e6bSDominik Dingel u16 parm_version; 57*3c038e6bSDominik Dingel u64 token_addr; 58*3c038e6bSDominik Dingel u64 select_mask; 59*3c038e6bSDominik Dingel u64 compare_mask; 60*3c038e6bSDominik Dingel u64 zarch; 61*3c038e6bSDominik Dingel }; 62*3c038e6bSDominik Dingel struct prs_parm parm; 63*3c038e6bSDominik Dingel int rc; 64*3c038e6bSDominik Dingel u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4; 65*3c038e6bSDominik Dingel u16 ry = (vcpu->arch.sie_block->ipa & 0x0f); 66*3c038e6bSDominik Dingel unsigned long hva_token = KVM_HVA_ERR_BAD; 67*3c038e6bSDominik Dingel 68*3c038e6bSDominik Dingel if (vcpu->run->s.regs.gprs[rx] & 7) 69*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 70*3c038e6bSDominik Dingel if (copy_from_guest(vcpu, &parm, vcpu->run->s.regs.gprs[rx], sizeof(parm))) 71*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 72*3c038e6bSDominik Dingel if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258) 73*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 74*3c038e6bSDominik Dingel 75*3c038e6bSDominik Dingel switch (parm.subcode) { 76*3c038e6bSDominik Dingel case 0: /* TOKEN */ 77*3c038e6bSDominik Dingel if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) { 78*3c038e6bSDominik Dingel /* 79*3c038e6bSDominik Dingel * If the pagefault handshake is already activated, 80*3c038e6bSDominik Dingel * the token must not be changed. We have to return 81*3c038e6bSDominik Dingel * decimal 8 instead, as mandated in SC24-6084. 82*3c038e6bSDominik Dingel */ 83*3c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 8; 84*3c038e6bSDominik Dingel return 0; 85*3c038e6bSDominik Dingel } 86*3c038e6bSDominik Dingel 87*3c038e6bSDominik Dingel if ((parm.compare_mask & parm.select_mask) != parm.compare_mask || 88*3c038e6bSDominik Dingel parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL) 89*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 90*3c038e6bSDominik Dingel 91*3c038e6bSDominik Dingel hva_token = gfn_to_hva(vcpu->kvm, gpa_to_gfn(parm.token_addr)); 92*3c038e6bSDominik Dingel if (kvm_is_error_hva(hva_token)) 93*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 94*3c038e6bSDominik Dingel 95*3c038e6bSDominik Dingel vcpu->arch.pfault_token = parm.token_addr; 96*3c038e6bSDominik Dingel vcpu->arch.pfault_select = parm.select_mask; 97*3c038e6bSDominik Dingel vcpu->arch.pfault_compare = parm.compare_mask; 98*3c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 0; 99*3c038e6bSDominik Dingel rc = 0; 100*3c038e6bSDominik Dingel break; 101*3c038e6bSDominik Dingel case 1: /* 102*3c038e6bSDominik Dingel * CANCEL 103*3c038e6bSDominik Dingel * Specification allows to let already pending tokens survive 104*3c038e6bSDominik Dingel * the cancel, therefore to reduce code complexity, we assume 105*3c038e6bSDominik Dingel * all outstanding tokens are already pending. 106*3c038e6bSDominik Dingel */ 107*3c038e6bSDominik Dingel if (parm.token_addr || parm.select_mask || 108*3c038e6bSDominik Dingel parm.compare_mask || parm.zarch) 109*3c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 110*3c038e6bSDominik Dingel 111*3c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 0; 112*3c038e6bSDominik Dingel /* 113*3c038e6bSDominik Dingel * If the pfault handling was not established or is already 114*3c038e6bSDominik Dingel * canceled SC24-6084 requests to return decimal 4. 115*3c038e6bSDominik Dingel */ 116*3c038e6bSDominik Dingel if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID) 117*3c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 4; 118*3c038e6bSDominik Dingel else 119*3c038e6bSDominik Dingel vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; 120*3c038e6bSDominik Dingel 121*3c038e6bSDominik Dingel rc = 0; 122*3c038e6bSDominik Dingel break; 123*3c038e6bSDominik Dingel default: 124*3c038e6bSDominik Dingel rc = -EOPNOTSUPP; 125*3c038e6bSDominik Dingel break; 126*3c038e6bSDominik Dingel } 127*3c038e6bSDominik Dingel 128*3c038e6bSDominik Dingel return rc; 129*3c038e6bSDominik Dingel } 130*3c038e6bSDominik Dingel 131e28acfeaSChristian Borntraeger static int __diag_time_slice_end(struct kvm_vcpu *vcpu) 132e28acfeaSChristian Borntraeger { 133e28acfeaSChristian Borntraeger VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); 134e28acfeaSChristian Borntraeger vcpu->stat.diagnose_44++; 1358733ac36SChristian Borntraeger kvm_vcpu_on_spin(vcpu); 136e28acfeaSChristian Borntraeger return 0; 137e28acfeaSChristian Borntraeger } 138e28acfeaSChristian Borntraeger 13941628d33SKonstantin Weitz static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) 14041628d33SKonstantin Weitz { 14141628d33SKonstantin Weitz struct kvm *kvm = vcpu->kvm; 14241628d33SKonstantin Weitz struct kvm_vcpu *tcpu; 14341628d33SKonstantin Weitz int tid; 14441628d33SKonstantin Weitz int i; 14541628d33SKonstantin Weitz 14641628d33SKonstantin Weitz tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 14741628d33SKonstantin Weitz vcpu->stat.diagnose_9c++; 14841628d33SKonstantin Weitz VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d", tid); 14941628d33SKonstantin Weitz 15041628d33SKonstantin Weitz if (tid == vcpu->vcpu_id) 15141628d33SKonstantin Weitz return 0; 15241628d33SKonstantin Weitz 15341628d33SKonstantin Weitz kvm_for_each_vcpu(i, tcpu, kvm) 15441628d33SKonstantin Weitz if (tcpu->vcpu_id == tid) { 15541628d33SKonstantin Weitz kvm_vcpu_yield_to(tcpu); 15641628d33SKonstantin Weitz break; 15741628d33SKonstantin Weitz } 15841628d33SKonstantin Weitz 15941628d33SKonstantin Weitz return 0; 16041628d33SKonstantin Weitz } 16141628d33SKonstantin Weitz 162e28acfeaSChristian Borntraeger static int __diag_ipl_functions(struct kvm_vcpu *vcpu) 163e28acfeaSChristian Borntraeger { 164e28acfeaSChristian Borntraeger unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; 1655a32c1afSChristian Borntraeger unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; 166e28acfeaSChristian Borntraeger 167e28acfeaSChristian Borntraeger VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode); 168e28acfeaSChristian Borntraeger switch (subcode) { 169e28acfeaSChristian Borntraeger case 3: 170e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; 171e28acfeaSChristian Borntraeger break; 172e28acfeaSChristian Borntraeger case 4: 173e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags = 0; 174e28acfeaSChristian Borntraeger break; 175e28acfeaSChristian Borntraeger default: 176b8e660b8SHeiko Carstens return -EOPNOTSUPP; 177e28acfeaSChristian Borntraeger } 178e28acfeaSChristian Borntraeger 1799e6dabefSCornelia Huck atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); 180e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; 181e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; 182e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; 183e28acfeaSChristian Borntraeger vcpu->run->exit_reason = KVM_EXIT_S390_RESET; 18433e19115SHeiko Carstens VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx", 185e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags); 186ade38c31SCornelia Huck trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags); 187e28acfeaSChristian Borntraeger return -EREMOTE; 188e28acfeaSChristian Borntraeger } 189e28acfeaSChristian Borntraeger 19010ccaa1eSCornelia Huck static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) 19110ccaa1eSCornelia Huck { 192800c1065SThomas Huth int ret; 19310ccaa1eSCornelia Huck 19410ccaa1eSCornelia Huck /* No virtio-ccw notification? Get out quickly. */ 19510ccaa1eSCornelia Huck if (!vcpu->kvm->arch.css_support || 19610ccaa1eSCornelia Huck (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) 19710ccaa1eSCornelia Huck return -EOPNOTSUPP; 19810ccaa1eSCornelia Huck 19910ccaa1eSCornelia Huck /* 20010ccaa1eSCornelia Huck * The layout is as follows: 20110ccaa1eSCornelia Huck * - gpr 2 contains the subchannel id (passed as addr) 20210ccaa1eSCornelia Huck * - gpr 3 contains the virtqueue index (passed as datamatch) 20385dfe87eSCornelia Huck * - gpr 4 contains the index on the bus (optionally) 20410ccaa1eSCornelia Huck */ 20585dfe87eSCornelia Huck ret = kvm_io_bus_write_cookie(vcpu->kvm, KVM_VIRTIO_CCW_NOTIFY_BUS, 206ff1f3cb4SDominik Dingel vcpu->run->s.regs.gprs[2] & 0xffffffff, 20785dfe87eSCornelia Huck 8, &vcpu->run->s.regs.gprs[3], 20885dfe87eSCornelia Huck vcpu->run->s.regs.gprs[4]); 20985dfe87eSCornelia Huck 21085dfe87eSCornelia Huck /* 21185dfe87eSCornelia Huck * Return cookie in gpr 2, but don't overwrite the register if the 21285dfe87eSCornelia Huck * diagnose will be handled by userspace. 21385dfe87eSCornelia Huck */ 21485dfe87eSCornelia Huck if (ret != -EOPNOTSUPP) 21585dfe87eSCornelia Huck vcpu->run->s.regs.gprs[2] = ret; 21685dfe87eSCornelia Huck /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */ 21710ccaa1eSCornelia Huck return ret < 0 ? ret : 0; 21810ccaa1eSCornelia Huck } 21910ccaa1eSCornelia Huck 220e28acfeaSChristian Borntraeger int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) 221e28acfeaSChristian Borntraeger { 222743db27cSHeiko Carstens int code = kvm_s390_get_base_disp_rs(vcpu) & 0xffff; 223e28acfeaSChristian Borntraeger 22493e1750fSThomas Huth if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 22593e1750fSThomas Huth return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 22693e1750fSThomas Huth 2275786fffaSCornelia Huck trace_kvm_s390_handle_diag(vcpu, code); 228e28acfeaSChristian Borntraeger switch (code) { 229388186bcSChristian Borntraeger case 0x10: 230388186bcSChristian Borntraeger return diag_release_pages(vcpu); 231e28acfeaSChristian Borntraeger case 0x44: 232e28acfeaSChristian Borntraeger return __diag_time_slice_end(vcpu); 23341628d33SKonstantin Weitz case 0x9c: 23441628d33SKonstantin Weitz return __diag_time_slice_end_directed(vcpu); 235*3c038e6bSDominik Dingel case 0x258: 236*3c038e6bSDominik Dingel return __diag_page_ref_service(vcpu); 237e28acfeaSChristian Borntraeger case 0x308: 238e28acfeaSChristian Borntraeger return __diag_ipl_functions(vcpu); 23910ccaa1eSCornelia Huck case 0x500: 24010ccaa1eSCornelia Huck return __diag_virtio_hypercall(vcpu); 241e28acfeaSChristian Borntraeger default: 242b8e660b8SHeiko Carstens return -EOPNOTSUPP; 243e28acfeaSChristian Borntraeger } 244e28acfeaSChristian Borntraeger } 245