KVM: x86: nSVM: Redirect IA32_PAT accesses to either hPAT or gPAT

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: 15038e1472 ("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>
This commit is contained in:
Jim Mattson 2026-04-07 12:03:28 -07:00 committed by Sean Christopherson
parent 02233c73f8
commit 573321b945
3 changed files with 33 additions and 13 deletions

View 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) &&

View 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 &&

View 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;