18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * handling diagnose instructions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2008, 2020 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Carsten Otte <cotte@de.ibm.com> 88c2ecf20Sopenharmony_ci * Christian Borntraeger <borntraeger@de.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kvm.h> 128c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 138c2ecf20Sopenharmony_ci#include <asm/gmap.h> 148c2ecf20Sopenharmony_ci#include <asm/virtio-ccw.h> 158c2ecf20Sopenharmony_ci#include "kvm-s390.h" 168c2ecf20Sopenharmony_ci#include "trace.h" 178c2ecf20Sopenharmony_ci#include "trace-s390.h" 188c2ecf20Sopenharmony_ci#include "gaccess.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int diag_release_pages(struct kvm_vcpu *vcpu) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci unsigned long start, end; 238c2ecf20Sopenharmony_ci unsigned long prefix = kvm_s390_get_prefix(vcpu); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 268c2ecf20Sopenharmony_ci end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + PAGE_SIZE; 278c2ecf20Sopenharmony_ci vcpu->stat.diagnose_10++; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (start & ~PAGE_MASK || end & ~PAGE_MASK || start >= end 308c2ecf20Sopenharmony_ci || start < 2 * PAGE_SIZE) 318c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * We checked for start >= end above, so lets check for the 378c2ecf20Sopenharmony_ci * fast path (no prefix swap page involved) 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci if (end <= prefix || start >= prefix + 2 * PAGE_SIZE) { 408c2ecf20Sopenharmony_ci gmap_discard(vcpu->arch.gmap, start, end); 418c2ecf20Sopenharmony_ci } else { 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * This is slow path. gmap_discard will check for start 448c2ecf20Sopenharmony_ci * so lets split this into before prefix, prefix, after 458c2ecf20Sopenharmony_ci * prefix and let gmap_discard make some of these calls 468c2ecf20Sopenharmony_ci * NOPs. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci gmap_discard(vcpu->arch.gmap, start, prefix); 498c2ecf20Sopenharmony_ci if (start <= prefix) 508c2ecf20Sopenharmony_ci gmap_discard(vcpu->arch.gmap, 0, PAGE_SIZE); 518c2ecf20Sopenharmony_ci if (end > prefix + PAGE_SIZE) 528c2ecf20Sopenharmony_ci gmap_discard(vcpu->arch.gmap, PAGE_SIZE, 2 * PAGE_SIZE); 538c2ecf20Sopenharmony_ci gmap_discard(vcpu->arch.gmap, prefix + 2 * PAGE_SIZE, end); 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int __diag_page_ref_service(struct kvm_vcpu *vcpu) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct prs_parm { 618c2ecf20Sopenharmony_ci u16 code; 628c2ecf20Sopenharmony_ci u16 subcode; 638c2ecf20Sopenharmony_ci u16 parm_len; 648c2ecf20Sopenharmony_ci u16 parm_version; 658c2ecf20Sopenharmony_ci u64 token_addr; 668c2ecf20Sopenharmony_ci u64 select_mask; 678c2ecf20Sopenharmony_ci u64 compare_mask; 688c2ecf20Sopenharmony_ci u64 zarch; 698c2ecf20Sopenharmony_ci }; 708c2ecf20Sopenharmony_ci struct prs_parm parm; 718c2ecf20Sopenharmony_ci int rc; 728c2ecf20Sopenharmony_ci u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4; 738c2ecf20Sopenharmony_ci u16 ry = (vcpu->arch.sie_block->ipa & 0x0f); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "diag page reference parameter block at 0x%llx", 768c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[rx]); 778c2ecf20Sopenharmony_ci vcpu->stat.diagnose_258++; 788c2ecf20Sopenharmony_ci if (vcpu->run->s.regs.gprs[rx] & 7) 798c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 808c2ecf20Sopenharmony_ci rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], rx, &parm, sizeof(parm)); 818c2ecf20Sopenharmony_ci if (rc) 828c2ecf20Sopenharmony_ci return kvm_s390_inject_prog_cond(vcpu, rc); 838c2ecf20Sopenharmony_ci if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258) 848c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci switch (parm.subcode) { 878c2ecf20Sopenharmony_ci case 0: /* TOKEN */ 888c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "pageref token addr 0x%llx " 898c2ecf20Sopenharmony_ci "select mask 0x%llx compare mask 0x%llx", 908c2ecf20Sopenharmony_ci parm.token_addr, parm.select_mask, parm.compare_mask); 918c2ecf20Sopenharmony_ci if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) { 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * If the pagefault handshake is already activated, 948c2ecf20Sopenharmony_ci * the token must not be changed. We have to return 958c2ecf20Sopenharmony_ci * decimal 8 instead, as mandated in SC24-6084. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[ry] = 8; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if ((parm.compare_mask & parm.select_mask) != parm.compare_mask || 1028c2ecf20Sopenharmony_ci parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL) 1038c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (kvm_is_error_gpa(vcpu->kvm, parm.token_addr)) 1068c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci vcpu->arch.pfault_token = parm.token_addr; 1098c2ecf20Sopenharmony_ci vcpu->arch.pfault_select = parm.select_mask; 1108c2ecf20Sopenharmony_ci vcpu->arch.pfault_compare = parm.compare_mask; 1118c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[ry] = 0; 1128c2ecf20Sopenharmony_ci rc = 0; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case 1: /* 1158c2ecf20Sopenharmony_ci * CANCEL 1168c2ecf20Sopenharmony_ci * Specification allows to let already pending tokens survive 1178c2ecf20Sopenharmony_ci * the cancel, therefore to reduce code complexity, we assume 1188c2ecf20Sopenharmony_ci * all outstanding tokens are already pending. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "pageref cancel addr 0x%llx", parm.token_addr); 1218c2ecf20Sopenharmony_ci if (parm.token_addr || parm.select_mask || 1228c2ecf20Sopenharmony_ci parm.compare_mask || parm.zarch) 1238c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[ry] = 0; 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * If the pfault handling was not established or is already 1288c2ecf20Sopenharmony_ci * canceled SC24-6084 requests to return decimal 4. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID) 1318c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[ry] = 4; 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rc = 0; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return rc; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int __diag_time_slice_end(struct kvm_vcpu *vcpu) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); 1488c2ecf20Sopenharmony_ci vcpu->stat.diagnose_44++; 1498c2ecf20Sopenharmony_ci kvm_vcpu_on_spin(vcpu, true); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct kvm_vcpu *tcpu; 1568c2ecf20Sopenharmony_ci int tid; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 1598c2ecf20Sopenharmony_ci vcpu->stat.diagnose_9c++; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* yield to self */ 1628c2ecf20Sopenharmony_ci if (tid == vcpu->vcpu_id) 1638c2ecf20Sopenharmony_ci goto no_yield; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* yield to invalid */ 1668c2ecf20Sopenharmony_ci tcpu = kvm_get_vcpu_by_id(vcpu->kvm, tid); 1678c2ecf20Sopenharmony_ci if (!tcpu) 1688c2ecf20Sopenharmony_ci goto no_yield; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* target already running */ 1718c2ecf20Sopenharmony_ci if (READ_ONCE(tcpu->cpu) >= 0) 1728c2ecf20Sopenharmony_ci goto no_yield; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (kvm_vcpu_yield_to(tcpu) <= 0) 1758c2ecf20Sopenharmony_ci goto no_yield; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: done", tid); 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_cino_yield: 1808c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: ignored", tid); 1818c2ecf20Sopenharmony_ci vcpu->stat.diagnose_9c_ignored++; 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int __diag_ipl_functions(struct kvm_vcpu *vcpu) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; 1888c2ecf20Sopenharmony_ci unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "diag ipl functions, subcode %lx", subcode); 1918c2ecf20Sopenharmony_ci vcpu->stat.diagnose_308++; 1928c2ecf20Sopenharmony_ci switch (subcode) { 1938c2ecf20Sopenharmony_ci case 3: 1948c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case 4: 1978c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags = 0; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * no need to check the return value of vcpu_stop as it can only have 2058c2ecf20Sopenharmony_ci * an error for protvirt, but protvirt means user cpu state 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) 2088c2ecf20Sopenharmony_ci kvm_s390_vcpu_stop(vcpu); 2098c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; 2108c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; 2118c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; 2128c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_S390_RESET; 2138c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx", 2148c2ecf20Sopenharmony_ci vcpu->run->s390_reset_flags); 2158c2ecf20Sopenharmony_ci trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags); 2168c2ecf20Sopenharmony_ci return -EREMOTE; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int ret; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci vcpu->stat.diagnose_500++; 2248c2ecf20Sopenharmony_ci /* No virtio-ccw notification? Get out quickly. */ 2258c2ecf20Sopenharmony_ci if (!vcpu->kvm->arch.css_support || 2268c2ecf20Sopenharmony_ci (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) 2278c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "diag 0x500 schid 0x%8.8x queue 0x%x cookie 0x%llx", 2308c2ecf20Sopenharmony_ci (u32) vcpu->run->s.regs.gprs[2], 2318c2ecf20Sopenharmony_ci (u32) vcpu->run->s.regs.gprs[3], 2328c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[4]); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * The layout is as follows: 2368c2ecf20Sopenharmony_ci * - gpr 2 contains the subchannel id (passed as addr) 2378c2ecf20Sopenharmony_ci * - gpr 3 contains the virtqueue index (passed as datamatch) 2388c2ecf20Sopenharmony_ci * - gpr 4 contains the index on the bus (optionally) 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci ret = kvm_io_bus_write_cookie(vcpu, KVM_VIRTIO_CCW_NOTIFY_BUS, 2418c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[2] & 0xffffffff, 2428c2ecf20Sopenharmony_ci 8, &vcpu->run->s.regs.gprs[3], 2438c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[4]); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Return cookie in gpr 2, but don't overwrite the register if the 2478c2ecf20Sopenharmony_ci * diagnose will be handled by userspace. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci if (ret != -EOPNOTSUPP) 2508c2ecf20Sopenharmony_ci vcpu->run->s.regs.gprs[2] = ret; 2518c2ecf20Sopenharmony_ci /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */ 2528c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ciint kvm_s390_handle_diag(struct kvm_vcpu *vcpu) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int code = kvm_s390_get_base_disp_rs(vcpu, NULL) & 0xffff; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 2608c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci trace_kvm_s390_handle_diag(vcpu, code); 2638c2ecf20Sopenharmony_ci switch (code) { 2648c2ecf20Sopenharmony_ci case 0x10: 2658c2ecf20Sopenharmony_ci return diag_release_pages(vcpu); 2668c2ecf20Sopenharmony_ci case 0x44: 2678c2ecf20Sopenharmony_ci return __diag_time_slice_end(vcpu); 2688c2ecf20Sopenharmony_ci case 0x9c: 2698c2ecf20Sopenharmony_ci return __diag_time_slice_end_directed(vcpu); 2708c2ecf20Sopenharmony_ci case 0x258: 2718c2ecf20Sopenharmony_ci return __diag_page_ref_service(vcpu); 2728c2ecf20Sopenharmony_ci case 0x308: 2738c2ecf20Sopenharmony_ci return __diag_ipl_functions(vcpu); 2748c2ecf20Sopenharmony_ci case 0x500: 2758c2ecf20Sopenharmony_ci return __diag_virtio_hypercall(vcpu); 2768c2ecf20Sopenharmony_ci default: 2778c2ecf20Sopenharmony_ci vcpu->stat.diagnose_other++; 2788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci} 281