diff options
Diffstat (limited to 'target-ppc/kvm.c')
-rw-r--r-- | target-ppc/kvm.c | 311 |
1 files changed, 264 insertions, 47 deletions
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 8e6441614e..9dff7607f1 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -61,6 +61,7 @@ static int cap_ppc_smt; static int cap_ppc_rma; static int cap_spapr_tce; static int cap_hior; +static int cap_one_reg; /* XXX We have a race condition where we actually have a level triggered * interrupt, but the infrastructure can't expose that yet, so the guest @@ -80,6 +81,8 @@ static void kvm_kick_cpu(void *opaque) qemu_cpu_kick(CPU(cpu)); } +static int kvm_ppc_register_host_cpu_type(void); + int kvm_arch_init(KVMState *s) { cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); @@ -89,6 +92,7 @@ int kvm_arch_init(KVMState *s) cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT); cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); + cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); cap_hior = kvm_check_extension(s, KVM_CAP_PPC_HIOR); if (!cap_interrupt_level) { @@ -96,6 +100,8 @@ int kvm_arch_init(KVMState *s) "VM to stall at times!\n"); } + kvm_ppc_register_host_cpu_type(); + return 0; } @@ -449,6 +455,202 @@ static void kvm_sw_tlb_put(PowerPCCPU *cpu) g_free(bitmap); } +static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret != 0) { + fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n", + spr, strerror(errno)); + } else { + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + env->spr[spr] = val.u32; + break; + + case KVM_REG_SIZE_U64: + env->spr[spr] = val.u64; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + } +} + +static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + val.u32 = env->spr[spr]; + break; + + case KVM_REG_SIZE_U64: + val.u64 = env->spr[spr]; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret != 0) { + fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n", + spr, strerror(errno)); + } +} + +static int kvm_put_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr = env->fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set FPSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + + vsr[0] = float64_val(env->fpr[i]); + vsr[1] = env->vsr[i]; + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set %s%d to KVM: %s\n", vsx ? "VSR" : "FPR", + i, strerror(errno)); + return ret; + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set VSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set VR%d to KVM: %s\n", i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + +static int kvm_get_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get FPSCR from KVM: %s\n", strerror(errno)); + return ret; + } else { + env->fpscr = fpscr; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get %s%d from KVM: %s\n", + vsx ? "VSR" : "FPR", i, strerror(errno)); + return ret; + } else { + env->fpr[i] = vsr[0]; + if (vsx) { + env->vsr[i] = vsr[1]; + } + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get VSCR from KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get VR%d from KVM: %s\n", + i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + int kvm_arch_put_registers(CPUState *cs, int level) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -489,6 +691,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) if (ret < 0) return ret; + kvm_put_fp(cs); + if (env->tlb_dirty) { kvm_sw_tlb_put(cpu); env->tlb_dirty = false; @@ -530,15 +734,22 @@ int kvm_arch_put_registers(CPUState *cs, int level) } if (cap_hior && (level >= KVM_PUT_RESET_STATE)) { - uint64_t hior = env->spr[SPR_HIOR]; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_HIOR, - .addr = (uintptr_t) &hior, - }; + kvm_put_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_put_one_spr(cs, id, i); + } } } @@ -587,6 +798,8 @@ int kvm_arch_get_registers(CPUState *cs) for (i = 0;i < 32; i++) env->gpr[i] = regs.gpr[i]; + kvm_get_fp(cs); + if (cap_booke_sregs) { ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); if (ret < 0) { @@ -721,6 +934,26 @@ int kvm_arch_get_registers(CPUState *cs) } } + if (cap_hior) { + kvm_get_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } + + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_get_one_spr(cs, id, i); + } + } + } + return 0; } @@ -1259,46 +1492,25 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) static void kvmppc_host_cpu_initfn(Object *obj) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(obj); - assert(kvm_enabled()); - - if (pcc->info->pvr != mfpvr()) { - fprintf(stderr, "Your host CPU is unsupported.\n" - "Please choose a supported model instead, see -cpu ?.\n"); - exit(1); - } } static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - uint32_t host_pvr = mfpvr(); - PowerPCCPUClass *pvr_pcc; - ppc_def_t *spec; uint32_t vmx = kvmppc_get_vmx(); uint32_t dfp = kvmppc_get_dfp(); - spec = g_malloc0(sizeof(*spec)); - - pvr_pcc = ppc_cpu_class_by_pvr(host_pvr); - if (pvr_pcc != NULL) { - memcpy(spec, pvr_pcc->info, sizeof(*spec)); - } - pcc->info = spec; - /* Override the display name for -cpu ? and QMP */ - pcc->info->name = "host"; - - /* Now fix up the spec with information we can query from the host */ + /* Now fix up the class with information we can query from the host */ if (vmx != -1) { /* Only override when we know what the host supports */ - alter_insns(&spec->insns_flags, PPC_ALTIVEC, vmx > 0); - alter_insns(&spec->insns_flags2, PPC2_VSX, vmx > 1); + alter_insns(&pcc->insns_flags, PPC_ALTIVEC, vmx > 0); + alter_insns(&pcc->insns_flags2, PPC2_VSX, vmx > 1); } if (dfp != -1) { /* Only override when we know what the host supports */ - alter_insns(&spec->insns_flags2, PPC2_DFP, dfp); + alter_insns(&pcc->insns_flags2, PPC2_DFP, dfp); } } @@ -1315,6 +1527,25 @@ int kvmppc_fixup_cpu(PowerPCCPU *cpu) return 0; } +static int kvm_ppc_register_host_cpu_type(void) +{ + TypeInfo type_info = { + .name = TYPE_HOST_POWERPC_CPU, + .instance_init = kvmppc_host_cpu_initfn, + .class_init = kvmppc_host_cpu_class_init, + }; + uint32_t host_pvr = mfpvr(); + PowerPCCPUClass *pvr_pcc; + + pvr_pcc = ppc_cpu_class_by_pvr(host_pvr); + if (pvr_pcc == NULL) { + return -1; + } + type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); + type_register(&type_info); + return 0; +} + bool kvm_arch_stop_on_emulation_error(CPUState *cpu) { @@ -1330,17 +1561,3 @@ int kvm_arch_on_sigbus(int code, void *addr) { return 1; } - -static const TypeInfo kvm_host_cpu_type_info = { - .name = TYPE_HOST_POWERPC_CPU, - .parent = TYPE_POWERPC_CPU, - .instance_init = kvmppc_host_cpu_initfn, - .class_init = kvmppc_host_cpu_class_init, -}; - -static void kvm_ppc_register_types(void) -{ - type_register_static(&kvm_host_cpu_type_info); -} - -type_init(kvm_ppc_register_types) |