mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v8 00/20] KVM: x86: Enable APX for guests
@ 2026-09-30 21:07 Chang S. Bae
  2026-09-30 21:07 ` [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
                   ` (20 more replies)
  0 siblings, 21 replies; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, chang.seok.bae

Hi Paolo,

Here is a rebase onto the KVM next tree including Zhao's test tag.

I expect Sashiko to reply to a bunch of patches. I previously found some
false positives, so I will comment on them again in this posting.

Thanks,
Chang

References:

 * Repository: git://github.com/intel/apx.git apx-kvm_v8
 * KVM unit test: https://lore.kernel.org/20260420212355.507827-1-chang.seok.bae@intel.com
 * Previous posting, V7:
   https://lore.kernel.org/kvm/20260902021804.718676-1-chang.seok.bae@intel.com

Thanks,
Chang

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: d4b7fb647204f0c81dfeae2d1a708e4d858e0c94
-- 
2.53.0


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-05 23:42   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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 3af7395c2430..690de9606d6d 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -163,6 +163,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 538ed1e80332..db1427a8b099 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	[flat|nested] 46+ messages in thread

* [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
  2026-09-30 21:07 ` [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-05 23:52   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Link: https://lore.kernel.org/adPRA4ZhnvbaXSn0@google.com
---
 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 db1427a8b099..e55462068bed 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..02308fb36f0f 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, always save EGPRs.
+	 */
+	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 612ab07d4100..cea5c273f149 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -994,6 +994,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	[flat|nested] 46+ messages in thread

* [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
  2026-09-30 21:07 ` [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
  2026-09-30 21:07 ` [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:13   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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.

Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 arch/x86/kvm/cpuid.c | 10 ++++++++
 arch/x86/kvm/cpuid.h |  2 ++
 arch/x86/kvm/x86.c   | 61 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 851f151efb35..a3f583a376ab 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/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe473..0df74422759f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3141,6 +3141,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)
 {
@@ -3162,8 +3205,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;
 }
 
@@ -3178,6 +3228,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;
@@ -3189,6 +3240,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	[flat|nested] 46+ messages in thread

* [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (2 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:17   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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.

Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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 151873407abd..67168cd3e7af 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5242,7 +5242,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;
@@ -5374,7 +5374,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;
@@ -5666,7 +5666,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;
@@ -5772,7 +5772,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;
@@ -5961,7 +5961,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;
@@ -5989,8 +5989,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;
@@ -5998,6 +5998,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) ||
@@ -6009,7 +6010,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);
 
@@ -6069,7 +6070,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;
@@ -6090,7 +6091,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);
 
@@ -6435,7 +6436,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;
 
@@ -6443,7 +6444,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 c6de848bd9ce..6bbccaa8263a 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 cea5c273f149..74c4b9dbe29b 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6154,7 +6154,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 {
@@ -6168,7 +6168,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);
 
@@ -6319,7 +6319,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 dc8517f15bc4..4751f27f006b 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -709,12 +709,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	[flat|nested] 46+ messages in thread

* [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (3 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:19   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc
  Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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 67168cd3e7af..f05ac3c9aa12 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5242,7 +5242,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;
@@ -5250,20 +5250,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);
@@ -5679,7 +5679,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)) {
 		/*
@@ -5727,8 +5727,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,
@@ -5801,8 +5801,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,
@@ -5813,7 +5813,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)
@@ -5990,7 +5990,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;
@@ -6010,8 +6010,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;
@@ -6023,7 +6023,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)
@@ -6070,8 +6070,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 {
@@ -6091,8 +6091,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 &
@@ -6106,7 +6106,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)
@@ -6436,16 +6436,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 74c4b9dbe29b..bd8505d69420 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6154,8 +6154,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;
@@ -6168,16 +6168,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 4751f27f006b..92eed58f48ad 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -715,14 +715,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	[flat|nested] 46+ messages in thread

* [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (4 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:20   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
---
 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 3f1b3096ff04..68835ad9af71 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -445,27 +445,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	[flat|nested] 46+ messages in thread

* [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (5 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:22   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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.

Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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 68835ad9af71..f98e40df5004 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -455,7 +455,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 f05ac3c9aa12..7229934c38bb 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -6358,7 +6358,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 bd8505d69420..ee975a1ae42f 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -5698,7 +5698,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);
@@ -5816,7 +5816,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 92eed58f48ad..863b08478544 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -330,6 +330,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	[flat|nested] 46+ messages in thread

* [PATCH v8 08/20] KVM: VMX: Support instruction information extension
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (6 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:40   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.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 f98e40df5004..0b1e2353db3b 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -277,6 +277,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 863b08478544..4a76af613720 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -330,9 +330,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 cpu_feature_enabled(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)
@@ -714,20 +723,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)
@@ -737,37 +748,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	[flat|nested] 46+ messages in thread

* [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (7 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:53   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc
  Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@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 2ad25781cefb..80b41d5559da 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -37,6 +37,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)
@@ -146,6 +147,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 7229934c38bb..69302b806a98 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -4773,6 +4773,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	[flat|nested] 46+ messages in thread

* [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (8 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  0:55   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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.

Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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	[flat|nested] 46+ messages in thread

* [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (9 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  4:34   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.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 c1b21282187f..a1a567fd0378 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -176,6 +176,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)
 
@@ -245,6 +246,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)
@@ -1061,6 +1063,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)
 {
@@ -1100,7 +1111,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);
 }
@@ -1119,9 +1130,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;
@@ -4255,7 +4266,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 */
@@ -4273,7 +4284,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),
@@ -4297,15 +4308,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 */
@@ -4335,17 +4346,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),
@@ -4385,12 +4396,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 */
@@ -4408,7 +4419,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 */
@@ -5001,6 +5012,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	[flat|nested] 46+ messages in thread

* [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (10 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  4:48   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.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 a1a567fd0378..eba4e9b7cd5c 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -4266,7 +4266,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 */
@@ -4859,6 +4859,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;
@@ -4912,7 +4923,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 */
@@ -4958,6 +4969,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;
@@ -4980,6 +5002,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 */
@@ -4997,17 +5025,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	[flat|nested] 46+ messages in thread

* [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (11 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  4:56   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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 eba4e9b7cd5c..f0d5a40eba27 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -5052,6 +5052,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	[flat|nested] 46+ messages in thread

* [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init()
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (12 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  4:58   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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
---
 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 a3f583a376ab..e72543088695 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 0df74422759f..91666b868ab9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -208,19 +208,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);
@@ -7059,6 +7047,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	[flat|nested] 46+ messages in thread

* [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (13 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  5:00   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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 7d59d301e1e5..2962501c4b67 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5616,8 +5616,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 91666b868ab9..76f70d2631f6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -699,6 +699,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	[flat|nested] 46+ messages in thread

* [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (14 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  5:01   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, chang.seok.bae

Set kvm_caps.supported_xcr0[APX] when the host has enabled the APX state
along with CONFIG_KVM_APX=y.

Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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 76f70d2631f6..8c6365122efb 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7132,6 +7132,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	[flat|nested] 46+ messages in thread

* [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (15 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  5:05   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc
  Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
Tested-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 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 82eddfa2347b..f38d5e8a3c05 100644
--- a/arch/x86/include/asm/cpuid/api.h
+++ b/arch/x86/include/asm/cpuid/api.h
@@ -182,6 +182,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 e72543088695..990ad7ea4818 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),
@@ -1456,7 +1460,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);
@@ -1727,6 +1731,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 2962501c4b67..7d9314645872 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5597,6 +5597,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	[flat|nested] 46+ messages in thread

* [PATCH v8 18/20] KVM: x86: Expose APX sub-features to userspace
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (16 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-09-30 21:07 ` [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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)

Tested-by: Zhao Liu <zhao1.liu@intel.com>
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 690de9606d6d..f1e51c055c0f 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -729,6 +729,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 990ad7ea4818..6c47e0ffedd8 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),
@@ -1733,7 +1737,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 80b41d5559da..b20c8be4fea6 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -69,6 +69,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)
 
@@ -112,6 +115,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	[flat|nested] 46+ messages in thread

* [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (17 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  5:31   ` Jim Mattson
  2026-09-30 21:07 ` [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
  2026-10-01 20:29 ` [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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>
---
 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 96bab7002d39..2bd5c77047a8 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -166,6 +166,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 6e6f70035508..6e3e7d96c571 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -108,6 +108,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 | \
@@ -195,6 +196,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)
@@ -917,6 +919,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 invlpg(u64 addr)
 {
 	__asm__ __volatile__("invlpg (%0)" : : "r"(addr) : "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	[flat|nested] 46+ messages in thread

* [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (18 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
@ 2026-09-30 21:07 ` Chang S. Bae
  2026-11-06  5:36   ` Jim Mattson
  2026-10-01 20:29 ` [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-09-30 21:07 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu, 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	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 00/20] KVM: x86: Enable APX for guests
  2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
                   ` (19 preceding siblings ...)
  2026-09-30 21:07 ` [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
@ 2026-10-01 20:29 ` Chang S. Bae
  2026-11-04  0:45   ` Chang S. Bae
  20 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-10-01 20:29 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu

On 9/30/2026 2:07 PM, Chang S. Bae wrote:
> 
> I expect Sashiko to reply to a bunch of patches. I previously found some
> false positives, so I will comment on them again in this posting.
Okay, I've provided my comments there.

Thanks,
Chang

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 00/20] KVM: x86: Enable APX for guests
  2026-10-01 20:29 ` [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
@ 2026-11-04  0:45   ` Chang S. Bae
  0 siblings, 0 replies; 46+ messages in thread
From: Chang S. Bae @ 2026-11-04  0:45 UTC (permalink / raw)
  To: pbonzini, seanjc; +Cc: kvm, x86, linux-kernel, zhao1.liu

On 10/1/2026 1:29 PM, Chang S. Bae wrote:
> On 9/30/2026 2:07 PM, Chang S. Bae wrote:
>>
>> I expect Sashiko to reply to a bunch of patches. I previously found some
>> false positives, so I will comment on them again in this posting.
> Okay, I've provided my comments there.

Just for the record, for anyone interested in this series:

   Paolo at the last PUCK meeting also mentioned that he had reviewed
   those comments and that we could move forward. So the next step is
   Sean's review and his take, as things like this need to go through the
   x86 KVM tree.

There are some minor conflicts in the VMX code on -rc1. While Sashiko is
a very useful tool, the comment here has been largely repeating since 
V5, so I would avoid another posting right now just not to repeat them 
again.

In the meantime, here is my rebase on top of 7.4-rc1:
   git://github.com/intel/apx.git apx-kvm_v8-for-7.4-rc1

Thanks,
Chang

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs
  2026-09-30 21:07 ` [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
@ 2026-11-05 23:42   ` Jim Mattson
  2026-11-06  5:32     ` Chang S. Bae
  0 siblings, 1 reply; 46+ messages in thread
From: Jim Mattson @ 2026-11-05 23:42 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:35 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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 3af7395c2430..690de9606d6d 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -163,6 +163,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,

In kvm_vcpu_reset(), KVM unconditionally zeroes the entire vcpu->arch.regs
array on both RESET and INIT:

    /* All GPRs except RDX (handled below) are zeroed on RESET/INIT. */
    memset(vcpu->arch.regs, 0, sizeof(vcpu->arch.regs));

However, according to the Intel APX Architecture Specification (rev. 9.0),
section 3.1.4.1.1 and Table 3.7, R16-R31 are unchanged on INIT:

  The main difference between EGPRs and legacy GPRs, is that EGPRs are not
  enabled by default (specifically XCR0-enabled) in 64-bit mode, and their
  INIT behavior is XMM-like; in that EGPRs and many other XSAVE-enabled
  register states are un-changed on INIT events as shown in Table 3.7.

By extending NR_VCPU_GENERAL_PURPOSE_REGS to include VCPU_REGS_R16 through
VCPU_REGS_R31 without updating kvm_vcpu_reset(), KVM will erroneously zero
R16-R31 when init_event is true.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache
  2026-09-30 21:07 ` [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
@ 2026-11-05 23:52   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-05 23:52 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:35 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Link: https://lore.kernel.org/adPRA4ZhnvbaXSn0@google.com
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI
  2026-09-30 21:07 ` [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
@ 2026-11-06  0:13   ` Jim Mattson
  2026-11-06  5:33     ` Chang S. Bae
  0 siblings, 1 reply; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:13 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:02 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.
>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> ---

[ ... ]

> +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;

Sashiko is right that this early return leaves stale values in R16-R31 when
XSTATE_BV[19] is 0.

In your reply to Sashiko, you wrote:

  This matches the existing XSAVE uABI semantics. XSTATE_BV specifies
  which state components are being updated. If userspace wants to zero
  the APX state, it must include the APX bit in XSTATE_BV; omitting the
  bit means "leave this component unchanged", just like the generic
  XSAVE copy path.

That is not how XRSTOR or KVM_SET_XSAVE works. In the XSAVE architecture,
XSTATE_BV[i] = 0 indicates that state component i is in its initial
configuration (typically zeros), not that it should be left unchanged. In
copy_uabi_to_xstate(), clearing bit i in xsave->header.xfeatures causes the
subsequent XRSTOR[S] in restore_fpregs_from_fpstate() to re-initialize
component i to its hardware INIT state. As the comment in
fpu_copy_uabi_to_guest_fpstate() notes, PKRU is the *only* exception to
this rule, for historical reasons:

    /*
     * Nullify @vpkru to preserve its current value if PKRU's bit isn't set
     * in the header.  KVM's odd ABI is to leave PKRU untouched in this
     * case (all other components are eventually re-initialized).
     */

Because R16-R31 are loaded into hardware from vcpu->arch.regs rather than
from guest_fpu via XRSTOR[S], kvm_copy_uabi_to_vcpu_regs() must explicitly
zero vcpu->arch.regs[VCPU_REGS_R16..VCPU_REGS_R31] when XFEATURE_MASK_APX
is clear in xstate->xsave.header.xfeatures.

[ ... ]

> @@ -3189,6 +3240,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,

If fpu_copy_uabi_to_guest_fpstate() fails, vcpu->arch.regs[R16..R31] will
have already been clobbered.

Moreover, if you call kvm_copy_uabi_to_vcpu_regs() *after*
fpu_copy_uabi_to_guest_fpstate() succeeds, you don't need to check
supported_xcr0 in kvm_copy_uabi_to_vcpu_regs(), since
fpu_copy_uabi_to_guest_fpstate() rejects any xfeatures bits not in
kvm_caps.supported_xcr0.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access
  2026-09-30 21:07 ` [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
@ 2026-11-06  0:17   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:17 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:20 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.
>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding
  2026-09-30 21:07 ` [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
@ 2026-11-06  0:19   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:19 UTC (permalink / raw)
  To: Chang S. Bae
  Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu, kernel test robot

On Wed, Sep 30, 2026 at 2:44 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines
  2026-09-30 21:07 ` [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
@ 2026-11-06  0:20   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:20 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 3:39 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification
  2026-09-30 21:07 ` [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
@ 2026-11-06  0:22   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:22 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:36 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.
>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 08/20] KVM: VMX: Support instruction information extension
  2026-09-30 21:07 ` [PATCH v8 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
@ 2026-11-06  0:40   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:40 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:55 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> Tested-by: Zhao Liu <zhao1.liu@intel.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 f98e40df5004..0b1e2353db3b 100644
> --- a/arch/x86/include/asm/vmx.h
> +++ b/arch/x86/include/asm/vmx.h
> @@ -277,6 +277,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 863b08478544..4a76af613720 100644
> --- a/arch/x86/kvm/vmx/vmx.h
> +++ b/arch/x86/kvm/vmx/vmx.h
> @@ -330,9 +330,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 cpu_feature_enabled(X86_FEATURE_APX);
> +}

Sashiko is not wrong. When KVM runs on top of Hyper-V with eVMCS enabled,
vmcs_read64() becomes evmcs_read64(). Because eVMCS v1 does not define a
mapping for EXTENDED_INSTRUCTION_INFO (0x2406) in vmcs_field_to_evmcs_1[],
get_evmcs_offset() triggers a WARN_ONCE() and evmcs_read64() returns 0 on
every instruction exit that calls vmx_get_instr_info(), even when APX is
not enabled for the guest.

Until eVMCS supports EXTENDED_INSTRUCTION_INFO, vmx_instr_info_extended()
must be predicated on !kvm_is_using_evmcs().

Moreover, enable_apx should be forced off when kvm_is_using_evmcs() is true.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information
  2026-09-30 21:07 ` [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
@ 2026-11-06  0:53   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:53 UTC (permalink / raw)
  To: Chang S. Bae
  Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu, Chao Gao

On Wed, Sep 30, 2026 at 2:37 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Link: https://lore.kernel.org/aRvOSnaUt1E+%2FpkC@intel.com
> ---

[ ... ]

> 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),

You need to add a case for VMCS12_CASE64(EXTENDED_INSTRUCTION_INFO) to
cpu_has_vmcs12_field().

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator
  2026-09-30 21:07 ` [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
@ 2026-11-06  0:55   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  0:55 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:37 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.
>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes
  2026-09-30 21:07 ` [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
@ 2026-11-06  4:34   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  4:34 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:38 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Link: https://lore.kernel.org/1ebf3a23-5671-41c1-8daa-c83f2f105936@redhat.com
> ---

[ ... ]

> @@ -1119,9 +1130,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);

Per Section 3.1.2.1 ("REX2 Prefix") of the Intel APX Architecture
Specification (rev. 9.0):

  Note that the R, X and B register identifiers can also address
  non-GPR register types, such as vector registers, segment registers,
  control registers and debug registers. When any of them does, the
  highest-order bits REX2.R4, REX2.X4 or REX2.B4 are generally
  ignored, except when the register being addressed is a control or
  debug register. Furthermore, if ModRM.Reg addresses a segment
  register, both REX2.R4 and REX2.R3 are ignored. For example, using
  the REX2 prefix, the instruction "ADDPD xmm1, xmm2/m128" can use all
  32 GPRs as the base and/or index registers in its memory operand,
  but it still cannot access XMM16 to XMM31 because REX2.R4 and
  REX2.B4 are ignored when the R and B register identifiers address
  vector registers.

In __decode_register_operand(), unlike the Mmx case (which masks
"reg &= 7;"), the Avx | Sse cases do not mask reg to 15 before setting
op->addr.xmm and calling kvm_read_sse_reg(). If a guest executes a
REX2-prefixed SSE instruction in twobyte_table (e.g., MOVAPS with
REX2.M0=1 and REX2.R4=1 or REX2.B4=1) that traps into the emulator,
reg will be in the range 16..31, hitting the default BUG() in
_kvm_read_sse_reg() / _kvm_write_sse_reg() and crashing the host.

Similarly, em_mov_rm_sreg() and em_mov_sreg_rm() use ctxt->modrm_reg
directly without masking off REX.R / REX2.R3 and REX2.R4, so setting
R3=1 or R4=1 on opcode 0x8C or 0x8E will erroneously inject #UD.

[ ... ]

> @@ -4297,15 +4308,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),

Per Section 3.1.2.1 and Section 3.1.3.3 ("64-Bit Absolute Direct Jump
(JMPABS)") of the Intel APX specification, opcode 0xA1 in legacy map 0 is
an exception to row 0xA* being reserved under REX2; REX2 (with M0=0 and
W=0) followed by 0xA1 encodes JMPABS. Marking 0xA1 with NoRex2 causes KVM
to raise a bogus #UD on a valid JMPABS instruction.

[ ... ]

> @@ -4385,12 +4396,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,

Per Section 3.1.2.1 of the Intel APX specification, all opcodes in row 0x3*
of legacy map 1 trigger #UD when prefixed with REX2. Leaving 0x36..0x3F as
N (NotImpl) causes KVM to return EMULATION_FAILED to userspace instead of
raising #UD when they are prefixed with REX2.

[ ... ]

> @@ -5001,6 +5012,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;

All other opcode attribute checks in x86_decode_insn() are performed on
ctxt->d after the "while (ctxt->d & GroupMask)" loop. Checking NoRex2
before group expansion means NoRex2 will be silently ignored if it is ever
placed on a group table entry. Should this check be moved right after the
GroupMask loop?

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode
  2026-09-30 21:07 ` [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
@ 2026-11-06  4:48   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  4:48 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 3:32 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.

This is only true for 0x40..0x4F, 0xC4, 0xC5, and 0xD5.

Per Section 3.1.2.1 ("REX2 Prefix") of the Intel APX Architecture
Specification (rev. 9.0):

  Furthermore, since the byte following a REX2 prefix is always
  interpreted as the main opcode byte, any legacy prefix byte (namely,
  0x66, 0x67, 0xF0, 0xF2, 0xF3, and segment overrides), a REX prefix
  byte (0x4*), a VEX prefix byte (0xC4 or 0xC5), an EVEX prefix byte
  (0x62), or another REX2 prefix byte (0xD5) following a REX2 prefix
  with REX2.M0 = 0 must #UD, because none of those bytes is the opcode
  of a valid instruction in legacy map 0 in 64-bit mode.

and:

  The 0x0F escape byte is neither needed nor allowed. (That is, the
  REX2 prefix followed by 0x0F triggers #UD.)

In opcode_table[], 0x0F, 0x26, 0x2E, 0x36, 0x3E, 0x62, 0x64, 0x65, 0x66,
0x67, 0xF0, 0xF2, and 0xF3 are all N (NotImpl), not No64. When any of these
bytes follows REX2 with M0=0, KVM will return EMULATION_FAILED to userspace
instead of raising #UD.

> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Link: https://lore.kernel.org/CABgObfYYGTvkYpeyqLSr9JgKMDA_STSff2hXBNchLZuKFU+MMA@mail.gmail.com
> ---

[ ... ]

> @@ -4958,6 +4969,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;
> +                       }

"ud" is defined as I(SrcNone, emulate_ud), and SrcNone is 0. Jumping to
done_modrm with opcode.flags == 0 sets ctxt->d = 0, which then hits:

        /* Unrecognised? */
        if (ctxt->d == 0)
                return EMULATION_FAILED;

before ctxt->execute is ever set. As a result, rex2_invalid() always
returns EMULATION_FAILED instead of injecting #UD.

Also, because any legacy prefix after a REX prefix resets ctxt->rex_prefix
to REX_NONE at the bottom of the prefix loop, a sequence such as 0x48 0x66
0xD5 will not be caught by rex2_invalid().  Table 3.1 ("Legacy Prefix
Applicability with REX2") lists 0x4* (REX) under "Prefix before REX2" as
"Illegal", whereas the accompanying text says, "A REX prefix (0x4*)
immediately preceding the REX2 prefix is not allowed and triggers #UD."
Does hardware raise #UD on 0x48 0x66 0xD5, or does the intervening legacy
prefix nullify the REX prefix as in legacy encodings?

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions
  2026-09-30 21:07 ` [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
@ 2026-11-06  4:56   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  4:56 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:54 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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 eba4e9b7cd5c..f0d5a40eba27 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -5052,6 +5052,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;

What is this patch actually doing? opcode_table[0x62] is already N
(NotImpl). Moreover, because opcode_table[0x62] does not have the ModRM
flag and init_decode_cache() does not clear ctxt->modrm, "(ctxt->modrm &
0xc0) == 0xc0" in non-64-bit mode inspects a stale ctxt->modrm from
whatever instruction was emulated previously.

If the intent is to mark opcode_table[0x62] with No64 (or NoRex2) so
that REX2 (with M0=0) followed by 0x62 raises #UD, then:
1. opcode_table[0x62] actually needs that flag, and
2. this EVEX check must be gated on ctxt->rex_prefix != REX2_PREFIX,
   or else it will clobber opcode.flags with NotImpl on "D5 00 62" and
   return EMULATION_FAILED instead of injecting #UD.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init()
  2026-09-30 21:07 ` [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
@ 2026-11-06  4:58   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  4:58 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:38 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings
  2026-09-30 21:07 ` [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
@ 2026-11-06  5:00   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:00 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:16 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0
  2026-09-30 21:07 ` [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
@ 2026-11-06  5:01   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:01 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 4:07 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> Set kvm_caps.supported_xcr0[APX] when the host has enabled the APX state
> along with CONFIG_KVM_APX=y.
>
> Tested-by: Zhao Liu <zhao1.liu@intel.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace
  2026-09-30 21:07 ` [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
@ 2026-11-06  5:05   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:05 UTC (permalink / raw)
  To: Chang S. Bae
  Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu, Peter Fang

On Wed, Sep 30, 2026 at 5:23 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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.

Per Section 3.1.4.3.1 ("Intel APX Feature and Enumeration") of the Intel
APX Architecture Specification (rev. 9.0):

  Any Intel processor that enumerates support for APX_F will also
  enumerate support for APX_NCI_NDD_NF.

Why split leaf 0x29 across this patch and patch 18? And if they are split,
why add the leaf 0x29 plumbing here (where "case 0x29:" just duplicates the
default case) instead of in patch 18?

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test
  2026-09-30 21:07 ` [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
@ 2026-11-06  5:31   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:31 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 5:07 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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>
> ---
>  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 96bab7002d39..2bd5c77047a8 100644
> --- a/tools/testing/selftests/kvm/Makefile.kvm
> +++ b/tools/testing/selftests/kvm/Makefile.kvm
> @@ -166,6 +166,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

Nit: please group x86/apx_test with the other x86/ tests above (e.g. next
to x86/amx_test), rather than after the common tests.

[ ... ]

> +static inline void xstatebv_set(void *xsave, uint64_t mask)
> +{
> +       *(uint64_t *)(xsave + XSAVE_HDR_OFFSET) |= mask;

Nit: u64.

[ ... ]

> +               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);

vcpu_xsave_set() should only be called when stage == USERSPACE_UPDATE.
Calling it unconditionally on every UCALL_SYNC overwrites the vCPU's XSAVE
state from userspace during GUEST_UPDATE and GUEST_APXOFF, which defeats
the purpose of verifying that KVM preserves the guest's EGPRs across
VM-exit/VM-entry and across XCR0.APX transitions on its own.

Also, please add a stage where userspace clears XFEATURE_MASK_APX in
XSTATE_BV (while leaving non-zero values in the XSAVE APX payload) and
calls vcpu_xsave_set(), then verifies that R16-R31 have been reset to 0 in
the guest (per patch 3).

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs
  2026-11-05 23:42   ` Jim Mattson
@ 2026-11-06  5:32     ` Chang S. Bae
  0 siblings, 0 replies; 46+ messages in thread
From: Chang S. Bae @ 2026-11-06  5:32 UTC (permalink / raw)
  To: Jim Mattson, Chang S. Bae
  Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On 11/5/26 15:42, Jim Mattson wrote:
> 
> In kvm_vcpu_reset(), KVM unconditionally zeroes the entire vcpu->arch.regs
> array on both RESET and INIT:
> 
>      /* All GPRs except RDX (handled below) are zeroed on RESET/INIT. */
>      memset(vcpu->arch.regs, 0, sizeof(vcpu->arch.regs));
> 
> However, according to the Intel APX Architecture Specification (rev. 9.0),
> section 3.1.4.1.1 and Table 3.7, R16-R31 are unchanged on INIT:
> 
>    The main difference between EGPRs and legacy GPRs, is that EGPRs are not
>    enabled by default (specifically XCR0-enabled) in 64-bit mode, and their
>    INIT behavior is XMM-like; in that EGPRs and many other XSAVE-enabled
>    register states are un-changed on INIT events as shown in Table 3.7.
> 
> By extending NR_VCPU_GENERAL_PURPOSE_REGS to include VCPU_REGS_R16 through
> VCPU_REGS_R31 without updating kvm_vcpu_reset(), KVM will erroneously zero
> R16-R31 when init_event is true.

Appreciate the review!

This was overlooked since switched away from XSAVE to the VCPU cache. 
Initially, I thought okay to keep along with other XSTATEs:

   The specification deliberately scopes out some areas. For example,
   Sections 3.1.4.4.2–7 note that initialization and reset behaviors
   follow existing XSTATE conventions.

Yes, you're right. The reset needs to be adjusted.

Thanks,
Chang

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI
  2026-11-06  0:13   ` Jim Mattson
@ 2026-11-06  5:33     ` Chang S. Bae
  2026-11-06  5:46       ` Jim Mattson
  0 siblings, 1 reply; 46+ messages in thread
From: Chang S. Bae @ 2026-11-06  5:33 UTC (permalink / raw)
  To: Jim Mattson, Chang S. Bae
  Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On 11/5/26 16:13, Jim Mattson wrote:
> On Wed, Sep 30, 2026 at 4:02 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>>
> 
>> +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;
> 
> Sashiko is right that this early return leaves stale values in R16-R31 when
> XSTATE_BV[19] is 0.
> 
> In your reply to Sashiko, you wrote:
> 
>    This matches the existing XSAVE uABI semantics. XSTATE_BV specifies
>    which state components are being updated. If userspace wants to zero
>    the APX state, it must include the APX bit in XSTATE_BV; omitting the
>    bit means "leave this component unchanged", just like the generic
>    XSAVE copy path.
> 
> That is not how XRSTOR or KVM_SET_XSAVE works. In the XSAVE architecture,
> XSTATE_BV[i] = 0 indicates that state component i is in its initial
> configuration (typically zeros), not that it should be left unchanged. In
> copy_uabi_to_xstate(), clearing bit i in xsave->header.xfeatures 

But copy_uabi_to_xstate() does not overwrite XSTATE_BV. Instead,

	/*
	 * Add back in the features that came in from userspace:
	 */
	xsave->header.xfeatures |= hdr.xfeatures;

This means that even if userspace clears a bit, the bit in the kernel 
XSAVE buffer remains set if it was already set. Consequently, clearing 
XSTATE_BV[i] in the userspace buffer does not necessarily reset the 
state component.

I'd follow up separately with update the wording, if needed, to match 
with this behavior.

>> @@ -3189,6 +3240,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,
> 
> If fpu_copy_uabi_to_guest_fpstate() fails, vcpu->arch.regs[R16..R31] will
> have already been clobbered.
> 
> Moreover, if you call kvm_copy_uabi_to_vcpu_regs() *after*
> fpu_copy_uabi_to_guest_fpstate() succeeds, you don't need to check
> supported_xcr0 in kvm_copy_uabi_to_vcpu_regs(), since
> fpu_copy_uabi_to_guest_fpstate() rejects any xfeatures bits not in
> kvm_caps.supported_xcr0.

I think this is a good point. Since it was decided to let APX state go 
through the fpstate anyway, it looks rather simplifying if switching the 
order.

Thanks,
Chang

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks
  2026-09-30 21:07 ` [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
@ 2026-11-06  5:36   ` Jim Mattson
  0 siblings, 0 replies; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:36 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Wed, Sep 30, 2026 at 2:40 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> 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);
> +

Nit: to match MPX and PKU below:

        if (this_cpu_has(X86_FEATURE_APX)) {
            GUEST_ASSERT(supported_xcr0 & XFEATURE_MASK_APX);
            write_egpr(16, 0xcccccccc);
        }

[ ... ]

> +#define ASSERT_XFEATURE_CONFLICT(supported_xcr0, xfeatures, conflicts)                 \
> +do {                                                                                   \
> +       uint64_t __supported = (supported_xcr0) & ((xfeatures) | (conflicts));          \

Nit: u64

Reviewed-by: Jim Mattson <jmattson@google.com>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI
  2026-11-06  5:33     ` Chang S. Bae
@ 2026-11-06  5:46       ` Jim Mattson
  2026-11-06  5:57         ` Chang S. Bae
  0 siblings, 1 reply; 46+ messages in thread
From: Jim Mattson @ 2026-11-06  5:46 UTC (permalink / raw)
  To: Chang S. Bae; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On Thu, Nov 5, 2026 at 9:33 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> On 11/5/26 16:13, Jim Mattson wrote:
> > On Wed, Sep 30, 2026 at 4:02 PM Chang S. Bae <chang.seok.bae@intel.com> wrote:
> >>
> >
> >> +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;
> >
> > Sashiko is right that this early return leaves stale values in R16-R31 when
> > XSTATE_BV[19] is 0.
> >
> > In your reply to Sashiko, you wrote:
> >
> >    This matches the existing XSAVE uABI semantics. XSTATE_BV specifies
> >    which state components are being updated. If userspace wants to zero
> >    the APX state, it must include the APX bit in XSTATE_BV; omitting the
> >    bit means "leave this component unchanged", just like the generic
> >    XSAVE copy path.
> >
> > That is not how XRSTOR or KVM_SET_XSAVE works. In the XSAVE architecture,
> > XSTATE_BV[i] = 0 indicates that state component i is in its initial
> > configuration (typically zeros), not that it should be left unchanged. In
> > copy_uabi_to_xstate(), clearing bit i in xsave->header.xfeatures
>
> But copy_uabi_to_xstate() does not overwrite XSTATE_BV. Instead,
>
>         /*
>          * Add back in the features that came in from userspace:
>          */
>         xsave->header.xfeatures |= hdr.xfeatures;
>
> This means that even if userspace clears a bit, the bit in the kernel
> XSAVE buffer remains set if it was already set. Consequently, clearing
> XSTATE_BV[i] in the userspace buffer does not necessarily reset the
> state component.

Look at the lines immediately preceding the ones you quoted in
copy_uabi_to_xstate():

    /*
     * The state that came in from userspace was user-state only.
     * Mask all the user states out of 'xfeatures':
     */
    xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR_ALL;

    /*
     * Add back in the features that came in from userspace:
     */
    xsave->header.xfeatures |= hdr.xfeatures;

All user-state bits in xsave->header.xfeatures are cleared before
hdr.xfeatures is ORed in. If userspace clears bit i for any user state
component in hdr.xfeatures, bit i in xsave->header.xfeatures is cleared,
and the component is reset to its initial state.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI
  2026-11-06  5:46       ` Jim Mattson
@ 2026-11-06  5:57         ` Chang S. Bae
  0 siblings, 0 replies; 46+ messages in thread
From: Chang S. Bae @ 2026-11-06  5:57 UTC (permalink / raw)
  To: Jim Mattson; +Cc: pbonzini, seanjc, kvm, x86, linux-kernel, zhao1.liu

On 11/5/2026 9:46 PM, Jim Mattson wrote:
> 
> Look at the lines immediately preceding the ones you quoted in
> copy_uabi_to_xstate():
> 
>      /*
>       * The state that came in from userspace was user-state only.
>       * Mask all the user states out of 'xfeatures':
>       */
>      xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR_ALL;
> 
>      /*
>       * Add back in the features that came in from userspace:
>       */
>      xsave->header.xfeatures |= hdr.xfeatures;
> 
> All user-state bits in xsave->header.xfeatures are cleared before
> hdr.xfeatures is ORed in. If userspace clears bit i for any user state
> component in hdr.xfeatures, bit i in xsave->header.xfeatures is cleared,
> and the component is reset to its initial state.

Ah, right. Looks like sticking to what I thought before...

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2026-11-06  5:57 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30 21:07 [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
2026-09-30 21:07 ` [PATCH v8 01/20] KVM: x86: Extend VCPU registers for EGPRs Chang S. Bae
2026-11-05 23:42   ` Jim Mattson
2026-11-06  5:32     ` Chang S. Bae
2026-09-30 21:07 ` [PATCH v8 02/20] KVM: VMX: Save guest EGPRs in VCPU cache Chang S. Bae
2026-11-05 23:52   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 03/20] KVM: x86: Support APX state for XSAVE ABI Chang S. Bae
2026-11-06  0:13   ` Jim Mattson
2026-11-06  5:33     ` Chang S. Bae
2026-11-06  5:46       ` Jim Mattson
2026-11-06  5:57         ` Chang S. Bae
2026-09-30 21:07 ` [PATCH v8 04/20] KVM: VMX: Refactor VMX instruction information access Chang S. Bae
2026-11-06  0:17   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 05/20] KVM: VMX: Refactor instruction information decoding Chang S. Bae
2026-11-06  0:19   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 06/20] KVM: VMX: Remove unused control-register access defines Chang S. Bae
2026-11-06  0:20   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 07/20] KVM: VMX: Refactor register index retrieval from exit qualification Chang S. Bae
2026-11-06  0:22   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 08/20] KVM: VMX: Support instruction information extension Chang S. Bae
2026-11-06  0:40   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 09/20] KVM: nVMX: Propagate extended instruction information Chang S. Bae
2026-11-06  0:53   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 10/20] KVM: x86: Support EGPR accessing and tracking for emulator Chang S. Bae
2026-11-06  0:55   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 11/20] KVM: x86: Handle EGPR index and REX2-incompatible opcodes Chang S. Bae
2026-11-06  4:34   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 12/20] KVM: x86: Support REX2-prefixed opcode decode Chang S. Bae
2026-11-06  4:48   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 13/20] KVM: x86: Reject EVEX-prefixed instructions Chang S. Bae
2026-11-06  4:56   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 14/20] KVM: x86: Move KVM_SUPPORTED_{XCR0,XSS} into kvm_x86_vendor_init() Chang S. Bae
2026-11-06  4:58   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 15/20] KVM: x86: Guard valid XCR0.APX settings Chang S. Bae
2026-11-06  5:00   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 16/20] KVM: x86: Add APX to supported XCR0 Chang S. Bae
2026-11-06  5:01   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 17/20] KVM: x86: Expose APX foundation feature to userspace Chang S. Bae
2026-11-06  5:05   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 18/20] KVM: x86: Expose APX sub-features " Chang S. Bae
2026-09-30 21:07 ` [PATCH v8 19/20] KVM: x86: selftests: Add APX state and ABI test Chang S. Bae
2026-11-06  5:31   ` Jim Mattson
2026-09-30 21:07 ` [PATCH v8 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks Chang S. Bae
2026-11-06  5:36   ` Jim Mattson
2026-10-01 20:29 ` [PATCH v8 00/20] KVM: x86: Enable APX for guests Chang S. Bae
2026-11-04  0:45   ` Chang S. Bae

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®