]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: x86: nSVM: Redirect IA32_PAT accesses to either hPAT or gPAT
authorJim Mattson <jmattson@google.com>
Tue, 7 Apr 2026 19:03:28 +0000 (12:03 -0700)
committerSean Christopherson <seanjc@google.com>
Thu, 14 May 2026 12:45:11 +0000 (05:45 -0700)
When handling PAT accesses from L2, route PAT accesses to either hPAT or
gPAT based on whether or not L2 has a separate PAT, i.e. if KVM is actually
emulating gPAT, instead of using L1's PAT for everything.  Specifically, if
KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled, the vCPU is in guest mode
with nested NPT enabled, *and* the access if from the guest (i.e. is not
from the host stuffing PAT as part of save/restore), then redirect guest
PAT accesses to the gPAT "register" in vmcb02, i.e. emulate gPAT for L2.

Always route non-guest accesses to hPAT, i.e. L1's PAT in vcpu->arch.pat,
to ensures that KVM_{G,S}ET_MSRS and KVM_{G,S}ET_NESTED_STATE are
independent of each other and can be ordered arbitrarily during save and
restore.  E.g. if KVM didn't exempt host accesses, then whether a write to
PAT hit hPAT or gPAT would vary based on whether userspace restores PAT
before or after nested state.  Note, gPAT is saved and restored separately
via KVM_{G,S}ET_NESTED_STATE.

WARN if there's a host-initiated access to PAT from within KVM_RUN, i.e. if
KVM itself initiated the access, as there are no such accesses today, and
it's not clear what the "right" behavior would be.

Fixes: 15038e147247 ("KVM: SVM: obey guest PAT")
Signed-off-by: Jim Mattson <jmattson@google.com>
Co-developed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/svm.h

index 536252e2d1bcab82c6a831f23e702814a14be828..e70839d4531cafffb272d4684ab9b25d00bfe71b 100644 (file)
@@ -707,15 +707,6 @@ static int nested_svm_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3,
        return 0;
 }
 
-void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm)
-{
-       if (!svm->nested.vmcb02.ptr)
-               return;
-
-       /* FIXME: merge g_pat from vmcb01 and vmcb12.  */
-       vmcb_set_gpat(svm->nested.vmcb02.ptr, svm->vmcb01.ptr->save.g_pat);
-}
-
 static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu)
 {
        return guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) &&
index d032d0b3d4c0f15a8141202f090db252beefaa9a..62d374ceffc1f7d97c636140a2ba7dd042fe1bf4 100644 (file)
@@ -2786,6 +2786,20 @@ static bool sev_es_prevent_msr_access(struct kvm_vcpu *vcpu,
               !msr_write_intercepted(to_svm(vcpu), msr_info->index);
 }
 
+static bool svm_pat_accesses_gpat(struct kvm_vcpu *vcpu, bool from_host)
+{
+       /*
+        * When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and nested
+        * NPT is enabled, L2 has a separate PAT from L1.  Guest accesses
+        * to IA32_PAT while running L2 target L2's gPAT; host-initiated
+        * accesses always target L1's hPAT so that KVM_GET/SET_MSRS and
+        * KVM_GET/SET_NESTED_STATE are independent of each other and can
+        * be ordered arbitrarily during save and restore.
+        */
+       WARN_ON_ONCE(from_host && vcpu->wants_to_run);
+       return !from_host && is_guest_mode(vcpu) && l2_has_separate_pat(vcpu);
+}
+
 static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
@@ -2902,6 +2916,12 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_AMD64_DE_CFG:
                msr_info->data = svm->msr_decfg;
                break;
+       case MSR_IA32_CR_PAT:
+               if (svm_pat_accesses_gpat(vcpu, msr_info->host_initiated)) {
+                       msr_info->data = svm->vmcb->save.g_pat;
+                       break;
+               }
+               return kvm_get_msr_common(vcpu, msr_info);
        default:
                return kvm_get_msr_common(vcpu, msr_info);
        }
@@ -2985,13 +3005,23 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
 
                break;
        case MSR_IA32_CR_PAT:
+               if (svm_pat_accesses_gpat(vcpu, msr->host_initiated)) {
+                       if (!kvm_pat_valid(data))
+                               return 1;
+
+                       vmcb_set_gpat(svm->vmcb, data);
+                       break;
+               }
+
                ret = kvm_set_msr_common(vcpu, msr);
                if (ret)
                        break;
 
-               vmcb_set_gpat(svm->vmcb01.ptr, data);
-               if (is_guest_mode(vcpu))
-                       nested_vmcb02_compute_g_pat(svm);
+               if (npt_enabled) {
+                       vmcb_set_gpat(svm->vmcb01.ptr, data);
+                       if (is_guest_mode(vcpu) && !l2_has_separate_pat(vcpu))
+                               vmcb_set_gpat(svm->vmcb, data);
+               }
                break;
        case MSR_IA32_SPEC_CTRL:
                if (!msr->host_initiated &&
index fb7360231d8b776e18a148f6473b466f0df01988..2b6733dffd76ffd096980bb9b1d00d12df6a54f6 100644 (file)
@@ -893,7 +893,6 @@ void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm,
 void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm,
                                    struct vmcb_save_area *save);
 void nested_sync_control_from_vmcb02(struct vcpu_svm *svm);
-void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm);
 void svm_switch_vmcb(struct vcpu_svm *svm, struct kvm_vmcb_info *target_vmcb);
 
 extern struct kvm_x86_nested_ops svm_nested_ops;