* [PATCH v4 1/3] [DO_NOT_MERGE] linux-headers: Add uapi header changes
2026-07-01 5:23 [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly for nested guests Amit Machhiwal
@ 2026-07-01 5:23 ` Amit Machhiwal
2026-07-01 5:23 ` [PATCH v4 2/3] target/ppc/kvm: Add support for querying host compatibility mode Amit Machhiwal
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Amit Machhiwal @ 2026-07-01 5:23 UTC (permalink / raw)
To: qemu-ppc, Harsh Prateek Bora
Cc: Amit Machhiwal, Vaibhav Jain, Nicholas Piggin, Chinmay Rath,
Glenn Miles, Paolo Bonzini, kvm, qemu-devel
This is a temporary patch intended for review and testing purposes only.
It syncs the QEMU linux-headers with the kernel v5 changes that introduce
the KVM_PPC_GET_COMPAT_CAPS ioctl for querying host CPU compatibility
capabilities. The struct kvm_ppc_compat_caps places 'size' as the first
field as required by copy_struct_from/to_user() versioning, with
KVM_PPC_COMPAT_CAPS_SIZE_VER0 (24) defined as the frozen version-floor
constant. The capability number is KVM_CAP_PPC_COMPAT_CAPS (250) and
the ioctl is defined as _IO so the ioctl number remains stable if the
struct grows in future versions. Capability bit definitions for POWER9,
POWER10, and POWER11 compatibility modes are also included.
The actual header sync will be done via scripts/update-linux-headers.sh
once the kernel changes [1] are merged upstream.
[1] https://lore.kernel.org/all/20260628083352.118b631-1-amachhiw@linux.ibm.com/
Signed-off-by: Amit Machhiwal <amachhiw@linux.ibm.com>
---
Changes in v4:
- Changed ioctl definition from _IOWR to _IO to match kernel v5.
- The ioctl number must remain stable if the struct grows; the size
field is read first by the kernel via get_user() before any
copy_struct_from_user() call.
| 20 ++++++++++++++++++++
| 4 ++++
2 files changed, 24 insertions(+)
--git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h
index 077c5437f521..e42c86481320 100644
--- a/linux-headers/asm-powerpc/kvm.h
+++ b/linux-headers/asm-powerpc/kvm.h
@@ -437,6 +437,26 @@ struct kvm_ppc_cpu_char {
__u64 behaviour_mask; /* valid bits in behaviour */
};
+/* For KVM_PPC_GET_COMPAT_CAPS */
+struct kvm_ppc_compat_caps {
+ __u64 size; /* Size of this structure */
+ __u64 flags; /* Reserved for future use */
+ __u64 compat_capabilities; /* Capabilities supported by the host */
+};
+#define KVM_PPC_COMPAT_CAPS_SIZE_VER0 24 /* sizeof first published struct */
+
+/*
+ * Capability bits for compat_capabilities field in kvm_ppc_compat_caps.
+ * These bits indicate which processor compatibility modes are supported.
+ */
+#define KVM_PPC_COMPAT_CAP_POWER9 (1ULL << 62)
+#define KVM_PPC_COMPAT_CAP_POWER10 (1ULL << 61)
+#define KVM_PPC_COMPAT_CAP_POWER11 (1ULL << 60)
+#define KVM_PPC_COMPAT_BITMASK (KVM_PPC_COMPAT_CAP_POWER9 | \
+ KVM_PPC_COMPAT_CAP_POWER10 | \
+ KVM_PPC_COMPAT_CAP_POWER11)
+
+
/*
* Values for character and character_mask.
* These are identical to the values used by H_GET_CPU_CHARACTERISTICS.
--git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 50e87ed72c20..6aa70b8d2e61 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -985,6 +985,8 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_USER_OPEREXEC 246
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
+#define KVM_CAP_S390_HPAGE_2G 249
+#define KVM_CAP_PPC_COMPAT_CAPS 250
struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1329,6 +1331,8 @@ struct kvm_s390_keyop {
/* Available with KVM_CAP_COUNTER_OFFSET */
#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset)
#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range)
+/* Available with KVM_CAP_PPC_COMPAT_CAPS */
+#define KVM_PPC_GET_COMPAT_CAPS _IO(KVMIO, 0xe4)
/* ioctl for vm fd */
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v4 2/3] target/ppc/kvm: Add support for querying host compatibility mode
2026-07-01 5:23 [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly for nested guests Amit Machhiwal
2026-07-01 5:23 ` [PATCH v4 1/3] [DO_NOT_MERGE] linux-headers: Add uapi header changes Amit Machhiwal
@ 2026-07-01 5:23 ` Amit Machhiwal
2026-07-01 5:23 ` [PATCH v4 3/3] target/ppc/kvm: Use host compatibility mode for nested guests Amit Machhiwal
2026-08-04 7:30 ` [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly " Gautam Menghani
3 siblings, 0 replies; 5+ messages in thread
From: Amit Machhiwal @ 2026-07-01 5:23 UTC (permalink / raw)
To: qemu-ppc, Harsh Prateek Bora
Cc: Amit Machhiwal, Vaibhav Jain, Nicholas Piggin, Chinmay Rath,
Glenn Miles, Paolo Bonzini, kvm, qemu-devel
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 issues the ioctl and returns the
compat_capabilities bitmap. The kvm_ppc_host_compat_pvr() function derives
the effective PVR from the bitmap using ctz64() to find the lowest set
bit (highest supported compat level in IBM MSB-0 numbering).
The struct kvm_ppc_compat_caps places 'size' first and userspace sets it
to sizeof(struct kvm_ppc_compat_caps) before calling the ioctl. The kernel
uses copy_struct_from/to_user() to handle forward and backward ABI
compatibility: an older userspace with a smaller struct gets trailing fields
zero-padded. When newer userspace passes a larger struct to an older kernel
(usize > ksize), the kernel unconditionally returns -E2BIG and writes its
own ksize back into host_compat.size. QEMU detects this, validates the
returned size against KVM_PPC_COMPAT_CAPS_SIZE_VER0, and retries with that
size.
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. The call is guarded with kvm_enabled() since
kvm_ppc_host_compat_pvr() invokes kvm_vm_ioctl() which dereferences
kvm_state; without the guard, a TCG guest on a CONFIG_KVM=y binary
would segfault.
If the capability is not supported or the query fails, the functions
return 0, allowing fallback to existing behavior.
Signed-off-by: Amit Machhiwal <amachhiw@linux.ibm.com>
---
Changes in v4:
- Replaced fprintf(stderr, ...) with error_report() for error reporting
in kvmppc_get_compat_caps().
- Replaced __builtin_ctzll() with the portable ctz64() helper.
- Simplified kvm_ppc_host_compat_pvr() to switch directly on the
capability bit value (e.g. KVM_PPC_COMPAT_CAP_POWER9) instead of a
derived index, removing the KVM_PPC_COMPAT_CAP_P*_IDX defines from
kvm_ppc.h.
- Dropped the post-ioctl size mismatch check; it is not meaningful with
the copy_struct_from/to_user() model.
- Added a function comment to kvm_ppc_host_compat_pvr().
- Guarded the kvm_ppc_host_compat_pvr() call in cas_check_pvr() with
kvm_enabled() to prevent a segfault when QEMU is built with
CONFIG_KVM=y but run as a TCG guest (kvm_state is NULL in that case).
hw/ppc/spapr_hcall.c | 14 +++++++++
target/ppc/kvm.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
target/ppc/kvm_ppc.h | 7 +++++
3 files changed, 96 insertions(+)
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 23bcd788daf6..708902934cff 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1136,6 +1136,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;
/*
@@ -1163,6 +1164,19 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t max_compat,
}
}
+ if (explicit_match && kvm_enabled()) {
+ 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..b1b596f004ef 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2602,6 +2602,81 @@ bool kvmppc_supports_ail_3(void)
return cap_ail_mode_3;
}
+#if defined(TARGET_PPC64)
+static target_ulong kvmppc_get_compat_caps(void)
+{
+ struct kvm_ppc_compat_caps host_compat;
+ int ret;
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_COMPAT_CAPS)) {
+ return 0;
+ }
+
+ /*
+ * Set size to sizeof(struct kvm_ppc_compat_caps) so the kernel applies
+ * copy_struct_from/to_user() versioning. size must be >= VER0.
+ */
+ 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 == -E2BIG && host_compat.size >= KVM_PPC_COMPAT_CAPS_SIZE_VER0) {
+ /*
+ * Kernel is older and knows only a smaller struct version. It
+ * wrote back its ksize into host_compat.size. Retry with that
+ * size so the kernel accepts the call.
+ *
+ * When a VER1 struct is introduced, add a check here:
+ * if (host_compat.size >= KVM_PPC_COMPAT_CAPS_SIZE_VER1) { ... }
+ */
+ uint64_t ksize = host_compat.size;
+ memset(&host_compat, 0, sizeof(host_compat));
+ host_compat.size = ksize;
+ ret = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_COMPAT_CAPS, &host_compat);
+ }
+
+ if (ret < 0) {
+ error_report("KVM: failed to get host CPU compat capabilities: %s",
+ strerror(-ret));
+ return 0;
+ }
+
+ return host_compat.compat_capabilities & KVM_PPC_COMPAT_BITMASK;
+}
+
+/*
+ * Return the effective host PVR based on the CPU compatibility mode
+ * reported by KVM. Returns 0 if no compat mode is active or the
+ * capability is not supported, in which case the caller falls back
+ * to the raw hardware PVR.
+ */
+uint32_t kvm_ppc_host_compat_pvr(void)
+{
+ uint32_t compat_host_pvr = 0;
+ uint64_t cap_idx = 0;
+ target_ulong host_caps = kvmppc_get_compat_caps();
+
+ if (host_caps) {
+ cap_idx = 1ULL << ctz64(host_caps);
+ switch (cap_idx) {
+ case KVM_PPC_COMPAT_CAP_POWER9:
+ compat_host_pvr = CPU_POWERPC_POWER9_DD22;
+ break;
+ case KVM_PPC_COMPAT_CAP_POWER10:
+ compat_host_pvr = CPU_POWERPC_POWER10_DD20;
+ break;
+ case KVM_PPC_COMPAT_CAP_POWER11:
+ 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..195dbaac5e17 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -81,6 +81,8 @@ 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);
+
+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 +442,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)
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v4 3/3] target/ppc/kvm: Use host compatibility mode for nested guests
2026-07-01 5:23 [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly for nested guests Amit Machhiwal
2026-07-01 5:23 ` [PATCH v4 1/3] [DO_NOT_MERGE] linux-headers: Add uapi header changes Amit Machhiwal
2026-07-01 5:23 ` [PATCH v4 2/3] target/ppc/kvm: Add support for querying host compatibility mode Amit Machhiwal
@ 2026-07-01 5:23 ` Amit Machhiwal
2026-08-04 7:30 ` [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly " Gautam Menghani
3 siblings, 0 replies; 5+ messages in thread
From: Amit Machhiwal @ 2026-07-01 5:23 UTC (permalink / raw)
To: qemu-ppc, Harsh Prateek Bora
Cc: Amit Machhiwal, Vaibhav Jain, Nicholas Piggin, Chinmay Rath,
Glenn Miles, Paolo Bonzini, kvm, qemu-devel
On POWER systems, the host CPU may run in a compatibility mode (e.g.,
a Power11 processor operating in Power10 compatibility mode). When
running nested KVM guests, QEMU currently derives the host CPU type
using mfpvr(), which reflects the physical processor version. This can
result in a mismatch between the CPU model used by QEMU and the
compatibility mode enforced by the host, leading to guest boot failures
such as "KVM-NESTEDv2: couldn't set guest wide elements".
Update kvm_ppc_get_host_cpu_class() to check if the host is running in
a compatibility mode using kvm_ppc_host_compat_pvr(). When available,
use the compatibility PVR instead of the raw hardware PVR when selecting
the CPU model. This ensures that QEMU selects a CPU model consistent
with the host compatibility mode, allowing nested guests to boot
correctly.
The guard uses #if defined(TARGET_PPC64) to prevent build breakage on
ppc32 targets where the POWER9/10/11 PVR constants are not defined.
Signed-off-by: Amit Machhiwal <amachhiw@linux.ibm.com>
---
Changes in v4:
- Updated commit log
- Added a #ifndef CONFIG_KVM compile-time assertion inside the
TARGET_PPC64 guard as a sanity check, since kvm_ppc_get_host_cpu_class()
is only ever built with CONFIG_KVM=y.
target/ppc/kvm.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index b1b596f004ef..105b838cdef9 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2682,6 +2682,18 @@ PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void)
uint32_t host_pvr = mfpvr();
PowerPCCPUClass *pvr_pcc;
+#if defined(TARGET_PPC64)
+#ifndef CONFIG_KVM
+#error "CONFIG_KVM is not enabled"
+#endif
+ uint32_t compat_host_pvr;
+
+ compat_host_pvr = kvm_ppc_host_compat_pvr();
+ if (compat_host_pvr) {
+ host_pvr = compat_host_pvr;
+ }
+#endif /* TARGET_PPC64 */
+
pvr_pcc = ppc_cpu_class_by_pvr(host_pvr);
if (pvr_pcc == NULL) {
pvr_pcc = ppc_cpu_class_by_pvr_mask(host_pvr);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly for nested guests
2026-07-01 5:23 [PATCH v4 0/3] ppc/kvm: Handle CPU compatibility mode correctly for nested guests Amit Machhiwal
` (2 preceding siblings ...)
2026-07-01 5:23 ` [PATCH v4 3/3] target/ppc/kvm: Use host compatibility mode for nested guests Amit Machhiwal
@ 2026-08-04 7:30 ` Gautam Menghani
3 siblings, 0 replies; 5+ messages in thread
From: Gautam Menghani @ 2026-08-04 7:30 UTC (permalink / raw)
To: Amit Machhiwal
Cc: qemu-ppc, Harsh Prateek Bora, Vaibhav Jain, Nicholas Piggin,
Chinmay Rath, Glenn Miles, Paolo Bonzini, kvm, qemu-devel
I tested this series with different compat mode settings for KVM on LPAR
and KVM on PowerNV.
Tested-by: Gautam Menghani <gautam@linux.ibm.com>
Reviewed-by: Gautam Menghani <gautam@linux.ibm.com>
Thanks,
Gautam
^ permalink raw reply [flat|nested] 5+ messages in thread