When kvm-intel.ko refuses to load due to a mismatched VMCS config, print
all mismatching offsets+values to make it easier to debug goofs during
development, and it to make it at least feasible to triage failures that
occur during production. E.g. if a physical core is flaky or is running
with the "wrong" microcode patch loaded, then a CPU can get a legitimate
mismatch even without KVM bugs.
Print the mismatches as 32-bit values as a compromise between hand coding
every field (to provide precise information) and printing individual bytes
(requires more effort to deduce the mismatch bit(s)). All fields in the
VMCS config are either 32-bit or 64-bit values, i.e. in many cases,
printing 32-bit values will be 100% precise, and in the others it's close
enough, especially when considering that MSR values are split into EDX:EAX
anyways.
E.g. on mismatch CET entry/exit controls, KVM will print:
kvm_intel: VMCS config on CPU 0 doesn't match reference config:
Offset 76 REF = 0x107fffff, CPU0 = 0x007fffff, mismatch = 0x10000000
Offset 84 REF = 0x0010f3ff, CPU0 = 0x0000f3ff, mismatch = 0x00100000
Opportunistically tweak the wording on the initial error message to say
"mismatch" instead of "inconsistent", as the VMCS config itself isn't
inconsistent, and the wording conflates the cross-CPU compatibility check
with the error_on_inconsistent_vmcs_config knob that treats inconsistent
VMCS configurations as errors (e.g. if a CPU supports CET entry controls
but no CET exit controls).
Cc: Jim Mattson <jmattson@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/vmx/vmx.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 7d373e32ea9c..700a8c47b4ca 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2962,8 +2962,22 @@ int vmx_check_processor_compat(void)
}
if (nested)
nested_vmx_setup_ctls_msrs(&vmcs_conf, vmx_cap.ept);
+
if (memcmp(&vmcs_config, &vmcs_conf, sizeof(struct vmcs_config))) {
- pr_err("Inconsistent VMCS config on CPU %d\n", cpu);
+ u32 *gold = (void *)&vmcs_config;
+ u32 *mine = (void *)&vmcs_conf;
+ int i;
+
+ BUILD_BUG_ON(sizeof(struct vmcs_config) % sizeof(u32));
+
+ pr_err("VMCS config on CPU %d doesn't match reference config:\n", cpu);
+ for (i = 0; i < sizeof(struct vmcs_config) / sizeof(u32); i++) {
+ if (gold[i] == mine[i])
+ continue;
+
+ pr_cont(" Offset %lu REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
+ i * sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
+ }
return -EIO;
}
return 0;
--
2.52.0.457.g6b5491de43-goog
On Fri, Jan 23, 2026, Sean Christopherson wrote:
> + pr_cont(" Offset %lu REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
> + i * sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
As pointed out by the kernel bot, sizeof() isn't an unsigned long on 32-bit.
Simplest fix is to force it to an int.
pr_cont(" Offset %u REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
i * (int)sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
On Mon, Jan 26, 2026 at 06:57:26AM -0800, Sean Christopherson wrote:
>On Fri, Jan 23, 2026, Sean Christopherson wrote:
>> + pr_cont(" Offset %lu REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
>> + i * sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
>
>As pointed out by the kernel bot, sizeof() isn't an unsigned long on 32-bit.
>Simplest fix is to force it to an int.
>
> pr_cont(" Offset %u REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
> i * (int)sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
Why pr_cont()? The previous line ends with '\n'. so, a plain pr_err() should work.
Anyway, the code looks good.
Reviewed-by: Chao Gao <chao.gao@intel.com>
On Tue, Jan 27, 2026, Chao Gao wrote:
> On Mon, Jan 26, 2026 at 06:57:26AM -0800, Sean Christopherson wrote:
> >On Fri, Jan 23, 2026, Sean Christopherson wrote:
> >> + pr_cont(" Offset %lu REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
> >> + i * sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
> >
> >As pointed out by the kernel bot, sizeof() isn't an unsigned long on 32-bit.
> >Simplest fix is to force it to an int.
> >
> > pr_cont(" Offset %u REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x\n",
> > i * (int)sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
>
> Why pr_cont()? The previous line ends with '\n'. so, a plain pr_err() should work.
To avoid the "kvm_intel:" formatting. E.g. with pr_cont():
[ 5.355958] kvm_intel: VMCS config on CPU 0 doesn't match reference config:
[ 5.355986] Offset 76 REF = 0x107fffff, CPU0 = 0x007fffff, mismatch = 0x10000000
[ 5.356019] Offset 84 REF = 0x0010f3ff, CPU0 = 0x0000f3ff, mismatch = 0x00100000
[ 5.356048] kvm: enabling virtualization on CPU0 failed
versus with pr_err():
[ 6.527945] kvm_intel: VMCS config on CPU 0 doesn't match reference config:
[ 6.527979] kvm_intel: Offset 76 REF = 0x107fffff, CPU0 = 0x007fffff, mismatch = 0x10000000
[ 6.528013] kvm_intel: Offset 84 REF = 0x0010f3ff, CPU0 = 0x0000f3ff, mismatch = 0x00100000
[ 6.528048] kvm: enabling virtualization on CPU0 failed
Ugh, but my use of pr_cont() isn't right, because the '\n' resets to KERN_DEFAULT,
i.e. not captured in the above is that the continuations are printed at "warn",
not "err" as intended.
Ah, and fixing that by shoving the newline into pr_cont():
pr_err("VMCS config on CPU %d doesn't match reference config:", cpu);
for (i = 0; i < sizeof(struct vmcs_config) / sizeof(u32); i++) {
if (gold[i] == mine[i])
continue;
pr_cont("\n Offset %u REF = 0x%08x, CPU%u = 0x%08x, mismatch = 0x%08x",
i * (int)sizeof(u32), gold[i], cpu, mine[i], gold[i] ^ mine[i]);
}
pr_cont("\n");
avoids generating new timestamps too, which is even more desirable.
[ 5.239320] kvm_intel: VMCS config on CPU 0 doesn't match reference config:
Offset 76 REF = 0x107fffff, CPU0 = 0x007fffff, mismatch = 0x10000000
Offset 84 REF = 0x0010f3ff, CPU0 = 0x0000f3ff, mismatch = 0x00100000
[ 5.239397] kvm: enabling virtualization on CPU0 failed
Unless someone strongly prefers re-printing the timestamp+kvm-intel, I'll go with
the above approach for v2.
Thanks for the reviews!
© 2016 - 2026 Red Hat, Inc.