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