Re: [PATCH v6 2/8] KVM: nSVM: Synthesize DecodeAssists EXITINFO for emulated intercepts

From: Jim Mattson

Date: Mon Sep 14 2026 - 15:00:16 EST


On Sat, Sep 12, 2026 at 11:44 PM Tina Zhang <zhang_wei@xxxxxxxxxxxxxx> wrote:
>
> When the x86 emulator encounters an instruction intercepted by L1,
> svm_check_intercept() synthesizes a nested VM-Exit without fresh hardware
> DecodeAssist state. Populate the architectural EXITINFO1 field when
> DecodeAssists is exposed to L1.
>
> Provide the GPR number for MOV CR/DR, the interrupt vector for INTn, and
> the linear address for INVLPG. Leave EXITINFO1 clear for CLTS, LMSW,
> SMSW, selective CR0 writes, and INVLPGA; the INVLPGA address remains in
> guest rAX. Leave EXITINFO1 unchanged when DecodeAssists is not exposed,
> and leave unrelated intercepts unchanged.
>
> Signed-off-by: Tina Zhang <zhang_wei@xxxxxxxxxxxxxx>
> ---
> arch/x86/kvm/svm/svm.c | 32 +++++++++++++++++++++++++++++++-
> 1 file changed, 31 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index ea647938a2a6..6834e1101bb6 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -4837,6 +4837,7 @@ static int svm_check_intercept(struct kvm_vcpu *vcpu,
> int vmexit, ret = X86EMUL_CONTINUE;
> struct __x86_intercept icpt_info;
> struct vmcb *vmcb = svm->vmcb;
> + bool decode_assists;
>
> if (info->intercept >= ARRAY_SIZE(x86_intercept_map))
> goto out;
> @@ -4846,14 +4847,26 @@ static int svm_check_intercept(struct kvm_vcpu *vcpu,
> if (stage != icpt_info.stage)
> goto out;
>
> + decode_assists = guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS);
> +
> switch (icpt_info.exit_code) {
> case SVM_EXIT_READ_CR0:
> if (info->intercept == x86_intercept_cr_read)
> icpt_info.exit_code += info->modrm_reg;
> +
> + if (decode_assists)
> + vmcb->control.exit_info_1 =
> + info->intercept == x86_intercept_cr_read ?
> + BIT_ULL(63) | (info->modrm_rm & 0xf) : 0;

Nit: CR_VALID | (info->modrm_rm & SVM_EXITINFO_REG_MASK)

> break;
> case SVM_EXIT_WRITE_CR0: {
> unsigned long cr0, val;
>
> + if (decode_assists)
> + vmcb->control.exit_info_1 =
> + info->intercept == x86_intercept_cr_write ?
> + BIT_ULL(63) | (info->modrm_rm & 0xf) : 0;

Same nit.

> +
> /*
> * Adjust the exit code accordingly if a CR other than CR0 is
> * being written, and skip straight to the common handling as
> @@ -4891,13 +4904,30 @@ static int svm_check_intercept(struct kvm_vcpu *vcpu,
> */
> cr0 = vcpu->arch.cr0 & ~SVM_CR0_SELECTIVE_MASK;
> val = info->src_val & ~SVM_CR0_SELECTIVE_MASK;
> - if (cr0 ^ val)
> + if (cr0 ^ val) {
> icpt_info.exit_code = SVM_EXIT_CR0_SEL_WRITE;
> + if (decode_assists)
> + vmcb->control.exit_info_1 = 0;
> + }

I don't think clearing EXITINFO1 here is correct. The APM says:

> MOV-to-CR0 Special Case. If the instruction is MOV-to-CR, the GPR number is provided. If the instruction is LMSW or CLTS, no additional information is provided and bit 63 is not set.

The Decode Assists depend on the instruction opcode, not on the intercept.

> break;
> }
> case SVM_EXIT_READ_DR0:
> case SVM_EXIT_WRITE_DR0:
> icpt_info.exit_code += info->modrm_reg;
> + if (decode_assists)
> + vmcb->control.exit_info_1 = info->modrm_rm & 0xf;

Nit: SVM_EXITINFO_REG_MASK

> + break;
> + case SVM_EXIT_SWINT:
> + if (decode_assists)
> + vmcb->control.exit_info_1 = info->src_val & 0xff;
> + break;
> + case SVM_EXIT_INVLPG:
> + if (decode_assists)
> + vmcb->control.exit_info_1 = info->invlpg_linear_addr;
> + break;
> + case SVM_EXIT_INVLPGA:
> + if (decode_assists)
> + vmcb->control.exit_info_1 = 0;
> break;
> case SVM_EXIT_MSR:
> if (info->intercept == x86_intercept_wrmsr)
> --
> 2.43.7
>