* [PATCH v6 00/20] KVM: x86: Enable APX for guests
@ 2026-07-29 19:16 Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
` (19 more replies)
0 siblings, 20 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Hi Paolo,
On 7/21/2026 12:09 AM, Paolo Bonzini wrote:
> please give a read to its comments in case it found something important.
Here is a small follow-up revision addressing Sashiko's comments:
* Patch02: Fix "register saving" wording in the changelog.
* Patch17: Add the APX leaf to cpuid_function_is_indexed().
Rerunning Sashiko locally, it still appears to be somewhat confused by
the potential in-kernel usages and a few ordering of changes, so it may
report similar issues again on this posting.
I assume the V5 changes are still under review, so attaching the previous
cover letter and leaving the V5 notes in each patch.
---
One topic worth highlighting first is about how to handle userspace
writes to the APX state through the KVM XSAVE API, following the
discussion here:
https://lore.kernel.org/CABgObfaocwfLdUBKJYYYEDH8X3n6iEbMsscbaCe_C=8JNnhDBA@mail.gmail.com
So what this revision ended up with is just to keep those copy paths from
userspace and acknowledge the redundant copy to VCPU cache / guest
fpstate. Patch3 has more context on that.
Besides that, this revision has several fixes and improvements. Sean
gave feedbacks. 0-day spotted on an issue. Also, since the preparatory
patches have been merged into the mainline, finally a review bot could
provide some feedbacks as well. The major changes are:
1. VMX Instruction Information Extension
During the time of V4, 0-day reported out an issue with patch7.
Fixing that also revealed patch4 was missing another function.
With a review bot's feeback, patch7 was also updated to ensure EGPR
indices for handling debug-register accesses. Also looking at
associated macros, patch6 was added for further cleanup.
2. Emulator Register Tracking
The review bot at hand also found a potential shift-out-bounds issue
in the register tracking. Patch10 was fixed.
3. APX Feature Exposure
As per Sean's feedback, the APX XCR0 and userspace exposure were
reworked a bit in patch14-18.
Thanks,
Chang
Reference:
* Repo: git://github.com/intel/apx.git apx-kvm_v6
* V5: https://lore.kernel.org/20260512011502.53072-1-chang.seok.bae@intel.com
* KVM-unit-test patch: https://lore.kernel.org/20260420212355.507827-1-chang.seok.bae@intel.com
Chang S. Bae (19):
KVM: x86: Extend VCPU registers for EGPRs
KVM: VMX: Save guest EGPRs in VCPU cache
KVM: x86: Support APX state for XSAVE ABI
KVM: VMX: Refactor VMX instruction information access
KVM: VMX: Refactor instruction information decoding
KVM: VMX: Remove unused control-register access defines
KVM: VMX: Refactor register index retrieval from exit qualification
KVM: VMX: Support instruction information extension
KVM: nVMX: Propagate extended instruction information
KVM: x86: Support EGPR accessing and tracking for emulator
KVM: x86: Handle EGPR index and REX2-incompatible opcodes
KVM: x86: Support REX2-prefixed opcode decode
KVM: x86: Reject EVEX-prefixed instructions
KVM: x86: Guard valid XCR0.APX settings
KVM: x86: Add APX to supported XCR0
KVM: x86: Expose APX foundation feature to userspace
KVM: x86: Expose APX sub-features to userspace
KVM: x86: selftests: Add APX state and ABI test
KVM: x86: selftests: Add APX state handling and XCR0 sanity checks
Sean Christopherson (1):
KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init()
arch/x86/Kconfig.assembler | 5 +
arch/x86/include/asm/cpuid/api.h | 1 +
arch/x86/include/asm/kvm_host.h | 19 ++
arch/x86/include/asm/kvm_vcpu_regs.h | 50 +++++
arch/x86/include/asm/vmx.h | 22 +-
arch/x86/kvm/Kconfig | 4 +
arch/x86/kvm/cpuid.c | 27 ++-
arch/x86/kvm/cpuid.h | 2 +
arch/x86/kvm/emulate.c | 121 +++++++----
arch/x86/kvm/kvm_emulate.h | 21 +-
arch/x86/kvm/reverse_cpuid.h | 6 +
arch/x86/kvm/svm/svm.c | 8 +-
arch/x86/kvm/vmenter.h | 1 +
arch/x86/kvm/vmx/nested.c | 75 +++----
arch/x86/kvm/vmx/nested.h | 2 +-
arch/x86/kvm/vmx/vmcs12.c | 1 +
arch/x86/kvm/vmx/vmcs12.h | 3 +-
arch/x86/kvm/vmx/vmenter.S | 31 ++-
arch/x86/kvm/vmx/vmx.c | 28 ++-
arch/x86/kvm/vmx/vmx.h | 77 ++++++-
arch/x86/kvm/x86.c | 92 +++++++--
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/processor.h | 120 +++++++++++
tools/testing/selftests/kvm/x86/apx_test.c | 191 ++++++++++++++++++
tools/testing/selftests/kvm/x86/state_test.c | 3 +
.../selftests/kvm/x86/xcr0_cpuid_test.c | 19 ++
26 files changed, 802 insertions(+), 128 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86/apx_test.c
base-commit: a204badd8432f93b7e862e7dac6db0fe3d65f370
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:04 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
` (18 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Extend the storage to include extended general-purpose registers (EGPRs:
R16-R31) when CONFIG_KVM_APX=y. This option is for VMX only.
Although EGPR state is XSAVE-managed, and thus could be stored in the
guest fpstate, VCPU storage provides a more convenient and efficient
access path for KVM.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/abL8SW5JS1aV5goa@google.com
---
arch/x86/include/asm/kvm_host.h | 18 ++++++++++++++++++
arch/x86/kvm/Kconfig | 4 ++++
2 files changed, 22 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index b517257a6315..48547842d9fa 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -178,6 +178,24 @@ enum kvm_reg {
VCPU_REGS_R13,
VCPU_REGS_R14,
VCPU_REGS_R15,
+#endif
+#ifdef CONFIG_KVM_APX
+ VCPU_REGS_R16,
+ VCPU_REGS_R17,
+ VCPU_REGS_R18,
+ VCPU_REGS_R19,
+ VCPU_REGS_R20,
+ VCPU_REGS_R21,
+ VCPU_REGS_R22,
+ VCPU_REGS_R23,
+ VCPU_REGS_R24,
+ VCPU_REGS_R25,
+ VCPU_REGS_R26,
+ VCPU_REGS_R27,
+ VCPU_REGS_R28,
+ VCPU_REGS_R29,
+ VCPU_REGS_R30,
+ VCPU_REGS_R31,
#endif
NR_VCPU_GENERAL_PURPOSE_REGS,
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 801bf9e520db..f27e3f2937f0 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -93,10 +93,14 @@ config KVM_SW_PROTECTED_VM
If unsure, say "N".
+config KVM_APX
+ bool
+
config KVM_INTEL
tristate "KVM for Intel (and compatible) processors support"
depends on KVM && IA32_FEAT_CTL
select X86_FRED if X86_64
+ select KVM_APX if X86_64
help
Provides support for KVM on processors equipped with Intel's VT
extensions, a.k.a. Virtual Machine Extensions (VMX).
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:05 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
` (17 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Save and restore the guest EGPRs on VM exit/entry if the system supports
Advanced Performance Extensions (APX).
There are two possible ways of handling EGPR accesses: reading them from
the processor lazily, or switching them in the vmentry/vmexit path and
accessing them via vcpu->arch.regs. Prepare for the latter; it does not
pollute kvm_reg_read/write() with conditionals to treat GPRs vs. EGPRs
differently, and it allows for future usage of APX registers in the
kernel.
KVM intercepts all XCR0 writes, therefore XCR0 cannot change during the
execution of VMLAUNCH/VMRESUME. Thus, for a guest that does not set
XCR0[APX], the EGPR state does not have to be loaded/saved.
The register saving path is placed after SPEC_CTRL is restored, so as
to avoid any mis-speculation into EGPR accesses when APX is disabled.
While saving may be skipped on VM-Fail, it is not worth the added
complication for a slow path.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/adPRA4ZhnvbaXSn0@google.com
---
V5 -> V6:
* `register restore` => `register saving` (Sashiko)
V4 -> V5:
* Massage the chagnelog (Paolo [*])
* Fix typo; use KVM_ENTER_EGPR_SWITCH consistently
[*]: Paolo's rewording appeared when it was once picked into his queue branch
---
arch/x86/Kconfig.assembler | 5 +++
arch/x86/include/asm/kvm_vcpu_regs.h | 50 ++++++++++++++++++++++++++++
arch/x86/kvm/Kconfig | 2 +-
arch/x86/kvm/vmenter.h | 1 +
arch/x86/kvm/vmx/vmenter.S | 31 +++++++++++++++--
arch/x86/kvm/vmx/vmx.c | 13 ++++++++
6 files changed, 99 insertions(+), 3 deletions(-)
diff --git a/arch/x86/Kconfig.assembler b/arch/x86/Kconfig.assembler
index b1c59fb0a4c9..1a86efb2a239 100644
--- a/arch/x86/Kconfig.assembler
+++ b/arch/x86/Kconfig.assembler
@@ -1,6 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+config AS_APX
+ def_bool $(as-instr64,xor %r16$(comma)%r16)
+ help
+ Supported by binutils >= 2.42 and LLVM integrated assembler >= 18
+
config AS_WRUSS
def_bool $(as-instr64,wrussq %rax$(comma)(%rbx))
help
diff --git a/arch/x86/include/asm/kvm_vcpu_regs.h b/arch/x86/include/asm/kvm_vcpu_regs.h
index d9f1528fab2b..d6658c9909aa 100644
--- a/arch/x86/include/asm/kvm_vcpu_regs.h
+++ b/arch/x86/include/asm/kvm_vcpu_regs.h
@@ -99,6 +99,56 @@
.ifc \r64,%r15
\opd = 15
.endif
+#endif
+#ifdef CONFIG_KVM_APX
+ .ifc \r64,%r16
+ \opd = 16
+ .endif
+ .ifc \r64,%r17
+ \opd = 17
+ .endif
+ .ifc \r64,%r18
+ \opd = 18
+ .endif
+ .ifc \r64,%r19
+ \opd = 19
+ .endif
+ .ifc \r64,%r20
+ \opd = 20
+ .endif
+ .ifc \r64,%r21
+ \opd = 21
+ .endif
+ .ifc \r64,%r22
+ \opd = 22
+ .endif
+ .ifc \r64,%r23
+ \opd = 23
+ .endif
+ .ifc \r64,%r24
+ \opd = 24
+ .endif
+ .ifc \r64,%r25
+ \opd = 25
+ .endif
+ .ifc \r64,%r26
+ \opd = 26
+ .endif
+ .ifc \r64,%r27
+ \opd = 27
+ .endif
+ .ifc \r64,%r28
+ \opd = 28
+ .endif
+ .ifc \r64,%r29
+ \opd = 29
+ .endif
+ .ifc \r64,%r30
+ \opd = 30
+ .endif
+ .ifc \r64,%r31
+ \opd = 31
+ .endif
#endif
.endm
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index f27e3f2937f0..eb71cd7e5b37 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -100,7 +100,7 @@ config KVM_INTEL
tristate "KVM for Intel (and compatible) processors support"
depends on KVM && IA32_FEAT_CTL
select X86_FRED if X86_64
- select KVM_APX if X86_64
+ select KVM_APX if X86_64 && AS_APX
help
Provides support for KVM on processors equipped with Intel's VT
extensions, a.k.a. Virtual Machine Extensions (VMX).
diff --git a/arch/x86/kvm/vmenter.h b/arch/x86/kvm/vmenter.h
index 77376812c81b..690c05696824 100644
--- a/arch/x86/kvm/vmenter.h
+++ b/arch/x86/kvm/vmenter.h
@@ -7,6 +7,7 @@
#define KVM_ENTER_VMRESUME BIT(0)
#define KVM_ENTER_SAVE_SPEC_CTRL BIT(1)
#define KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(2)
+#define KVM_ENTER_EGPR_SWITCH BIT(3)
#ifdef __ASSEMBLER__
.macro RESTORE_GUEST_SPEC_CTRL_BODY guest_spec_ctrl:req, label:req
diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S
index 00d807505fc8..702d86ade6b1 100644
--- a/arch/x86/kvm/vmx/vmenter.S
+++ b/arch/x86/kvm/vmx/vmenter.S
@@ -16,6 +16,7 @@
* @flags: KVM_ENTER_VMRESUME: use VMRESUME instead of VMLAUNCH
* KVM_ENTER_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl
* KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO: vCPU can access host MMIO
+ * KVM_ENTER_EGPR_SWITCH: load/store guest EGPRs
*
* Returns:
* 0 on VM-Exit, 1 on VM-Fail
@@ -46,6 +47,16 @@ SYM_FUNC_START(__vmx_vcpu_run)
/* Reload @vmx, _ASM_ARG1 may be modified by vmx_update_host_rsp(). */
mov WORD_SIZE(%_ASM_SP), %_ASM_DI
+#ifdef CONFIG_KVM_APX
+ ALTERNATIVE "jmp .Lload_egprs_done", "", X86_FEATURE_APX
+ testl $KVM_ENTER_EGPR_SWITCH, (%_ASM_SP)
+ jz .Lload_egprs_done
+ LOAD_REGS %_ASM_DI, VMX_vcpu_arch_regs, \
+ %r16, %r17, %r18, %r19, %r20, %r21, %r22, %r23, \
+ %r24, %r25, %r26, %r27, %r28, %r29, %r30, %r31
+.Lload_egprs_done:
+#endif
+
/*
* Unlike AMD there's no V_SPEC_CTRL here, so do not leave the body
* out of line. Clobbers RAX, RCX, RDX, RSI.
@@ -193,8 +204,24 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL)
mov %_ASM_BX, %_ASM_AX
/* Pop our saved arguments from the stack */
- pop %_ASM_BX
- pop %_ASM_BX
+ pop %_ASM_BX /* @flags */
+ pop %_ASM_DI /* @vmx */
+
+#ifdef CONFIG_KVM_APX
+ ALTERNATIVE "jmp .Lclear_egprs_done", "", X86_FEATURE_APX
+ test $KVM_ENTER_EGPR_SWITCH, %_ASM_BX
+ jz .Lclear_egprs_done
+ /*
+ * Unlike legacy GPRs, saving could be conditional here on VM-Fail,
+ * which however isn't in fastpath. Instead, simply saving EGPRs always.
+ */
+ STORE_REGS %_ASM_DI, VMX_vcpu_arch_regs, \
+ %r16, %r17, %r18, %r19, %r20, %r21, %r22, %r23, \
+ %r24, %r25, %r26, %r27, %r28, %r29, %r30, %r31
+ CLEAR_REGS %r16d, %r17d, %r18d, %r19d, %r20d, %r21d, %r22d, %r23d, \
+ %r24d, %r25d, %r26d, %r27d, %r28d, %r29d, %r30d, %r31d
+.Lclear_egprs_done:
+#endif
/* ... and then the callee-save registers */
pop %_ASM_BX
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 3681d565f177..1c86919d14d4 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -991,6 +991,19 @@ unsigned int __vmx_vcpu_enter_flags(struct vcpu_vmx *vmx)
kvm_vcpu_can_access_host_mmio(&vmx->vcpu))
flags |= KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO;
+ /*
+ * KVM intercepts XSETBV and thus always tracks the guest XCR0. EGPR
+ * save/restore is gated by this flag. The resulting behavior is:
+ *
+ * - When the guest enables APX, KVM restores EGPRs (initially zeroed).
+ * - When the guest disables APX, EGPRs are preserved in the VCPU cache.
+ * - When APX is re-enabled, the saved state is restored, which matches
+ * architectural expectations.
+ */
+ if (IS_ENABLED(CONFIG_KVM_APX) && cpu_feature_enabled(X86_FEATURE_APX) &&
+ vmx->vcpu.arch.xcr0 & XFEATURE_MASK_APX)
+ flags |= KVM_ENTER_EGPR_SWITCH;
+
return flags;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:31 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
` (16 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Introduce a facility to copy APX state between the VCPU cache and the
userspace buffer as the VCPU cache is the single source of truth.
The existing fpstate copy functions historically sync all XSTATEs in
between userspace and kernel buffers [1]. In this regard, any additional
state handling logic should be consistent with them -- i.e. validation of
XSTATE_BV against the supported XCR0 mask.
Now with the two copy paths, their invocations require to take care of
these facts:
* When exporting to userspace, the fpstate function should run first
since it zeros out the area of components either not present or
inactive. Then the VCPU cache function ensures copying APX state.
* On the opposite way, both will copy the state to both storages. This
duplication is avoidable by tweaking either the generic function or
XSTATE_BV/XCR0. But the optimization while at a slow path looks to
rather add complication. So keep the implementation simple.
[1] Except for PKRU state, as stored in struct thread_struct.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Copy to both storages
The optimization bit had been reconsidered a little. V4 was attempting to
introduce the one-off exception into the generic copy function. Before
that, V3 had the tweak at the KVM site under the APX copy function.
But unless either approach could reach some agreeable shape, the tweak
appears to remain mere optimization. Here the third option would be
simply absorbing the duplication, which isn't significant in this slow
path in practice. Making simple looks to be a overall win, otherwise.
---
arch/x86/kvm/cpuid.c | 10 +++++++
arch/x86/kvm/cpuid.h | 2 ++
arch/x86/kvm/vmx/vmenter.S | 2 +-
arch/x86/kvm/x86.c | 61 ++++++++++++++++++++++++++++++++++++++
4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 2698fa42cd97..122e46db9a15 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -60,6 +60,16 @@ void __init kvm_init_xstate_sizes(void)
}
}
+u32 xstate_size(unsigned int xfeature)
+{
+ return xstate_sizes[xfeature].eax;
+}
+
+u32 xstate_offset(unsigned int xfeature)
+{
+ return xstate_sizes[xfeature].ebx;
+}
+
u32 xstate_required_size(u64 xstate_bv, bool compacted)
{
u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index 8d863f45585d..1716f3b6b885 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -66,6 +66,8 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
void __init kvm_init_xstate_sizes(void);
u32 xstate_required_size(u64 xstate_bv, bool compacted);
+u32 xstate_size(unsigned int xfeature);
+u32 xstate_offset(unsigned int xfeature);
int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu);
int cpuid_query_maxguestphyaddr(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S
index 702d86ade6b1..02308fb36f0f 100644
--- a/arch/x86/kvm/vmx/vmenter.S
+++ b/arch/x86/kvm/vmx/vmenter.S
@@ -213,7 +213,7 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL)
jz .Lclear_egprs_done
/*
* Unlike legacy GPRs, saving could be conditional here on VM-Fail,
- * which however isn't in fastpath. Instead, simply saving EGPRs always.
+ * which however isn't in fastpath. Instead, always save EGPRs.
*/
STORE_REGS %_ASM_DI, VMX_vcpu_arch_regs, \
%r16, %r17, %r18, %r19, %r20, %r21, %r22, %r23, \
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0626e835e9eb..5578df10179a 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3124,6 +3124,49 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
return 0;
}
+#ifdef CONFIG_KVM_APX
+static void kvm_copy_vcpu_regs_to_uabi(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0)
+{
+ union fpregs_state *xstate = (union fpregs_state *)buf;
+
+ BUILD_BUG_ON(NR_VCPU_GENERAL_PURPOSE_REGS <= VCPU_REGS_R31);
+
+ if (!(supported_xcr0 & XFEATURE_MASK_APX))
+ return;
+
+ memcpy(buf + xstate_offset(XFEATURE_APX),
+ &vcpu->arch.regs[VCPU_REGS_R16],
+ xstate_size(XFEATURE_APX));
+
+ xstate->xsave.header.xfeatures |= XFEATURE_MASK_APX;
+}
+
+static int kvm_copy_uabi_to_vcpu_regs(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0)
+{
+ union fpregs_state *xstate = (union fpregs_state *)buf;
+
+ if (!(xstate->xsave.header.xfeatures & XFEATURE_MASK_APX))
+ return 0;
+
+ if (!(supported_xcr0 & XFEATURE_MASK_APX))
+ return -EINVAL;
+
+ BUILD_BUG_ON(NR_VCPU_GENERAL_PURPOSE_REGS <= VCPU_REGS_R31);
+
+ memcpy(&vcpu->arch.regs[VCPU_REGS_R16],
+ buf + xstate_offset(XFEATURE_APX),
+ xstate_size(XFEATURE_APX));
+
+ return 0;
+}
+#else
+static void kvm_copy_vcpu_regs_to_uabi(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0) { }
+static int kvm_copy_uabi_to_vcpu_regs(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0)
+{
+ return 0;
+}
+#endif
+
static int kvm_vcpu_ioctl_x86_get_xsave2(struct kvm_vcpu *vcpu,
u8 *state, unsigned int size)
{
@@ -3145,8 +3188,15 @@ static int kvm_vcpu_ioctl_x86_get_xsave2(struct kvm_vcpu *vcpu,
if (fpstate_is_confidential(&vcpu->arch.guest_fpu))
return vcpu->kvm->arch.has_protected_state ? -EINVAL : 0;
+ /*
+ * This copy function zeros out userspace memory for any gap from the
+ * guest fpstate. So invoke before copying any other state, i.e. APX,
+ * that is not saved in fpstate.
+ */
fpu_copy_guest_fpstate_to_uabi(&vcpu->arch.guest_fpu, state, size,
supported_xcr0, vcpu->arch.pkru);
+ kvm_copy_vcpu_regs_to_uabi(vcpu, state, supported_xcr0);
+
return 0;
}
@@ -3161,6 +3211,7 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
struct kvm_xsave *guest_xsave)
{
union fpregs_state *xstate = (union fpregs_state *)guest_xsave->region;
+ int err;
if (fpstate_is_confidential(&vcpu->arch.guest_fpu))
return vcpu->kvm->arch.has_protected_state ? -EINVAL : 0;
@@ -3172,6 +3223,16 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
*/
xstate->xsave.header.xfeatures &= ~vcpu->arch.guest_fpu.fpstate->xfd;
+ /*
+ * This and the following copy functions copy APX state into each
+ * storage. The VCPU cache is the single source of truth but avoiding
+ * this redundant copy tends to introduce special-case handling. Since
+ * this lies in a slow path, keep the implementation simple.
+ */
+ err = kvm_copy_uabi_to_vcpu_regs(vcpu, guest_xsave->region, kvm_caps.supported_xcr0);
+ if (err)
+ return err;
+
return fpu_copy_uabi_to_guest_fpstate(&vcpu->arch.guest_fpu,
guest_xsave->region,
kvm_caps.supported_xcr0,
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (2 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:02 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
` (15 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Introduce a helper that returns the instruction information as a 64-bit
value and adjust existing sites to prepare for a wider field.
The VMX instruction information field is currently 32 bits. Future
extensions may expand this field to support extended register IDs,
requiring a wider width. The change provides a single access point for
the transition.
No functional change intended.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Convert vmx_get_msr_imm_reg() and keep `u32 type` in handle_invept()
---
arch/x86/kvm/vmx/nested.c | 23 ++++++++++++-----------
arch/x86/kvm/vmx/nested.h | 2 +-
arch/x86/kvm/vmx/vmx.c | 6 +++---
arch/x86/kvm/vmx/vmx.h | 10 ++++++++--
4 files changed, 24 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 0635e92471c8..92b530baa0ee 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5234,7 +5234,7 @@ static void nested_vmx_triple_fault(struct kvm_vcpu *vcpu)
* #UD, #GP, or #SS.
*/
int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
- u32 vmx_instruction_info, bool wr, int len, gva_t *ret)
+ u64 vmx_instruction_info, bool wr, int len, gva_t *ret)
{
gva_t off;
bool exn;
@@ -5366,7 +5366,7 @@ static int nested_vmx_get_vmptr(struct kvm_vcpu *vcpu, gpa_t *vmpointer,
int r;
if (get_vmx_mem_address(vcpu, vmx_get_exit_qual(vcpu),
- vmcs_read32(VMX_INSTRUCTION_INFO), false,
+ vmx_get_instr_info(), false,
sizeof(*vmpointer), &gva)) {
*ret = 1;
return -EINVAL;
@@ -5651,7 +5651,7 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
struct vmcs12 *vmcs12 = is_guest_mode(vcpu) ? get_shadow_vmcs12(vcpu)
: get_vmcs12(vcpu);
unsigned long exit_qualification = vmx_get_exit_qual(vcpu);
- u32 instr_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ u64 instr_info = vmx_get_instr_info();
struct vcpu_vmx *vmx = to_vmx(vcpu);
struct x86_exception e;
unsigned long field;
@@ -5757,7 +5757,7 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
struct vmcs12 *vmcs12 = is_guest_mode(vcpu) ? get_shadow_vmcs12(vcpu)
: get_vmcs12(vcpu);
unsigned long exit_qualification = vmx_get_exit_qual(vcpu);
- u32 instr_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ u64 instr_info = vmx_get_instr_info();
struct vcpu_vmx *vmx = to_vmx(vcpu);
struct x86_exception e;
unsigned long field;
@@ -5946,7 +5946,7 @@ static int handle_vmptrld(struct kvm_vcpu *vcpu)
static int handle_vmptrst(struct kvm_vcpu *vcpu)
{
unsigned long exit_qual = vmx_get_exit_qual(vcpu);
- u32 instr_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ u64 instr_info = vmx_get_instr_info();
gpa_t current_vmptr = to_vmx(vcpu)->nested.current_vmptr;
struct x86_exception e;
gva_t gva;
@@ -5974,8 +5974,8 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu)
static int handle_invept(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- u32 vmx_instruction_info, types;
unsigned long type, roots_to_free;
+ u64 vmx_instruction_info;
struct kvm_mmu *mmu;
gva_t gva;
struct x86_exception e;
@@ -5983,6 +5983,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
u64 eptp, gpa;
} operand;
int i, r, gpr_index;
+ u32 types;
if (!(vmx->nested.msrs.secondary_ctls_high &
SECONDARY_EXEC_ENABLE_EPT) ||
@@ -5994,7 +5995,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
if (!nested_vmx_check_permission(vcpu))
return 1;
- vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ vmx_instruction_info = vmx_get_instr_info();
gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
type = kvm_register_read(vcpu, gpr_index);
@@ -6054,7 +6055,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
static int handle_invvpid(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- u32 vmx_instruction_info;
+ u64 vmx_instruction_info;
unsigned long type, types;
gva_t gva;
struct x86_exception e;
@@ -6075,7 +6076,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
if (!nested_vmx_check_permission(vcpu))
return 1;
- vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ vmx_instruction_info = vmx_get_instr_info();
gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
type = kvm_register_read(vcpu, gpr_index);
@@ -6428,7 +6429,7 @@ static bool nested_vmx_exit_handled_encls(struct kvm_vcpu *vcpu,
static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
struct vmcs12 *vmcs12, gpa_t bitmap)
{
- u32 vmx_instruction_info;
+ u64 vmx_instruction_info;
unsigned long field;
u8 b;
@@ -6436,7 +6437,7 @@ static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
return true;
/* Decode instruction info and find the field to access */
- vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ vmx_instruction_info = vmx_get_instr_info();
field = kvm_register_read(vcpu, (((vmx_instruction_info) >> 28) & 0xf));
/* Out-of-range fields always cause a VM exit from L2 to L1 */
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index 6d6cd5904ddf..fb4f1757cb40 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -51,7 +51,7 @@ void nested_sync_vmcs12_to_shadow(struct kvm_vcpu *vcpu);
int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data);
int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata);
int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
- u32 vmx_instruction_info, bool wr, int len, gva_t *ret);
+ u64 vmx_instruction_info, bool wr, int len, gva_t *ret);
bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port,
int size);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 1c86919d14d4..8a4b578252c2 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6158,7 +6158,7 @@ static int handle_monitor_trap(struct kvm_vcpu *vcpu)
static int handle_invpcid(struct kvm_vcpu *vcpu)
{
- u32 vmx_instruction_info;
+ u64 vmx_instruction_info;
unsigned long type;
gva_t gva;
struct {
@@ -6172,7 +6172,7 @@ static int handle_invpcid(struct kvm_vcpu *vcpu)
return 1;
}
- vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ vmx_instruction_info = vmx_get_instr_info();
gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
type = kvm_register_read(vcpu, gpr_index);
@@ -6323,7 +6323,7 @@ static int handle_notify(struct kvm_vcpu *vcpu)
static int vmx_get_msr_imm_reg(struct kvm_vcpu *vcpu)
{
- return vmx_get_instr_info_reg(vmcs_read32(VMX_INSTRUCTION_INFO));
+ return vmx_get_instr_info_reg(vmx_get_instr_info());
}
static int handle_rdmsr_imm(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index de9de0d2016c..a3f58a3d90e5 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -702,12 +702,18 @@ static inline bool vmx_guest_state_valid(struct kvm_vcpu *vcpu)
void dump_vmcs(struct kvm_vcpu *vcpu);
-static inline int vmx_get_instr_info_reg(u32 vmx_instr_info)
+/* A placeholder to smoothen 64-bit extension */
+static inline u64 vmx_get_instr_info(void)
+{
+ return vmcs_read32(VMX_INSTRUCTION_INFO);
+}
+
+static inline int vmx_get_instr_info_reg(u64 vmx_instr_info)
{
return (vmx_instr_info >> 3) & 0xf;
}
-static inline int vmx_get_instr_info_reg2(u32 vmx_instr_info)
+static inline int vmx_get_instr_info_reg2(u64 vmx_instr_info)
{
return (vmx_instr_info >> 28) & 0xf;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 05/20] KVM: VMX: Refactor instruction information decoding
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (3 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
` (14 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc
Cc: kvm, x86, linux-kernel, chang.seok.bae, kernel test robot
KVM currently decodes the VMX instruction information field using a mix
of open-coded bit manipulations and ad hoc helpers. Convert all decoding
to use helpers to centralize the decoding logic for the transition to a
wider instruction information.
No functional change intended.
Tested-by: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V5 -> V6: Add Tested-by (0day)
V4 -> V5: Fix handle_vmread() first accesses reg2 (0-day).
---
arch/x86/kvm/vmx/nested.c | 58 +++++++++++++++++++--------------------
arch/x86/kvm/vmx/vmx.c | 9 +++---
arch/x86/kvm/vmx/vmx.h | 48 +++++++++++++++++++++++++++++---
3 files changed, 77 insertions(+), 38 deletions(-)
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 92b530baa0ee..fdec041e95c7 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5234,7 +5234,7 @@ static void nested_vmx_triple_fault(struct kvm_vcpu *vcpu)
* #UD, #GP, or #SS.
*/
int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
- u64 vmx_instruction_info, bool wr, int len, gva_t *ret)
+ u64 instr_info, bool wr, int len, gva_t *ret)
{
gva_t off;
bool exn;
@@ -5242,20 +5242,20 @@ int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
/*
* According to Vol. 3B, "Information for VM Exits Due to Instruction
- * Execution", on an exit, vmx_instruction_info holds most of the
- * addressing components of the operand. Only the displacement part
- * is put in exit_qualification (see 3B, "Basic VM-Exit Information").
+ * Execution", on an exit, instr_info holds most of the addressing
+ * components of the operand. Only the displacement part is put in
+ * exit_qualification (see 3B, "Basic VM-Exit Information").
* For how an actual address is calculated from all these components,
* refer to Vol. 1, "Operand Addressing".
*/
- int scaling = vmx_instruction_info & 3;
- int addr_size = (vmx_instruction_info >> 7) & 7;
- bool is_reg = vmx_instruction_info & (1u << 10);
- int seg_reg = (vmx_instruction_info >> 15) & 7;
- int index_reg = (vmx_instruction_info >> 18) & 0xf;
- bool index_is_valid = !(vmx_instruction_info & (1u << 22));
- int base_reg = (vmx_instruction_info >> 23) & 0xf;
- bool base_is_valid = !(vmx_instruction_info & (1u << 27));
+ int scaling = vmx_get_instr_info_scaling(instr_info);
+ int addr_size = vmx_get_instr_info_addr_size(instr_info);
+ bool is_reg = vmx_get_instr_info_is_reg(instr_info);
+ int seg_reg = vmx_get_instr_info_seg_reg(instr_info);
+ int index_reg = vmx_get_instr_info_index_reg(instr_info);
+ bool index_is_valid = vmx_get_instr_info_index_is_valid(instr_info);
+ int base_reg = vmx_get_instr_info_base_reg(instr_info);
+ bool base_is_valid = vmx_get_instr_info_base_is_valid(instr_info);
if (is_reg) {
kvm_queue_exception(vcpu, UD_VECTOR);
@@ -5664,7 +5664,7 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
return 1;
/* Decode instruction info and find the field to read */
- field = kvm_register_read(vcpu, (((instr_info) >> 28) & 0xf));
+ field = kvm_register_read(vcpu, vmx_get_instr_info_reg2(instr_info));
if (!nested_vmx_is_evmptr12_valid(vmx)) {
/*
@@ -5712,8 +5712,8 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
* Note that the number of bits actually copied is 32 or 64 depending
* on the guest's mode (32 or 64 bit), not on the given field's length.
*/
- if (instr_info & BIT(10)) {
- kvm_register_write(vcpu, (((instr_info) >> 3) & 0xf), value);
+ if (vmx_get_instr_info_is_reg(instr_info)) {
+ kvm_register_write(vcpu, vmx_get_instr_info_reg(instr_info), value);
} else {
len = is_64_bit_mode(vcpu) ? 8 : 4;
if (get_vmx_mem_address(vcpu, exit_qualification,
@@ -5786,8 +5786,8 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
get_vmcs12(vcpu)->vmcs_link_pointer == INVALID_GPA))
return nested_vmx_failInvalid(vcpu);
- if (instr_info & BIT(10))
- value = kvm_register_read(vcpu, (((instr_info) >> 3) & 0xf));
+ if (vmx_get_instr_info_is_reg(instr_info))
+ value = kvm_register_read(vcpu, vmx_get_instr_info_reg(instr_info));
else {
len = is_64_bit_mode(vcpu) ? 8 : 4;
if (get_vmx_mem_address(vcpu, exit_qualification,
@@ -5798,7 +5798,7 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
return kvm_handle_memory_failure(vcpu, r, &e);
}
- field = kvm_register_read(vcpu, (((instr_info) >> 28) & 0xf));
+ field = kvm_register_read(vcpu, vmx_get_instr_info_reg2(instr_info));
offset = get_vmcs12_field_offset(field);
if (offset < 0)
@@ -5975,7 +5975,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
unsigned long type, roots_to_free;
- u64 vmx_instruction_info;
+ u64 instr_info;
struct kvm_mmu *mmu;
gva_t gva;
struct x86_exception e;
@@ -5995,8 +5995,8 @@ static int handle_invept(struct kvm_vcpu *vcpu)
if (!nested_vmx_check_permission(vcpu))
return 1;
- vmx_instruction_info = vmx_get_instr_info();
- gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
+ instr_info = vmx_get_instr_info();
+ gpr_index = vmx_get_instr_info_reg2(instr_info);
type = kvm_register_read(vcpu, gpr_index);
types = (vmx->nested.msrs.ept_caps >> VMX_EPT_EXTENT_SHIFT) & 6;
@@ -6008,7 +6008,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
* operand is read even if it isn't needed (e.g., for type==global)
*/
if (get_vmx_mem_address(vcpu, vmx_get_exit_qual(vcpu),
- vmx_instruction_info, false, sizeof(operand), &gva))
+ instr_info, false, sizeof(operand), &gva))
return 1;
r = kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e);
if (r != X86EMUL_CONTINUE)
@@ -6055,8 +6055,8 @@ static int handle_invept(struct kvm_vcpu *vcpu)
static int handle_invvpid(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- u64 vmx_instruction_info;
unsigned long type, types;
+ u64 instr_info;
gva_t gva;
struct x86_exception e;
struct {
@@ -6076,8 +6076,8 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
if (!nested_vmx_check_permission(vcpu))
return 1;
- vmx_instruction_info = vmx_get_instr_info();
- gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
+ instr_info = vmx_get_instr_info();
+ gpr_index = vmx_get_instr_info_reg2(instr_info);
type = kvm_register_read(vcpu, gpr_index);
types = (vmx->nested.msrs.vpid_caps &
@@ -6091,7 +6091,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
* operand is read even if it isn't needed (e.g., for type==global)
*/
if (get_vmx_mem_address(vcpu, vmx_get_exit_qual(vcpu),
- vmx_instruction_info, false, sizeof(operand), &gva))
+ instr_info, false, sizeof(operand), &gva))
return 1;
r = kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e);
if (r != X86EMUL_CONTINUE)
@@ -6429,16 +6429,16 @@ static bool nested_vmx_exit_handled_encls(struct kvm_vcpu *vcpu,
static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
struct vmcs12 *vmcs12, gpa_t bitmap)
{
- u64 vmx_instruction_info;
unsigned long field;
+ u64 instr_info;
u8 b;
if (!nested_cpu_has_shadow_vmcs(vmcs12))
return true;
/* Decode instruction info and find the field to access */
- vmx_instruction_info = vmx_get_instr_info();
- field = kvm_register_read(vcpu, (((vmx_instruction_info) >> 28) & 0xf));
+ instr_info = vmx_get_instr_info();
+ field = kvm_register_read(vcpu, vmx_get_instr_info_reg2(instr_info));
/* Out-of-range fields always cause a VM exit from L2 to L1 */
if (field >> 15)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 8a4b578252c2..c49bc6fabae3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6158,8 +6158,8 @@ static int handle_monitor_trap(struct kvm_vcpu *vcpu)
static int handle_invpcid(struct kvm_vcpu *vcpu)
{
- u64 vmx_instruction_info;
unsigned long type;
+ u64 instr_info;
gva_t gva;
struct {
u64 pcid;
@@ -6172,16 +6172,15 @@ static int handle_invpcid(struct kvm_vcpu *vcpu)
return 1;
}
- vmx_instruction_info = vmx_get_instr_info();
- gpr_index = vmx_get_instr_info_reg2(vmx_instruction_info);
+ instr_info = vmx_get_instr_info();
+ gpr_index = vmx_get_instr_info_reg2(instr_info);
type = kvm_register_read(vcpu, gpr_index);
/* According to the Intel instruction reference, the memory operand
* is read even if it isn't needed (e.g., for type==all)
*/
if (get_vmx_mem_address(vcpu, vmx_get_exit_qual(vcpu),
- vmx_instruction_info, false,
- sizeof(operand), &gva))
+ instr_info, false, sizeof(operand), &gva))
return 1;
return kvm_handle_invpcid(vcpu, type, gva);
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index a3f58a3d90e5..acb4d7ac8b0a 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -708,14 +708,54 @@ static inline u64 vmx_get_instr_info(void)
return vmcs_read32(VMX_INSTRUCTION_INFO);
}
-static inline int vmx_get_instr_info_reg(u64 vmx_instr_info)
+static inline int vmx_get_instr_info_reg(u64 instr_info)
{
- return (vmx_instr_info >> 3) & 0xf;
+ return (instr_info >> 3) & 0xf;
}
-static inline int vmx_get_instr_info_reg2(u64 vmx_instr_info)
+static inline int vmx_get_instr_info_reg2(u64 instr_info)
{
- return (vmx_instr_info >> 28) & 0xf;
+ return (instr_info >> 28) & 0xf;
+}
+
+static inline int vmx_get_instr_info_scaling(u64 instr_info)
+{
+ return instr_info & 3;
+}
+
+static inline int vmx_get_instr_info_addr_size(u64 instr_info)
+{
+ return (instr_info >> 7) & 7;
+}
+
+static inline bool vmx_get_instr_info_is_reg(u64 instr_info)
+{
+ return !!(instr_info & BIT(10));
+}
+
+static inline int vmx_get_instr_info_seg_reg(u64 instr_info)
+{
+ return (instr_info >> 15) & 7;
+}
+
+static inline int vmx_get_instr_info_index_reg(u64 instr_info)
+{
+ return (instr_info >> 18) & 0xf;
+}
+
+static inline bool vmx_get_instr_info_index_is_valid(u64 instr_info)
+{
+ return !(instr_info & BIT(22));
+}
+
+static inline int vmx_get_instr_info_base_reg(u64 instr_info)
+{
+ return (instr_info >> 23) & 0xf;
+}
+
+static inline bool vmx_get_instr_info_base_is_valid(u64 instr_info)
+{
+ return !(instr_info & BIT(27));
}
static inline bool vmx_can_use_ipiv(struct kvm_vcpu *vcpu)
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 06/20] KVM: VMX: Remove unused control-register access defines
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (4 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
` (13 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
CONTROL_REG_ACCESS_REG becomes misleading with EGPRs. Rather than leave
stale, unused definitions behind, remove them all.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: New patch
When looking around DEBUG_REG_ACCESS_REG() for the next patch, this dead
code was discovered.
---
arch/x86/include/asm/vmx.h | 19 -------------------
1 file changed, 19 deletions(-)
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index ed2ded531e55..30cf48e1c390 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -444,27 +444,8 @@ enum vmcs_field {
/*
* Exit Qualifications for MOV for Control Register Access
*/
-#define CONTROL_REG_ACCESS_NUM 0x7 /* 2:0, number of control reg.*/
-#define CONTROL_REG_ACCESS_TYPE 0x30 /* 5:4, access type */
-#define CONTROL_REG_ACCESS_REG 0xf00 /* 10:8, general purpose reg. */
#define LMSW_SOURCE_DATA_SHIFT 16
#define LMSW_SOURCE_DATA (0xFFFF << LMSW_SOURCE_DATA_SHIFT) /* 16:31 lmsw source */
-#define REG_EAX (0 << 8)
-#define REG_ECX (1 << 8)
-#define REG_EDX (2 << 8)
-#define REG_EBX (3 << 8)
-#define REG_ESP (4 << 8)
-#define REG_EBP (5 << 8)
-#define REG_ESI (6 << 8)
-#define REG_EDI (7 << 8)
-#define REG_R8 (8 << 8)
-#define REG_R9 (9 << 8)
-#define REG_R10 (10 << 8)
-#define REG_R11 (11 << 8)
-#define REG_R12 (12 << 8)
-#define REG_R13 (13 << 8)
-#define REG_R14 (14 << 8)
-#define REG_R15 (15 << 8)
/*
* Exit Qualifications for MOV for Debug Register Access
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 07/20] KVM: VMX: Refactor register index retrieval from exit qualification
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (5 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
` (12 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Introduce a helper to extract the register index from the VMX exit
qualification field.
In addition to the VMX instruction information field, exit qualification
also encodes a register index. This field will expand into the previously
reserved bit for extended register IDs. This refactoring will simplify
the extended register handling without code duplication.
No functional change intended.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Switch the argument and convert DEBUG_REG_ACCESS_REG() (review bot [*])
[*]: locally running Sashiko with gemini-3.1-pro-preview
---
arch/x86/include/asm/vmx.h | 1 -
arch/x86/kvm/vmx/nested.c | 2 +-
arch/x86/kvm/vmx/vmx.c | 4 ++--
arch/x86/kvm/vmx/vmx.h | 5 +++++
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 30cf48e1c390..5645a84f4fbc 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -454,7 +454,6 @@ enum vmcs_field {
#define DEBUG_REG_ACCESS_TYPE 0x10 /* 4, direction of access */
#define TYPE_MOV_TO_DR (0 << 4)
#define TYPE_MOV_FROM_DR (1 << 4)
-#define DEBUG_REG_ACCESS_REG(eq) (((eq) >> 8) & 0xf) /* 11:8, general purpose reg. */
/*
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index fdec041e95c7..8e0523e40477 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -6351,7 +6351,7 @@ static bool nested_vmx_exit_handled_cr(struct kvm_vcpu *vcpu,
switch ((exit_qualification >> 4) & 3) {
case 0: /* mov to cr */
- reg = (exit_qualification >> 8) & 15;
+ reg = vmx_get_exit_qual_reg(exit_qualification);
val = kvm_register_read(vcpu, reg);
switch (cr) {
case 0:
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index c49bc6fabae3..e7f968e66bfd 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -5692,7 +5692,7 @@ static int handle_cr(struct kvm_vcpu *vcpu)
exit_qualification = vmx_get_exit_qual(vcpu);
cr = exit_qualification & 15;
- reg = (exit_qualification >> 8) & 15;
+ reg = vmx_get_exit_qual_reg(exit_qualification);
switch ((exit_qualification >> 4) & 3) {
case 0: /* mov to cr */
val = kvm_register_read(vcpu, reg);
@@ -5810,7 +5810,7 @@ static int handle_dr(struct kvm_vcpu *vcpu)
return 1;
}
- reg = DEBUG_REG_ACCESS_REG(exit_qualification);
+ reg = vmx_get_exit_qual_reg(exit_qualification);
if (exit_qualification & TYPE_MOV_FROM_DR) {
kvm_register_write(vcpu, reg, kvm_get_dr(vcpu, dr));
err = 0;
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index acb4d7ac8b0a..f3b0ec7ccb03 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -323,6 +323,11 @@ static __always_inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu)
return vt->exit_qualification;
}
+static inline int vmx_get_exit_qual_reg(unsigned long exit_qualification)
+{
+ return (exit_qualification >> 8) & 0xf;
+}
+
static __always_inline u32 vmx_get_intr_info(struct kvm_vcpu *vcpu)
{
struct vcpu_vt *vt = to_vt(vcpu);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 08/20] KVM: VMX: Support instruction information extension
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (6 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:00 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
` (11 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Define the VMCS field offset for the extended instruction information.
Then, support 5-bit register indices retrieval from VMCS fields.
Note the APX enumeration alone indicates the extension is available.
For safety, do not assume that previously reserved bits were zero
on older implementations.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/aakEsXJgO-3m2xca@google.com
---
Note for the review of extensions in following functions:
+-------+------------------+------------------------------------+
| Bits | Field Name | Related KVM Functions |
+=======+==================+====================================+
| 3:2 | ASIZE | vmx_get_instr_info_addr_size() |
+-------+------------------+------------------------------------+
| 4 | Mem/Reg (1=>reg) | vmx_get_instr_info_is_reg() |
+-------+------------------+------------------------------------+
| 9:7 | Segment | vmx_get_instr_info_seg_reg() |
+-------+------------------+------------------------------------+
| 10 | IndexInvalid | vmx_get_instr_info_base_reg() |
+-------+------------------+------------------------------------+
| 11 | BaseInvalid | vmx_get_instr_info_base_is_valid() |
+-------+------------------+------------------------------------+
| 20:16 | Reg1 | vmx_get_instr_info_reg() |
+-------+------------------+------------------------------------+
| 28:24 | Index | vmx_get_instr_info_index_reg() |
+-------+------------------+------------------------------------+
| 36:32 | Base | vmx_get_instr_info_base_reg() |
+-------+------------------+------------------------------------+
| 44:40 | Reg2 | vmx_get_instr_info_reg2() |
+-------+------------------+------------------------------------+
Reference:
APX Specification, Table 3.11 Extended Instruction-Information (EII)
VMCS Field
---
arch/x86/include/asm/vmx.h | 2 ++
arch/x86/kvm/vmx/vmx.h | 42 +++++++++++++++++++++++++++-----------
2 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 5645a84f4fbc..2334f52071f9 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -276,6 +276,8 @@ enum vmcs_field {
PID_POINTER_TABLE_HIGH = 0x00002043,
GUEST_PHYSICAL_ADDRESS = 0x00002400,
GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401,
+ EXTENDED_INSTRUCTION_INFO = 0x00002406,
+ EXTENDED_INSTRUCTION_INFO_HIGH = 0x00002407,
VMCS_LINK_POINTER = 0x00002800,
VMCS_LINK_POINTER_HIGH = 0x00002801,
GUEST_IA32_DEBUGCTL = 0x00002802,
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index f3b0ec7ccb03..2f53338f84f0 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -323,9 +323,18 @@ static __always_inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu)
return vt->exit_qualification;
}
+/*
+ * The APX enumeration guarantees the presence of the extended fields.
+ * The host CPUID bit alone is sufficient to rely on it.
+ */
+static inline bool vmx_instr_info_extended(void)
+{
+ return static_cpu_has(X86_FEATURE_APX);
+}
+
static inline int vmx_get_exit_qual_reg(unsigned long exit_qualification)
{
- return (exit_qualification >> 8) & 0xf;
+ return (exit_qualification >> 8) & (vmx_instr_info_extended() ? 0x1f : 0xf);
}
static __always_inline u32 vmx_get_intr_info(struct kvm_vcpu *vcpu)
@@ -707,20 +716,22 @@ static inline bool vmx_guest_state_valid(struct kvm_vcpu *vcpu)
void dump_vmcs(struct kvm_vcpu *vcpu);
-/* A placeholder to smoothen 64-bit extension */
static inline u64 vmx_get_instr_info(void)
{
- return vmcs_read32(VMX_INSTRUCTION_INFO);
+ return vmx_instr_info_extended() ? vmcs_read64(EXTENDED_INSTRUCTION_INFO) :
+ vmcs_read32(VMX_INSTRUCTION_INFO);
}
static inline int vmx_get_instr_info_reg(u64 instr_info)
{
- return (instr_info >> 3) & 0xf;
+ return vmx_instr_info_extended() ? (instr_info >> 16) & 0x1f :
+ (instr_info >> 3) & 0xf;
}
static inline int vmx_get_instr_info_reg2(u64 instr_info)
{
- return (instr_info >> 28) & 0xf;
+ return vmx_instr_info_extended() ? (instr_info >> 40) & 0x1f :
+ (instr_info >> 28) & 0xf;
}
static inline int vmx_get_instr_info_scaling(u64 instr_info)
@@ -730,37 +741,44 @@ static inline int vmx_get_instr_info_scaling(u64 instr_info)
static inline int vmx_get_instr_info_addr_size(u64 instr_info)
{
- return (instr_info >> 7) & 7;
+ return vmx_instr_info_extended() ? (instr_info >> 2) & 3 :
+ (instr_info >> 7) & 7;
}
static inline bool vmx_get_instr_info_is_reg(u64 instr_info)
{
- return !!(instr_info & BIT(10));
+ return vmx_instr_info_extended() ? !!(instr_info & BIT(4)) :
+ !!(instr_info & BIT(10));
}
static inline int vmx_get_instr_info_seg_reg(u64 instr_info)
{
- return (instr_info >> 15) & 7;
+ return vmx_instr_info_extended() ? (instr_info >> 7) & 7 :
+ (instr_info >> 15) & 7;
}
static inline int vmx_get_instr_info_index_reg(u64 instr_info)
{
- return (instr_info >> 18) & 0xf;
+ return vmx_instr_info_extended() ? (instr_info >> 24) & 0x1f :
+ (instr_info >> 18) & 0xf;
}
static inline bool vmx_get_instr_info_index_is_valid(u64 instr_info)
{
- return !(instr_info & BIT(22));
+ return vmx_instr_info_extended() ? !(instr_info & BIT(10)) :
+ !(instr_info & BIT(22));
}
static inline int vmx_get_instr_info_base_reg(u64 instr_info)
{
- return (instr_info >> 23) & 0xf;
+ return vmx_instr_info_extended() ? (instr_info >> 32) & 0x1f :
+ (instr_info >> 23) & 0xf;
}
static inline bool vmx_get_instr_info_base_is_valid(u64 instr_info)
{
- return !(instr_info & BIT(27));
+ return vmx_instr_info_extended() ? !(instr_info & BIT(11)) :
+ !(instr_info & BIT(27));
}
static inline bool vmx_can_use_ipiv(struct kvm_vcpu *vcpu)
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (7 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:01 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
` (10 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae, Chao Gao
Define the new extended_instruction_info field in struct vmcs12 and
propagate it to nested VMX. Gate the propagation on the guest APX
enumeration, which aligns with bare metal behavior. Thus, define the
CPUID bit here too.
Suggested-by: Chao Gao <chao.gao@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/aRvOSnaUt1E+%2FpkC@intel.com
---
arch/x86/kvm/reverse_cpuid.h | 2 ++
arch/x86/kvm/vmx/nested.c | 6 ++++++
arch/x86/kvm/vmx/vmcs12.c | 1 +
arch/x86/kvm/vmx/vmcs12.h | 3 ++-
4 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 657f5f743ed9..de90697c4e5a 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -35,6 +35,7 @@
#define X86_FEATURE_AVX_VNNI_INT16 KVM_X86_FEATURE(CPUID_7_1_EDX, 10)
#define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14)
#define X86_FEATURE_AVX10 KVM_X86_FEATURE(CPUID_7_1_EDX, 19)
+#define KVM_X86_FEATURE_APX KVM_X86_FEATURE(CPUID_7_1_EDX, 21)
/* Intel-defined sub-features, CPUID level 0x00000007:2 (EDX) */
#define X86_FEATURE_INTEL_PSFD KVM_X86_FEATURE(CPUID_7_2_EDX, 0)
@@ -144,6 +145,7 @@ static __always_inline u32 __feature_translate(int x86_feature)
KVM_X86_TRANSLATE_FEATURE(SGX1);
KVM_X86_TRANSLATE_FEATURE(SGX2);
KVM_X86_TRANSLATE_FEATURE(SGX_EDECCSSA);
+ KVM_X86_TRANSLATE_FEATURE(APX);
KVM_X86_TRANSLATE_FEATURE(CONSTANT_TSC);
KVM_X86_TRANSLATE_FEATURE(PERFMON_V2);
KVM_X86_TRANSLATE_FEATURE(RRSBA_CTRL);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 8e0523e40477..1684f5d1dbf4 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -4766,6 +4766,12 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
vmcs12->vm_exit_intr_info = exit_intr_info;
vmcs12->vm_exit_instruction_len = exit_insn_len;
vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+ /*
+ * The APX enumeration guarantees the presence of the extended
+ * fields. This CPUID bit alone is sufficient to rely on it.
+ */
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_APX))
+ vmcs12->extended_instruction_info = vmcs_read64(EXTENDED_INSTRUCTION_INFO);
/*
* According to spec, there's no need to store the guest's
diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c
index 1ebe67c384ad..267aa64f005e 100644
--- a/arch/x86/kvm/vmx/vmcs12.c
+++ b/arch/x86/kvm/vmx/vmcs12.c
@@ -53,6 +53,7 @@ static const u16 kvm_supported_vmcs12_field_offsets[] __initconst = {
FIELD64(XSS_EXIT_BITMAP, xss_exit_bitmap),
FIELD64(ENCLS_EXITING_BITMAP, encls_exiting_bitmap),
FIELD64(GUEST_PHYSICAL_ADDRESS, guest_physical_address),
+ FIELD64(EXTENDED_INSTRUCTION_INFO, extended_instruction_info),
FIELD64(VMCS_LINK_POINTER, vmcs_link_pointer),
FIELD64(GUEST_IA32_DEBUGCTL, guest_ia32_debugctl),
FIELD64(GUEST_IA32_PAT, guest_ia32_pat),
diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h
index 21cd1b75e4fd..25e9a41c248f 100644
--- a/arch/x86/kvm/vmx/vmcs12.h
+++ b/arch/x86/kvm/vmx/vmcs12.h
@@ -71,7 +71,7 @@ struct __packed vmcs12 {
u64 pml_address;
u64 encls_exiting_bitmap;
u64 tsc_multiplier;
- u64 padding64[1]; /* room for future expansion */
+ u64 extended_instruction_info;
/*
* To allow migration of L1 (complete with its L2 guests) between
* machines of different natural widths (32 or 64 bit), we cannot have
@@ -261,6 +261,7 @@ static inline void vmx_check_vmcs12_offsets(void)
CHECK_OFFSET(pml_address, 312);
CHECK_OFFSET(encls_exiting_bitmap, 320);
CHECK_OFFSET(tsc_multiplier, 328);
+ CHECK_OFFSET(extended_instruction_info, 336);
CHECK_OFFSET(cr0_guest_host_mask, 344);
CHECK_OFFSET(cr4_guest_host_mask, 352);
CHECK_OFFSET(cr0_read_shadow, 360);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 10/20] KVM: x86: Support EGPR accessing and tracking for emulator
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (8 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
` (9 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Extend the emulator context and GPR accessors to handle EGPRs before
adding support for REX2-prefixed instructions. Like VCPU cache, the
emulator can uniformly cache and track all GPRs.
Also replace `1 << reg` with `BIT(reg)` in reg_read()/reg_write(). The
former performs a signed 32-bit shift, which becomes undefined for R31.
BIT() instead shifts an unsigned value that is suitable for all indices.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Fix possible shift-out-of-bounds (review bot [*])
[*]: locally running Sashiko with gemini-3.1-pro-preview
---
arch/x86/kvm/kvm_emulate.h | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 3e375af15c03..b1498f618c81 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -109,13 +109,13 @@ struct x86_instruction_info {
struct x86_emulate_ops {
void (*vm_bugged)(struct x86_emulate_ctxt *ctxt);
/*
- * read_gpr: read a general purpose register (rax - r15)
+ * read_gpr: read a general purpose register (rax - r31)
*
* @reg: gpr number.
*/
ulong (*read_gpr)(struct x86_emulate_ctxt *ctxt, unsigned reg);
/*
- * write_gpr: write a general purpose register (rax - r15)
+ * write_gpr: write a general purpose register (rax - r31)
*
* @reg: gpr number.
* @val: value to write.
@@ -321,7 +321,9 @@ typedef void (*fastop_t)(struct fastop *);
* also uses _eip, RIP cannot be a register operand nor can it be an operand in
* a ModRM or SIB byte.
*/
-#ifdef CONFIG_X86_64
+#if defined(CONFIG_KVM_APX)
+#define NR_EMULATOR_GPRS 32
+#elif defined(CONFIG_X86_64)
#define NR_EMULATOR_GPRS 16
#else
#define NR_EMULATOR_GPRS 8
@@ -381,9 +383,9 @@ struct x86_emulate_ctxt {
u8 lock_prefix;
u8 rep_prefix;
/* bitmaps of registers in _regs[] that can be read */
- u16 regs_valid;
+ u32 regs_valid;
/* bitmaps of registers in _regs[] that have been written */
- u16 regs_dirty;
+ u32 regs_dirty;
/* modrm */
u8 modrm;
u8 modrm_mod;
@@ -547,8 +549,8 @@ static inline ulong reg_read(struct x86_emulate_ctxt *ctxt, unsigned nr)
if (KVM_EMULATOR_BUG_ON(nr >= NR_EMULATOR_GPRS, ctxt))
nr &= NR_EMULATOR_GPRS - 1;
- if (!(ctxt->regs_valid & (1 << nr))) {
- ctxt->regs_valid |= 1 << nr;
+ if (!(ctxt->regs_valid & BIT(nr))) {
+ ctxt->regs_valid |= BIT(nr);
ctxt->_regs[nr] = ctxt->ops->read_gpr(ctxt, nr);
}
return ctxt->_regs[nr];
@@ -562,8 +564,8 @@ static inline ulong *reg_write(struct x86_emulate_ctxt *ctxt, unsigned nr)
BUILD_BUG_ON(sizeof(ctxt->regs_dirty) * BITS_PER_BYTE < NR_EMULATOR_GPRS);
BUILD_BUG_ON(sizeof(ctxt->regs_valid) * BITS_PER_BYTE < NR_EMULATOR_GPRS);
- ctxt->regs_valid |= 1 << nr;
- ctxt->regs_dirty |= 1 << nr;
+ ctxt->regs_valid |= BIT(nr);
+ ctxt->regs_dirty |= BIT(nr);
return &ctxt->_regs[nr];
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (9 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:04 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
` (8 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Prepare the emulator for REX2 handling by introducing the NoRex2 opcode
flag and supporting extended register indices. For the latter, factor out
common logic for calculating register IDs.
REX2 does not support three-byte opcodes. Instead, the REX2.M bit selects
between one- and two-byte opcode tables, which were previously
distinguished by the 0x0F escape byte.
Some legacy instructions in those tables never reference extended
registers. When prefixed with REX, such instructions are treated as if
the prefix were absent. In contrast, a REX2 prefix causes a #UD, which
should be handled explicitly.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/1ebf3a23-5671-41c1-8daa-c83f2f105936@redhat.com
---
arch/x86/kvm/emulate.c | 80 +++++++++++++++++++++++---------------
arch/x86/kvm/kvm_emulate.h | 1 +
2 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index b566ab5c7515..12a822870d08 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -175,6 +175,7 @@
#define TwoMemOp ((u64)1 << 55) /* Instruction has two memory operand */
#define IsBranch ((u64)1 << 56) /* Instruction is considered a branch. */
#define ShadowStack ((u64)1 << 57) /* Instruction affects Shadow Stacks. */
+#define NoRex2 ((u64)1 << 58) /* Instruction not present in REX2 maps */
#define DstXacc (DstAccLo | SrcAccHi | SrcWrite)
@@ -244,6 +245,7 @@ enum rex_bits {
REX_X = 2,
REX_R = 4,
REX_W = 8,
+ REX_M = 0x80,
};
static void writeback_registers(struct x86_emulate_ctxt *ctxt)
@@ -1079,6 +1081,15 @@ static int em_fnstsw(struct x86_emulate_ctxt *ctxt)
return X86EMUL_CONTINUE;
}
+static __always_inline int rex_get_rxb(u8 rex, u8 fld)
+{
+ BUILD_BUG_ON(!__builtin_constant_p(fld));
+ BUILD_BUG_ON(fld != REX_B && fld != REX_X && fld != REX_R);
+
+ rex >>= ffs(fld) - 1;
+ return (rex & 1 ? 8 : 0) + (rex & 0x10 ? 16 : 0);
+}
+
static void __decode_register_operand(struct x86_emulate_ctxt *ctxt,
struct operand *op, int reg)
{
@@ -1118,7 +1129,7 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
if (ctxt->d & ModRM)
reg = ctxt->modrm_reg;
else
- reg = (ctxt->b & 7) | (ctxt->rex_bits & REX_B ? 8 : 0);
+ reg = (ctxt->b & 7) | rex_get_rxb(ctxt->rex_bits, REX_B);
__decode_register_operand(ctxt, op, reg);
}
@@ -1137,9 +1148,9 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
int rc = X86EMUL_CONTINUE;
ulong modrm_ea = 0;
- ctxt->modrm_reg = (ctxt->rex_bits & REX_R ? 8 : 0);
- index_reg = (ctxt->rex_bits & REX_X ? 8 : 0);
- base_reg = (ctxt->rex_bits & REX_B ? 8 : 0);
+ ctxt->modrm_reg = rex_get_rxb(ctxt->rex_bits, REX_R);
+ index_reg = rex_get_rxb(ctxt->rex_bits, REX_X);
+ base_reg = rex_get_rxb(ctxt->rex_bits, REX_B);
ctxt->modrm_mod = (ctxt->modrm & 0xc0) >> 6;
ctxt->modrm_reg |= (ctxt->modrm & 0x38) >> 3;
@@ -4247,7 +4258,7 @@ static const struct opcode opcode_table[256] = {
/* 0x38 - 0x3F */
I6ALU(NoWrite, em_cmp), N, N,
/* 0x40 - 0x4F */
- X8(I(DstReg, em_inc)), X8(I(DstReg, em_dec)),
+ X8(I(DstReg | NoRex2, em_inc)), X8(I(DstReg | NoRex2, em_dec)),
/* 0x50 - 0x57 */
X8(I(SrcReg | Stack, em_push)),
/* 0x58 - 0x5F */
@@ -4265,7 +4276,7 @@ static const struct opcode opcode_table[256] = {
I2bvIP(DstDI | SrcDX | Mov | String | Unaligned, em_in, ins, check_perm_in), /* insb, insw/insd */
I2bvIP(SrcSI | DstDX | String, em_out, outs, check_perm_out), /* outsb, outsw/outsd */
/* 0x70 - 0x7F */
- X16(D(SrcImmByte | NearBranch | IsBranch)),
+ X16(D(SrcImmByte | NearBranch | IsBranch | NoRex2)),
/* 0x80 - 0x87 */
G(ByteOp | DstMem | SrcImm, group1),
G(DstMem | SrcImm, group1),
@@ -4289,15 +4300,15 @@ static const struct opcode opcode_table[256] = {
II(ImplicitOps | Stack, em_popf, popf),
I(ImplicitOps, em_sahf), I(ImplicitOps, em_lahf),
/* 0xA0 - 0xA7 */
- I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov),
- I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov),
- I2bv(SrcSI | DstDI | Mov | String | TwoMemOp, em_mov),
- I2bv(SrcSI | DstDI | String | NoWrite | TwoMemOp, em_cmp_r),
+ I2bv(DstAcc | SrcMem | Mov | MemAbs | NoRex2, em_mov),
+ I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable | NoRex2, em_mov),
+ I2bv(SrcSI | DstDI | Mov | String | TwoMemOp | NoRex2, em_mov),
+ I2bv(SrcSI | DstDI | String | NoWrite | TwoMemOp | NoRex2, em_cmp_r),
/* 0xA8 - 0xAF */
- I2bv(DstAcc | SrcImm | NoWrite, em_test),
- I2bv(SrcAcc | DstDI | Mov | String, em_mov),
- I2bv(SrcSI | DstAcc | Mov | String, em_mov),
- I2bv(SrcAcc | DstDI | String | NoWrite, em_cmp_r),
+ I2bv(DstAcc | SrcImm | NoWrite | NoRex2, em_test),
+ I2bv(SrcAcc | DstDI | Mov | String | NoRex2, em_mov),
+ I2bv(SrcSI | DstAcc | Mov | String | NoRex2, em_mov),
+ I2bv(SrcAcc | DstDI | String | NoWrite | NoRex2, em_cmp_r),
/* 0xB0 - 0xB7 */
X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)),
/* 0xB8 - 0xBF */
@@ -4327,17 +4338,17 @@ static const struct opcode opcode_table[256] = {
/* 0xD8 - 0xDF */
N, E(0, &escape_d9), N, E(0, &escape_db), N, E(0, &escape_dd), N, N,
/* 0xE0 - 0xE7 */
- X3(I(SrcImmByte | NearBranch | IsBranch, em_loop)),
- I(SrcImmByte | NearBranch | IsBranch, em_jcxz),
- I2bvIP(SrcImmUByte | DstAcc, em_in, in, check_perm_in),
- I2bvIP(SrcAcc | DstImmUByte, em_out, out, check_perm_out),
+ X3(I(SrcImmByte | NearBranch | IsBranch | NoRex2, em_loop)),
+ I(SrcImmByte | NearBranch | IsBranch | NoRex2, em_jcxz),
+ I2bvIP(SrcImmUByte | DstAcc | NoRex2, em_in, in, check_perm_in),
+ I2bvIP(SrcAcc | DstImmUByte | NoRex2, em_out, out, check_perm_out),
/* 0xE8 - 0xEF */
- I(SrcImm | NearBranch | IsBranch | ShadowStack, em_call),
- D(SrcImm | ImplicitOps | NearBranch | IsBranch),
- I(SrcImmFAddr | No64 | IsBranch, em_jmp_far),
- D(SrcImmByte | ImplicitOps | NearBranch | IsBranch),
- I2bvIP(SrcDX | DstAcc, em_in, in, check_perm_in),
- I2bvIP(SrcAcc | DstDX, em_out, out, check_perm_out),
+ I(SrcImm | NearBranch | IsBranch | ShadowStack | NoRex2, em_call),
+ D(SrcImm | ImplicitOps | NearBranch | IsBranch | NoRex2),
+ I(SrcImmFAddr | No64 | IsBranch | NoRex2, em_jmp_far),
+ D(SrcImmByte | ImplicitOps | NearBranch | IsBranch | NoRex2),
+ I2bvIP(SrcDX | DstAcc | NoRex2, em_in, in, check_perm_in),
+ I2bvIP(SrcAcc | DstDX | NoRex2, em_out, out, check_perm_out),
/* 0xF0 - 0xF7 */
N, DI(ImplicitOps, icebp), N, N,
DI(ImplicitOps | Priv, hlt), D(ImplicitOps),
@@ -4378,12 +4389,12 @@ static const struct opcode twobyte_table[256] = {
N, GP(ModRM | DstMem | SrcReg | Mov | Sse | Avx, &pfx_0f_2b),
N, N, N, N,
/* 0x30 - 0x3F */
- II(ImplicitOps | Priv, em_wrmsr, wrmsr),
- IIP(ImplicitOps, em_rdtsc, rdtsc, check_rdtsc),
- II(ImplicitOps | Priv, em_rdmsr, rdmsr),
- IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc),
- I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack, em_sysenter),
- I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack, em_sysexit),
+ II(ImplicitOps | Priv | NoRex2, em_wrmsr, wrmsr),
+ IIP(ImplicitOps | NoRex2, em_rdtsc, rdtsc, check_rdtsc),
+ II(ImplicitOps | Priv | NoRex2, em_rdmsr, rdmsr),
+ IIP(ImplicitOps | NoRex2, em_rdpmc, rdpmc, check_rdpmc),
+ I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | NoRex2, em_sysenter),
+ I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack | NoRex2, em_sysexit),
N, N,
N, N, N, N, N, N, N, N,
/* 0x40 - 0x4F */
@@ -4401,7 +4412,7 @@ static const struct opcode twobyte_table[256] = {
N, N, N, N,
N, N, N, GP(SrcReg | DstMem | ModRM | Mov, &pfx_0f_6f_0f_7f),
/* 0x80 - 0x8F */
- X16(D(SrcImm | NearBranch | IsBranch)),
+ X16(D(SrcImm | NearBranch | IsBranch | NoRex2)),
/* 0x90 - 0x9F */
X16(D(ByteOp | DstMem | SrcNone | ModRM| Mov)),
/* 0xA0 - 0xA7 */
@@ -4994,6 +5005,13 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
opcode = opcode_table[ctxt->b];
}
+ /*
+ * Instructions marked with NoRex2 ignore a legacy REX prefix, but #UD
+ * should be raised when prefixed with REX2.
+ */
+ if (opcode.flags & NoRex2 && ctxt->rex_prefix == REX2_PREFIX)
+ opcode.flags = Undefined;
+
if (opcode.flags & ModRM)
ctxt->modrm = insn_fetch(u8, ctxt);
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index b1498f618c81..f1ca1938af71 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -335,6 +335,7 @@ typedef void (*fastop_t)(struct fastop *);
enum rex_type {
REX_NONE,
REX_PREFIX,
+ REX2_PREFIX,
};
struct x86_emulate_ctxt {
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 12/20] KVM: x86: Support REX2-prefixed opcode decode
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (10 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
` (7 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Extend the instruction decoder to recognize and handle the REX2 prefix,
including validation of prefix sequences and correct opcode table
selection.
REX2 is a terminal prefix: once 0xD5 is encountered, the following byte
is the opcode. When REX.M=0, most prefix bytes are invalid after REX2,
including REX, VEX, EVEX, and another REX2. Also, REX2-prefixed
instructions are only valid in 64-bit mode.
All of the invalid prefix combinations after REX2 coincide with opcodes
that are architecturally invalid in 64-bit mode. Thus, marking such
opcodes with No64 in opcode_table[] naturally disallows those illegal
prefix sequences.
The 0x40–0x4F opcode row was missing the No64 flag. While NoRex2 already
invalidates REX2 for these opcodes, adding No64 makes opcode attributes
explicit and complete.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/CABgObfYYGTvkYpeyqLSr9JgKMDA_STSff2hXBNchLZuKFU+MMA@mail.gmail.com
---
arch/x86/kvm/emulate.c | 38 ++++++++++++++++++++++++++++++++++----
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 12a822870d08..cb075bd74f76 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -4258,7 +4258,7 @@ static const struct opcode opcode_table[256] = {
/* 0x38 - 0x3F */
I6ALU(NoWrite, em_cmp), N, N,
/* 0x40 - 0x4F */
- X8(I(DstReg | NoRex2, em_inc)), X8(I(DstReg | NoRex2, em_dec)),
+ X8(I(DstReg | NoRex2 | No64, em_inc)), X8(I(DstReg | NoRex2 | No64, em_dec)),
/* 0x50 - 0x57 */
X8(I(SrcReg | Stack, em_push)),
/* 0x58 - 0x5F */
@@ -4852,6 +4852,17 @@ static int x86_decode_avx(struct x86_emulate_ctxt *ctxt,
return rc;
}
+static inline bool rex2_invalid(struct x86_emulate_ctxt *ctxt)
+{
+ const struct x86_emulate_ops *ops = ctxt->ops;
+ u64 xcr = 0;
+
+ return ctxt->rex_prefix == REX_PREFIX ||
+ !(ops->get_cr(ctxt, 4) & X86_CR4_OSXSAVE) ||
+ ops->get_xcr(ctxt, 0, &xcr) ||
+ !(xcr & XFEATURE_MASK_APX);
+}
+
int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int emulation_type)
{
int rc = X86EMUL_CONTINUE;
@@ -4905,7 +4916,7 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
ctxt->op_bytes = def_op_bytes;
ctxt->ad_bytes = def_ad_bytes;
- /* Legacy prefixes. */
+ /* Legacy and REX/REX2 prefixes. */
for (;;) {
switch (ctxt->b = insn_fetch(u8, ctxt)) {
case 0x66: /* operand-size override */
@@ -4951,6 +4962,17 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
ctxt->rex_prefix = REX_PREFIX;
ctxt->rex_bits = ctxt->b & 0xf;
continue;
+ case 0xd5: /* REX2 */
+ if (mode != X86EMUL_MODE_PROT64)
+ goto done_prefixes;
+ if (rex2_invalid(ctxt)) {
+ opcode = ud;
+ goto done_modrm;
+ }
+ ctxt->rex_prefix = REX2_PREFIX;
+ ctxt->rex_bits = insn_fetch(u8, ctxt);
+ ctxt->b = insn_fetch(u8, ctxt);
+ goto done_prefixes;
case 0xf0: /* LOCK */
ctxt->lock_prefix = 1;
break;
@@ -4973,6 +4995,12 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
if (ctxt->rex_bits & REX_W)
ctxt->op_bytes = 8;
+ /* REX2 opcode is one byte unless M-bit selects the two-byte map */
+ if (ctxt->rex_bits & REX_M)
+ goto decode_twobytes;
+ else if (ctxt->rex_prefix == REX2_PREFIX)
+ goto decode_onebyte;
+
/* Opcode byte(s). */
if (ctxt->b == 0xc4 || ctxt->b == 0xc5) {
/* VEX or LDS/LES */
@@ -4990,17 +5018,19 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
goto done;
} else if (ctxt->b == 0x0f) {
/* Two- or three-byte opcode */
- ctxt->opcode_len = 2;
ctxt->b = insn_fetch(u8, ctxt);
+decode_twobytes:
+ ctxt->opcode_len = 2;
opcode = twobyte_table[ctxt->b];
/* 0F_38 opcode map */
- if (ctxt->b == 0x38) {
+ if (ctxt->b == 0x38 && ctxt->rex_prefix != REX2_PREFIX) {
ctxt->opcode_len = 3;
ctxt->b = insn_fetch(u8, ctxt);
opcode = opcode_map_0f_38[ctxt->b];
}
} else {
+decode_onebyte:
/* Opcode byte(s). */
opcode = opcode_table[ctxt->b];
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (11 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:58 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
` (6 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Explicitly mark EVEX-prefixed opcodes (0x62) as unsupported.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/kvm/emulate.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index cb075bd74f76..fc7f9f17c1da 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -5045,6 +5045,11 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
if (opcode.flags & ModRM)
ctxt->modrm = insn_fetch(u8, ctxt);
+ /* EVEX-prefixed instructions are not implemented */
+ if (ctxt->opcode_len == 1 && ctxt->b == 0x62 &&
+ (mode == X86EMUL_MODE_PROT64 || (ctxt->modrm & 0xc0) == 0xc0))
+ opcode.flags = NotImpl;
+
done_modrm:
ctxt->d = opcode.flags;
while (ctxt->d & GroupMask) {
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init()
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (12 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
` (5 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
From: Sean Christopherson <seanjc@google.com>
Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() as "const u64"
values so that KVM's initialization code can further manipulate the set
of supported XCR0/XSS features without ugly #ifdeffery and without
risking other code treating KVM_SUPPORTED_{XCR0,XSS} as 100%
authoritative.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/ahmtyMNsj2BCoEEo@google.com
---
V4 -> V5: New patch (Sean)
---
arch/x86/kvm/cpuid.c | 2 +-
arch/x86/kvm/x86.c | 25 +++++++++++++------------
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 122e46db9a15..1fc7a0f9a13a 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -264,7 +264,7 @@ static u32 kvm_apply_cpuid_pv_features_quirk(struct kvm_vcpu *vcpu)
/*
* Calculate guest's supported XCR0 taking into account guest CPUID data and
- * KVM's supported XCR0 (comprised of host's XCR0 and KVM_SUPPORTED_XCR0).
+ * KVM's supported XCR0 (the host's actual XCR0 masked by what KVM supports).
*/
static u64 cpuid_get_supported_xcr0(struct kvm_vcpu *vcpu)
{
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 5578df10179a..84b2dcfcb007 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -191,19 +191,7 @@ module_param(pi_inject_timer, bint, 0644);
static bool __read_mostly mitigate_smt_rsb;
module_param(mitigate_smt_rsb, bool, 0444);
-#define KVM_SUPPORTED_XCR0 (XFEATURE_MASK_FP | XFEATURE_MASK_SSE \
- | XFEATURE_MASK_YMM | XFEATURE_MASK_BNDREGS \
- | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \
- | XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE)
-
#define XFEATURE_MASK_CET_ALL (XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL)
-/*
- * Note, KVM supports exposing PT to the guest, but does not support context
- * switching PT via XSTATE (KVM's PT virtualization relies on perf; swapping
- * PT via guest XSTATE would clobber perf state), i.e. KVM doesn't support
- * IA32_XSS[bit 8] (guests can/must use RDMSR/WRMSR to save/restore PT MSRs).
- */
-#define KVM_SUPPORTED_XSS (XFEATURE_MASK_CET_ALL)
bool __read_mostly allow_smaller_maxphyaddr = 0;
EXPORT_SYMBOL_FOR_KVM_INTERNAL(allow_smaller_maxphyaddr);
@@ -7009,6 +6997,19 @@ static void kvm_x86_check_cpu_compat(void *ret)
int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
{
+ const u64 KVM_SUPPORTED_XCR0 = XFEATURE_MASK_FP | XFEATURE_MASK_SSE |
+ XFEATURE_MASK_YMM | XFEATURE_MASK_BNDREGS |
+ XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 |
+ XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE;
+ /*
+ * Note, KVM supports exposing PT to the guest, but does not support
+ * context switching PT via XSTATE (KVM's PT virtualization relies on
+ * perf; swapping PT via guest XSTATE would clobber perf state), i.e.
+ * KVM doesn't support IA32_XSS[bit 8] (guests can/must use RDMSR/WRMSR
+ * to save/restore PT MSRs).
+ */
+ const u64 KVM_SUPPORTED_XSS = XFEATURE_MASK_CET_ALL;
+
u64 host_pat;
int r, cpu;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 15/20] KVM: x86: Guard valid XCR0.APX settings
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (13 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
` (4 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Prevent invalid XCR0.APX configurations in two cases: conflict with MPX
and lack of SVM support.
In the non-compacted XSAVE format, APX and MPX conflict on the same
offset. Although MPX is being deprecated in practice, KVM should
explicitly reject such configurations that set both bits.
At this point, only VMX supports EGPRs. SVM will require corresponding
extensions to handle EGPR indices.
Updating the supported XCR0 mask will be done separately.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/ab3f4937-38f5-4354-8850-bf773c159bbe@redhat.com
---
arch/x86/kvm/svm/svm.c | 7 ++++++-
arch/x86/kvm/x86.c | 4 ++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ef69a51ab27f..eb1b2ba1261d 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5612,8 +5612,13 @@ static __init int svm_hardware_setup(void)
return -EOPNOTSUPP;
}
+ /*
+ * APX introduces EGPRs, which require additional VMCB support.
+ * Disable APX until the necessary extensions are handled.
+ */
kvm_caps.supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS |
- XFEATURE_MASK_BNDCSR);
+ XFEATURE_MASK_BNDCSR |
+ XFEATURE_MASK_APX);
if (tsc_scaling) {
if (!boot_cpu_has(X86_FEATURE_TSCRATEMSR)) {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 84b2dcfcb007..266d7a02855b 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -682,6 +682,10 @@ int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
(!(xcr0 & XFEATURE_MASK_BNDCSR)))
return 1;
+ /* MPX and APX conflict in the non-compacted XSAVE format */
+ if (xcr0 & XFEATURE_MASK_BNDREGS && xcr0 & XFEATURE_MASK_APX)
+ return 1;
+
if (xcr0 & XFEATURE_MASK_AVX512) {
if (!(xcr0 & XFEATURE_MASK_YMM))
return 1;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (14 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:06 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
` (3 subsequent siblings)
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Set kvm_caps.supported_xcr0[APX] when the host has enabled the APX state
along with CONFIG_KVM_APX=y.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Separate out from userspace exposure (Sean)
---
arch/x86/kvm/x86.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 266d7a02855b..48f8a2d69987 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7082,6 +7082,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
if (boot_cpu_has(X86_FEATURE_XSAVE)) {
kvm_host.xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
kvm_caps.supported_xcr0 = kvm_host.xcr0 & KVM_SUPPORTED_XCR0;
+ if (IS_ENABLED(CONFIG_KVM_APX))
+ kvm_caps.supported_xcr0 |= kvm_host.xcr0 & XFEATURE_MASK_APX;
}
if (boot_cpu_has(X86_FEATURE_XSAVES)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 17/20] KVM: x86: Expose APX foundation feature to userspace
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (15 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
` (2 subsequent siblings)
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae, Peter Fang
Expose the APX feature to userspace component. Update the maximum
supported CPUID leaf to 0x29 to include the APX leaf.
On SVM, do not advertise APX, as EGPR support is not yet implemented.
No APX sub-features are enumerated yet. Those will be exposed in a
separate patch.
Originally-by: Peter Fang <peter.fang@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V5 -> V6: Add APX leaf 0x29 to cpuid_function_is_indexed() (Sashiko)
---
arch/x86/include/asm/cpuid/api.h | 1 +
arch/x86/kvm/cpuid.c | 10 +++++++++-
arch/x86/kvm/svm/svm.c | 1 +
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpuid/api.h b/arch/x86/include/asm/cpuid/api.h
index 44fa82e1267c..73ebb462ba0e 100644
--- a/arch/x86/include/asm/cpuid/api.h
+++ b/arch/x86/include/asm/cpuid/api.h
@@ -181,6 +181,7 @@ static __always_inline bool cpuid_function_is_indexed(u32 function)
case 0x1e:
case 0x1f:
case 0x24:
+ case 0x29:
case 0x8000001d:
return true;
}
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 1fc7a0f9a13a..db95f6b578d0 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1062,8 +1062,12 @@ void kvm_initialize_cpu_caps(void)
F(AVX_VNNI_INT16),
F(PREFETCHITI),
F(AVX10),
+ SCATTERED_F(APX),
);
+ if (!IS_ENABLED(CONFIG_KVM_APX))
+ kvm_cpu_cap_clear(X86_FEATURE_APX);
+
kvm_cpu_cap_init(CPUID_7_2_EDX,
F(INTEL_PSFD),
F(IPRED_CTRL),
@@ -1442,7 +1446,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
switch (function) {
case 0:
/* Limited to the highest leaf implemented in KVM. */
- entry->eax = min(entry->eax, 0x24U);
+ entry->eax = min(entry->eax, 0x29U);
break;
case 1:
cpuid_entry_override(entry, CPUID_1_EDX);
@@ -1713,6 +1717,10 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
}
break;
}
+ /* APX sub-features */
+ case 0x29:
+ entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
+ break;
case KVM_CPUID_SIGNATURE: {
const u32 *sigptr = (const u32 *)KVM_SIGNATURE;
entry->eax = KVM_CPUID_FEATURES;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index eb1b2ba1261d..573411777662 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5593,6 +5593,7 @@ static __init void svm_set_cpu_caps(void)
*/
kvm_cpu_cap_clear(X86_FEATURE_BUS_LOCK_DETECT);
kvm_cpu_cap_clear(X86_FEATURE_MSR_IMM);
+ kvm_cpu_cap_clear(X86_FEATURE_APX);
kvm_setup_xss_caps();
kvm_finalize_cpu_caps();
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 18/20] KVM: x86: Expose APX sub-features to userspace
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (16 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:01 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Add CPUID leaf 0x29 sub-leaf 0 to expose these APX sub-features, if the
APX foundation is available:
* New Conditional Instructions (NCI)
* New Data Destination (NDD)
* Flags Suppression (NF)
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/cpuid.c | 7 ++++++-
arch/x86/kvm/reverse_cpuid.h | 4 ++++
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 48547842d9fa..7df964f32ba0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -868,6 +868,7 @@ enum kvm_only_cpuid_leafs {
CPUID_7_1_ECX,
CPUID_1E_1_EAX,
CPUID_24_1_ECX,
+ CPUID_29_0_EBX,
NR_KVM_CPU_CAPS,
NKVMCAPINTS = NR_KVM_CPU_CAPS - NCAPINTS,
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index db95f6b578d0..0339700e885d 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1112,6 +1112,10 @@ void kvm_initialize_cpu_caps(void)
F(AVX10_VNNI_INT),
);
+ kvm_cpu_cap_init(CPUID_29_0_EBX,
+ F(APX_NCI_NDD_NF),
+ );
+
kvm_cpu_cap_init(CPUID_8000_0001_ECX,
F(LAHF_LM),
F(CMP_LEGACY),
@@ -1719,7 +1723,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
}
/* APX sub-features */
case 0x29:
- entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
+ if (!kvm_cpu_cap_has(X86_FEATURE_APX))
+ entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
break;
case KVM_CPUID_SIGNATURE: {
const u32 *sigptr = (const u32 *)KVM_SIGNATURE;
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index de90697c4e5a..cff071ccf926 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -67,6 +67,9 @@
/* Intel-defined sub-features, CPUID level 0x00000024:1 (ECX) */
#define X86_FEATURE_AVX10_VNNI_INT KVM_X86_FEATURE(CPUID_24_1_ECX, 2)
+/* Intel-defined sub-features, CPUID level 0x00000029:0 (EBX) */
+#define X86_FEATURE_APX_NCI_NDD_NF KVM_X86_FEATURE(CPUID_29_0_EBX, 0)
+
/* CPUID level 0x80000007 (EDX). */
#define KVM_X86_FEATURE_CONSTANT_TSC KVM_X86_FEATURE(CPUID_8000_0007_EDX, 8)
@@ -110,6 +113,7 @@ static const struct cpuid_reg reverse_cpuid[] = {
[CPUID_7_1_ECX] = { 7, 1, CPUID_ECX},
[CPUID_1E_1_EAX] = { 0x1e, 1, CPUID_EAX},
[CPUID_24_1_ECX] = { 0x24, 1, CPUID_ECX},
+ [CPUID_29_0_EBX] = { 0x29, 0, CPUID_EBX},
};
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (17 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
2026-07-29 20:08 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
19 siblings, 1 reply; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Test APX-specific behavior and ABI interactions as these are unique
comparing to other state components:
* EGPR state is saved on VM entry assembly (unlike other components).
* The saved state is retained even if the guest disables APX.
* EGPR state is exposed via the XSAVE ABI while not stored in the
kernel XSAVE buffer.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V4 -> V5: Use vcpu_xsave2_get() instead of vcpu_xsave_get() (review bot [*])
[*]: locally running Sashiko with gemini-3.1-pro-preview
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/processor.h | 120 +++++++++++
tools/testing/selftests/kvm/x86/apx_test.c | 191 ++++++++++++++++++
3 files changed, 312 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86/apx_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 4ace12606e93..373d685caae2 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -162,6 +162,7 @@ TEST_GEN_PROGS_x86 += rseq_test
TEST_GEN_PROGS_x86 += steal_time
TEST_GEN_PROGS_x86 += system_counter_offset_test
TEST_GEN_PROGS_x86 += pre_fault_memory_test
+TEST_GEN_PROGS_x86 += x86/apx_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 7d3a27bc0d84..5f5acc33486a 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -107,6 +107,7 @@ struct xstate {
#define XFEATURE_MASK_LBR BIT_ULL(15)
#define XFEATURE_MASK_XTILE_CFG BIT_ULL(17)
#define XFEATURE_MASK_XTILE_DATA BIT_ULL(18)
+#define XFEATURE_MASK_APX BIT_ULL(19)
#define XFEATURE_MASK_AVX512 (XFEATURE_MASK_OPMASK | \
XFEATURE_MASK_ZMM_Hi256 | \
@@ -194,6 +195,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26)
#define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29)
#define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31)
+#define X86_FEATURE_APX KVM_X86_CPU_FEATURE(0x7, 1, EDX, 21)
#define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17)
#define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18)
#define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3)
@@ -877,6 +879,124 @@ static inline void write_sse_reg(int reg, const sse128_t *data)
}
}
+static inline unsigned long read_egpr(int reg)
+{
+ unsigned long data = 0;
+
+ /* mov %r16..%r31, %rax */
+ switch (reg) {
+ case 16:
+ asm(".byte 0xd5, 0x48, 0x89, 0xc0" : "=a"(data));
+ break;
+ case 17:
+ asm(".byte 0xd5, 0x48, 0x89, 0xc8" : "=a"(data));
+ break;
+ case 18:
+ asm(".byte 0xd5, 0x48, 0x89, 0xd0" : "=a"(data));
+ break;
+ case 19:
+ asm(".byte 0xd5, 0x48, 0x89, 0xd8" : "=a"(data));
+ break;
+ case 20:
+ asm(".byte 0xd5, 0x48, 0x89, 0xe0" : "=a"(data));
+ break;
+ case 21:
+ asm(".byte 0xd5, 0x48, 0x89, 0xe8" : "=a"(data));
+ break;
+ case 22:
+ asm(".byte 0xd5, 0x48, 0x89, 0xf0" : "=a"(data));
+ break;
+ case 23:
+ asm(".byte 0xd5, 0x48, 0x89, 0xf8" : "=a"(data));
+ break;
+ case 24:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xc0" : "=a"(data));
+ break;
+ case 25:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xc8" : "=a"(data));
+ break;
+ case 26:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xd0" : "=a"(data));
+ break;
+ case 27:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xd8" : "=a"(data));
+ break;
+ case 28:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xe0" : "=a"(data));
+ break;
+ case 29:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xe8" : "=a"(data));
+ break;
+ case 30:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xf0" : "=a"(data));
+ break;
+ case 31:
+ asm(".byte 0xd5, 0x4c, 0x89, 0xf8" : "=a"(data));
+ break;
+ default:
+ BUG();
+ }
+
+ return data;
+}
+
+static inline void write_egpr(int reg, unsigned long data)
+{
+ /* mov %%rax, %r16...%r31*/
+ switch (reg) {
+ case 16:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc0" : : "a"(data));
+ break;
+ case 17:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc1" : : "a"(data));
+ break;
+ case 18:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc2" : : "a"(data));
+ break;
+ case 19:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc3" : : "a"(data));
+ break;
+ case 20:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc4" : : "a"(data));
+ break;
+ case 21:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc5" : : "a"(data));
+ break;
+ case 22:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc6" : : "a"(data));
+ break;
+ case 23:
+ asm(".byte 0xd5, 0x18, 0x89, 0xc7" : : "a"(data));
+ break;
+ case 24:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc0" : : "a"(data));
+ break;
+ case 25:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc1" : : "a"(data));
+ break;
+ case 26:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc2" : : "a"(data));
+ break;
+ case 27:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc3" : : "a"(data));
+ break;
+ case 28:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc4" : : "a"(data));
+ break;
+ case 29:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc5" : : "a"(data));
+ break;
+ case 30:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc6" : : "a"(data));
+ break;
+ case 31:
+ asm(".byte 0xd5, 0x19, 0x89, 0xc7" : : "a"(data));
+ break;
+ default:
+ BUG();
+ }
+}
+
static inline void cpu_relax(void)
{
asm volatile("rep; nop" ::: "memory");
diff --git a/tools/testing/selftests/kvm/x86/apx_test.c b/tools/testing/selftests/kvm/x86/apx_test.c
new file mode 100644
index 000000000000..4825003d0616
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/apx_test.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "processor.h"
+
+enum stages {
+ GUEST_UPDATE,
+ USERSPACE_UPDATE,
+ GUEST_APXOFF,
+};
+
+enum egpr_ops {
+ EGPRS_WRITE,
+ EGPRS_CHECK,
+};
+
+#define for_each_egpr(reg) for (reg = 16; reg <= 31; reg++)
+
+/*
+ * Deterministic per-stage test values for EGPRs so that guest and
+ * userspace can validate state transitions.
+ */
+static inline unsigned long egpr_data(enum stages stage, int reg)
+{
+ switch (stage) {
+ case GUEST_UPDATE:
+ return 0xabcd + reg;
+ case USERSPACE_UPDATE:
+ return 0xbcde + reg;
+ case GUEST_APXOFF:
+ return 0xcdef + reg;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Read/write or validate EGPR values either directly via registers
+ * (guest context) or via a provided buffer (userspace XSAVE).
+ */
+static bool handle_egprs(enum egpr_ops ops, unsigned long *egprs, enum stages stage)
+{
+ unsigned long data;
+ int reg;
+
+ for_each_egpr(reg) {
+ data = egpr_data(stage, reg);
+
+ if (ops == EGPRS_WRITE) {
+ if (egprs)
+ egprs[reg - 16] = data;
+ else
+ write_egpr(reg, data);
+ continue;
+ }
+
+ if (ops != EGPRS_CHECK)
+ return false;
+
+ if (egprs) {
+ if (egprs[reg - 16] != data)
+ return false;
+ continue;
+ }
+
+ if (read_egpr(reg) != data)
+ return false;
+ }
+
+ return true;
+}
+
+static void write_egprs(enum stages stage)
+{
+ handle_egprs(EGPRS_WRITE, NULL, stage);
+}
+
+static bool validate_egprs(enum stages stage)
+{
+ return handle_egprs(EGPRS_CHECK, NULL, stage);
+}
+
+static void test_guest_update(void)
+{
+ write_egprs(GUEST_UPDATE);
+ GUEST_SYNC(GUEST_UPDATE);
+ GUEST_ASSERT(validate_egprs(GUEST_UPDATE));
+}
+
+static void test_userspace_update(void)
+{
+ /* Userspace updates EGPR state via the KVM XSAVE ABI */
+ GUEST_SYNC(USERSPACE_UPDATE);
+ GUEST_ASSERT(validate_egprs(USERSPACE_UPDATE));
+}
+
+static void test_guest_apxoff(void)
+{
+ write_egprs(GUEST_APXOFF);
+ /* Disable APX to verify state is preserved */
+ GUEST_ASSERT(!xsetbv_safe(0, this_cpu_supported_xcr0() & ~XFEATURE_MASK_APX));
+ GUEST_SYNC(GUEST_APXOFF);
+ GUEST_ASSERT(!xsetbv_safe(0, this_cpu_supported_xcr0()));
+ GUEST_ASSERT(validate_egprs(GUEST_APXOFF));
+}
+
+static void guest_code(void)
+{
+ set_cr4(get_cr4() | X86_CR4_OSXSAVE);
+ GUEST_ASSERT(!xsetbv_safe(0, this_cpu_supported_xcr0()));
+
+ test_guest_update();
+ test_userspace_update();
+ test_guest_apxoff();
+
+ GUEST_DONE();
+}
+
+#define X86_PROPERTY_XSTATE_APX_OFFSET KVM_X86_CPU_PROPERTY(0xd, 19, EBX, 0, 31)
+#define XSAVE_HDR_OFFSET 512
+
+static inline unsigned long *xsave_egprs(void *xsave)
+{
+ return xsave + kvm_cpu_property(X86_PROPERTY_XSTATE_APX_OFFSET);
+}
+
+static inline void xstatebv_set(void *xsave, uint64_t mask)
+{
+ *(uint64_t *)(xsave + XSAVE_HDR_OFFSET) |= mask;
+}
+
+static void write_xsave_egprs(void *xsave, enum stages stage)
+{
+ handle_egprs(EGPRS_WRITE, xsave_egprs(xsave), stage);
+ xstatebv_set(xsave, XFEATURE_MASK_APX);
+}
+
+static bool validate_xsave_egprs(void *xsave, enum stages stage)
+{
+ return handle_egprs(EGPRS_CHECK, xsave_egprs(xsave), stage);
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_xsave *xsave;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ enum stages stage;
+ struct ucall uc;
+ int xsave_size;
+
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_APX));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ xsave_size = vm_check_cap(vcpu->vm, KVM_CAP_XSAVE2);
+ TEST_ASSERT(xsave_size, "KVM_CAP_XSAVE2 not supported");
+ xsave = malloc(xsave_size);
+ TEST_ASSERT(xsave, "Failed to allocate XSAVE buffer");
+
+ while (1) {
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC: {
+ stage = uc.args[1];
+ vcpu_xsave2_get(vcpu, xsave);
+ if (stage == USERSPACE_UPDATE) {
+ write_xsave_egprs(xsave, stage);
+ } else {
+ TEST_ASSERT(validate_xsave_egprs(xsave, stage),
+ "EGPR state mismatch in userspace XSAVE buffer");
+ }
+ vcpu_xsave_set(vcpu, xsave);
+ break;
+ }
+ case UCALL_DONE:
+ goto done;
+ default:
+ TEST_FAIL("Unknown ucall %lu", uc.cmd);
+ }
+ }
+
+done:
+ free(xsave);
+ kvm_vm_free(vm);
+ return 0;
+}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v6 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
` (18 preceding siblings ...)
2026-07-29 19:16 ` [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
@ 2026-07-29 19:16 ` Chang S. Bae
19 siblings, 0 replies; 32+ messages in thread
From: Chang S. Bae @ 2026-07-29 19:16 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, chang.seok.bae
Now that KVM exposes the APX feature to guests, extend some existing
selftests to validate XCR0 configuration and state management.
Since APX repurposes the XSAVE area previously used by MPX in the
non-compacted format, add a check to ensure that MPX state is not set
when APX is enabled.
Also, load non-init APX state data in the guest so that XSTATE_BV[APX] is
set, allowing validation of APX state testing.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
tools/testing/selftests/kvm/x86/state_test.c | 3 +++
.../selftests/kvm/x86/xcr0_cpuid_test.c | 19 +++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c
index 4a1056a6cb8d..9727dbb2b981 100644
--- a/tools/testing/selftests/kvm/x86/state_test.c
+++ b/tools/testing/selftests/kvm/x86/state_test.c
@@ -164,6 +164,9 @@ static void __attribute__((__flatten__)) guest_code(void *arg)
asm volatile ("vmovupd %0, %%zmm16" :: "m" (buffer));
}
+ if (supported_xcr0 & XFEATURE_MASK_APX)
+ write_egpr(16, 0xcccccccc);
+
if (this_cpu_has(X86_FEATURE_MPX)) {
u64 bounds[2] = { 10, 0xffffffffull };
u64 output[2] = { };
diff --git a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
index 40dc9e6b3fad..f74978ef5951 100644
--- a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
+++ b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
@@ -46,6 +46,20 @@ do { \
__supported, (xfeatures)); \
} while (0)
+/*
+ * Verify that mutually exclusive architectural features do not overlap.
+ * For example, APX and MPX must never be reported as supported together.
+ */
+#define ASSERT_XFEATURE_CONFLICT(supported_xcr0, xfeatures, conflicts) \
+do { \
+ uint64_t __supported = (supported_xcr0) & ((xfeatures) | (conflicts)); \
+ \
+ __GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \
+ !(__supported & (conflicts)), \
+ "supported = 0x%lx, xfeatures = 0x%llx, conflicts = 0x%llx", \
+ __supported, (xfeatures), (conflicts)); \
+} while (0)
+
static void guest_code(void)
{
u64 initial_xcr0;
@@ -79,6 +93,11 @@ static void guest_code(void)
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
XFEATURE_MASK_XTILE);
+ /* Check APX by ensuring MPX is not exposed concurrently */
+ ASSERT_XFEATURE_CONFLICT(supported_xcr0,
+ XFEATURE_MASK_APX,
+ XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
+
vector = xsetbv_safe(0, XFEATURE_MASK_FP);
__GUEST_ASSERT(!vector,
"Expected success on XSETBV(FP), got %s",
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions
2026-07-29 19:16 ` [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
@ 2026-07-29 19:58 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 19:58 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly added EVEX prefix check evaluates an uninitialized or stale `ctxt->modrm` byte in 32-bit mode and is entirely redundant because KVM already maps opcode 0x62 to `NotImpl`.
--
--- Patch [13]: [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions ---
commit 086b81f7f3526e32d2a152bf460681542a47ce74
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: Reject EVEX-prefixed instructions
This commit explicitly marks EVEX-prefixed opcodes (0x62) as unsupported
in the x86 instruction emulator.
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index cb075bd74f769..fc7f9f17c1da4 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -5045,6 +5045,11 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
> if (opcode.flags & ModRM)
> ctxt->modrm = insn_fetch(u8, ctxt);
>
> + /* EVEX-prefixed instructions are not implemented */
> + if (ctxt->opcode_len == 1 && ctxt->b == 0x62 &&
> + (mode == X86EMUL_MODE_PROT64 || (ctxt->modrm & 0xc0) == 0xc0))
[Severity: Medium]
Could this evaluate an uninitialized or stale ctxt->modrm?
Looking at x86_decode_insn(), since opcode 0x62 is already mapped to
NotImpl in the KVM opcode_table, the opcode.flags & ModRM condition will
be false, and ctxt->modrm will not be fetched just above this check.
When evaluating this in 32-bit mode, would it end up reading an
uninitialized struct member, even though the instruction is already
set to be rejected anyway?
> + opcode.flags = NotImpl;
> +
> done_modrm:
> ctxt->d = opcode.flags;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=13
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 08/20] KVM: VMX: Support instruction information extension
2026-07-29 19:16 ` [PATCH v6 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
@ 2026-07-29 20:00 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:00 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The new EXTENDED_INSTRUCTION_INFO (0x2406) field is read unconditionally based on host CPU capability but is not added to nested VMX (vmcs12) or Enlightened VMCS (eVMCS) tracking structures, breaking instruction parsing for L1 hypervisors.
- [High] Guest-triggerable WARN_ON_ONCE Denial of Service via APX extended register parsing when KVM is compiled without CONFIG_KVM_APX.
--
--- Patch [8]: [PATCH v6 08/20] KVM: VMX: Support instruction information extension ---
commit 79c771b60a674c5dc19ac6cc97b8e80b339be936
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: VMX: Support instruction information extension
Define the VMCS field offset for the extended instruction information.
Then, support 5-bit register indices retrieval from VMCS fields.
Link: https://lore.kernel.org/aakEsXJgO-3m2xca@google.com
> diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
> index f3b0ec7ccb03a..2f53338f84f0b 100644
> --- a/arch/x86/kvm/vmx/vmx.h
> +++ b/arch/x86/kvm/vmx/vmx.h
> @@ -323,9 +323,18 @@ static __always_inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu)
> return vt->exit_qualification;
> }
>
> +/*
> + * The APX enumeration guarantees the presence of the extended fields.
> + * The host CPUID bit alone is sufficient to rely on it.
> + */
> +static inline bool vmx_instr_info_extended(void)
> +{
> + return static_cpu_has(X86_FEATURE_APX);
> +}
> +
> static inline int vmx_get_exit_qual_reg(unsigned long exit_qualification)
> {
> - return (exit_qualification >> 8) & 0xf;
> + return (exit_qualification >> 8) & (vmx_instr_info_extended() ? 0x1f : 0xf);
> }
[Severity: High]
Does this expose the host to a guest-triggerable warning if KVM is
compiled without APX support?
If the host hardware supports APX but the KVM software configuration does
not, NR_VCPU_GENERAL_PURPOSE_REGS remains 16. A malicious guest could
execute a trapping APX instruction using an extended register, such as
r16.
This would cause vmx_get_exit_qual_reg to extract a 5-bit register index
greater than or equal to 16. When passed to kvm_register_read, it would
trigger the bounds check:
arch/x86/kvm/regs.h:kvm_register_read_raw() {
if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_GENERAL_PURPOSE_REGS))
return 0;
...
}
This allows an unprivileged guest to spam the host kernel log or cause a
host panic if panic_on_warn is set. Should this check also verify KVM's
software capability for APX?
[ ... ]
> @@ -707,20 +716,22 @@ static inline bool vmx_guest_state_valid(struct kvm_vcpu *vcpu)
>
> void dump_vmcs(struct kvm_vcpu *vcpu);
>
> -/* A placeholder to smoothen 64-bit extension */
> static inline u64 vmx_get_instr_info(void)
> {
> - return vmcs_read32(VMX_INSTRUCTION_INFO);
> + return vmx_instr_info_extended() ? vmcs_read64(EXTENDED_INSTRUCTION_INFO) :
> + vmcs_read32(VMX_INSTRUCTION_INFO);
> }
[Severity: High]
Does reading EXTENDED_INSTRUCTION_INFO unconditionally on APX-capable hosts
break nested virtualization and enlightened VMCS?
When KVM runs a nested L1 hypervisor on an APX host, L0's prepare_vmcs12
only copies the legacy VMX_INSTRUCTION_INFO. The new
EXTENDED_INSTRUCTION_INFO is missing from vmcs12.
Additionally, if L1 attempts to read EXTENDED_INSTRUCTION_INFO via VMREAD,
L0 will fail the lookup in handle_vmread and inject a VM-fail because the
field is entirely missing from KVM's supported vmcs12 fields:
arch/x86/kvm/vmx/nested.c:handle_vmread() {
offset = get_vmcs12_field_offset(field);
if (offset < 0)
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
...
}
Similarly, if KVM itself runs as L1 on Hyper-V, reading this field
fails to find the eVMCS offset mapping in get_evmcs_offset, triggering a
WARN_ONCE and returning 0.
Do we need to add EXTENDED_INSTRUCTION_INFO to the nested VMX and eVMCS
tracking structures?
>
> static inline int vmx_get_instr_info_reg(u64 instr_info)
> {
> - return (instr_info >> 3) & 0xf;
> + return vmx_instr_info_extended() ? (instr_info >> 16) & 0x1f :
> + (instr_info >> 3) & 0xf;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=8
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 18/20] KVM: x86: Expose APX sub-features to userspace
2026-07-29 19:16 ` [PATCH v6 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
@ 2026-07-29 20:01 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:01 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] KVM fails to mask host CPUID leaf 0x29 with its virtualization capabilities, unconditionally exposing future host hardware features to userspace.
--
--- Patch [18]: [PATCH v6 18/20] KVM: x86: Expose APX sub-features to userspace ---
commit 858481784d49e0b77ae999aa39a6b6f4ada7e521
Author: Chang S. Bae <chang.seok.bae@intel.com>
Subject: KVM: x86: Expose APX sub-features to userspace
This commit adds CPUID leaf 0x29 sub-leaf 0 to expose APX sub-features
(NCI, NDD, NF) to userspace if the APX foundation is available.
> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
> --- a/arch/x86/kvm/cpuid.c
> +++ b/arch/x86/kvm/cpuid.c
[ ... ]
> @@ -1719,7 +1723,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
> }
> /* APX sub-features */
> case 0x29:
> - entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
> + if (!kvm_cpu_cap_has(X86_FEATURE_APX))
> + entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
[Severity: High]
If the host supports X86_FEATURE_APX, does __do_cpuid_func() bypass masking
the KVM CPU capabilities for leaf 0x29?
When kvm_cpu_cap_has(X86_FEATURE_APX) is true, this path breaks out without
applying cpuid_entry_override(entry, CPUID_29_0_EBX) or clearing the unused
registers (eax, ecx, edx).
Could this leak unmasked host CPUID values for leaf 0x29 directly to
userspace when KVM_GET_SUPPORTED_CPUID is called? Exposing unvetted
hardware features might break guest live migration to older hosts.
> break;
> case KVM_CPUID_SIGNATURE: {
> const u32 *sigptr = (const u32 *)KVM_SIGNATURE;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=18
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information
2026-07-29 19:16 ` [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
@ 2026-07-29 20:01 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:01 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The patch series reads EXTENDED_INSTRUCTION_INFO using vmcs_read64() if APX is supported, but fails to map this field for enlightened VMCS (eVMCS), triggering a host kernel WARNING and corrupting instruction emulation when KVM runs on Hyper-V.
- [Medium] The logic to propagate EXTENDED_INSTRUCTION_INFO is dead code because KVM's CPUID capability tracking never initializes or exposes X86_FEATURE_APX.
--
--- Patch [9]: [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information ---
commit 649ca5041fdd2df30db144e44ba02783451c334f
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: nVMX: Propagate extended instruction information
Define the new extended_instruction_info field in struct vmcs12 and
propagate it to nested VMX. Gate the propagation on the guest APX
enumeration, which aligns with bare metal behavior.
Link: https://lore.kernel.org/aRvOSnaUt1E+%2FpkC@intel.com
> diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
> index 657f5f743ed97..de90697c4e5a0 100644
> --- a/arch/x86/kvm/reverse_cpuid.h
> +++ b/arch/x86/kvm/reverse_cpuid.h
[ ... ]
> @@ -35,6 +35,7 @@
> #define X86_FEATURE_AVX_VNNI_INT16 KVM_X86_FEATURE(CPUID_7_1_EDX, 10)
> #define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14)
> #define X86_FEATURE_AVX10 KVM_X86_FEATURE(CPUID_7_1_EDX, 19)
> +#define KVM_X86_FEATURE_APX KVM_X86_FEATURE(CPUID_7_1_EDX, 21)
>
> /* Intel-defined sub-features, CPUID level 0x00000007:2 (EDX) */
> #define X86_FEATURE_INTEL_PSFD KVM_X86_FEATURE(CPUID_7_2_EDX, 0)
[ ... ]
> diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
> index 8e0523e40477d..1684f5d1dbf4d 100644
> --- a/arch/x86/kvm/vmx/nested.c
> +++ b/arch/x86/kvm/vmx/nested.c
[ ... ]
> @@ -4766,6 +4766,12 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
> vmcs12->vm_exit_intr_info = exit_intr_info;
> vmcs12->vm_exit_instruction_len = exit_insn_len;
> vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
> + /*
> + * The APX enumeration guarantees the presence of the extended
> + * fields. This CPUID bit alone is sufficient to rely on it.
> + */
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_APX))
[Severity: Medium]
Will this condition ever evaluate to true?
It appears X86_FEATURE_APX is defined in arch/x86/kvm/reverse_cpuid.h but is
never enabled in the capabilities mask in cpuid.c (e.g., kvm_cpu_caps).
Without initializing it there, it seems this feature will be stripped from
vcpu->arch.cpu_caps, making this block dead code.
> + vmcs12->extended_instruction_info = vmcs_read64(EXTENDED_INSTRUCTION_INFO);
[Severity: High]
Does vmcs_read64() handle EXTENDED_INSTRUCTION_INFO correctly when KVM is
running on Hyper-V with enlightened VMCS (eVMCS) enabled?
Since EXTENDED_INSTRUCTION_INFO isn't mapped for eVMCS, attempting to read
it here (once the dead code issue is fixed) will trigger a host kernel
WARNING and return 0.
This could corrupt the instruction emulation state and cause a
guest-triggerable host log denial of service when an L2 guest executes an
instruction that induces a VM-exit reading instruction info.
>
> /*
> * According to spec, there's no need to store the guest's
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=9
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access
2026-07-29 19:16 ` [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
@ 2026-07-29 20:02 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:02 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incomplete refactoring of VMX instruction information access leaves brittle hardcoded bit shifts.
--
--- Patch [4]: [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access ---
commit b0d7d66df897ae19fd14659a411d846da0d89653
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: VMX: Refactor VMX instruction information access
Introduce a helper that returns the instruction information as a 64-bit
value and adjust existing sites to prepare for a wider field.
> diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
> index 0635e92471c89..92b530baa0ee9 100644
> --- a/arch/x86/kvm/vmx/nested.c
> +++ b/arch/x86/kvm/vmx/nested.c
> @@ -5234,7 +5234,7 @@ static void nested_vmx_triple_fault(struct kvm_vcpu *vcpu)
> * #UD, #GP, or #SS.
> */
> int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
> - u32 vmx_instruction_info, bool wr, int len, gva_t *ret)
> + u64 vmx_instruction_info, bool wr, int len, gva_t *ret)
> {
[Severity: Medium]
This is a pre-existing issue, but while we are transitioning this function
to take a u64, should we also update the memory operand extraction logic
inside it?
It currently contains open-coded bit shifts that might not handle the wider
format correctly when extended register IDs are introduced:
int index_reg = (vmx_instruction_info >> 18) & 0xf;
bool index_is_valid = !(vmx_instruction_info & (1u << 22));
int base_reg = (vmx_instruction_info >> 23) & 0xf;
[ ... ]
> @@ -6428,7 +6429,7 @@ static bool nested_vmx_exit_handled_encls(struct kvm_vcpu *vcpu,
> static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
> struct vmcs12 *vmcs12, gpa_t bitmap)
> {
> - u32 vmx_instruction_info;
> + u64 vmx_instruction_info;
> unsigned long field;
> u8 b;
>
> @@ -6436,7 +6437,7 @@ static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
> return true;
>
> /* Decode instruction info and find the field to access */
> - vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
> + vmx_instruction_info = vmx_get_instr_info();
> field = kvm_register_read(vcpu, (((vmx_instruction_info) >> 28) & 0xf));
[Severity: Medium]
Since the goal is to prepare existing sites for a wider field, should this
open-coded bit shift be converted to use the vmx_get_instr_info_reg2() helper?
If the 64-bit extension changes register bit offsets in the future, this
hardcoded shift could read incorrect bits.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=4
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs
2026-07-29 19:16 ` [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
@ 2026-07-29 20:04 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:04 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch extends the VCPU register array (`vcpu->arch.regs`) for APX Extended General-Purpose Registers (R16-R31) but omits the required synchronization logic with the guest hardware or XSAVE state, leading to broken register caching and silent guest state corruption.
--
--- Patch [1]: [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs ---
commit c513074e3dcbca9bda2fa54321ecd4e8c1d2229b
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: Extend VCPU registers for EGPRs
This commit extends the VCPU register storage array to include APX Extended
General-Purpose Registers (EGPRs R16-R31) for VMX when KVM_APX is enabled.
It uses VCPU storage to provide an access path for KVM instead of directly
reading the guest fpstate.
Link: https://lore.kernel.org/abL8SW5JS1aV5goa@google.com
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index b517257a63153..48547842d9fa0 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
[ ... ]
> @@ -178,6 +178,24 @@ enum kvm_reg {
> VCPU_REGS_R13,
> VCPU_REGS_R14,
> VCPU_REGS_R15,
> +#endif
> +#ifdef CONFIG_KVM_APX
> + VCPU_REGS_R16,
> + VCPU_REGS_R17,
[Severity: High]
Does adding these registers before NR_VCPU_GENERAL_PURPOSE_REGS break KVM's
register caching assumptions?
KVM assumes all registers defined before NR_VCPU_GENERAL_PURPOSE_REGS are
either eagerly saved and restored by assembly context switches or explicitly
defined in the lazy load set. Because R16-R31 are not in the lazy load set,
KVM's initialization will set their availability bits to 1, assuming they are
always synchronized by hardware context switches.
However, the context switch assembly only saves and restores up to R15 and
does not touch R16-R31. When the KVM host emulator accesses R16-R31 to
emulate an instruction, won't it read stale or uninitialized data from the
software array?
Furthermore, if KVM writes to these registers during emulation, won't those
writes be silently lost upon resuming the guest since they are never
synchronized back to the guest's XSAVE state?
> + VCPU_REGS_R18,
> + VCPU_REGS_R19,
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=1
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes
2026-07-29 19:16 ` [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
@ 2026-07-29 20:04 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:04 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Incomplete NoRex2 enforcement on legacy instructions and architectural flaw in flag checking prevents group instruction support.
- [Medium] Missing NoRex2 flag on legacy SIMD instructions creates a future Guest-to-Host DoS (Kernel Panic) via out-of-bounds register read.
--
--- Patch [11]: [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes ---
commit 60f040dc7da6f4fa4728dd8cf6193faca78101de
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: Handle EGPR index and REX2-incompatible opcodes
Prepare the emulator for REX2 handling by introducing the NoRex2 opcode
flag and supporting extended register indices. For the latter, factor out
common logic for calculating register IDs.
Link: https://lore.kernel.org/1ebf3a23-5671-41c1-8daa-c83f2f105936@redhat.com
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index b566ab5c75156..12a822870d08e 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
[ ... ]
> @@ -1118,7 +1129,7 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
> if (ctxt->d & ModRM)
> reg = ctxt->modrm_reg;
> else
> - reg = (ctxt->b & 7) | (ctxt->rex_bits & REX_B ? 8 : 0);
> + reg = (ctxt->b & 7) | rex_get_rxb(ctxt->rex_bits, REX_B);
>
> __decode_register_operand(ctxt, op, reg);
> }
[Severity: Medium]
Are legacy SSE and AVX instructions (e.g., MOVAPS at 0x0F 0x28 in
twobyte_table) missing the NoRex2 flag in the emulation tables?
If a guest executes a REX2-prefixed legacy SIMD instruction, the new
rex_get_rxb() logic would extract extended bits, producing a register
index between 16 and 31.
During operand decoding, this out-of-bounds index is passed along:
arch/x86/kvm/emulate.c:__decode_register_operand() {
...
if (ctxt->d & (Avx|Sse)) {
op->type = OP_XMM;
op->bytes = 16;
op->addr.xmm = reg;
kvm_read_sse_reg(reg, &op->vec_val);
...
}
Will this pass the out-of-bounds index (16-31) to kvm_read_sse_reg()?
The underlying accessor _kvm_read_sse_reg() only handles indices 0-15 and
hits a default BUG() case for anything higher, which would result in a host
kernel panic.
[ ... ]
> @@ -4994,6 +5005,13 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
> opcode = opcode_table[ctxt->b];
> }
>
> + /*
> + * Instructions marked with NoRex2 ignore a legacy REX prefix, but #UD
> + * should be raised when prefixed with REX2.
> + */
> + if (opcode.flags & NoRex2 && ctxt->rex_prefix == REX2_PREFIX)
> + opcode.flags = Undefined;
> +
> if (opcode.flags & ModRM)
> ctxt->modrm = insn_fetch(u8, ctxt);
>
[Severity: Medium]
Since this check is performed immediately after fetching the base opcode,
but before the while (ctxt->d & GroupMask) loop resolves nested group
instructions (such as call far in group 5), will this fail to enforce
the NoRex2 restriction on instructions nested within a group?
Should this check be evaluated against ctxt->d after the group decoding
loop instead?
Also, numerous legacy instructions (e.g., cpuid, hlt, cli, sti, pushf,
popf) appear to be missing the NoRex2 flag in the twobyte_table and
opcode_table, which might allow erroneous emulation instead of injecting
an undefined opcode exception.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=11
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache
2026-07-29 19:16 ` [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
@ 2026-07-29 20:05 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:05 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Live migration of APX-enabled guests results in silent guest state corruption (loss of EGPRs) due to a conflict between manual VCPU register caching and XSAVE state management.
--
--- Patch [2]: [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache ---
commit 8d65deb78b6fe07e8687f8aa1c29bd4e028cfac7
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: VMX: Save guest EGPRs in VCPU cache
This patch saves and restores the guest EGPRs on VM exit/entry if the system
supports Advanced Performance Extensions (APX). It saves the EGPR state into
vcpu->arch.regs and clears the hardware registers on exit.
Link: https://lore.kernel.org/adPRA4ZhnvbaXSn0@google.com
> diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S
> index 00d807505fc88..702d86ade6b1d 100644
> --- a/arch/x86/kvm/vmx/vmenter.S
> +++ b/arch/x86/kvm/vmx/vmenter.S
[ ... ]
> @@ -193,8 +204,24 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL)
> mov %_ASM_BX, %_ASM_AX
>
> /* Pop our saved arguments from the stack */
> - pop %_ASM_BX
> - pop %_ASM_BX
> + pop %_ASM_BX /* @flags */
> + pop %_ASM_DI /* @vmx */
> +
> +#ifdef CONFIG_KVM_APX
> + ALTERNATIVE "jmp .Lclear_egprs_done", "", X86_FEATURE_APX
> + test $KVM_ENTER_EGPR_SWITCH, %_ASM_BX
> + jz .Lclear_egprs_done
> + /*
> + * Unlike legacy GPRs, saving could be conditional here on VM-Fail,
> + * which however isn't in fastpath. Instead, simply saving EGPRs always.
> + */
> + STORE_REGS %_ASM_DI, VMX_vcpu_arch_regs, \
> + %r16, %r17, %r18, %r19, %r20, %r21, %r22, %r23, \
> + %r24, %r25, %r26, %r27, %r28, %r29, %r30, %r31
> + CLEAR_REGS %r16d, %r17d, %r18d, %r19d, %r20d, %r21d, %r22d, %r23d, \
> + %r24d, %r25d, %r26d, %r27d, %r28d, %r29d, %r30d, %r31d
[Severity: High]
Does clearing the hardware EGPRs here cause state loss during live migration?
When KVM later unloads the vCPU and calls kvm_put_guest_fpu(), it uses the
hardware XSAVES instruction to capture the guest's FPU/extended state into
vcpu->arch.guest_fpu. Because the hardware EGPRs were zeroed here, XSAVES
populates the APX component of the XSAVE buffer with zeros.
When userspace subsequently calls KVM_GET_XSAVE2 to serialize the state for
migration, it exports the zeroed guest_fpu without synchronizing the true
register values from vcpu->arch.regs.
Additionally, userspace cannot fallback to KVM_GET_REGS because the UAPI
struct kvm_regs hasn't been updated to include R16-R31.
Could this lead to APX-enabled guests resuming with zeroed EGPRs after a
migration?
> +.Lclear_egprs_done:
> +#endif
>
> /* ... and then the callee-save registers */
> pop %_ASM_BX
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=2
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0
2026-07-29 19:16 ` [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
@ 2026-07-29 20:06 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:06 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Premature exposure of APX XSTATE support to userspace via CPUID leaf 0xD.
--
--- Patch [16]: [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0 ---
commit 1240d82e8b8dccfbe1fa4527616e2e4b0d49ebec
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: Add APX to supported XCR0
This commit modifies KVM to include the APX state bit in its supported XCR0
capabilities when the host has APX enabled. This is done conditionally
based on CONFIG_KVM_APX.
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 266d7a02855bb..48f8a2d699879 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -7082,6 +7082,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
> if (boot_cpu_has(X86_FEATURE_XSAVE)) {
> kvm_host.xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
> kvm_caps.supported_xcr0 = kvm_host.xcr0 & KVM_SUPPORTED_XCR0;
> + if (IS_ENABLED(CONFIG_KVM_APX))
> + kvm_caps.supported_xcr0 |= kvm_host.xcr0 & XFEATURE_MASK_APX;
[Severity: Medium]
The commit notes mention separating this out from userspace exposure, but
doesn't adding this flag to kvm_caps.supported_xcr0 directly expose APX to
userspace via KVM_GET_SUPPORTED_CPUID for leaf 0xD?
If userspace tools pass this leaf to the guest, would this allow the guest to
discover and enable APX state saving via XSETBV without standard capability
gating? This seems to conflict with KVM's expectation that new guest-visible
features default off and remain enumerable through standard interfaces.
> }
>
> if (boot_cpu_has(X86_FEATURE_XSAVES)) {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=16
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test
2026-07-29 19:16 ` [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
@ 2026-07-29 20:08 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:08 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The inline assembly in `read_egpr` lacks the `volatile` qualifier, allowing the compiler to cache or incorrectly reorder hardware register reads.
--
--- Patch [19]: [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test ---
commit b6d4e7dba224ce722415441a2fa9a8f67f8cb213
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: selftests: Add APX state and ABI test
This commit adds a selftest to verify APX-specific behavior and ABI
interactions for EGPR state. It tests state transitions across guest
updates, userspace updates, and guest APX disable scenarios.
> diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
> index 7d3a27bc0d842..5f5acc33486ab 100644
> --- a/tools/testing/selftests/kvm/include/x86/processor.h
> +++ b/tools/testing/selftests/kvm/include/x86/processor.h
[ ... ]
> +static inline unsigned long read_egpr(int reg)
> +{
> + unsigned long data = 0;
> +
> + /* mov %r16..%r31, %rax */
> + switch (reg) {
> + case 16:
> + asm(".byte 0xd5, 0x48, 0x89, 0xc0" : "=a"(data));
[Severity: Medium]
Since the inline assembly in read_egpr() has an output operand but no
inputs, could the compiler aggressively optimize these reads?
Without the volatile qualifier, GCC treats an asm block with only outputs
as a pure function. This means the compiler might cache the result across
synchronization points, as a memory clobber in GUEST_SYNC wouldn't
invalidate pure register-based expressions.
Would adding the volatile keyword here be safer to ensure the test
always validates the actual CPU state rather than a potentially cached
compiler value?
> + break;
> + case 17:
> + asm(".byte 0xd5, 0x48, 0x89, 0xc8" : "=a"(data));
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=19
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI
2026-07-29 19:16 ` [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
@ 2026-07-29 20:31 ` sashiko-bot
0 siblings, 0 replies; 32+ messages in thread
From: sashiko-bot @ 2026-07-29 20:31 UTC (permalink / raw)
To: Chang S. Bae; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] State Leak / Incomplete Reset for APX EGPRs
- [High] APX Support is Dead Code / Broken via XCR0 Masking
Pre-existing issues:
- [High] ABI Breakage of KVM_GET_XSAVE on CPUs with APX
--
--- Patch [3]: [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI ---
commit 4bbfbdc2e52df0cb9917db88fdc15027d1a8a890
Author: Chang S. Bae <chang.seok.bae@intel.com>
KVM: x86: Support APX state for XSAVE ABI
This commit introduces a facility to copy APX state between the VCPU cache and
the userspace buffer. It handles the APX EGPRs specifically, copying them
to and from vcpu->arch.regs when the APX feature bit is set.
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 0626e835e9eb7..5578df10179ae 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
[ ... ]
> @@ -3124,6 +3124,49 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
> return 0;
> }
>
> +#ifdef CONFIG_KVM_APX
> +static void kvm_copy_vcpu_regs_to_uabi(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0)
> +{
> + union fpregs_state *xstate = (union fpregs_state *)buf;
> +
> + BUILD_BUG_ON(NR_VCPU_GENERAL_PURPOSE_REGS <= VCPU_REGS_R31);
> +
> + if (!(supported_xcr0 & XFEATURE_MASK_APX))
> + return;
> +
> + memcpy(buf + xstate_offset(XFEATURE_APX),
> + &vcpu->arch.regs[VCPU_REGS_R16],
> + xstate_size(XFEATURE_APX));
> +
> + xstate->xsave.header.xfeatures |= XFEATURE_MASK_APX;
> +}
> +
> +static int kvm_copy_uabi_to_vcpu_regs(struct kvm_vcpu *vcpu, void *buf, u64 supported_xcr0)
> +{
> + union fpregs_state *xstate = (union fpregs_state *)buf;
> +
> + if (!(xstate->xsave.header.xfeatures & XFEATURE_MASK_APX))
> + return 0;
[Severity: High]
Does this early return inadvertently leak the values of APX extended GPRs
across VM resets or migrations?
If userspace calls KVM_SET_XSAVE with XFEATURE_MASK_APX cleared in the
XSAVE header, the XSAVE INIT state semantics dictate that the corresponding
state should be reset. By returning 0 early here, KVM fails to zero out
vcpu->arch.regs[VCPU_REGS_R16] through vcpu->arch.regs[VCPU_REGS_R31]. KVM
will then manually load these stale register values into the CPU during
VM-entry.
> +
> + if (!(supported_xcr0 & XFEATURE_MASK_APX))
> + return -EINVAL;
[Severity: High]
Does this code inadvertently make APX support dead code by failing to add
XFEATURE_MASK_APX to KVM_SUPPORTED_XCR0?
Because XFEATURE_MASK_APX is omitted from KVM_SUPPORTED_XCR0, it is stripped
from KVM's capabilities. This means supported_xcr0 will never contain the APX
bit, causing KVM_SET_XSAVE to unconditionally fail with -EINVAL here, and
preventing APX state from being exported during KVM_GET_XSAVE2.
> +
> + BUILD_BUG_ON(NR_VCPU_GENERAL_PURPOSE_REGS <= VCPU_REGS_R31);
> +
> + memcpy(&vcpu->arch.regs[VCPU_REGS_R16],
> + buf + xstate_offset(XFEATURE_APX),
> + xstate_size(XFEATURE_APX));
> +
> + return 0;
> +}
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but there is an ABI breakage with
KVM_GET_XSAVE on CPUs with APX that affects this feature area.
Because APX is not defined as an XFD dynamic feature, it is unconditionally
included in fpu_user_cfg.default_features. On CPUs supporting both AMX and APX,
the uncompacted offset of APX pushes fpu_user_cfg.default_size over 4096 bytes.
During VCPU creation in fpu_alloc_guest_fpstate():
gfpu->uabi_size = sizeof(struct kvm_xsave);
if (WARN_ON_ONCE(fpu_user_cfg.default_size > gfpu->uabi_size))
gfpu->uabi_size = fpu_user_cfg.default_size;
This inflates gfpu->uabi_size to match default_size. Later, calls to the
legacy KVM_GET_XSAVE ioctl unconditionally fail with -EINVAL because they
enforce uabi_size <= sizeof(struct kvm_xsave) (4096 bytes), breaking backwards
compatibility for older VMMs.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729191656.598771-1-chang.seok.bae@intel.com?part=3
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2026-07-29 20:31 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 19:16 [PATCH v6 00/20] KVM: x86: Enable APX for guests Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
2026-07-29 20:04 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
2026-07-29 20:05 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
2026-07-29 20:31 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
2026-07-29 20:02 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
2026-07-29 20:00 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
2026-07-29 20:01 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
2026-07-29 20:04 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
2026-07-29 19:58 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
2026-07-29 20:06 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
2026-07-29 19:16 ` [PATCH v6 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
2026-07-29 20:01 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
2026-07-29 20:08 ` sashiko-bot
2026-07-29 19:16 ` [PATCH v6 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.