There are currently a batch of occasionally used SPRs whose state we do not synchronize with KVM. This might be a problem for debugging, and will definitely be a problem for savevm / migration. KVM now supports accessing these registers via the KVM_{GET,SET}_ONE_REG interface, so this patch wires up the code to use this on the qemu side.
Signed-off-by: David Gibson <da...@gibson.dropbear.id.au> --- target-ppc/cpu.h | 4 ++ target-ppc/kvm.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 136 insertions(+), 10 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 953146e..6cb79fe 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1319,6 +1319,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_MPC_CMPH (0x09B) #define SPR_MPC_LCTRL1 (0x09C) #define SPR_MPC_LCTRL2 (0x09D) +#define SPR_UAMOR (0x09D) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_VRSAVE (0x100) @@ -1535,6 +1536,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_UPERF0 (0x310) #define SPR_UPERF1 (0x311) #define SPR_UPERF2 (0x312) +#define SPR_MMCRA (0x312) #define SPR_UPERF3 (0x313) #define SPR_620_PMC1W (0x313) #define SPR_UPERF4 (0x314) @@ -1544,7 +1546,9 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_UPERF7 (0x317) #define SPR_UPERF8 (0x318) #define SPR_UPERF9 (0x319) +#define SPR_PMC7 (0x319) #define SPR_UPERFA (0x31A) +#define SPR_PMC8 (0x31A) #define SPR_UPERFB (0x31B) #define SPR_620_MMCR0W (0x31B) #define SPR_UPERFC (0x31C) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 2f4f068..7604d0b 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 @@ -89,6 +90,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) { @@ -444,6 +446,76 @@ 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)); + } +} + int kvm_arch_put_registers(CPUState *cs, int level) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -524,16 +596,35 @@ 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, - }; - - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; + if (cap_one_reg) { + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers. There's still a good chance things will work, + * as long as we don't try to migrate */ + kvm_put_one_spr(cs, KVM_REG_PPC_DABR, SPR_DABR); + kvm_put_one_spr(cs, KVM_REG_PPC_DSCR, SPR_DSCR); + kvm_put_one_spr(cs, KVM_REG_PPC_PURR, SPR_PURR); + kvm_put_one_spr(cs, KVM_REG_PPC_SPURR, SPR_SPURR); + kvm_put_one_spr(cs, KVM_REG_PPC_DAR, SPR_DAR); + kvm_put_one_spr(cs, KVM_REG_PPC_DSISR, SPR_DSISR); + kvm_put_one_spr(cs, KVM_REG_PPC_AMR, SPR_AMR); + kvm_put_one_spr(cs, KVM_REG_PPC_UAMOR, SPR_UAMOR); + + kvm_put_one_spr(cs, KVM_REG_PPC_MMCR0, SPR_MMCR0); + kvm_put_one_spr(cs, KVM_REG_PPC_MMCR1, SPR_MMCR1); + kvm_put_one_spr(cs, KVM_REG_PPC_MMCRA, SPR_MMCRA); + + kvm_put_one_spr(cs, KVM_REG_PPC_PMC1, SPR_PMC1); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC2, SPR_PMC2); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC3, SPR_PMC3); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC4, SPR_PMC4); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC5, SPR_PMC5); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC6, SPR_PMC6); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC7, SPR_PMC7); + kvm_put_one_spr(cs, KVM_REG_PPC_PMC8, SPR_PMC8); + + if (cap_hior && (level >= KVM_PUT_RESET_STATE)) { + kvm_put_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); } } @@ -716,6 +807,37 @@ int kvm_arch_get_registers(CPUState *cs) } } + if (cap_one_reg) { + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers. There's still a good chance things will work, + * as long as we don't try to migrate */ + if (cap_hior) { + kvm_get_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } + kvm_get_one_spr(cs, KVM_REG_PPC_DABR, SPR_DABR); + kvm_get_one_spr(cs, KVM_REG_PPC_DSCR, SPR_DSCR); + kvm_get_one_spr(cs, KVM_REG_PPC_PURR, SPR_PURR); + kvm_get_one_spr(cs, KVM_REG_PPC_SPURR, SPR_SPURR); + kvm_get_one_spr(cs, KVM_REG_PPC_DAR, SPR_DAR); + kvm_get_one_spr(cs, KVM_REG_PPC_DSISR, SPR_DSISR); + kvm_get_one_spr(cs, KVM_REG_PPC_AMR, SPR_AMR); + kvm_get_one_spr(cs, KVM_REG_PPC_UAMOR, SPR_UAMOR); + + kvm_get_one_spr(cs, KVM_REG_PPC_MMCR0, SPR_MMCR0); + kvm_get_one_spr(cs, KVM_REG_PPC_MMCR1, SPR_MMCR1); + kvm_get_one_spr(cs, KVM_REG_PPC_MMCRA, SPR_MMCRA); + + kvm_get_one_spr(cs, KVM_REG_PPC_PMC1, SPR_PMC1); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC2, SPR_PMC2); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC3, SPR_PMC3); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC4, SPR_PMC4); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC5, SPR_PMC5); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC6, SPR_PMC6); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC7, SPR_PMC7); + kvm_get_one_spr(cs, KVM_REG_PPC_PMC8, SPR_PMC8); + } + return 0; } -- 1.7.10.4