18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * handling interprocessor communication 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2008, 2013 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Carsten Otte <cotte@de.ibm.com> 88c2ecf20Sopenharmony_ci * Christian Borntraeger <borntraeger@de.ibm.com> 98c2ecf20Sopenharmony_ci * Christian Ehrhardt <ehrhardt@de.ibm.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kvm.h> 138c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <asm/sigp.h> 168c2ecf20Sopenharmony_ci#include "gaccess.h" 178c2ecf20Sopenharmony_ci#include "kvm-s390.h" 188c2ecf20Sopenharmony_ci#include "trace.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int __sigp_sense(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu, 218c2ecf20Sopenharmony_ci u64 *reg) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci const bool stopped = kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_STOPPED); 248c2ecf20Sopenharmony_ci int rc; 258c2ecf20Sopenharmony_ci int ext_call_pending; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci ext_call_pending = kvm_s390_ext_call_pending(dst_vcpu); 288c2ecf20Sopenharmony_ci if (!stopped && !ext_call_pending) 298c2ecf20Sopenharmony_ci rc = SIGP_CC_ORDER_CODE_ACCEPTED; 308c2ecf20Sopenharmony_ci else { 318c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 328c2ecf20Sopenharmony_ci if (ext_call_pending) 338c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_EXT_CALL_PENDING; 348c2ecf20Sopenharmony_ci if (stopped) 358c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_STOPPED; 368c2ecf20Sopenharmony_ci rc = SIGP_CC_STATUS_STORED; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", dst_vcpu->vcpu_id, 408c2ecf20Sopenharmony_ci rc); 418c2ecf20Sopenharmony_ci return rc; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int __inject_sigp_emergency(struct kvm_vcpu *vcpu, 458c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct kvm_s390_irq irq = { 488c2ecf20Sopenharmony_ci .type = KVM_S390_INT_EMERGENCY, 498c2ecf20Sopenharmony_ci .u.emerg.code = vcpu->vcpu_id, 508c2ecf20Sopenharmony_ci }; 518c2ecf20Sopenharmony_ci int rc = 0; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci rc = kvm_s390_inject_vcpu(dst_vcpu, &irq); 548c2ecf20Sopenharmony_ci if (!rc) 558c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", 568c2ecf20Sopenharmony_ci dst_vcpu->vcpu_id); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return rc ? rc : SIGP_CC_ORDER_CODE_ACCEPTED; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int __sigp_emergency(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return __inject_sigp_emergency(vcpu, dst_vcpu); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, 678c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, 688c2ecf20Sopenharmony_ci u16 asn, u64 *reg) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci const u64 psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT; 718c2ecf20Sopenharmony_ci u16 p_asn, s_asn; 728c2ecf20Sopenharmony_ci psw_t *psw; 738c2ecf20Sopenharmony_ci bool idle; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci idle = is_vcpu_idle(vcpu); 768c2ecf20Sopenharmony_ci psw = &dst_vcpu->arch.sie_block->gpsw; 778c2ecf20Sopenharmony_ci p_asn = dst_vcpu->arch.sie_block->gcr[4] & 0xffff; /* Primary ASN */ 788c2ecf20Sopenharmony_ci s_asn = dst_vcpu->arch.sie_block->gcr[3] & 0xffff; /* Secondary ASN */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Inject the emergency signal? */ 818c2ecf20Sopenharmony_ci if (!is_vcpu_stopped(vcpu) 828c2ecf20Sopenharmony_ci || (psw->mask & psw_int_mask) != psw_int_mask 838c2ecf20Sopenharmony_ci || (idle && psw->addr != 0) 848c2ecf20Sopenharmony_ci || (!idle && (asn == p_asn || asn == s_asn))) { 858c2ecf20Sopenharmony_ci return __inject_sigp_emergency(vcpu, dst_vcpu); 868c2ecf20Sopenharmony_ci } else { 878c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 888c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INCORRECT_STATE; 898c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int __sigp_external_call(struct kvm_vcpu *vcpu, 948c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, u64 *reg) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct kvm_s390_irq irq = { 978c2ecf20Sopenharmony_ci .type = KVM_S390_INT_EXTERNAL_CALL, 988c2ecf20Sopenharmony_ci .u.extcall.code = vcpu->vcpu_id, 998c2ecf20Sopenharmony_ci }; 1008c2ecf20Sopenharmony_ci int rc; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci rc = kvm_s390_inject_vcpu(dst_vcpu, &irq); 1038c2ecf20Sopenharmony_ci if (rc == -EBUSY) { 1048c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 1058c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_EXT_CALL_PENDING; 1068c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 1078c2ecf20Sopenharmony_ci } else if (rc == 0) { 1088c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", 1098c2ecf20Sopenharmony_ci dst_vcpu->vcpu_id); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return rc ? rc : SIGP_CC_ORDER_CODE_ACCEPTED; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int __sigp_stop(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct kvm_s390_irq irq = { 1188c2ecf20Sopenharmony_ci .type = KVM_S390_SIGP_STOP, 1198c2ecf20Sopenharmony_ci }; 1208c2ecf20Sopenharmony_ci int rc; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci rc = kvm_s390_inject_vcpu(dst_vcpu, &irq); 1238c2ecf20Sopenharmony_ci if (rc == -EBUSY) 1248c2ecf20Sopenharmony_ci rc = SIGP_CC_BUSY; 1258c2ecf20Sopenharmony_ci else if (rc == 0) 1268c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", 1278c2ecf20Sopenharmony_ci dst_vcpu->vcpu_id); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return rc; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int __sigp_stop_and_store_status(struct kvm_vcpu *vcpu, 1338c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, u64 *reg) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct kvm_s390_irq irq = { 1368c2ecf20Sopenharmony_ci .type = KVM_S390_SIGP_STOP, 1378c2ecf20Sopenharmony_ci .u.stop.flags = KVM_S390_STOP_FLAG_STORE_STATUS, 1388c2ecf20Sopenharmony_ci }; 1398c2ecf20Sopenharmony_ci int rc; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci rc = kvm_s390_inject_vcpu(dst_vcpu, &irq); 1428c2ecf20Sopenharmony_ci if (rc == -EBUSY) 1438c2ecf20Sopenharmony_ci rc = SIGP_CC_BUSY; 1448c2ecf20Sopenharmony_ci else if (rc == 0) 1458c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sent sigp stop and store status to cpu %x", 1468c2ecf20Sopenharmony_ci dst_vcpu->vcpu_id); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return rc; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter, 1528c2ecf20Sopenharmony_ci u64 *status_reg) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned int i; 1558c2ecf20Sopenharmony_ci struct kvm_vcpu *v; 1568c2ecf20Sopenharmony_ci bool all_stopped = true; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci kvm_for_each_vcpu(i, v, vcpu->kvm) { 1598c2ecf20Sopenharmony_ci if (v == vcpu) 1608c2ecf20Sopenharmony_ci continue; 1618c2ecf20Sopenharmony_ci if (!is_vcpu_stopped(v)) 1628c2ecf20Sopenharmony_ci all_stopped = false; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci *status_reg &= 0xffffffff00000000UL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Reject set arch order, with czam we're always in z/Arch mode. */ 1688c2ecf20Sopenharmony_ci *status_reg |= (all_stopped ? SIGP_STATUS_INVALID_PARAMETER : 1698c2ecf20Sopenharmony_ci SIGP_STATUS_INCORRECT_STATE); 1708c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int __sigp_set_prefix(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu, 1748c2ecf20Sopenharmony_ci u32 address, u64 *reg) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct kvm_s390_irq irq = { 1778c2ecf20Sopenharmony_ci .type = KVM_S390_SIGP_SET_PREFIX, 1788c2ecf20Sopenharmony_ci .u.prefix.address = address & 0x7fffe000u, 1798c2ecf20Sopenharmony_ci }; 1808c2ecf20Sopenharmony_ci int rc; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Make sure the new value is valid memory. We only need to check the 1848c2ecf20Sopenharmony_ci * first page, since address is 8k aligned and memory pieces are always 1858c2ecf20Sopenharmony_ci * at least 1MB aligned and have at least a size of 1MB. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (kvm_is_error_gpa(vcpu->kvm, irq.u.prefix.address)) { 1888c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 1898c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INVALID_PARAMETER; 1908c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci rc = kvm_s390_inject_vcpu(dst_vcpu, &irq); 1948c2ecf20Sopenharmony_ci if (rc == -EBUSY) { 1958c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 1968c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INCORRECT_STATE; 1978c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return rc; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu, 2048c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, 2058c2ecf20Sopenharmony_ci u32 addr, u64 *reg) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int rc; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_STOPPED)) { 2108c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 2118c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INCORRECT_STATE; 2128c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci addr &= 0x7ffffe00; 2168c2ecf20Sopenharmony_ci rc = kvm_s390_store_status_unloaded(dst_vcpu, addr); 2178c2ecf20Sopenharmony_ci if (rc == -EFAULT) { 2188c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 2198c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INVALID_PARAMETER; 2208c2ecf20Sopenharmony_ci rc = SIGP_CC_STATUS_STORED; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci return rc; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int __sigp_sense_running(struct kvm_vcpu *vcpu, 2268c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, u64 *reg) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int rc; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!test_kvm_facility(vcpu->kvm, 9)) { 2318c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 2328c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_INVALID_ORDER; 2338c2ecf20Sopenharmony_ci return SIGP_CC_STATUS_STORED; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_RUNNING)) { 2378c2ecf20Sopenharmony_ci /* running */ 2388c2ecf20Sopenharmony_ci rc = SIGP_CC_ORDER_CODE_ACCEPTED; 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci /* not running */ 2418c2ecf20Sopenharmony_ci *reg &= 0xffffffff00000000UL; 2428c2ecf20Sopenharmony_ci *reg |= SIGP_STATUS_NOT_RUNNING; 2438c2ecf20Sopenharmony_ci rc = SIGP_CC_STATUS_STORED; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", 2478c2ecf20Sopenharmony_ci dst_vcpu->vcpu_id, rc); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return rc; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int __prepare_sigp_re_start(struct kvm_vcpu *vcpu, 2538c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, u8 order_code) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct kvm_s390_local_interrupt *li = &dst_vcpu->arch.local_int; 2568c2ecf20Sopenharmony_ci /* handle (RE)START in user space */ 2578c2ecf20Sopenharmony_ci int rc = -EOPNOTSUPP; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* make sure we don't race with STOP irq injection */ 2608c2ecf20Sopenharmony_ci spin_lock(&li->lock); 2618c2ecf20Sopenharmony_ci if (kvm_s390_is_stop_irq_pending(dst_vcpu)) 2628c2ecf20Sopenharmony_ci rc = SIGP_CC_BUSY; 2638c2ecf20Sopenharmony_ci spin_unlock(&li->lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return rc; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __prepare_sigp_cpu_reset(struct kvm_vcpu *vcpu, 2698c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu, u8 order_code) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci /* handle (INITIAL) CPU RESET in user space */ 2728c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int __prepare_sigp_unknown(struct kvm_vcpu *vcpu, 2768c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci /* handle unknown orders in user space */ 2798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int handle_sigp_dst(struct kvm_vcpu *vcpu, u8 order_code, 2838c2ecf20Sopenharmony_ci u16 cpu_addr, u32 parameter, u64 *status_reg) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int rc; 2868c2ecf20Sopenharmony_ci struct kvm_vcpu *dst_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, cpu_addr); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!dst_vcpu) 2898c2ecf20Sopenharmony_ci return SIGP_CC_NOT_OPERATIONAL; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * SIGP RESTART, SIGP STOP, and SIGP STOP AND STORE STATUS orders 2938c2ecf20Sopenharmony_ci * are processed asynchronously. Until the affected VCPU finishes 2948c2ecf20Sopenharmony_ci * its work and calls back into KVM to clear the (RESTART or STOP) 2958c2ecf20Sopenharmony_ci * interrupt, we need to return any new non-reset orders "busy". 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * This is important because a single VCPU could issue: 2988c2ecf20Sopenharmony_ci * 1) SIGP STOP $DESTINATION 2998c2ecf20Sopenharmony_ci * 2) SIGP SENSE $DESTINATION 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * If the SIGP SENSE would not be rejected as "busy", it could 3028c2ecf20Sopenharmony_ci * return an incorrect answer as to whether the VCPU is STOPPED 3038c2ecf20Sopenharmony_ci * or OPERATING. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (order_code != SIGP_INITIAL_CPU_RESET && 3068c2ecf20Sopenharmony_ci order_code != SIGP_CPU_RESET) { 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * Lockless check. Both SIGP STOP and SIGP (RE)START 3098c2ecf20Sopenharmony_ci * properly synchronize everything while processing 3108c2ecf20Sopenharmony_ci * their orders, while the guest cannot observe a 3118c2ecf20Sopenharmony_ci * difference when issuing other orders from two 3128c2ecf20Sopenharmony_ci * different VCPUs. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (kvm_s390_is_stop_irq_pending(dst_vcpu) || 3158c2ecf20Sopenharmony_ci kvm_s390_is_restart_irq_pending(dst_vcpu)) 3168c2ecf20Sopenharmony_ci return SIGP_CC_BUSY; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci switch (order_code) { 3208c2ecf20Sopenharmony_ci case SIGP_SENSE: 3218c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_sense++; 3228c2ecf20Sopenharmony_ci rc = __sigp_sense(vcpu, dst_vcpu, status_reg); 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case SIGP_EXTERNAL_CALL: 3258c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_external_call++; 3268c2ecf20Sopenharmony_ci rc = __sigp_external_call(vcpu, dst_vcpu, status_reg); 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case SIGP_EMERGENCY_SIGNAL: 3298c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_emergency++; 3308c2ecf20Sopenharmony_ci rc = __sigp_emergency(vcpu, dst_vcpu); 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case SIGP_STOP: 3338c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_stop++; 3348c2ecf20Sopenharmony_ci rc = __sigp_stop(vcpu, dst_vcpu); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case SIGP_STOP_AND_STORE_STATUS: 3378c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_stop_store_status++; 3388c2ecf20Sopenharmony_ci rc = __sigp_stop_and_store_status(vcpu, dst_vcpu, status_reg); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case SIGP_STORE_STATUS_AT_ADDRESS: 3418c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_store_status++; 3428c2ecf20Sopenharmony_ci rc = __sigp_store_status_at_addr(vcpu, dst_vcpu, parameter, 3438c2ecf20Sopenharmony_ci status_reg); 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci case SIGP_SET_PREFIX: 3468c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_prefix++; 3478c2ecf20Sopenharmony_ci rc = __sigp_set_prefix(vcpu, dst_vcpu, parameter, status_reg); 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case SIGP_COND_EMERGENCY_SIGNAL: 3508c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_cond_emergency++; 3518c2ecf20Sopenharmony_ci rc = __sigp_conditional_emergency(vcpu, dst_vcpu, parameter, 3528c2ecf20Sopenharmony_ci status_reg); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case SIGP_SENSE_RUNNING: 3558c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_sense_running++; 3568c2ecf20Sopenharmony_ci rc = __sigp_sense_running(vcpu, dst_vcpu, status_reg); 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci case SIGP_START: 3598c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_start++; 3608c2ecf20Sopenharmony_ci rc = __prepare_sigp_re_start(vcpu, dst_vcpu, order_code); 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case SIGP_RESTART: 3638c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_restart++; 3648c2ecf20Sopenharmony_ci rc = __prepare_sigp_re_start(vcpu, dst_vcpu, order_code); 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case SIGP_INITIAL_CPU_RESET: 3678c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_init_cpu_reset++; 3688c2ecf20Sopenharmony_ci rc = __prepare_sigp_cpu_reset(vcpu, dst_vcpu, order_code); 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case SIGP_CPU_RESET: 3718c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_cpu_reset++; 3728c2ecf20Sopenharmony_ci rc = __prepare_sigp_cpu_reset(vcpu, dst_vcpu, order_code); 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_unknown++; 3768c2ecf20Sopenharmony_ci rc = __prepare_sigp_unknown(vcpu, dst_vcpu); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) 3808c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 4, 3818c2ecf20Sopenharmony_ci "sigp order %u -> cpu %x: handled in user space", 3828c2ecf20Sopenharmony_ci order_code, dst_vcpu->vcpu_id); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return rc; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int handle_sigp_order_in_user_space(struct kvm_vcpu *vcpu, u8 order_code, 3888c2ecf20Sopenharmony_ci u16 cpu_addr) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci if (!vcpu->kvm->arch.user_sigp) 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci switch (order_code) { 3948c2ecf20Sopenharmony_ci case SIGP_SENSE: 3958c2ecf20Sopenharmony_ci case SIGP_EXTERNAL_CALL: 3968c2ecf20Sopenharmony_ci case SIGP_EMERGENCY_SIGNAL: 3978c2ecf20Sopenharmony_ci case SIGP_COND_EMERGENCY_SIGNAL: 3988c2ecf20Sopenharmony_ci case SIGP_SENSE_RUNNING: 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci /* update counters as we're directly dropping to user space */ 4018c2ecf20Sopenharmony_ci case SIGP_STOP: 4028c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_stop++; 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci case SIGP_STOP_AND_STORE_STATUS: 4058c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_stop_store_status++; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case SIGP_STORE_STATUS_AT_ADDRESS: 4088c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_store_status++; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci case SIGP_STORE_ADDITIONAL_STATUS: 4118c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_store_adtl_status++; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case SIGP_SET_PREFIX: 4148c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_prefix++; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case SIGP_START: 4178c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_start++; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case SIGP_RESTART: 4208c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_restart++; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case SIGP_INITIAL_CPU_RESET: 4238c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_init_cpu_reset++; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case SIGP_CPU_RESET: 4268c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_cpu_reset++; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_unknown++; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci VCPU_EVENT(vcpu, 3, "SIGP: order %u for CPU %d handled in userspace", 4328c2ecf20Sopenharmony_ci order_code, cpu_addr); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 1; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; 4408c2ecf20Sopenharmony_ci int r3 = vcpu->arch.sie_block->ipa & 0x000f; 4418c2ecf20Sopenharmony_ci u32 parameter; 4428c2ecf20Sopenharmony_ci u16 cpu_addr = vcpu->run->s.regs.gprs[r3]; 4438c2ecf20Sopenharmony_ci u8 order_code; 4448c2ecf20Sopenharmony_ci int rc; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* sigp in userspace can exit */ 4478c2ecf20Sopenharmony_ci if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 4488c2ecf20Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci order_code = kvm_s390_get_base_disp_rs(vcpu, NULL); 4518c2ecf20Sopenharmony_ci if (handle_sigp_order_in_user_space(vcpu, order_code, cpu_addr)) 4528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (r1 % 2) 4558c2ecf20Sopenharmony_ci parameter = vcpu->run->s.regs.gprs[r1]; 4568c2ecf20Sopenharmony_ci else 4578c2ecf20Sopenharmony_ci parameter = vcpu->run->s.regs.gprs[r1 + 1]; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci trace_kvm_s390_handle_sigp(vcpu, order_code, cpu_addr, parameter); 4608c2ecf20Sopenharmony_ci switch (order_code) { 4618c2ecf20Sopenharmony_ci case SIGP_SET_ARCHITECTURE: 4628c2ecf20Sopenharmony_ci vcpu->stat.instruction_sigp_arch++; 4638c2ecf20Sopenharmony_ci rc = __sigp_set_arch(vcpu, parameter, 4648c2ecf20Sopenharmony_ci &vcpu->run->s.regs.gprs[r1]); 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci default: 4678c2ecf20Sopenharmony_ci rc = handle_sigp_dst(vcpu, order_code, cpu_addr, 4688c2ecf20Sopenharmony_ci parameter, 4698c2ecf20Sopenharmony_ci &vcpu->run->s.regs.gprs[r1]); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (rc < 0) 4738c2ecf20Sopenharmony_ci return rc; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci kvm_s390_set_psw_cc(vcpu, rc); 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * Handle SIGP partial execution interception. 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * This interception will occur at the source cpu when a source cpu sends an 4838c2ecf20Sopenharmony_ci * external call to a target cpu and the target cpu has the WAIT bit set in 4848c2ecf20Sopenharmony_ci * its cpuflags. Interception will occurr after the interrupt indicator bits at 4858c2ecf20Sopenharmony_ci * the target cpu have been set. All error cases will lead to instruction 4868c2ecf20Sopenharmony_ci * interception, therefore nothing is to be checked or prepared. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ciint kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int r3 = vcpu->arch.sie_block->ipa & 0x000f; 4918c2ecf20Sopenharmony_ci u16 cpu_addr = vcpu->run->s.regs.gprs[r3]; 4928c2ecf20Sopenharmony_ci struct kvm_vcpu *dest_vcpu; 4938c2ecf20Sopenharmony_ci u8 order_code = kvm_s390_get_base_disp_rs(vcpu, NULL); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (order_code == SIGP_EXTERNAL_CALL) { 4968c2ecf20Sopenharmony_ci trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci dest_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, cpu_addr); 4998c2ecf20Sopenharmony_ci BUG_ON(dest_vcpu == NULL); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci kvm_s390_vcpu_wakeup(dest_vcpu); 5028c2ecf20Sopenharmony_ci kvm_s390_set_psw_cc(vcpu, SIGP_CC_ORDER_CODE_ACCEPTED); 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5078c2ecf20Sopenharmony_ci} 508