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> 16deedabb2SMartin Schwidefsky #include <asm/pgalloc.h> 1710ccaa1eSCornelia Huck #include <asm/virtio-ccw.h> 18e28acfeaSChristian Borntraeger #include "kvm-s390.h" 195786fffaSCornelia Huck #include "trace.h" 20ade38c31SCornelia Huck #include "trace-s390.h" 213c038e6bSDominik Dingel #include "gaccess.h" 22e28acfeaSChristian Borntraeger 23388186bcSChristian Borntraeger static int diag_release_pages(struct kvm_vcpu *vcpu) 24388186bcSChristian Borntraeger { 25388186bcSChristian Borntraeger unsigned long start, end; 26fda902cbSMichael Mueller unsigned long prefix = kvm_s390_get_prefix(vcpu); 27388186bcSChristian Borntraeger 285a32c1afSChristian Borntraeger start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 295a32c1afSChristian Borntraeger end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; 30388186bcSChristian Borntraeger 31388186bcSChristian Borntraeger if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end 32388186bcSChristian Borntraeger || start < 2 * PAGE_SIZE) 33388186bcSChristian Borntraeger return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 34388186bcSChristian Borntraeger 35388186bcSChristian Borntraeger VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); 36388186bcSChristian Borntraeger vcpu->stat.diagnose_10++; 37388186bcSChristian Borntraeger 38388186bcSChristian Borntraeger /* we checked for start > end above */ 39388186bcSChristian Borntraeger if (end < prefix || start >= prefix + 2 * PAGE_SIZE) { 40388186bcSChristian Borntraeger gmap_discard(start, end, vcpu->arch.gmap); 41388186bcSChristian Borntraeger } else { 42388186bcSChristian Borntraeger if (start < prefix) 43388186bcSChristian Borntraeger gmap_discard(start, prefix, vcpu->arch.gmap); 44388186bcSChristian Borntraeger if (end >= prefix) 45388186bcSChristian Borntraeger gmap_discard(prefix + 2 * PAGE_SIZE, 46388186bcSChristian Borntraeger end, vcpu->arch.gmap); 47388186bcSChristian Borntraeger } 48388186bcSChristian Borntraeger return 0; 49388186bcSChristian Borntraeger } 50388186bcSChristian Borntraeger 513c038e6bSDominik Dingel static int __diag_page_ref_service(struct kvm_vcpu *vcpu) 523c038e6bSDominik Dingel { 533c038e6bSDominik Dingel struct prs_parm { 543c038e6bSDominik Dingel u16 code; 553c038e6bSDominik Dingel u16 subcode; 563c038e6bSDominik Dingel u16 parm_len; 573c038e6bSDominik Dingel u16 parm_version; 583c038e6bSDominik Dingel u64 token_addr; 593c038e6bSDominik Dingel u64 select_mask; 603c038e6bSDominik Dingel u64 compare_mask; 613c038e6bSDominik Dingel u64 zarch; 623c038e6bSDominik Dingel }; 633c038e6bSDominik Dingel struct prs_parm parm; 643c038e6bSDominik Dingel int rc; 653c038e6bSDominik Dingel u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4; 663c038e6bSDominik Dingel u16 ry = (vcpu->arch.sie_block->ipa & 0x0f); 673c038e6bSDominik Dingel 683c038e6bSDominik Dingel if (vcpu->run->s.regs.gprs[rx] & 7) 693c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 7081480cc1SHeiko Carstens rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], &parm, sizeof(parm)); 7181480cc1SHeiko Carstens if (rc) 7281480cc1SHeiko Carstens return kvm_s390_inject_prog_cond(vcpu, rc); 733c038e6bSDominik Dingel if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258) 743c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 753c038e6bSDominik Dingel 763c038e6bSDominik Dingel switch (parm.subcode) { 773c038e6bSDominik Dingel case 0: /* TOKEN */ 783c038e6bSDominik Dingel if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) { 793c038e6bSDominik Dingel /* 803c038e6bSDominik Dingel * If the pagefault handshake is already activated, 813c038e6bSDominik Dingel * the token must not be changed. We have to return 823c038e6bSDominik Dingel * decimal 8 instead, as mandated in SC24-6084. 833c038e6bSDominik Dingel */ 843c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 8; 853c038e6bSDominik Dingel return 0; 863c038e6bSDominik Dingel } 873c038e6bSDominik Dingel 883c038e6bSDominik Dingel if ((parm.compare_mask & parm.select_mask) != parm.compare_mask || 893c038e6bSDominik Dingel parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL) 903c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 913c038e6bSDominik Dingel 9281480cc1SHeiko Carstens if (kvm_is_error_gpa(vcpu->kvm, parm.token_addr)) 933c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 943c038e6bSDominik Dingel 953c038e6bSDominik Dingel vcpu->arch.pfault_token = parm.token_addr; 963c038e6bSDominik Dingel vcpu->arch.pfault_select = parm.select_mask; 973c038e6bSDominik Dingel vcpu->arch.pfault_compare = parm.compare_mask; 983c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 0; 993c038e6bSDominik Dingel rc = 0; 1003c038e6bSDominik Dingel break; 1013c038e6bSDominik Dingel case 1: /* 1023c038e6bSDominik Dingel * CANCEL 1033c038e6bSDominik Dingel * Specification allows to let already pending tokens survive 1043c038e6bSDominik Dingel * the cancel, therefore to reduce code complexity, we assume 1053c038e6bSDominik Dingel * all outstanding tokens are already pending. 1063c038e6bSDominik Dingel */ 1073c038e6bSDominik Dingel if (parm.token_addr || parm.select_mask || 1083c038e6bSDominik Dingel parm.compare_mask || parm.zarch) 1093c038e6bSDominik Dingel return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 1103c038e6bSDominik Dingel 1113c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 0; 1123c038e6bSDominik Dingel /* 1133c038e6bSDominik Dingel * If the pfault handling was not established or is already 1143c038e6bSDominik Dingel * canceled SC24-6084 requests to return decimal 4. 1153c038e6bSDominik Dingel */ 1163c038e6bSDominik Dingel if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID) 1173c038e6bSDominik Dingel vcpu->run->s.regs.gprs[ry] = 4; 1183c038e6bSDominik Dingel else 1193c038e6bSDominik Dingel vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; 1203c038e6bSDominik Dingel 1213c038e6bSDominik Dingel rc = 0; 1223c038e6bSDominik Dingel break; 1233c038e6bSDominik Dingel default: 1243c038e6bSDominik Dingel rc = -EOPNOTSUPP; 1253c038e6bSDominik Dingel break; 1263c038e6bSDominik Dingel } 1273c038e6bSDominik Dingel 1283c038e6bSDominik Dingel return rc; 1293c038e6bSDominik Dingel } 1303c038e6bSDominik 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 179*6352e4d2SDavid Hildenbrand if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) 1806852d7b6SDavid Hildenbrand kvm_s390_vcpu_stop(vcpu); 181e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; 182e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; 183e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; 184e28acfeaSChristian Borntraeger vcpu->run->exit_reason = KVM_EXIT_S390_RESET; 18533e19115SHeiko Carstens VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx", 186e28acfeaSChristian Borntraeger vcpu->run->s390_reset_flags); 187ade38c31SCornelia Huck trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags); 188e28acfeaSChristian Borntraeger return -EREMOTE; 189e28acfeaSChristian Borntraeger } 190e28acfeaSChristian Borntraeger 19110ccaa1eSCornelia Huck static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) 19210ccaa1eSCornelia Huck { 193800c1065SThomas Huth int ret; 19410ccaa1eSCornelia Huck 19510ccaa1eSCornelia Huck /* No virtio-ccw notification? Get out quickly. */ 19610ccaa1eSCornelia Huck if (!vcpu->kvm->arch.css_support || 19710ccaa1eSCornelia Huck (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) 19810ccaa1eSCornelia Huck return -EOPNOTSUPP; 19910ccaa1eSCornelia Huck 20010ccaa1eSCornelia Huck /* 20110ccaa1eSCornelia Huck * The layout is as follows: 20210ccaa1eSCornelia Huck * - gpr 2 contains the subchannel id (passed as addr) 20310ccaa1eSCornelia Huck * - gpr 3 contains the virtqueue index (passed as datamatch) 20485dfe87eSCornelia Huck * - gpr 4 contains the index on the bus (optionally) 20510ccaa1eSCornelia Huck */ 20685dfe87eSCornelia Huck ret = kvm_io_bus_write_cookie(vcpu->kvm, KVM_VIRTIO_CCW_NOTIFY_BUS, 207ff1f3cb4SDominik Dingel vcpu->run->s.regs.gprs[2] & 0xffffffff, 20885dfe87eSCornelia Huck 8, &vcpu->run->s.regs.gprs[3], 20985dfe87eSCornelia Huck vcpu->run->s.regs.gprs[4]); 21085dfe87eSCornelia Huck 21185dfe87eSCornelia Huck /* 21285dfe87eSCornelia Huck * Return cookie in gpr 2, but don't overwrite the register if the 21385dfe87eSCornelia Huck * diagnose will be handled by userspace. 21485dfe87eSCornelia Huck */ 21585dfe87eSCornelia Huck if (ret != -EOPNOTSUPP) 21685dfe87eSCornelia Huck vcpu->run->s.regs.gprs[2] = ret; 21785dfe87eSCornelia Huck /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */ 21810ccaa1eSCornelia Huck return ret < 0 ? ret : 0; 21910ccaa1eSCornelia Huck } 22010ccaa1eSCornelia Huck 221e28acfeaSChristian Borntraeger int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) 222e28acfeaSChristian Borntraeger { 223743db27cSHeiko Carstens int code = kvm_s390_get_base_disp_rs(vcpu) & 0xffff; 224e28acfeaSChristian Borntraeger 22593e1750fSThomas Huth if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 22693e1750fSThomas Huth return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 22793e1750fSThomas Huth 2285786fffaSCornelia Huck trace_kvm_s390_handle_diag(vcpu, code); 229e28acfeaSChristian Borntraeger switch (code) { 230388186bcSChristian Borntraeger case 0x10: 231388186bcSChristian Borntraeger return diag_release_pages(vcpu); 232e28acfeaSChristian Borntraeger case 0x44: 233e28acfeaSChristian Borntraeger return __diag_time_slice_end(vcpu); 23441628d33SKonstantin Weitz case 0x9c: 23541628d33SKonstantin Weitz return __diag_time_slice_end_directed(vcpu); 2363c038e6bSDominik Dingel case 0x258: 2373c038e6bSDominik Dingel return __diag_page_ref_service(vcpu); 238e28acfeaSChristian Borntraeger case 0x308: 239e28acfeaSChristian Borntraeger return __diag_ipl_functions(vcpu); 24010ccaa1eSCornelia Huck case 0x500: 24110ccaa1eSCornelia Huck return __diag_virtio_hypercall(vcpu); 242e28acfeaSChristian Borntraeger default: 243b8e660b8SHeiko Carstens return -EOPNOTSUPP; 244e28acfeaSChristian Borntraeger } 245e28acfeaSChristian Borntraeger } 246