diff options
Diffstat (limited to 'target-s390x/kvm.c')
-rw-r--r-- | target-s390x/kvm.c | 517 |
1 files changed, 443 insertions, 74 deletions
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 80ac6215fb..7f745726bd 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -109,6 +109,7 @@ #define ICPT_WAITPSW 0x1c #define ICPT_SOFT_INTERCEPT 0x24 #define ICPT_CPU_STOP 0x28 +#define ICPT_OPEREXC 0x2c #define ICPT_IO 0x40 #define NR_LOCAL_IRQS 32 @@ -131,6 +132,8 @@ 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; @@ -174,6 +177,18 @@ int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit) return kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); } +static bool kvm_s390_cmma_available(void) +{ + static bool initialized, value; + + if (!initialized) { + initialized = true; + value = kvm_vm_check_mem_attr(kvm_state, KVM_S390_VM_MEM_ENABLE_CMMA) && + kvm_vm_check_mem_attr(kvm_state, KVM_S390_VM_MEM_CLR_CMMA); + } + return value; +} + void kvm_s390_cmma_reset(void) { int rc; @@ -182,11 +197,15 @@ void kvm_s390_cmma_reset(void) .attr = KVM_S390_VM_MEM_CLR_CMMA, }; + if (!mem_path || !kvm_s390_cmma_available()) { + return; + } + rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); trace_kvm_clear_cmma(rc); } -static void kvm_s390_enable_cmma(KVMState *s) +static void kvm_s390_enable_cmma(void) { int rc; struct kvm_device_attr attr = { @@ -194,12 +213,7 @@ static void kvm_s390_enable_cmma(KVMState *s) .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (!kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_ENABLE_CMMA) || - !kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_CLR_CMMA)) { - return; - } - - rc = kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); + rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); trace_kvm_enable_cmma(rc); } @@ -248,8 +262,10 @@ static void kvm_s390_init_dea_kw(void) void kvm_s390_crypto_reset(void) { - kvm_s390_init_aes_kw(); - kvm_s390_init_dea_kw(); + if (s390_has_feat(S390_FEAT_MSA_EXT_3)) { + kvm_s390_init_aes_kw(); + kvm_s390_init_dea_kw(); + } } int kvm_arch_init(MachineState *ms, KVMState *s) @@ -259,10 +275,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP); cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ); - if (!mem_path) { - kvm_s390_enable_cmma(s); - } - if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { phys_mem_set_alloc(legacy_s390_alloc); @@ -277,6 +289,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + qemu_mutex_init(&qemu_sigp_mutex); + return 0; } @@ -665,16 +679,37 @@ static void *legacy_s390_alloc(size_t size, uint64_t *align) return mem == MAP_FAILED ? NULL : mem; } -/* DIAG 501 is used for sw breakpoints */ -static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; +static uint8_t const *sw_bp_inst; +static uint8_t sw_bp_ilen; + +static void determine_sw_breakpoint_instr(void) +{ + /* DIAG 501 is used for sw breakpoints with old kernels */ + static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; + /* Instruction 0x0000 is used for sw breakpoints with recent kernels */ + static const uint8_t instr_0x0000[] = {0x00, 0x00}; + + if (sw_bp_inst) { + return; + } + if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_USER_INSTR0, 0)) { + sw_bp_inst = diag_501; + sw_bp_ilen = sizeof(diag_501); + DPRINTF("KVM: will use 4-byte sw breakpoints.\n"); + } else { + sw_bp_inst = instr_0x0000; + sw_bp_ilen = sizeof(instr_0x0000); + DPRINTF("KVM: will use 2-byte sw breakpoints.\n"); + } +} int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { + determine_sw_breakpoint_instr(); if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, - sizeof(diag_501), 0) || - cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, - sizeof(diag_501), 1)) { + sw_bp_ilen, 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)sw_bp_inst, sw_bp_ilen, 1)) { return -EINVAL; } return 0; @@ -682,14 +717,14 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { - uint8_t t[sizeof(diag_501)]; + uint8_t t[MAX_ILEN]; - if (cpu_memory_rw_debug(cs, bp->pc, t, sizeof(diag_501), 0)) { + if (cpu_memory_rw_debug(cs, bp->pc, t, sw_bp_ilen, 0)) { return -EINVAL; - } else if (memcmp(t, diag_501, sizeof(diag_501))) { + } else if (memcmp(t, sw_bp_inst, sw_bp_ilen)) { return -EINVAL; } else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, - sizeof(diag_501), 1)) { + sw_bp_ilen, 1)) { return -EINVAL; } @@ -1310,7 +1345,7 @@ static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run) cpu_synchronize_state(CPU(cpu)); - pc = env->psw.addr - 4; + pc = env->psw.addr - sw_bp_ilen; if (kvm_find_sw_breakpoint(CPU(cpu), pc)) { env->psw.addr = pc; return EXCP_DEBUG; @@ -1354,7 +1389,6 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) } typedef struct SigpInfo { - S390CPU *cpu; uint64_t param; int cc; uint64_t *status_reg; @@ -1367,38 +1401,40 @@ static void set_sigp_status(SigpInfo *si, uint64_t status) si->cc = SIGP_CC_STATUS_STORED; } -static void sigp_start(void *arg) +static void sigp_start(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; - if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } - s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + s390_cpu_set_state(CPU_STATE_OPERATING, cpu); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_stop(void *arg) +static void sigp_stop(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; struct kvm_s390_irq irq = { .type = KVM_S390_SIGP_STOP, }; - if (s390_cpu_get_state(si->cpu) != CPU_STATE_OPERATING) { + if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } /* disabled wait - sleeping in user space */ - if (CPU(si->cpu)->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + if (cs->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, cpu); } else { /* execute the stop function */ - si->cpu->env.sigp_order = SIGP_STOP; - kvm_s390_vcpu_interrupt(si->cpu, &irq); + cpu->env.sigp_order = SIGP_STOP; + kvm_s390_vcpu_interrupt(cpu, &irq); } si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -1465,65 +1501,67 @@ static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) return 0; } -static void sigp_stop_and_store_status(void *arg) +static void sigp_stop_and_store_status(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; struct kvm_s390_irq irq = { .type = KVM_S390_SIGP_STOP, }; /* disabled wait - sleeping in user space */ - if (s390_cpu_get_state(si->cpu) == CPU_STATE_OPERATING && - CPU(si->cpu)->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, cpu); } - switch (s390_cpu_get_state(si->cpu)) { + switch (s390_cpu_get_state(cpu)) { case CPU_STATE_OPERATING: - si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; - kvm_s390_vcpu_interrupt(si->cpu, &irq); + cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; + kvm_s390_vcpu_interrupt(cpu, &irq); /* store will be performed when handling the stop intercept */ break; case CPU_STATE_STOPPED: /* already stopped, just store the status */ - cpu_synchronize_state(CPU(si->cpu)); - kvm_s390_store_status(si->cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true); + cpu_synchronize_state(cs); + kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true); break; } si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_store_status_at_address(void *arg) +static void sigp_store_status_at_address(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; uint32_t address = si->param & 0x7ffffe00u; /* cpu has to be stopped */ - if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } - cpu_synchronize_state(CPU(si->cpu)); + cpu_synchronize_state(cs); - if (kvm_s390_store_status(si->cpu, address, false)) { + if (kvm_s390_store_status(cpu, address, false)) { set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); return; } si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_store_adtl_status(void *arg) +static void sigp_store_adtl_status(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; - if (!kvm_check_extension(kvm_state, KVM_CAP_S390_VECTOR_REGISTERS)) { + if (!s390_has_feat(S390_FEAT_VECTOR)) { set_sigp_status(si, SIGP_STAT_INVALID_ORDER); return; } /* cpu has to be stopped */ - if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -1534,31 +1572,32 @@ static void sigp_store_adtl_status(void *arg) return; } - cpu_synchronize_state(CPU(si->cpu)); + cpu_synchronize_state(cs); - if (kvm_s390_store_adtl_status(si->cpu, si->param)) { + if (kvm_s390_store_adtl_status(cpu, si->param)) { set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); return; } si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_restart(void *arg) +static void sigp_restart(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; struct kvm_s390_irq irq = { .type = KVM_S390_RESTART, }; - switch (s390_cpu_get_state(si->cpu)) { + 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(CPU(si->cpu)); - do_restart_interrupt(&si->cpu->env); - s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + cpu_synchronize_state(cs); + do_restart_interrupt(&cpu->env); + s390_cpu_set_state(CPU_STATE_OPERATING, cpu); break; case CPU_STATE_OPERATING: - kvm_s390_vcpu_interrupt(si->cpu, &irq); + kvm_s390_vcpu_interrupt(cpu, &irq); break; } si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; @@ -1566,20 +1605,18 @@ static void sigp_restart(void *arg) int kvm_s390_cpu_restart(S390CPU *cpu) { - SigpInfo si = { - .cpu = cpu, - }; + SigpInfo si = {}; run_on_cpu(CPU(cpu), sigp_restart, &si); DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env); return 0; } -static void sigp_initial_cpu_reset(void *arg) +static void sigp_initial_cpu_reset(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); SigpInfo *si = arg; - CPUState *cs = CPU(si->cpu); - S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); cpu_synchronize_state(cs); scc->initial_cpu_reset(cs); @@ -1587,11 +1624,11 @@ static void sigp_initial_cpu_reset(void *arg) si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_cpu_reset(void *arg) +static void sigp_cpu_reset(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); SigpInfo *si = arg; - CPUState *cs = CPU(si->cpu); - S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); cpu_synchronize_state(cs); scc->cpu_reset(cs); @@ -1599,12 +1636,13 @@ static void sigp_cpu_reset(void *arg) si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_set_prefix(void *arg) +static void sigp_set_prefix(CPUState *cs, void *arg) { + S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg; uint32_t addr = si->param & 0x7fffe000u; - cpu_synchronize_state(CPU(si->cpu)); + cpu_synchronize_state(cs); if (!address_space_access_valid(&address_space_memory, addr, sizeof(struct LowCore), false)) { @@ -1613,13 +1651,13 @@ static void sigp_set_prefix(void *arg) } /* cpu has to be stopped */ - if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } - si->cpu->env.psa = addr; - cpu_synchronize_post_init(CPU(si->cpu)); + cpu->env.psa = addr; + cpu_synchronize_post_init(cs); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -1627,7 +1665,6 @@ static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, uint64_t param, uint64_t *status_reg) { SigpInfo si = { - .cpu = dst_cpu, .param = param, .status_reg = status_reg, }; @@ -1743,6 +1780,11 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) status_reg = &env->regs[r1]; param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; + 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); @@ -1752,7 +1794,9 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) 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); @@ -1864,6 +1908,14 @@ static int handle_intercept(S390CPU *cpu) cpu->env.sigp_order = 0; r = EXCP_HALTED; break; + case ICPT_OPEREXC: + /* currently only instr 0x0000 after enabled via capability */ + r = handle_sw_breakpoint(cpu, run); + if (r == -ENOENT) { + enter_pgmcheck(cpu, PGM_OPERATION); + r = 0; + } + break; case ICPT_SOFT_INTERCEPT: fprintf(stderr, "KVM unimplemented icpt SOFT\n"); exit(1); @@ -1949,7 +2001,7 @@ static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar) strcpy((char *)sysib.ext_names[0], "KVMguest"); } /* Insert UUID */ - memcpy(sysib.vm[0].uuid, qemu_uuid, sizeof(sysib.vm[0].uuid)); + memcpy(sysib.vm[0].uuid, &qemu_uuid, sizeof(sysib.vm[0].uuid)); s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, sizeof(sysib)); } @@ -2089,7 +2141,7 @@ static uint64_t build_channel_report_mcic(void) MCIC_VB_WP | MCIC_VB_MS | MCIC_VB_PM | MCIC_VB_IA | MCIC_VB_FP | MCIC_VB_GR | MCIC_VB_CR | MCIC_VB_ST | MCIC_VB_AR | MCIC_VB_PR | MCIC_VB_FC | MCIC_VB_CT | MCIC_VB_CC; - if (kvm_check_extension(kvm_state, KVM_CAP_S390_VECTOR_REGISTERS)) { + if (s390_has_feat(S390_FEAT_VECTOR)) { mcic |= MCIC_VB_VR; } return mcic; @@ -2282,3 +2334,320 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) { abort(); } + +static inline int test_bit_inv(long nr, const unsigned long *addr) +{ + return test_bit(BE_BIT_NR(nr), addr); +} + +static inline void set_bit_inv(long nr, unsigned long *addr) +{ + set_bit(BE_BIT_NR(nr), addr); +} + +static int query_cpu_subfunc(S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_subfunc prop; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_MACHINE_SUBFUNC, + .addr = (uint64_t) &prop, + }; + int rc; + + rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); + if (rc) { + return rc; + } + + /* + * We're going to add all subfunctions now, if the corresponding feature + * is available that unlocks the query functions. + */ + s390_add_from_feat_block(features, S390_FEAT_TYPE_PLO, prop.plo); + if (test_bit(S390_FEAT_TOD_CLOCK_STEERING, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_PTFF, prop.ptff); + } + if (test_bit(S390_FEAT_MSA, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_KMAC, prop.kmac); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KMC, prop.kmc); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KM, prop.km); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KIMD, prop.kimd); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KLMD, prop.klmd); + } + if (test_bit(S390_FEAT_MSA_EXT_3, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_PCKMO, prop.pckmo); + } + if (test_bit(S390_FEAT_MSA_EXT_4, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_KMCTR, prop.kmctr); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KMF, prop.kmf); + s390_add_from_feat_block(features, S390_FEAT_TYPE_KMO, prop.kmo); + s390_add_from_feat_block(features, S390_FEAT_TYPE_PCC, prop.pcc); + } + if (test_bit(S390_FEAT_MSA_EXT_5, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_PPNO, prop.ppno); + } + return 0; +} + +static int configure_cpu_subfunc(const S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_subfunc prop = {}; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_PROCESSOR_SUBFUNC, + .addr = (uint64_t) &prop, + }; + + if (!kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_PROCESSOR_SUBFUNC)) { + /* hardware support might be missing, IBC will handle most of this */ + return 0; + } + + s390_fill_feat_block(features, S390_FEAT_TYPE_PLO, prop.plo); + if (test_bit(S390_FEAT_TOD_CLOCK_STEERING, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_PTFF, prop.ptff); + prop.ptff[0] |= 0x80; /* query is always available */ + } + if (test_bit(S390_FEAT_MSA, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_KMAC, prop.kmac); + prop.kmac[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KMC, prop.kmc); + prop.kmc[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KM, prop.km); + prop.km[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KIMD, prop.kimd); + prop.kimd[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KLMD, prop.klmd); + prop.klmd[0] |= 0x80; /* query is always available */ + } + if (test_bit(S390_FEAT_MSA_EXT_3, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_PCKMO, prop.pckmo); + prop.pckmo[0] |= 0x80; /* query is always available */ + } + if (test_bit(S390_FEAT_MSA_EXT_4, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_KMCTR, prop.kmctr); + prop.kmctr[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KMF, prop.kmf); + prop.kmf[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_KMO, prop.kmo); + prop.kmo[0] |= 0x80; /* query is always available */ + s390_fill_feat_block(features, S390_FEAT_TYPE_PCC, prop.pcc); + prop.pcc[0] |= 0x80; /* query is always available */ + } + if (test_bit(S390_FEAT_MSA_EXT_5, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_PPNO, prop.ppno); + prop.ppno[0] |= 0x80; /* query is always available */ + } + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); +} + +static int kvm_to_feat[][2] = { + { KVM_S390_VM_CPU_FEAT_ESOP, S390_FEAT_ESOP }, + { KVM_S390_VM_CPU_FEAT_SIEF2, S390_FEAT_SIE_F2 }, + { KVM_S390_VM_CPU_FEAT_64BSCAO , S390_FEAT_SIE_64BSCAO }, + { KVM_S390_VM_CPU_FEAT_SIIF, S390_FEAT_SIE_SIIF }, + { KVM_S390_VM_CPU_FEAT_GPERE, S390_FEAT_SIE_GPERE }, + { KVM_S390_VM_CPU_FEAT_GSLS, S390_FEAT_SIE_GSLS }, + { KVM_S390_VM_CPU_FEAT_IB, S390_FEAT_SIE_IB }, + { KVM_S390_VM_CPU_FEAT_CEI, S390_FEAT_SIE_CEI }, + { KVM_S390_VM_CPU_FEAT_IBS, S390_FEAT_SIE_IBS }, + { KVM_S390_VM_CPU_FEAT_SKEY, S390_FEAT_SIE_SKEY }, + { KVM_S390_VM_CPU_FEAT_CMMA, S390_FEAT_SIE_CMMA }, + { KVM_S390_VM_CPU_FEAT_PFMFI, S390_FEAT_SIE_PFMFI}, + { KVM_S390_VM_CPU_FEAT_SIGPIF, S390_FEAT_SIE_SIGPIF}, +}; + +static int query_cpu_feat(S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_feat prop; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_MACHINE_FEAT, + .addr = (uint64_t) &prop, + }; + int rc; + int i; + + rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); + if (rc) { + return rc; + } + + for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) { + if (test_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat)) { + set_bit(kvm_to_feat[i][1], features); + } + } + return 0; +} + +static int configure_cpu_feat(const S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_feat prop = {}; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_PROCESSOR_FEAT, + .addr = (uint64_t) &prop, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) { + if (test_bit(kvm_to_feat[i][1], features)) { + set_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat); + } + } + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); +} + +bool kvm_s390_cpu_models_supported(void) +{ + if (!cpu_model_allowed()) { + /* compatibility machines interfere with the cpu model */ + return false; + } + return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_MACHINE) && + kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_PROCESSOR) && + kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_MACHINE_FEAT) && + kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_PROCESSOR_FEAT) && + kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_MACHINE_SUBFUNC); +} + +void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) +{ + struct kvm_s390_vm_cpu_machine prop = {}; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_MACHINE, + .addr = (uint64_t) &prop, + }; + uint16_t unblocked_ibc = 0, cpu_type = 0; + int rc; + + memset(model, 0, sizeof(*model)); + + if (!kvm_s390_cpu_models_supported()) { + error_setg(errp, "KVM doesn't support CPU models"); + return; + } + + /* query the basic cpu model properties */ + rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); + if (rc) { + error_setg(errp, "KVM: Error querying host CPU model: %d", rc); + return; + } + + cpu_type = cpuid_type(prop.cpuid); + if (has_ibc(prop.ibc)) { + model->lowest_ibc = lowest_ibc(prop.ibc); + unblocked_ibc = unblocked_ibc(prop.ibc); + } + model->cpu_id = cpuid_id(prop.cpuid); + model->cpu_ver = 0xff; + + /* get supported cpu features indicated via STFL(E) */ + s390_add_from_feat_block(model->features, S390_FEAT_TYPE_STFL, + (uint8_t *) prop.fac_mask); + /* dat-enhancement facility 2 has no bit but was introduced with stfle */ + if (test_bit(S390_FEAT_STFLE, model->features)) { + set_bit(S390_FEAT_DAT_ENH_2, model->features); + } + /* get supported cpu features indicated e.g. via SCLP */ + rc = query_cpu_feat(model->features); + if (rc) { + error_setg(errp, "KVM: Error querying CPU features: %d", rc); + return; + } + /* get supported cpu subfunctions indicated via query / test bit */ + rc = query_cpu_subfunc(model->features); + if (rc) { + error_setg(errp, "KVM: Error querying CPU subfunctions: %d", rc); + return; + } + + /* with cpu model support, CMM is only indicated if really available */ + if (kvm_s390_cmma_available()) { + set_bit(S390_FEAT_CMM, model->features); + } + + if (s390_known_cpu_type(cpu_type)) { + /* we want the exact model, even if some features are missing */ + model->def = s390_find_cpu_def(cpu_type, ibc_gen(unblocked_ibc), + ibc_ec_ga(unblocked_ibc), NULL); + } else { + /* model unknown, e.g. too new - search using features */ + model->def = s390_find_cpu_def(0, ibc_gen(unblocked_ibc), + ibc_ec_ga(unblocked_ibc), + model->features); + } + if (!model->def) { + error_setg(errp, "KVM: host CPU model could not be identified"); + return; + } + /* strip of features that are not part of the maximum model */ + bitmap_and(model->features, model->features, model->def->full_feat, + S390_FEAT_MAX); +} + +void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) +{ + struct kvm_s390_vm_cpu_processor prop = { + .fac_list = { 0 }, + }; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_PROCESSOR, + .addr = (uint64_t) &prop, + }; + int rc; + + if (!model) { + /* compatibility handling if cpu models are disabled */ + if (kvm_s390_cmma_available() && !mem_path) { + kvm_s390_enable_cmma(); + } + return; + } + if (!kvm_s390_cpu_models_supported()) { + error_setg(errp, "KVM doesn't support CPU models"); + return; + } + prop.cpuid = s390_cpuid_from_cpu_model(model); + prop.ibc = s390_ibc_from_cpu_model(model); + /* configure cpu features indicated via STFL(e) */ + s390_fill_feat_block(model->features, S390_FEAT_TYPE_STFL, + (uint8_t *) prop.fac_list); + rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + if (rc) { + error_setg(errp, "KVM: Error configuring the CPU model: %d", rc); + return; + } + /* configure cpu features indicated e.g. via SCLP */ + rc = configure_cpu_feat(model->features); + if (rc) { + error_setg(errp, "KVM: Error configuring CPU features: %d", rc); + return; + } + /* configure cpu subfunctions indicated via query / test bit */ + rc = configure_cpu_subfunc(model->features); + if (rc) { + error_setg(errp, "KVM: Error configuring CPU subfunctions: %d", rc); + return; + } + /* enable CMM via CMMA - disable on hugetlbfs */ + if (test_bit(S390_FEAT_CMM, model->features)) { + if (mem_path) { + error_report("Warning: CMM will not be enabled because it is not " + "compatible to hugetlbfs."); + } else { + kvm_s390_enable_cmma(); + } + } +} |