[PATCH v2 4/8] KVM: nSVM: Propagate hardware DecodeAssist bytes to VMCB12

From: Tina Zhang

Date: Tue Jul 14 2026 - 01:11:28 EST


When DecodeAssists is exposed to L1, copy fresh hardware instruction
bytes from VMCB02 to VMCB12 for data #NPF and intercepted #PF VM-Exits.

Clear VMCB12's instruction-byte state for instruction-fetch faults,
unrelated exits, and exits without fresh VMCB02 state so that stale bytes
are not exposed to L1.

Signed-off-by: Tina Zhang <zhang_wei@xxxxxxxxxxxxxx>
---
arch/x86/kvm/svm/nested.c | 53 +++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 70e2ee3af78b..9e12eab7eed9 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -40,6 +40,57 @@ static void nested_svm_clear_insn_bytes(struct vmcb *vmcb)
sizeof(vmcb->control.insn_bytes));
}

+static void nested_svm_copy_insn_bytes(struct vmcb *to,
+ const struct vmcb *from)
+{
+ u8 insn_len = from->control.insn_len;
+
+ nested_svm_clear_insn_bytes(to);
+
+ 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 bool nested_svm_vmexit_supports_insn_bytes(struct kvm_vcpu *vcpu,
+ const struct vmcb *vmcb12)
+{
+ u64 exit_code = vmcb12->control.exit_code;
+
+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS))
+ return false;
+
+ if (exit_code != SVM_EXIT_NPF &&
+ exit_code != SVM_EXIT_EXCP_BASE + PF_VECTOR)
+ return false;
+
+ return !(vmcb12->control.exit_info_1 & PFERR_FETCH_MASK);
+}
+
+/*
+ * Rebuild VMCB12's DecodeAssist bytes for the nested VM-Exit. Use fresh
+ * hardware VMCB02 state when available; otherwise report a zero byte count.
+ */
+static void nested_svm_update_vmcb12_insn_bytes(struct kvm_vcpu *vcpu,
+ struct vmcb *vmcb12,
+ struct vmcb *vmcb02)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ bool vmcb02_insn_bytes_fresh = svm->nested.vmcb02_insn_bytes_fresh;
+
+ svm->nested.vmcb02_insn_bytes_fresh = false;
+
+ nested_svm_clear_insn_bytes(vmcb12);
+
+ if (!nested_svm_vmexit_supports_insn_bytes(vcpu, vmcb12))
+ return;
+
+ if (vmcb02_insn_bytes_fresh)
+ nested_svm_copy_insn_bytes(vmcb12, vmcb02);
+}
+
static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
struct x86_exception *fault)
{
@@ -1263,6 +1314,8 @@ 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;

+ nested_svm_update_vmcb12_insn_bytes(vcpu, vmcb12, vmcb02);
+
if (nested_vmcb12_has_lbrv(vcpu))
svm_copy_lbrs(&vmcb12->save, &vmcb02->save);

--
2.43.7