diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 85713795de..f31c67e1b1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -90,6 +90,7 @@ static int cap_htm; /* Hardware transactional memory support */ static int cap_mmu_radix; static int cap_mmu_hash_v3; static int cap_resize_hpt; +static int cap_ppc_pvr_compat; static uint32_t debug_inst_opcode; @@ -147,6 +148,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX); cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT); + /* + * Note: setting it to false because there is not such capability + * in KVM at this moment. + * + * TODO: call kvm_vm_check_extension() with the right capability + * after the kernel starts implementing it.*/ + cap_ppc_pvr_compat = false; if (!cap_interrupt_level) { fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " @@ -2785,3 +2793,33 @@ void kvmppc_update_sdr1(target_ulong sdr1) run_on_cpu(cs, kvmppc_pivot_hpt_cpu, RUN_ON_CPU_TARGET_PTR(sdr1)); } } + +/* + * This is a helper function to detect a post migration scenario + * in which a guest, running as KVM-HV, freezes in cpu_post_load because + * the guest kernel can't handle a PVR value other than the actual host + * PVR in KVM_SET_SREGS, even if pvr_match() returns true. + * + * If we don't have cap_ppc_pvr_compat and we're not running in PR + * (so, we're HV), return true. The workaround itself is done in + * cpu_post_load. + * + * The order here is important: we'll only check for KVM PR as a + * fallback if the guest kernel can't handle the situation itself. + * We need to avoid as much as possible querying the running KVM type + * in QEMU level. + */ +bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + + if (!kvm_enabled()) { + return false; + } + + if (cap_ppc_pvr_compat) { + return false; + } + + return !kvmppc_is_pr(cs->kvm_state); +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 6bc6fb3e2d..381afe6240 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -67,6 +67,7 @@ void kvmppc_check_papr_resize_hpt(Error **errp); int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, target_ulong flags, int shift); int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); void kvmppc_update_sdr1(target_ulong sdr1); +bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path); diff --git a/target/ppc/machine.c b/target/ppc/machine.c index abe0a1cdf0..e36b7100cb 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -9,6 +9,7 @@ #include "mmu-hash64.h" #include "migration/cpu.h" #include "qapi/error.h" +#include "kvm_ppc.h" static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) { @@ -249,6 +250,27 @@ static int cpu_post_load(void *opaque, int version_id) } } + /* + * If we're running with KVM HV, there is a chance that the guest + * is running with KVM HV and its kernel does not have the + * capability of dealing with a different PVR other than this + * exact host PVR in KVM_SET_SREGS. If that happens, the + * guest freezes after migration. + * + * The function kvmppc_pvr_workaround_required does this verification + * by first checking if the kernel has the cap, returning true immediately + * if that is the case. Otherwise, it checks if we're running in KVM PR. + * If the guest kernel does not have the cap and we're not running KVM-PR + * (so, it is running KVM-HV), we need to ensure that KVM_SET_SREGS will + * receive the PVR it expects as a workaround. + * + */ +#if defined(CONFIG_KVM) + if (kvmppc_pvr_workaround_required(cpu)) { + env->spr[SPR_PVR] = env->spr_cb[SPR_PVR].default_value; + } +#endif + env->lr = env->spr[SPR_LR]; env->ctr = env->spr[SPR_CTR]; cpu_write_xer(env, env->spr[SPR_XER]);