diff options
Diffstat (limited to 'target-s390x/kvm.c')
-rw-r--r-- | target-s390x/kvm.c | 226 |
1 files changed, 213 insertions, 13 deletions
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index b48c643b36..ea18015793 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -44,6 +44,8 @@ #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/ipl.h" +#include "hw/s390x/ebcdic.h" +#include "exec/memattrs.h" /* #define DEBUG_KVM */ @@ -108,6 +110,14 @@ #define ICPT_CPU_STOP 0x28 #define ICPT_IO 0x40 +#define NR_LOCAL_IRQS 32 +/* + * Needs to be big enough to contain max_cpus emergency signals + * and in addition NR_LOCAL_IRQS interrupts + */ +#define VCPU_IRQ_BUF_SIZE (sizeof(struct kvm_s390_irq) * \ + (max_cpus + NR_LOCAL_IRQS)) + static CPUWatchpoint hw_watchpoint; /* * We don't use a list because this structure is also used to transmit the @@ -122,6 +132,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static int cap_sync_regs; static int cap_async_pf; +static int cap_mem_op; +static int cap_s390_irq; static void *legacy_s390_alloc(size_t size, uint64_t *align); @@ -246,6 +258,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) { cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); + cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP); + cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ); kvm_s390_enable_cmma(s); @@ -255,6 +269,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); + kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); return 0; } @@ -268,6 +283,7 @@ int kvm_arch_init_vcpu(CPUState *cs) { S390CPU *cpu = S390_CPU(cs); kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state); + cpu->irqstate = g_malloc0(VCPU_IRQ_BUF_SIZE); return 0; } @@ -548,6 +564,46 @@ int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } +/** + * kvm_s390_mem_op: + * @addr: the logical start address in guest memory + * @ar: the access register number + * @hostbuf: buffer in host memory. NULL = do only checks w/o copying + * @len: length that should be transfered + * @is_write: true = write, false = read + * Returns: 0 on success, non-zero if an exception or error occured + * + * Use KVM ioctl to read/write from/to guest memory. An access exception + * is injected into the vCPU in case of translation errors. + */ +int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, + int len, bool is_write) +{ + struct kvm_s390_mem_op mem_op = { + .gaddr = addr, + .flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION, + .size = len, + .op = is_write ? KVM_S390_MEMOP_LOGICAL_WRITE + : KVM_S390_MEMOP_LOGICAL_READ, + .buf = (uint64_t)hostbuf, + .ar = ar, + }; + int ret; + + if (!cap_mem_op) { + return -ENOSYS; + } + if (!hostbuf) { + mem_op.flags |= KVM_S390_MEMOP_F_CHECK_ONLY; + } + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_S390_MEM_OP, &mem_op); + if (ret < 0) { + error_printf("KVM_S390_MEM_OP failed: %s\n", strerror(-ret)); + } + return ret; +} + /* * Legacy layout for s390: * Older S390 KVM requires the topmost vma of the RAM to be @@ -725,8 +781,9 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) { } -void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) +MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) { + return MEMTXATTRS_UNSPECIFIED; } int kvm_arch_process_async_events(CPUState *cs) @@ -783,10 +840,9 @@ static int s390_kvm_irq_to_interrupt(struct kvm_s390_irq *irq, return r; } -void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq) +static void inject_vcpu_irq_legacy(CPUState *cs, struct kvm_s390_irq *irq) { struct kvm_s390_interrupt kvmint = {}; - CPUState *cs = CPU(cpu); int r; r = s390_kvm_irq_to_interrupt(irq, &kvmint); @@ -802,6 +858,23 @@ void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq) } } +void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq) +{ + CPUState *cs = CPU(cpu); + int r; + + if (cap_s390_irq) { + r = kvm_vcpu_ioctl(cs, KVM_S390_IRQ, irq); + if (!r) { + return; + } + error_report("KVM failed to inject interrupt %llx", irq->type); + exit(1); + } + + inject_vcpu_irq_legacy(cs, irq); +} + static void __kvm_s390_floating_interrupt(struct kvm_s390_irq *irq) { struct kvm_s390_interrupt kvmint = {}; @@ -975,7 +1048,8 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) return rc; } -static uint64_t get_base_disp_rxy(S390CPU *cpu, struct kvm_run *run) +static uint64_t get_base_disp_rxy(S390CPU *cpu, struct kvm_run *run, + uint8_t *ar) { CPUS390XState *env = &cpu->env; uint32_t x2 = (run->s390_sieic.ipa & 0x000f); @@ -986,12 +1060,16 @@ static uint64_t get_base_disp_rxy(S390CPU *cpu, struct kvm_run *run) if (disp2 & 0x80000) { disp2 += 0xfff00000; } + if (ar) { + *ar = base2; + } return (base2 ? env->regs[base2] : 0) + (x2 ? env->regs[x2] : 0) + (long)(int)disp2; } -static uint64_t get_base_disp_rsy(S390CPU *cpu, struct kvm_run *run) +static uint64_t get_base_disp_rsy(S390CPU *cpu, struct kvm_run *run, + uint8_t *ar) { CPUS390XState *env = &cpu->env; uint32_t base2 = run->s390_sieic.ipb >> 28; @@ -1001,6 +1079,9 @@ static uint64_t get_base_disp_rsy(S390CPU *cpu, struct kvm_run *run) if (disp2 & 0x80000) { disp2 += 0xfff00000; } + if (ar) { + *ar = base2; + } return (base2 ? env->regs[base2] : 0) + (long)(int)disp2; } @@ -1032,11 +1113,12 @@ static int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run) { uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; uint64_t fiba; + uint8_t ar; cpu_synchronize_state(CPU(cpu)); - fiba = get_base_disp_rxy(cpu, run); + fiba = get_base_disp_rxy(cpu, run, &ar); - return stpcifc_service_call(cpu, r1, fiba); + return stpcifc_service_call(cpu, r1, fiba, ar); } static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) @@ -1058,22 +1140,24 @@ static int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; uint8_t r3 = run->s390_sieic.ipa & 0x000f; uint64_t gaddr; + uint8_t ar; cpu_synchronize_state(CPU(cpu)); - gaddr = get_base_disp_rsy(cpu, run); + gaddr = get_base_disp_rsy(cpu, run, &ar); - return pcistb_service_call(cpu, r1, r3, gaddr); + return pcistb_service_call(cpu, r1, r3, gaddr, ar); } static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) { uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; uint64_t fiba; + uint8_t ar; cpu_synchronize_state(CPU(cpu)); - fiba = get_base_disp_rxy(cpu, run); + fiba = get_base_disp_rxy(cpu, run, &ar); - return mpcifc_service_call(cpu, r1, fiba); + return mpcifc_service_call(cpu, r1, fiba, ar); } static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) @@ -1202,7 +1286,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) * For any diagnose call we support, bits 48-63 of the resulting * address specify the function code; the remainder is ignored. */ - func_code = decode_basedisp_rs(&cpu->env, ipb) & DIAG_KVM_CODE_MASK; + func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK; switch (func_code) { case DIAG_IPL: kvm_handle_diag_308(cpu, run); @@ -1549,7 +1633,8 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) cpu_synchronize_state(CPU(cpu)); /* get order code */ - order = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK; + order = decode_basedisp_rs(env, run->s390_sieic.ipb, NULL) + & SIGP_ORDER_MASK; status_reg = &env->regs[r1]; param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; @@ -1723,6 +1808,72 @@ static int handle_tsch(S390CPU *cpu) return ret; } +static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar) +{ + struct sysib_322 sysib; + int del; + + if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) { + return; + } + /* Shift the stack of Extended Names to prepare for our own data */ + memmove(&sysib.ext_names[1], &sysib.ext_names[0], + sizeof(sysib.ext_names[0]) * (sysib.count - 1)); + /* First virt level, that doesn't provide Ext Names delimits stack. It is + * assumed it's not capable of managing Extended Names for lower levels. + */ + for (del = 1; del < sysib.count; del++) { + if (!sysib.vm[del].ext_name_encoding || !sysib.ext_names[del][0]) { + break; + } + } + if (del < sysib.count) { + memset(sysib.ext_names[del], 0, + sizeof(sysib.ext_names[0]) * (sysib.count - del)); + } + /* Insert short machine name in EBCDIC, padded with blanks */ + if (qemu_name) { + memset(sysib.vm[0].name, 0x40, sizeof(sysib.vm[0].name)); + ebcdic_put(sysib.vm[0].name, qemu_name, MIN(sizeof(sysib.vm[0].name), + strlen(qemu_name))); + } + sysib.vm[0].ext_name_encoding = 2; /* 2 = UTF-8 */ + memset(sysib.ext_names[0], 0, sizeof(sysib.ext_names[0])); + /* If hypervisor specifies zero Extended Name in STSI322 SYSIB, it's + * considered by s390 as not capable of providing any Extended Name. + * Therefore if no name was specified on qemu invocation, we go with the + * same "KVMguest" default, which KVM has filled into short name field. + */ + if (qemu_name) { + strncpy((char *)sysib.ext_names[0], qemu_name, + sizeof(sysib.ext_names[0])); + } else { + strcpy((char *)sysib.ext_names[0], "KVMguest"); + } + /* Insert UUID */ + memcpy(sysib.vm[0].uuid, qemu_uuid, sizeof(sysib.vm[0].uuid)); + + s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, sizeof(sysib)); +} + +static int handle_stsi(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + struct kvm_run *run = cs->kvm_run; + + switch (run->s390_stsi.fc) { + case 3: + if (run->s390_stsi.sel1 != 2 || run->s390_stsi.sel2 != 2) { + return 0; + } + /* Only sysib 3.2.2 needs post-handling for now. */ + insert_stsi_3_2_2(cpu, run->s390_stsi.addr, run->s390_stsi.ar); + return 0; + default: + return 0; + } +} + static int kvm_arch_handle_debug_exit(S390CPU *cpu) { CPUState *cs = CPU(cpu); @@ -1772,6 +1923,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_S390_TSCH: ret = handle_tsch(cpu); break; + case KVM_EXIT_S390_STSI: + ret = handle_stsi(cpu); + break; case KVM_EXIT_DEBUG: ret = kvm_arch_handle_debug_exit(cpu); break; @@ -1916,6 +2070,52 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) return ret; } +void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu) +{ + struct kvm_s390_irq_state irq_state; + CPUState *cs = CPU(cpu); + int32_t bytes; + + if (!kvm_check_extension(kvm_state, KVM_CAP_S390_IRQ_STATE)) { + return; + } + + irq_state.buf = (uint64_t) cpu->irqstate; + irq_state.len = VCPU_IRQ_BUF_SIZE; + + bytes = kvm_vcpu_ioctl(cs, KVM_S390_GET_IRQ_STATE, &irq_state); + if (bytes < 0) { + cpu->irqstate_saved_size = 0; + error_report("Migration of interrupt state failed"); + return; + } + + cpu->irqstate_saved_size = bytes; +} + +int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + struct kvm_s390_irq_state irq_state; + int r; + + if (!kvm_check_extension(kvm_state, KVM_CAP_S390_IRQ_STATE)) { + return -ENOSYS; + } + + if (cpu->irqstate_saved_size == 0) { + return 0; + } + irq_state.buf = (uint64_t) cpu->irqstate; + irq_state.len = cpu->irqstate_saved_size; + + r = kvm_vcpu_ioctl(cs, KVM_S390_SET_IRQ_STATE, &irq_state); + if (r) { + error_report("Setting interrupt state failed %d", r); + } + return r; +} + int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data) { |