mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
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:
parent
02233c73f8
commit
573321b945
|
|
@ -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) &&
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user