* [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests
2026-06-29 12:52 [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1 Tina Zhang
@ 2026-06-29 12:52 ` Tina Zhang
2026-06-29 13:12 ` sashiko-bot
2026-06-30 15:11 ` Jim Mattson
2026-06-29 12:52 ` [PATCH 2/2] KVM: selftests: Add nested SVM DecodeAssists test Tina Zhang
2026-06-30 15:52 ` [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1 Jim Mattson
2 siblings, 2 replies; 6+ messages in thread
From: Tina Zhang @ 2026-06-29 12:52 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, kvm
Cc: Shuah Khan, zhouyanjing, linux-kselftest, linux-kernel
Hardware records DecodeAssist information, i.e. the instruction length and
bytes, in the VMCB for exits such as #NPF. KVM does not currently expose
that information when an L2 exit is reflected to L1, so a nested hypervisor
cannot consume the hardware-provided decode state and may need to fetch and
decode the L2 instruction itself.
Advertise DecodeAssists to L1 when the host supports it, and copy
insn_len/insn_bytes from VMCB02 to VMCB12 for nested VM-Exits that
reflect a real hardware exit. Gate the copy on L1's guest CPUID so the
advertised feature and the propagated state stay in sync.
Report decode information only for exits that actually have fresh hardware
state. KVM synthesizes several nested VM-Exits, e.g. VMRUN failures,
synthetic #NPF exits, reflected exceptions, and interrupt/NMI windows, that
do not carry VMCB02 DecodeAssist output. Track only the verbatim VMCB02
reflection path with nested.decode_assists_valid, clear VMCB02's decode
fields before VMRUN, and clear VMCB12's fields for ineligible exits. Use
insn_len == 0 to tell L1 that no decode information is available, and avoid
exposing stale bytes from a previous L2 exit.
Tested-by: Yongwei Xu <xuyongwei@hygon.cn>
Signed-off-by: Tina Zhang <zhang_wei@open-hieco.net>
---
arch/x86/kvm/svm/nested.c | 47 ++++++++++++++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.c | 3 +++
arch/x86/kvm/svm/svm.h | 6 +++++
3 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index b340dc9991ad..50e9ffa5f9fd 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -33,6 +33,33 @@
#define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK
+static void nested_svm_clear_decode_assists(struct vmcb *vmcb)
+{
+ vmcb->control.insn_len = 0;
+ memset(vmcb->control.insn_bytes, 0,
+ sizeof(vmcb->control.insn_bytes));
+}
+
+static void nested_svm_copy_decode_assists(struct vmcb *to, struct vmcb *from)
+{
+ u8 insn_len = from->control.insn_len;
+
+ nested_svm_clear_decode_assists(to);
+
+ /*
+ * Hardware leaves insn_len zero when no DecodeAssist data is available.
+ * Keep that as the nested-visible "no decode info" marker.
+ */
+ if (!insn_len)
+ return;
+
+ if (WARN_ON_ONCE(insn_len > sizeof(from->control.insn_bytes)))
+ return;
+
+ to->control.insn_len = insn_len;
+ memcpy(to->control.insn_bytes, from->control.insn_bytes, insn_len);
+}
+
static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
struct x86_exception *fault)
{
@@ -838,7 +865,10 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
/*
* Filled at exit: exit_code, exit_info_1, exit_info_2, exit_int_info,
* exit_int_info_err, next_rip, insn_len, insn_bytes.
+ * Clear stale DecodeAssist data before L2 runs.
*/
+ nested_svm_clear_decode_assists(vmcb02);
+ svm->nested.decode_assists_valid = false;
if (guest_cpu_cap_has(vcpu, X86_FEATURE_VGIF) &&
(vmcb12_ctrl->int_ctl & V_GIF_ENABLE_MASK))
@@ -1251,6 +1281,18 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu)
if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS))
vmcb12->control.next_rip = vmcb02->control.next_rip;
+ /*
+ * Copy DecodeAssist data only for real VMCB02 exits. KVM-synthesized
+ * exits report no decode info to L1.
+ */
+ if (svm->nested.decode_assists_valid &&
+ guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS))
+ nested_svm_copy_decode_assists(vmcb12, vmcb02);
+ else
+ nested_svm_clear_decode_assists(vmcb12);
+
+ svm->nested.decode_assists_valid = false;
+
if (nested_vmcb12_has_lbrv(vcpu))
svm_copy_lbrs(&vmcb12->save, &vmcb02->save);
@@ -1593,8 +1635,11 @@ int nested_svm_exit_handled(struct vcpu_svm *svm)
vmexit = nested_svm_intercept(svm);
- if (vmexit == NESTED_EXIT_DONE)
+ if (vmexit == NESTED_EXIT_DONE) {
+ /* This path reflects a real VMCB02 exit directly to L1. */
+ svm->nested.decode_assists_valid = true;
nested_svm_vmexit(svm);
+ }
return vmexit;
}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index e02a38da5296..366e7ef787b2 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5470,6 +5470,9 @@ static __init void svm_set_cpu_caps(void)
*/
kvm_cpu_cap_set(X86_FEATURE_FLUSHBYASID);
+ if (boot_cpu_has(X86_FEATURE_DECODEASSISTS))
+ kvm_cpu_cap_set(X86_FEATURE_DECODEASSISTS);
+
if (nrips)
kvm_cpu_cap_set(X86_FEATURE_NRIPS);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 5137416be593..be7fdcd124b7 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -238,6 +238,12 @@ struct svm_nested_state {
* on its side.
*/
bool force_msr_bitmap_recalc;
+
+ /*
+ * True only while reflecting a real VMCB02 exit whose DecodeAssist
+ * fields may be copied to VMCB12. KVM-synthesized exits leave it clear.
+ */
+ bool decode_assists_valid;
};
struct vcpu_sev_es_state {
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests
2026-06-29 12:52 ` [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests Tina Zhang
@ 2026-06-29 13:12 ` sashiko-bot
2026-06-30 15:11 ` Jim Mattson
1 sibling, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-06-29 13:12 UTC (permalink / raw)
To: Tina Zhang; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch completely fails to propagate DecodeAssist information for hardware-generated #NPF exits, breaking its primary stated goal.
--
commit 25d49971bfa61318056a7a414a7878b30f3a2e0d
Author: Tina Zhang <zhang_wei@open-hieco.net>
KVM: nSVM: Virtualize DecodeAssists for nested guests
This commit advertises DecodeAssists to L1 nested guests and copies
instruction length and bytes from VMCB02 to VMCB12 for nested VM-Exits.
It introduces the decode_assists_valid flag to track real hardware exits
and prevent KVM from exposing stale bytes for KVM-synthesized exits.
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 961804df5f451..86b5cd45a52fe 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
[ ... ]
> @@ -1608,8 +1650,11 @@ int nested_svm_exit_handled(struct vcpu_svm *svm)
>
> vmexit = nested_svm_intercept(svm);
>
> - if (vmexit == NESTED_EXIT_DONE)
> + if (vmexit == NESTED_EXIT_DONE) {
> + /* This path reflects a real VMCB02 exit directly to L1. */
> + svm->nested.decode_assists_valid = true;
> nested_svm_vmexit(svm);
> + }
>
> return vmexit;
> }
[Severity: High]
Does this patch actually propagate DecodeAssist information for #NPF exits?
Looking at nested_svm_exit_special(), SVM_EXIT_NPF unconditionally returns
NESTED_EXIT_HOST. This bypasses nested_svm_exit_handled() entirely, meaning
svm->nested.decode_assists_valid is never set to true for nested page faults.
When KVM later reflects the fault to L1 via nested_svm_inject_npf_exit():
arch/x86/kvm/svm/nested.c:nested_svm_inject_npf_exit() {
...
nested_svm_vmexit(svm);
}
The decode_assists_valid flag will still be false. Won't this cause
nested_svm_vmexit_update_vmcb12() to unconditionally clear the decode
fields, silently discarding the valid hardware DecodeAssist data in VMCB02
that the commit message mentions?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260629125205.52394-1-zhang_wei@open-hieco.net?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests
2026-06-29 12:52 ` [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests Tina Zhang
2026-06-29 13:12 ` sashiko-bot
@ 2026-06-30 15:11 ` Jim Mattson
1 sibling, 0 replies; 6+ messages in thread
From: Jim Mattson @ 2026-06-30 15:11 UTC (permalink / raw)
To: Tina Zhang
Cc: Sean Christopherson, Paolo Bonzini, kvm, Shuah Khan, zhouyanjing,
linux-kselftest, linux-kernel
On Mon, Jun 29, 2026 at 5:56 AM Tina Zhang <zhang_wei@open-hieco.net> wrote:
>
> Hardware records DecodeAssist information, i.e. the instruction length and
> bytes, in the VMCB for exits such as #NPF. KVM does not currently expose
> that information when an L2 exit is reflected to L1, so a nested hypervisor
> cannot consume the hardware-provided decode state and may need to fetch and
> decode the L2 instruction itself.
>
> Advertise DecodeAssists to L1 when the host supports it, and copy
> insn_len/insn_bytes from VMCB02 to VMCB12 for nested VM-Exits that
> reflect a real hardware exit. Gate the copy on L1's guest CPUID so the
> advertised feature and the propagated state stay in sync.
>
> Report decode information only for exits that actually have fresh hardware
> state. KVM synthesizes several nested VM-Exits, e.g. VMRUN failures,
> synthetic #NPF exits, reflected exceptions, and interrupt/NMI windows, that
> do not carry VMCB02 DecodeAssist output. Track only the verbatim VMCB02
> reflection path with nested.decode_assists_valid, clear VMCB02's decode
> fields before VMRUN, and clear VMCB12's fields for ineligible exits. Use
> insn_len == 0 to tell L1 that no decode information is available, and avoid
> exposing stale bytes from a previous L2 exit.
You can't just set instr_len to 0 on a whim. Per the APM, "The default
number of bytes is always 15. Fewer bytes are returned only if a fault
occurs while fetching." Every #PF and #NPF VM-exit to L1 *must*
provide this information, even in cases where you can't simply copy
the information from VMCB02.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] KVM: selftests: Add nested SVM DecodeAssists test
2026-06-29 12:52 [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1 Tina Zhang
2026-06-29 12:52 ` [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests Tina Zhang
@ 2026-06-29 12:52 ` Tina Zhang
2026-06-30 15:52 ` [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1 Jim Mattson
2 siblings, 0 replies; 6+ messages in thread
From: Tina Zhang @ 2026-06-29 12:52 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, kvm
Cc: Shuah Khan, zhouyanjing, linux-kselftest, linux-kernel
Add a selftest for nested SVM DecodeAssists virtualization.
Run L2 with a non-present NPT mapping to trigger a nested page fault, then
verify that L1 sees non-zero DecodeAssist state in VMCB12 and that the
reported instruction bytes match the instruction at L2's RIP. This covers
the path that copies hardware-provided decode information from VMCB02 to
VMCB12.
Re-enter L2 at a VMMCALL after the #NPF and verify that any reported bytes
match the new RIP. KVM reuses VMCB02 across nested runs, so the second
exit checks that DecodeAssist state from the earlier #NPF is not leaked
into an unrelated nested VM-Exit.
Also verify that KVM exposes DecodeAssists in L1's SVM CPUID leaf when the
feature is supported by the host.
Tested-by: Yongwei Xu <xuyongwei@hygon.cn>
Signed-off-by: Tina Zhang <zhang_wei@open-hieco.net>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/processor.h | 1 +
.../kvm/x86/svm_nested_decode_assists_test.c | 99 +++++++++++++++++++
3 files changed, 101 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86/svm_nested_decode_assists_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 9118a5a51b89..23bf51074392 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -114,6 +114,7 @@ TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test
TEST_GEN_PROGS_x86 += x86/svm_vmcall_test
TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test
TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme
+TEST_GEN_PROGS_x86 += x86/svm_nested_decode_assists_test
TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test
TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test
TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 77f576ee7789..ee4520ff2f89 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -201,6 +201,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_LBRV KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 1)
#define X86_FEATURE_NRIPS KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 3)
#define X86_FEATURE_TSCRATEMSR KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 4)
+#define X86_FEATURE_DECODEASSISTS KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 7)
#define X86_FEATURE_PAUSEFILTER KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 10)
#define X86_FEATURE_PFTHRESHOLD KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 12)
#define X86_FEATURE_V_VMSAVE_VMLOAD KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 15)
diff --git a/tools/testing/selftests/kvm/x86/svm_nested_decode_assists_test.c b/tools/testing/selftests/kvm/x86/svm_nested_decode_assists_test.c
new file mode 100644
index 000000000000..6b5d38d9d36c
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/svm_nested_decode_assists_test.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+
+#define L2_GUEST_STACK_SIZE 64
+
+static uint64_t npf_target __aligned(PAGE_SIZE);
+
+static void l2_guest_code(void)
+{
+ asm volatile("mov (%0), %%rax" : : "r"(&npf_target) : "rax", "memory");
+ GUEST_FAIL("L2 access did not cause a nested page fault");
+}
+
+static void l2_vmmcall_code(void)
+{
+ asm volatile("vmmcall");
+ GUEST_FAIL("L2 did not exit on VMMCALL");
+}
+
+/*
+ * Whenever hardware reports DecodeAssist information (insn_len != 0), the bytes
+ * KVM reflects into VMCB12 must be the instruction at the current RIP, never
+ * stale bytes carried over from a previous L2 exit.
+ */
+static void assert_decode_assists_sane(struct vmcb *vmcb)
+{
+ GUEST_ASSERT(vmcb->control.insn_len <=
+ sizeof(vmcb->control.insn_bytes));
+ if (vmcb->control.insn_len)
+ GUEST_ASSERT(!memcmp(vmcb->control.insn_bytes,
+ (void *)vmcb->save.rip,
+ vmcb->control.insn_len));
+}
+
+static void l1_guest_code(struct svm_test_data *svm)
+{
+ unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
+ struct vmcb *vmcb = svm->vmcb;
+
+ GUEST_ASSERT(this_cpu_has(X86_FEATURE_DECODEASSISTS));
+
+ generic_svm_setup(svm, l2_guest_code,
+ &l2_guest_stack[L2_GUEST_STACK_SIZE]);
+
+ /* A real nested #NPF must reflect valid decode information to L1. */
+ run_guest(vmcb, svm->vmcb_gpa);
+
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_NPF);
+ GUEST_ASSERT(vmcb->control.insn_len);
+ assert_decode_assists_sane(vmcb);
+
+ /*
+ * Redirect L2 past the still-faulting access to a VMMCALL. KVM reuses
+ * its internal VMCB02, which now holds the #NPF's decode bytes, so this
+ * exercises the path that must clear DecodeAssist state and ensures the
+ * prior exit's bytes are not leaked into a subsequent exit.
+ */
+ vmcb->save.rip = (u64)l2_vmmcall_code;
+ run_guest(vmcb, svm->vmcb_gpa);
+
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL);
+ assert_decode_assists_sane(vmcb);
+
+ GUEST_DONE();
+}
+
+int main(int argc, char *argv[])
+{
+ gva_t svm_gva, npf_gva;
+ gpa_t npf_gpa;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ u64 *pte;
+
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_NPT));
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_DECODEASSISTS));
+
+ vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+ vm_enable_npt(vm);
+ vcpu_alloc_svm(vm, &svm_gva);
+ npf_gva = (gva_t)&npf_target;
+ npf_gpa = addr_gva2gpa(vm, npf_gva);
+
+ tdp_identity_map_default_memslots(vm);
+ pte = tdp_get_pte(vm, npf_gpa);
+ *pte &= ~PTE_PRESENT_MASK(&vm->stage2_mmu);
+
+ vcpu_args_set(vcpu, 1, svm_gva);
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
+
+ kvm_vm_free(vm);
+ return 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1
2026-06-29 12:52 [PATCH 0/2] KVM: nSVM: Expose DecodeAssists to L1 Tina Zhang
2026-06-29 12:52 ` [PATCH 1/2] KVM: nSVM: Virtualize DecodeAssists for nested guests Tina Zhang
2026-06-29 12:52 ` [PATCH 2/2] KVM: selftests: Add nested SVM DecodeAssists test Tina Zhang
@ 2026-06-30 15:52 ` Jim Mattson
2 siblings, 0 replies; 6+ messages in thread
From: Jim Mattson @ 2026-06-30 15:52 UTC (permalink / raw)
To: Tina Zhang
Cc: Sean Christopherson, Paolo Bonzini, kvm, Shuah Khan, zhouyanjing,
linux-kselftest, linux-kernel
On Mon, Jun 29, 2026 at 5:57 AM Tina Zhang <zhang_wei@open-hieco.net> wrote:
>
> The SVM DecodeAssists feature is reported in CPUID
> Fn8000_000A_EDX[7]. When available, hardware provides the length and bytes
> of the intercepted instruction in the VMCB, allowing a hypervisor to consume
> the decode information directly instead of re-decoding the instruction in
> software on relevant VM-Exit paths.
DecodeAssists actually comprises four components:
* GPR number in EXITINFO1 for MOV-CR and MOV-DR VM-exits
* Software interrupt number in EXITINFO1 for INTn VM-exits
* Linear address in EXITINFO1 for INVLPG[A] VM-exits
* Guest instruction bytes and length in the VMCB for #NPF and #PF VM-exits
You only partially address the last component.
^ permalink raw reply [flat|nested] 6+ messages in thread