* [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors @ 2026-01-15 13:17 Alejandro Vallejo 2026-01-15 16:43 ` [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature Alejandro Vallejo 2026-01-28 23:15 ` [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Jim Mattson 0 siblings, 2 replies; 8+ messages in thread From: Alejandro Vallejo @ 2026-01-15 13:17 UTC (permalink / raw) To: kvm; +Cc: Alejandro Vallejo, Christopherson, Paolo Bonzini Enable exposing DecodeAssists to guests. Performs a copyout of the insn_len and insn_bytes fields of the VMCB when the vCPU has the feature enabled. Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> --- I wrote a little smoke test for kvm-unit-tests too. I'll send it shortly in reply to this email. --- arch/x86/kvm/cpuid.c | 1 + arch/x86/kvm/svm/nested.c | 6 ++++++ arch/x86/kvm/svm/svm.c | 3 +++ 3 files changed, 10 insertions(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 88a5426674a10..da9a63c8289e5 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1181,6 +1181,7 @@ void kvm_set_cpu_caps(void) VENDOR_F(FLUSHBYASID), VENDOR_F(NRIPS), VENDOR_F(TSCRATEMSR), + VENDOR_F(DECODEASSISTS), VENDOR_F(V_VMSAVE_VMLOAD), VENDOR_F(LBRV), VENDOR_F(PAUSEFILTER), diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index ba0f11c68372b..dc8a8e67a22c2 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1128,6 +1128,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm) vmcb12->save.ssp = vmcb02->save.ssp; } + if (guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS)) { + memcpy(vmcb12->control.insn_bytes, vmcb02->control.insn_bytes, + ARRAY_SIZE(vmcb12->control.insn_bytes)); + vmcb12->control.insn_len = vmcb02->control.insn_len; + } + vmcb12->control.int_state = vmcb02->control.int_state; vmcb12->control.exit_code = vmcb02->control.exit_code; vmcb12->control.exit_code_hi = vmcb02->control.exit_code_hi; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 24d59ccfa40d9..8cf6d7904030e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5223,6 +5223,9 @@ static __init void svm_set_cpu_caps(void) if (nrips) kvm_cpu_cap_set(X86_FEATURE_NRIPS); + if (boot_cpu_has(X86_FEATURE_DECODEASSISTS)) + kvm_cpu_cap_set(X86_FEATURE_DECODEASSISTS); + if (npt_enabled) kvm_cpu_cap_set(X86_FEATURE_NPT); base-commit: 0499add8efd72456514c6218c062911ccc922a99 -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature 2026-01-15 13:17 [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Alejandro Vallejo @ 2026-01-15 16:43 ` Alejandro Vallejo 2026-01-15 18:32 ` Sean Christopherson 2026-01-28 23:15 ` [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Jim Mattson 1 sibling, 1 reply; 8+ messages in thread From: Alejandro Vallejo @ 2026-01-15 16:43 UTC (permalink / raw) To: kvm; +Cc: Alejandro Vallejo, Paolo Bonzini Tests an intercepted #PF accesing the last (unmapped) qword of the virtual address space. The assist ought provides a prefetched code stream starting at the offending instruction. This is little more than a smoke test. There's more cases not covered. Namely, CR/DR MOVs, INTn, INVLPG, nested PFs, and fault-on-fetch. Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> --- I'm not a big fan of using a literal -8ULL as "unbacked va", but I'm not sure how to instruct the harness to give me a hole. Likewise, some cases remain untested, with the interesting one (fault-on-fetch) requiring some cumbersome setup (put the codestream in the 14 bytes leading to a non-present NPT page. Happy to add such a case, but I'm not sure how. As for all other cases, KVM copies ext_info_1 unconditionally, which is where the decoded info goes. It's highly unlikely they would ever provide much value. Happy to add them too if they're deemed useful. --- lib/x86/processor.h | 1 + x86/svm_tests.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 42dd2d2a..32ffd015 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -374,6 +374,7 @@ struct x86_cpu_feature { #define X86_FEATURE_LBRV X86_CPU_FEATURE(0x8000000A, 0, EDX, 1) #define X86_FEATURE_NRIPS X86_CPU_FEATURE(0x8000000A, 0, EDX, 3) #define X86_FEATURE_TSCRATEMSR X86_CPU_FEATURE(0x8000000A, 0, EDX, 4) +#define X86_FEATURE_DECODE_ASSISTS X86_CPU_FEATURE(0x8000000A, 0, EDX, 7) #define X86_FEATURE_PAUSEFILTER X86_CPU_FEATURE(0x8000000A, 0, EDX, 10) #define X86_FEATURE_PFTHRESHOLD X86_CPU_FEATURE(0x8000000A, 0, EDX, 12) #define X86_FEATURE_VGIF X86_CPU_FEATURE(0x8000000A, 0, EDX, 16) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 37616476..5c93d738 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -409,6 +409,36 @@ static bool check_next_rip(struct svm_test *test) return address == vmcb->control.next_rip; } +static bool decode_assists_supported(void) +{ + return this_cpu_has(X86_FEATURE_DECODE_ASSISTS); +} + +static void prepare_decode_assists(struct svm_test *test) +{ + vmcb->control.intercept_exceptions |= (1ULL << PF_VECTOR); +} + +static void test_decode_assists(struct svm_test *test) +{ + asm volatile (".globl opcode_pre\n\t" + "opcode_pre:\n\t" + "mov %0, (%0)\n\t" /* #PF */ + ".globl opcode_post\n\t" + "opcode_post:\n\t" :: "r"(-8ULL)); +} + +static bool check_decode_assists(struct svm_test *test) +{ + extern unsigned char opcode_pre[], opcode_post[]; + unsigned len = (unsigned)(opcode_post - opcode_pre); + + return vmcb->control.exit_code == (SVM_EXIT_EXCP_BASE + PF_VECTOR)) && + !memcmp(vmcb->control.insn_bytes, opcode_pre, len) && + vmcb->control.insn_len >= len && + vmcb->control.insn_len <= ARRAY_SIZE(vmcb->control.insn_bytes); +} + extern u8 *msr_bitmap; static bool is_x2apic; @@ -3628,6 +3658,9 @@ struct svm_test svm_tests[] = { { "next_rip", next_rip_supported, prepare_next_rip, default_prepare_gif_clear, test_next_rip, default_finished, check_next_rip }, + { "decode_assists", decode_assists_supported, prepare_decode_assists, + default_prepare_gif_clear, test_decode_assists, + default_finished, check_decode_assists }, { "msr intercept check", default_supported, prepare_msr_intercept, default_prepare_gif_clear, test_msr_intercept, msr_intercept_finished, check_msr_intercept }, base-commit: 31d91f5c9b7546471b729491664b05c933d64a7a -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature 2026-01-15 16:43 ` [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature Alejandro Vallejo @ 2026-01-15 18:32 ` Sean Christopherson 2026-01-16 12:59 ` Alejandro Vallejo 0 siblings, 1 reply; 8+ messages in thread From: Sean Christopherson @ 2026-01-15 18:32 UTC (permalink / raw) To: Alejandro Vallejo; +Cc: kvm, Paolo Bonzini, Yosry Ahmed, Kevin Cheng +Kevin and Yosry, who are working on similar tests On Thu, Jan 15, 2026, Alejandro Vallejo wrote: > Tests an intercepted #PF accesing the last (unmapped) qword of the > virtual address space. The assist ought provides a prefetched > code stream starting at the offending instruction. > > This is little more than a smoke test. There's more cases not covered. > Namely, CR/DR MOVs, INTn, INVLPG, nested PFs, and fault-on-fetch. > > Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> > --- > I'm not a big fan of using a literal -8ULL as "unbacked va", but I'm not > sure how to instruct the harness to give me a hole. Allocate a page, then use install_pte() or install_page_prot() to create a mapping that will #PF. > Likewise, some cases remain > untested, with the interesting one (fault-on-fetch) requiring some cumbersome > setup (put the codestream in the 14 bytes leading to a non-present NPT page. Not _that_ cumbersome though. Allocate a page, install_page_prot() with NX, copy/write an instruction to the page, jump/call into the page. run_in_user_ex() makes it even easier to do that at CPL3. All the above said, I would rather piggyback the access test and not reinvent the wheel. E.g. wrap ac_test_run() a la vmx_pf_exception_test(), then intercept #PF to verify the instruction information is filled as expected. We'd need to modify ac_test_do_access() to record what instruction it expected to fault, but that shouldn't be _too_ hard, and it might even force us to improve the inscrutable asm blob. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature 2026-01-15 18:32 ` Sean Christopherson @ 2026-01-16 12:59 ` Alejandro Vallejo 2026-01-16 13:03 ` Alejandro Vallejo 0 siblings, 1 reply; 8+ messages in thread From: Alejandro Vallejo @ 2026-01-16 12:59 UTC (permalink / raw) To: Sean Christopherson; +Cc: kvm, Paolo Bonzini, Yosry Ahmed, Kevin Cheng Hi, On Thu Jan 15, 2026 at 7:32 PM CET, Sean Christopherson wrote: > +Kevin and Yosry, who are working on similar tests > > On Thu, Jan 15, 2026, Alejandro Vallejo wrote: >> Tests an intercepted #PF accesing the last (unmapped) qword of the >> virtual address space. The assist ought provides a prefetched >> code stream starting at the offending instruction. >> >> This is little more than a smoke test. There's more cases not covered. >> Namely, CR/DR MOVs, INTn, INVLPG, nested PFs, and fault-on-fetch. >> >> Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> >> --- >> I'm not a big fan of using a literal -8ULL as "unbacked va", but I'm not >> sure how to instruct the harness to give me a hole. > > Allocate a page, then use install_pte() or install_page_prot() to create a mapping > that will #PF. Right. It hadn't clicked for me exactly how L1 and L2 are related in the harness. So this test group runs with the L2 reusing the mappings from the L1? > >> Likewise, some cases remain >> untested, with the interesting one (fault-on-fetch) requiring some cumbersome >> setup (put the codestream in the 14 bytes leading to a non-present NPT page. > > Not _that_ cumbersome though. Allocate a page, install_page_prot() with NX, > copy/write an instruction to the page, jump/call into the page. > > run_in_user_ex() makes it even easier to do that at CPL3. I didn't mean fetch as in faulted instruction, but rather the CPU hitting a missing PT as it fetches the 15 bytes of the codestream including and following the faulting instruction. That'd be... opcode_pre: mov ($not_present), %al /* #PF! */ not_present: /* page boundary */ int3; /* unmapped in the NPT */ ... where the CPU would try to fetch int3, and a bunch more bytes on VMEXIT but fail, thus leaving insn_len as "not_present - opcode_pre" rather than 15. In the absence of faults the CPU always fetches as much as possible on #PF/NPT-fault intercepts. A complete testcase would show the expected non-15 number as length. > > All the above said, I would rather piggyback the access test and not reinvent the > wheel. E.g. wrap ac_test_run() a la vmx_pf_exception_test(), then intercept #PF > to verify the instruction information is filled as expected. We'd need to modify > ac_test_do_access() to record what instruction it expected to fault, but that > shouldn't be _too_ hard, and it might even force us to improve the inscrutable > asm blob. I can see how this would be better for the simple test I have in this patch, but it would probably overcomplicate ac_test_do_access() in the more complete version with the instruction being at the boundary of a missing page. I'll try to integrate it anyway on the off chance any other caller happens to need it. I'm skeptical the result would look pretty, but we'll see; maybe it turns out alright. I'll get back to you after I find some time to play with it. On another note, would you prefer individual tests for every single assist or does it sound ok to have this single test for the #PF case? There's no meaningful difference between #PF and NPT faults as far as the assists are concerned, and all others are implicitly covered by virtue of exit_info_1 being unconditionally copied irrespective of exit code. They are trivial to write, so it's simply a matter of the test volume you're willing to have. Cheers for the feedback, Alejandro ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature 2026-01-16 12:59 ` Alejandro Vallejo @ 2026-01-16 13:03 ` Alejandro Vallejo 0 siblings, 0 replies; 8+ messages in thread From: Alejandro Vallejo @ 2026-01-16 13:03 UTC (permalink / raw) To: Alejandro Vallejo, Sean Christopherson Cc: kvm, Paolo Bonzini, Yosry Ahmed, Kevin Cheng On Fri Jan 16, 2026 at 1:59 PM CET, Alejandro Vallejo wrote: > I didn't mean fetch as in faulted instruction, but rather the CPU hitting a > missing PT as it fetches the 15 bytes of the codestream including and following > the faulting instruction. That'd be... > > opcode_pre: > mov ($not_present), %al /* #PF! */ > not_present: /* page boundary */ > int3; /* unmapped in the NPT */ self-correction: NPT or PT. Cheers, Alejandro ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors 2026-01-15 13:17 [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Alejandro Vallejo 2026-01-15 16:43 ` [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature Alejandro Vallejo @ 2026-01-28 23:15 ` Jim Mattson 2026-01-29 16:53 ` Alejandro Vallejo 1 sibling, 1 reply; 8+ messages in thread From: Jim Mattson @ 2026-01-28 23:15 UTC (permalink / raw) To: Alejandro Vallejo; +Cc: kvm, Christopherson, Paolo Bonzini On Thu, Jan 15, 2026 at 5:26 AM Alejandro Vallejo <alejandro.garciavallejo@amd.com> wrote: > > Enable exposing DecodeAssists to guests. Performs a copyout of > the insn_len and insn_bytes fields of the VMCB when the vCPU has > the feature enabled. > > Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> > --- > I wrote a little smoke test for kvm-unit-tests too. I'll send it shortly in > reply to this email. > --- > arch/x86/kvm/cpuid.c | 1 + > arch/x86/kvm/svm/nested.c | 6 ++++++ > arch/x86/kvm/svm/svm.c | 3 +++ > 3 files changed, 10 insertions(+) > > diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c > index 88a5426674a10..da9a63c8289e5 100644 > --- a/arch/x86/kvm/cpuid.c > +++ b/arch/x86/kvm/cpuid.c > @@ -1181,6 +1181,7 @@ void kvm_set_cpu_caps(void) > VENDOR_F(FLUSHBYASID), > VENDOR_F(NRIPS), > VENDOR_F(TSCRATEMSR), > + VENDOR_F(DECODEASSISTS), > VENDOR_F(V_VMSAVE_VMLOAD), > VENDOR_F(LBRV), > VENDOR_F(PAUSEFILTER), > diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c > index ba0f11c68372b..dc8a8e67a22c2 100644 > --- a/arch/x86/kvm/svm/nested.c > +++ b/arch/x86/kvm/svm/nested.c > @@ -1128,6 +1128,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm) > vmcb12->save.ssp = vmcb02->save.ssp; > } > > + if (guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS)) { > + memcpy(vmcb12->control.insn_bytes, vmcb02->control.insn_bytes, > + ARRAY_SIZE(vmcb12->control.insn_bytes)); > + vmcb12->control.insn_len = vmcb02->control.insn_len; > + } This only works if the #VMEXIT is being forwarded from vmcb02. This does not work if the #VMEXIT is synthesized by L0 (e.g. via nested_svm_inject_npf_exit() or nested_svm_inject_exception_vmexit() for #PF). > vmcb12->control.int_state = vmcb02->control.int_state; > vmcb12->control.exit_code = vmcb02->control.exit_code; > vmcb12->control.exit_code_hi = vmcb02->control.exit_code_hi; > diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c > index 24d59ccfa40d9..8cf6d7904030e 100644 > --- a/arch/x86/kvm/svm/svm.c > +++ b/arch/x86/kvm/svm/svm.c > @@ -5223,6 +5223,9 @@ static __init void svm_set_cpu_caps(void) > if (nrips) > kvm_cpu_cap_set(X86_FEATURE_NRIPS); > > + if (boot_cpu_has(X86_FEATURE_DECODEASSISTS)) > + kvm_cpu_cap_set(X86_FEATURE_DECODEASSISTS); > + > if (npt_enabled) > kvm_cpu_cap_set(X86_FEATURE_NPT); > > > base-commit: 0499add8efd72456514c6218c062911ccc922a99 DECODEASSISTS consists of more than instruction bytes and instruction length. There is also EXITINFO1 for MOV CRx, MOV DRx, INTn, and INVLPG. Since L2 typically gets dibs on a #VMEXIT (in nested_svm_intercept()), these typically fall into the "forwarded #VMEXIT" category. However, these instructions can also be emulated, in which case the vmcb12 intercepts are checked and a #VMEXIT may be synthesized. In that case, svm_check_intercept() needs to populate EXITINFO1 appropriately. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors 2026-01-28 23:15 ` [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Jim Mattson @ 2026-01-29 16:53 ` Alejandro Vallejo 2026-01-29 18:08 ` Jim Mattson 0 siblings, 1 reply; 8+ messages in thread From: Alejandro Vallejo @ 2026-01-29 16:53 UTC (permalink / raw) To: Jim Mattson; +Cc: kvm, Christopherson, Paolo Bonzini Hi, I've been busy with other matters and didn't have time to push this through, but I very definitely intend to. On Thu Jan 29, 2026 at 12:15 AM CET, Jim Mattson wrote: > On Thu, Jan 15, 2026 at 5:26 AM Alejandro Vallejo > <alejandro.garciavallejo@amd.com> wrote: >> >> Enable exposing DecodeAssists to guests. Performs a copyout of >> the insn_len and insn_bytes fields of the VMCB when the vCPU has >> the feature enabled. >> >> Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> >> --- >> I wrote a little smoke test for kvm-unit-tests too. I'll send it shortly in >> reply to this email. >> --- >> arch/x86/kvm/cpuid.c | 1 + >> arch/x86/kvm/svm/nested.c | 6 ++++++ >> arch/x86/kvm/svm/svm.c | 3 +++ >> 3 files changed, 10 insertions(+) >> >> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c >> index 88a5426674a10..da9a63c8289e5 100644 >> --- a/arch/x86/kvm/cpuid.c >> +++ b/arch/x86/kvm/cpuid.c >> @@ -1181,6 +1181,7 @@ void kvm_set_cpu_caps(void) >> VENDOR_F(FLUSHBYASID), >> VENDOR_F(NRIPS), >> VENDOR_F(TSCRATEMSR), >> + VENDOR_F(DECODEASSISTS), >> VENDOR_F(V_VMSAVE_VMLOAD), >> VENDOR_F(LBRV), >> VENDOR_F(PAUSEFILTER), >> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c >> index ba0f11c68372b..dc8a8e67a22c2 100644 >> --- a/arch/x86/kvm/svm/nested.c >> +++ b/arch/x86/kvm/svm/nested.c >> @@ -1128,6 +1128,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm) >> vmcb12->save.ssp = vmcb02->save.ssp; >> } >> >> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS)) { >> + memcpy(vmcb12->control.insn_bytes, vmcb02->control.insn_bytes, >> + ARRAY_SIZE(vmcb12->control.insn_bytes)); >> + vmcb12->control.insn_len = vmcb02->control.insn_len; >> + } > > This only works if the #VMEXIT is being forwarded from vmcb02. This > does not work if the #VMEXIT is synthesized by L0 (e.g. via > nested_svm_inject_npf_exit() or nested_svm_inject_exception_vmexit() > for #PF). I very definitely didn't consider that. Subtle. Thanks for bringing it up. > >> vmcb12->control.int_state = vmcb02->control.int_state; >> vmcb12->control.exit_code = vmcb02->control.exit_code; >> vmcb12->control.exit_code_hi = vmcb02->control.exit_code_hi; >> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c >> index 24d59ccfa40d9..8cf6d7904030e 100644 >> --- a/arch/x86/kvm/svm/svm.c >> +++ b/arch/x86/kvm/svm/svm.c >> @@ -5223,6 +5223,9 @@ static __init void svm_set_cpu_caps(void) >> if (nrips) >> kvm_cpu_cap_set(X86_FEATURE_NRIPS); >> >> + if (boot_cpu_has(X86_FEATURE_DECODEASSISTS)) >> + kvm_cpu_cap_set(X86_FEATURE_DECODEASSISTS); >> + >> if (npt_enabled) >> kvm_cpu_cap_set(X86_FEATURE_NPT); >> >> >> base-commit: 0499add8efd72456514c6218c062911ccc922a99 > > DECODEASSISTS consists of more than instruction bytes and instruction > length. There is also EXITINFO1 for MOV CRx, MOV DRx, INTn, and > INVLPG. Right, I didn't do anything about those because exit_info_1 is unconditionally copied anyway when forwarding from vmcb02 to vmcb12. Of course that doesn't account for the emulation paths you mentioned before. > Since L2 typically gets dibs on a #VMEXIT (in > nested_svm_intercept()), these typically fall into the "forwarded > #VMEXIT" category. However, these instructions can also be emulated, > in which case the vmcb12 intercepts are checked and a #VMEXIT may be > synthesized. In that case, svm_check_intercept() needs to populate > EXITINFO1 appropriately. I'm trying to think of ways to test this. It's really quite subtle. So testing MOVs to/from CR/DR would be... 1. Take a page L0 always does trap-and-emulate on (IOAPIC? LAPIC? HPET?) 2. Map it on L2. 3. Have L1 intercept MOV to/from CR/DR 4. Have L2 do a MOV to/from CR/DR from/to that region. 5. Ensure the VMCB at the L1 has exit_info_1 correctly populated. INVLPG and INTn presumably would always be in the intercept union, so these are unaffected. As for #PF and NPT intercepts... I don't _THINK_ they are affected either? I don't see which L1/L2 configs might make the L0 emulator execute an instruction on behalf of the L2 and THEN exit to L1 with an exit code of #PF/NPT. Even considering FEP, that ought to affect L1, and not L2. And even if L2 had FEP enabled that would materialise as a forwarded #UD and not something emulated by L0. So, unless I'm missing something (which I may very well be, seeing how I missed this already), synthesising exit_info_1 for the MOV to/from CR/DR ought to suffice. Which is far less annoying than teaching the emulator to fetch more data than the current instruction, so I really hope I'm not wrong. Let me know otherwise, and thanks again for the review. Cheers, Alejandro ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors 2026-01-29 16:53 ` Alejandro Vallejo @ 2026-01-29 18:08 ` Jim Mattson 0 siblings, 0 replies; 8+ messages in thread From: Jim Mattson @ 2026-01-29 18:08 UTC (permalink / raw) To: Alejandro Vallejo; +Cc: kvm, Christopherson, Paolo Bonzini On Thu, Jan 29, 2026 at 8:54 AM Alejandro Vallejo <alejandro.garciavallejo@amd.com> wrote: > > Hi, > > I've been busy with other matters and didn't have time to push this through, > but I very definitely intend to. > > On Thu Jan 29, 2026 at 12:15 AM CET, Jim Mattson wrote: > > On Thu, Jan 15, 2026 at 5:26 AM Alejandro Vallejo > > <alejandro.garciavallejo@amd.com> wrote: > >> > >> Enable exposing DecodeAssists to guests. Performs a copyout of > >> the insn_len and insn_bytes fields of the VMCB when the vCPU has > >> the feature enabled. > >> > >> Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com> > >> --- > >> I wrote a little smoke test for kvm-unit-tests too. I'll send it shortly in > >> reply to this email. > >> --- > >> arch/x86/kvm/cpuid.c | 1 + > >> arch/x86/kvm/svm/nested.c | 6 ++++++ > >> arch/x86/kvm/svm/svm.c | 3 +++ > >> 3 files changed, 10 insertions(+) > >> > >> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c > >> index 88a5426674a10..da9a63c8289e5 100644 > >> --- a/arch/x86/kvm/cpuid.c > >> +++ b/arch/x86/kvm/cpuid.c > >> @@ -1181,6 +1181,7 @@ void kvm_set_cpu_caps(void) > >> VENDOR_F(FLUSHBYASID), > >> VENDOR_F(NRIPS), > >> VENDOR_F(TSCRATEMSR), > >> + VENDOR_F(DECODEASSISTS), > >> VENDOR_F(V_VMSAVE_VMLOAD), > >> VENDOR_F(LBRV), > >> VENDOR_F(PAUSEFILTER), > >> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c > >> index ba0f11c68372b..dc8a8e67a22c2 100644 > >> --- a/arch/x86/kvm/svm/nested.c > >> +++ b/arch/x86/kvm/svm/nested.c > >> @@ -1128,6 +1128,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm) > >> vmcb12->save.ssp = vmcb02->save.ssp; > >> } > >> > >> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS)) { > >> + memcpy(vmcb12->control.insn_bytes, vmcb02->control.insn_bytes, > >> + ARRAY_SIZE(vmcb12->control.insn_bytes)); > >> + vmcb12->control.insn_len = vmcb02->control.insn_len; > >> + } > > > > This only works if the #VMEXIT is being forwarded from vmcb02. This > > does not work if the #VMEXIT is synthesized by L0 (e.g. via > > nested_svm_inject_npf_exit() or nested_svm_inject_exception_vmexit() > > for #PF). > > I very definitely didn't consider that. Subtle. Thanks for bringing it up. > > > > >> vmcb12->control.int_state = vmcb02->control.int_state; > >> vmcb12->control.exit_code = vmcb02->control.exit_code; > >> vmcb12->control.exit_code_hi = vmcb02->control.exit_code_hi; > >> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c > >> index 24d59ccfa40d9..8cf6d7904030e 100644 > >> --- a/arch/x86/kvm/svm/svm.c > >> +++ b/arch/x86/kvm/svm/svm.c > >> @@ -5223,6 +5223,9 @@ static __init void svm_set_cpu_caps(void) > >> if (nrips) > >> kvm_cpu_cap_set(X86_FEATURE_NRIPS); > >> > >> + if (boot_cpu_has(X86_FEATURE_DECODEASSISTS)) > >> + kvm_cpu_cap_set(X86_FEATURE_DECODEASSISTS); > >> + > >> if (npt_enabled) > >> kvm_cpu_cap_set(X86_FEATURE_NPT); > >> > >> > >> base-commit: 0499add8efd72456514c6218c062911ccc922a99 > > > > DECODEASSISTS consists of more than instruction bytes and instruction > > length. There is also EXITINFO1 for MOV CRx, MOV DRx, INTn, and > > INVLPG. > > Right, I didn't do anything about those because exit_info_1 is unconditionally > copied anyway when forwarding from vmcb02 to vmcb12. Of course that doesn't > account for the emulation paths you mentioned before. > > > Since L2 typically gets dibs on a #VMEXIT (in > > nested_svm_intercept()), these typically fall into the "forwarded > > #VMEXIT" category. However, these instructions can also be emulated, > > in which case the vmcb12 intercepts are checked and a #VMEXIT may be > > synthesized. In that case, svm_check_intercept() needs to populate > > EXITINFO1 appropriately. > > I'm trying to think of ways to test this. > > It's really quite subtle. So testing MOVs to/from CR/DR would be... > > 1. Take a page L0 always does trap-and-emulate on (IOAPIC? LAPIC? HPET?) > 2. Map it on L2. > 3. Have L1 intercept MOV to/from CR/DR > 4. Have L2 do a MOV to/from CR/DR from/to that region. > 5. Ensure the VMCB at the L1 has exit_info_1 correctly populated. > > INVLPG and INTn presumably would always be in the intercept union, so these are > unaffected. Any of these instructions can be emulated. I think the easiest way to test is FEP (force emulation prefix), which will result in a #UD #VMEXIT to L0. Then, during emulation, if L0 finds the intercept bit set in vmcb12, it will synthesize the appropriate #VMEXIT to L1. > As for #PF and NPT intercepts... I don't _THINK_ they are affected either? I > don't see which L1/L2 configs might make the L0 emulator execute an instruction > on behalf of the L2 and THEN exit to L1 with an exit code of #PF/NPT. MOVBE on Bulldozer? (I don't actually have a Bulldozer to check.) KVM is happy to emulate MOVBE on hardware that doesn't support it. See KVM_GET_EMULATED_CPUID. > Even considering FEP, that ought to affect L1, and not L2. And even if L2 had > FEP enabled that would materialise as a forwarded #UD and not something emulated > by L0. The #UD is handled by the host first. See nested_svm_exit_special(). Hence, FEP should result in emulation by L0. The main reason for this is that if L0 advertises an instruction to L0 that hardware doesn't support (e.g. MOVBE), then L0 is responsible for emulating that instruction for both L1 and L2. > So, unless I'm missing something (which I may very well be, seeing how I missed > this already), synthesising exit_info_1 for the MOV to/from CR/DR ought to > suffice. Which is far less annoying than teaching the emulator to fetch more > data than the current instruction, so I really hope I'm not wrong. > > Let me know otherwise, and thanks again for the review. > > Cheers, > Alejandro ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-01-29 18:08 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-01-15 13:17 [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Alejandro Vallejo 2026-01-15 16:43 ` [kvm-unit-tests] x86: Add #PF test case for the SVM DecodeAssists feature Alejandro Vallejo 2026-01-15 18:32 ` Sean Christopherson 2026-01-16 12:59 ` Alejandro Vallejo 2026-01-16 13:03 ` Alejandro Vallejo 2026-01-28 23:15 ` [PATCH] KVM: nSVM: Expose SVM DecodeAssists to guest hypervisors Jim Mattson 2026-01-29 16:53 ` Alejandro Vallejo 2026-01-29 18:08 ` Jim Mattson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox