[PATCH v2 1/8] KVM: x86: nSVM: Redirect IA32_PAT accesses to either hPAT or gPAT
From: Jim Mattson
Date: Thu Jan 15 2026 - 18:22:14 EST
When the vCPU is in guest mode with nested NPT enabled, guest accesses to
IA32_PAT are redirected to the gPAT register, which is stored in
vmcb02->save.g_pat.
Non-guest accesses (e.g. from userspace) to IA32_PAT are always redirected
to hPAT, which is stored in vcpu->arch.pat.
This is architected behavior. It also makes it possible to restore a new
checkpoint on an old kernel with reasonable semantics. After the restore,
gPAT will be lost, and L2 will run on L1's PAT. Note that the old kernel
would have always run L2 on L1's PAT.
Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx>
---
arch/x86/kvm/svm/svm.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7041498a8091..3f8581adf0c1 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -2846,6 +2846,13 @@ 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 (!msr_info->host_initiated && is_guest_mode(vcpu) &&
+ nested_npt_enabled(svm))
+ msr_info->data = svm->vmcb->save.g_pat; /* gPAT */
+ else
+ msr_info->data = vcpu->arch.pat; /* hPAT */
+ break;
default:
return kvm_get_msr_common(vcpu, msr_info);
}
@@ -2929,14 +2936,24 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
break;
case MSR_IA32_CR_PAT:
- ret = kvm_set_msr_common(vcpu, msr);
- if (ret)
- break;
+ if (!kvm_pat_valid(data))
+ return 1;
- svm->vmcb01.ptr->save.g_pat = data;
- if (is_guest_mode(vcpu))
- nested_vmcb02_compute_g_pat(svm);
- vmcb_mark_dirty(svm->vmcb, VMCB_NPT);
+ if (!msr->host_initiated && is_guest_mode(vcpu) &&
+ nested_npt_enabled(svm)) {
+ svm->vmcb->save.g_pat = data; /* gPAT */
+ vmcb_mark_dirty(svm->vmcb, VMCB_NPT);
+ } else {
+ vcpu->arch.pat = data; /* hPAT */
+ if (npt_enabled) {
+ svm->vmcb01.ptr->save.g_pat = data;
+ vmcb_mark_dirty(svm->vmcb01.ptr, VMCB_NPT);
+ if (is_guest_mode(vcpu)) {
+ svm->vmcb->save.g_pat = data;
+ vmcb_mark_dirty(svm->vmcb, VMCB_NPT);
+ }
+ }
+ }
break;
case MSR_IA32_SPEC_CTRL:
if (!msr->host_initiated &&
--
2.52.0.457.g6b5491de43-goog