diff options
Diffstat (limited to 'target/s390x/kvm.c')
-rw-r--r-- | target/s390x/kvm.c | 349 |
1 files changed, 6 insertions, 343 deletions
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 1950291f95..b2e36559c8 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -135,8 +135,6 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; -static QemuMutex qemu_sigp_mutex; - static int cap_sync_regs; static int cap_async_pf; static int cap_mem_op; @@ -322,8 +320,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) */ /* kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); */ - qemu_mutex_init(&qemu_sigp_mutex); - return 0; } @@ -1508,355 +1504,22 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) return r; } -typedef struct SigpInfo { - uint64_t param; - int cc; - uint64_t *status_reg; -} SigpInfo; - -static void set_sigp_status(SigpInfo *si, uint64_t status) -{ - *si->status_reg &= 0xffffffff00000000ULL; - *si->status_reg |= status; - si->cc = SIGP_CC_STATUS_STORED; -} - -static void sigp_start(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; - return; - } - - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_stop(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - - if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; - return; - } - - /* disabled wait - sleeping in user space */ - if (cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); - } else { - /* execute the stop function */ - cpu->env.sigp_order = SIGP_STOP; - cpu_inject_stop(cpu); - } - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - - /* disabled wait - sleeping in user space */ - if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); - } - - switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_OPERATING: - cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; - cpu_inject_stop(cpu); - /* store will be performed when handling the stop intercept */ - break; - case CPU_STATE_STOPPED: - /* already stopped, just store the status */ - cpu_synchronize_state(cs); - s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); - break; - } - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - uint32_t address = si->param & 0x7ffffe00u; - - /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { - set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); - return; - } - - cpu_synchronize_state(cs); - - if (s390_store_status(cpu, address, false)) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -#define ADTL_SAVE_LC_MASK 0xfUL -static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - uint8_t lc = si->param & ADTL_SAVE_LC_MASK; - hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK; - hwaddr len = 1UL << (lc ? lc : 10); - - if (!s390_has_feat(S390_FEAT_VECTOR) && - !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) { - set_sigp_status(si, SIGP_STAT_INVALID_ORDER); - return; - } - - /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { - set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); - return; - } - - /* address must be aligned to length */ - if (addr & (len - 1)) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - - /* no GS: only lc == 0 is valid */ - if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) && - lc != 0) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - - /* GS: 0, 10, 11, 12 are valid */ - if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && - lc != 0 && - lc != 10 && - lc != 11 && - lc != 12) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - - cpu_synchronize_state(cs); - - if (s390_store_adtl_status(cpu, addr, len)) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_restart(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - - switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_STOPPED: - /* the restart irq has to be delivered prior to any other pending irq */ - cpu_synchronize_state(cs); - do_restart_interrupt(&cpu->env); - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); - break; - case CPU_STATE_OPERATING: - cpu_inject_restart(cpu); - break; - } - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -int kvm_s390_cpu_restart(S390CPU *cpu) -{ - SigpInfo si = {}; - - run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); - return 0; -} - -static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - SigpInfo *si = arg.host_ptr; - - cpu_synchronize_state(cs); - scc->initial_cpu_reset(cs); - cpu_synchronize_post_reset(cs); - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - SigpInfo *si = arg.host_ptr; - - cpu_synchronize_state(cs); - scc->cpu_reset(cs); - cpu_synchronize_post_reset(cs); - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) -{ - S390CPU *cpu = S390_CPU(cs); - SigpInfo *si = arg.host_ptr; - uint32_t addr = si->param & 0x7fffe000u; - - cpu_synchronize_state(cs); - - if (!address_space_access_valid(&address_space_memory, addr, - sizeof(struct LowCore), false)) { - set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); - return; - } - - /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { - set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); - return; - } - - cpu->env.psa = addr; - cpu_synchronize_post_init(cs); - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; -} - -static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, - uint64_t param, uint64_t *status_reg) -{ - SigpInfo si = { - .param = param, - .status_reg = status_reg, - }; - - /* cpu available? */ - if (dst_cpu == NULL) { - return SIGP_CC_NOT_OPERATIONAL; - } - - /* only resets can break pending orders */ - if (dst_cpu->env.sigp_order != 0 && - order != SIGP_CPU_RESET && - order != SIGP_INITIAL_CPU_RESET) { - return SIGP_CC_BUSY; - } - - switch (order) { - case SIGP_START: - run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_STOP: - run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_RESTART: - run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_STOP_STORE_STATUS: - run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_STORE_STATUS_ADDR: - run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_STORE_ADTL_STATUS: - run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_SET_PREFIX: - run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_INITIAL_CPU_RESET: - run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); - break; - case SIGP_CPU_RESET: - run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); - break; - default: - set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); - } - - return si.cc; -} - -static int sigp_set_architecture(S390CPU *cpu, uint32_t param, - uint64_t *status_reg) -{ - CPUState *cur_cs; - S390CPU *cur_cpu; - bool all_stopped = true; - - CPU_FOREACH(cur_cs) { - cur_cpu = S390_CPU(cur_cs); - - if (cur_cpu == cpu) { - continue; - } - if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { - all_stopped = false; - } - } - - *status_reg &= 0xffffffff00000000ULL; - - /* Reject set arch order, with czam we're always in z/Arch mode. */ - *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER : - SIGP_STAT_INCORRECT_STATE); - return SIGP_CC_STATUS_STORED; -} - -static int handle_sigp(S390CPU *cpu, uint8_t ipa1, uint32_t ipb) +static int kvm_s390_handle_sigp(S390CPU *cpu, uint8_t ipa1, uint32_t ipb) { CPUS390XState *env = &cpu->env; const uint8_t r1 = ipa1 >> 4; const uint8_t r3 = ipa1 & 0x0f; int ret; uint8_t order; - uint64_t *status_reg; - uint64_t param; - S390CPU *dst_cpu = NULL; cpu_synchronize_state(CPU(cpu)); /* get order code */ - order = decode_basedisp_rs(env, ipb, NULL) - & SIGP_ORDER_MASK; - status_reg = &env->regs[r1]; - param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; + order = decode_basedisp_rs(env, ipb, NULL) & SIGP_ORDER_MASK; - if (qemu_mutex_trylock(&qemu_sigp_mutex)) { - ret = SIGP_CC_BUSY; - goto out; - } - - switch (order) { - case SIGP_SET_ARCH: - ret = sigp_set_architecture(cpu, param, status_reg); - break; - default: - /* all other sigp orders target a single vcpu */ - dst_cpu = s390_cpu_addr2state(env->regs[r3]); - ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg); - } - qemu_mutex_unlock(&qemu_sigp_mutex); - -out: - trace_kvm_sigp_finished(order, CPU(cpu)->cpu_index, - dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); - - if (ret >= 0) { - setcc(cpu, ret); - return 0; - } - - return ret; + ret = handle_sigp(env, order, r1, r3); + setcc(cpu, ret); + return 0; } static int handle_instruction(S390CPU *cpu, struct kvm_run *run) @@ -1884,7 +1547,7 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) r = handle_diag(cpu, run, run->s390_sieic.ipb); break; case IPA0_SIGP: - r = handle_sigp(cpu, ipa1, run->s390_sieic.ipb); + r = kvm_s390_handle_sigp(cpu, ipa1, run->s390_sieic.ipb); break; } |