On Thu, Mar 26, 2026 at 10:50 AM Jim Mattson <[email protected]> wrote:
>
> Define a quirk to control whether nested SVM shares L1's PAT with L2
> (legacy behavior) or gives L2 its own independent gPAT (correct behavior
> per the APM).
>
> When the quirk is enabled (default), L2 shares L1's PAT, preserving the
> legacy KVM behavior. When userspace disables the quirk, KVM correctly
> virtualizes the PAT for nested SVM guests, giving L2 a separate gPAT as
> specified in the AMD architecture.
>
> Signed-off-by: Jim Mattson <[email protected]>
> ---
> Documentation/virt/kvm/api.rst | 14 ++++++++++++++
> arch/x86/include/asm/kvm_host.h | 3 ++-
> arch/x86/include/uapi/asm/kvm.h | 1 +
> arch/x86/kvm/svm/svm.h | 7 +++++++
> 4 files changed, 24 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 032516783e96..2d56f17e3760 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -8551,6 +8551,20 @@ KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM By default,
> KVM relaxes the consisten
> bit to be cleared. Note that the
> vmcs02
> bit is still completely
> controlled by the
> host, regardless of the quirk
> setting.
> +
> +KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT By default, KVM for nested SVM
> guests
> + shares the IA32_PAT MSR between
> L1 and
> + L2. This is legacy behavior and
> does
> + not match the AMD architecture
> + specification. When this quirk is
> + disabled and nested paging (NPT)
> is
> + enabled for L2, KVM correctly
> + virtualizes a separate guest PAT
> + register for L2, using the g_pat
> + field in the VMCB. When NPT is
> + disabled for L2, L1 and L2
> continue
> + to share the IA32_PAT MSR
> regardless
> + of the quirk setting.
> ========================================
> ================================================
>
> 7.32 KVM_CAP_MAX_VCPU_ID
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index d3bdc9828133..0809d8f28208 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -2511,7 +2511,8 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot,
> unsigned long npages);
> KVM_X86_QUIRK_SLOT_ZAP_ALL | \
> KVM_X86_QUIRK_STUFF_FEATURE_MSRS | \
> KVM_X86_QUIRK_IGNORE_GUEST_PAT | \
> - KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM)
> + KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM \
There is a missing "|" here, it's fixed in patch 3, but I think it
should be fixed up here (maybe when applied).
> + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT)
>
> #define KVM_X86_CONDITIONAL_QUIRKS \
> (KVM_X86_QUIRK_CD_NW_CLEARED | \
> diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
> index 5f2b30d0405c..3ada2fa9ca86 100644
> --- a/arch/x86/include/uapi/asm/kvm.h
> +++ b/arch/x86/include/uapi/asm/kvm.h
> @@ -477,6 +477,7 @@ struct kvm_sync_regs {
> #define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8)
> #define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9)
> #define KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM (1 << 10)
> +#define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT (1 << 11)
>
> #define KVM_STATE_NESTED_FORMAT_VMX 0
> #define KVM_STATE_NESTED_FORMAT_SVM 1
> diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
> index ff1e4b4dc998..67aa5d34332e 100644
> --- a/arch/x86/kvm/svm/svm.h
> +++ b/arch/x86/kvm/svm/svm.h
> @@ -616,6 +616,13 @@ static inline bool nested_npt_enabled(struct vcpu_svm
> *svm)
> return svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_NP;
> }
>
> +static inline bool l2_has_separate_pat(struct vcpu_svm *svm)
> +{
> + return nested_npt_enabled(svm) &&
> + !kvm_check_has_quirk(svm->vcpu.kvm,
> + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT);
> +}
> +
> static inline bool nested_vnmi_enabled(struct vcpu_svm *svm)
> {
> return guest_cpu_cap_has(&svm->vcpu, X86_FEATURE_VNMI) &&
> --
> 2.53.0.1018.g2bb0e51243-goog
>