Amit Machhiwal <[email protected]> writes:

> Add infrastructure to query the host CPU compatibility mode via the
> KVM_PPC_GET_COMPAT_CAPS ioctl. This allows QEMU to determine if the
> host is running in a compatibility mode (e.g., a Power11 processor
> operating in Power10 compatibility mode).
>
> The kvmppc_get_compat_caps() function queries KVM for host compatibility
> capabilities with size field validation for ABI compatibility between
> QEMU and the kernel. The kvm_ppc_host_compat_pvr() function derives the
> effective PVR based on the compatibility mode reported by KVM.
>
> Additionally, cas_check_pvr() in hw/ppc/spapr_hcall.c is updated to
> prevent fallback to raw mode when the host is running in compatibility
> mode. This ensures that nested guests cannot exceed the host's
> compatibility level.
>
> If the capability is not supported or the query fails, the functions
> return 0, allowing fallback to existing behavior.
>
> Signed-off-by: Amit Machhiwal <[email protected]>
> ---
>  hw/ppc/spapr_hcall.c | 14 ++++++++++
>  target/ppc/kvm.c     | 65 ++++++++++++++++++++++++++++++++++++++++++++
>  target/ppc/kvm_ppc.h | 16 +++++++++++
>  3 files changed, 95 insertions(+)
>
> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
> index 60ba215e8611..68c29fbe141b 100644
> --- a/hw/ppc/spapr_hcall.c
> +++ b/hw/ppc/spapr_hcall.c
> @@ -1135,6 +1135,7 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t 
> max_compat,
>  {
>      bool explicit_match = false; /* Matched the CPU's real PVR */
>      uint32_t best_compat = 0;
> +    uint32_t compat_host_pvr = 0;
>      int i;
>  
>      /*
> @@ -1162,6 +1163,19 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, 
> uint32_t max_compat,
>          }
>      }
>  
> +    if (explicit_match) {
> +        compat_host_pvr = kvm_ppc_host_compat_pvr();
> +        /*
> +         * If the host is booted in a compatibility mode, do not try booting 
> in
> +         * the raw mode as it may allow KVM guests to boot with a higher CPU
> +         * version compared to what host was booted with; which should not be
> +         * allowed.
> +         */
> +        if (compat_host_pvr) {
> +            explicit_match = false;
> +        }
> +    }
> +
>      *raw_mode_supported = explicit_match;
>  
>      /* Parsing finished */
> diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
> index b94c2997a07f..9e5006e0c2cd 100644
> --- a/target/ppc/kvm.c
> +++ b/target/ppc/kvm.c
> @@ -2602,6 +2602,71 @@ bool kvmppc_supports_ail_3(void)
>      return cap_ail_mode_3;
>  }
>  
> +#if defined(TARGET_PPC64)
This define would be implicit for this file. I think you should target
these functions for CONFIG_KVM. Otherwise you will have multiple
definitions of kvm_ppc_host_compat_pvr()

> +static target_ulong kvmppc_get_compat_caps(void)
> +{
> +    struct kvm_ppc_compat_caps host_compat;
> +    target_ulong host_caps;
> +    int ret;
> +
> +    if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_COMPAT_CAPS)) {
> +        return 0;
> +    }
> +
> +    /* Initialize the structure with size field for forward compatibility */
> +    memset(&host_compat, 0, sizeof(host_compat));
> +    host_compat.size = sizeof(host_compat);
> +
> +    ret = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_COMPAT_CAPS, &host_compat);
> +    if (ret < 0) {
> +        fprintf(stderr, "KVM: failed to get host capabilities\n");
Should use error_report instead of fprintf(stderr,..
> +        return 0;
> +    }
> +
> +    /*
> +     * Validate the returned size matches our structure size.
> +     * The kernel validates that userspace provides sufficient size before
> +     * the ioctl, and returns its own structure size. A mismatch indicates
> +     * a version incompatibility between QEMU and the kernel.
> +     */
> +    if (host_compat.size != sizeof(host_compat)) 
This error handling differs from the kernel code which can indeed return
host_compat.size < sizeof(host_compat).
> +        fprintf(stderr, "KVM: host_compat size mismatch (expected %zu, got 
> %lu)\n",
> +                sizeof(host_compat), host_compat.size);
Error message can be more user friendly. Also your should use
error_report instead of fprintf(stderr,..
> +        return 0;
> +    }

> +
> +    host_caps = host_compat.compat_capabilities;
> +    return host_caps;
Minor: Why not just return host_compat.compat_capabilities instead of
this extra store
> +}
> +
Minor: Minor comment about this function would be nice to have
> +uint32_t kvm_ppc_host_compat_pvr(void)
> +{
> +    uint32_t compat_host_pvr = 0;
> +    int cap_idx = 0;
> +    target_ulong host_caps = kvmppc_get_compat_caps();
> +
> +    host_caps = host_caps & KVM_PPC_COMPAT_BITMASK;
> +    if (host_caps) {
> +        cap_idx = 63 - __builtin_ctzll(host_caps);
you probably need to use clz64() instead of this.

> +        switch (cap_idx) {
> +        case KVM_PPC_COMPAT_CAP_P9_IDX:
> +            compat_host_pvr = CPU_POWERPC_POWER9_DD22;
> +            break;
> +        case KVM_PPC_COMPAT_CAP_P10_IDX:
> +            compat_host_pvr = CPU_POWERPC_POWER10_DD20;
> +            break;
> +        case KVM_PPC_COMPAT_CAP_P11_IDX:
> +            compat_host_pvr = CPU_POWERPC_POWER11_DD20;
> +            break;
> +        default:
> +            break;
> +        }
> +    }
> +
> +    return compat_host_pvr;
> +}
> +#endif /* TARGET_PPC64 */
> +
>  PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void)
>  {
>      uint32_t host_pvr = mfpvr();
> diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
> index 742881231e16..5970e7383740 100644
> --- a/target/ppc/kvm_ppc.h
> +++ b/target/ppc/kvm_ppc.h
> @@ -81,6 +81,17 @@ bool kvmppc_supports_ail_3(void);
>  int kvmppc_enable_hwrng(void);
>  int kvmppc_put_books_sregs(PowerPCCPU *cpu);
>  PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void);
> +
> +/*
> + * Bit position indices for KVM_PPC_COMPAT_CAP_* capabilities.
> + * These represent the result of (63 - bit_number) for each capability bit.
> + * Used for identifying which compatibility mode is supported by the host.
> + */
> +#define KVM_PPC_COMPAT_CAP_P9_IDX   1  /* 63 - 62 */
> +#define KVM_PPC_COMPAT_CAP_P10_IDX  2  /* 63 - 61 */
> +#define KVM_PPC_COMPAT_CAP_P11_IDX  3  /* 63 - 60 */
Can these be derived from KVM_PPC_COMPAT_CAP_POWERXX
Also if these defines are only used in kvm_ppc_host_compat_pvr() then
should be defined near to it.

> +
> +uint32_t kvm_ppc_host_compat_pvr(void);
>  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);
> @@ -440,6 +451,11 @@ static inline PowerPCCPUClass 
> *kvm_ppc_get_host_cpu_class(void)
>      return NULL;
>  }
>  
> +static inline uint32_t kvm_ppc_host_compat_pvr(void)
> +{
> +    return 0;
> +}
> +
>  static inline void kvmppc_check_papr_resize_hpt(Error **errp)
>  {
>  }
> -- 
> 2.50.1 (Apple Git-155)
>

-- 
Cheers
~ Vaibhav

Reply via email to