From 959d3f7565352fab844ae73a722631c5f615427f Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 9 Mar 2026 12:00:43 -0600 Subject: [PATCH 01/11] x86/virt/sev: Drop support for SNP hotplug During an SNP_INIT(_EX), the SEV firmware checks that all CPUs have the SNP syscfg bit set, and fails if they do not. As such, it does not make sense to have offline CPUs: the firmware will fail initialization because of the offlined ones that the kernel did not initialize. Further, there is a bug: during SNP_INIT(_EX) the firmware requires the MFDM syscfg bit to be set in addition to having SNP enabled, which the previous hotplug code did not do. Since k8_check_syscfg_dram_mod_en() enforces this be cleared, hotplug wouldn't work. Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260309180053.2389118-2-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index a4f3a364fb65..f404c609582c 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -130,33 +130,20 @@ static unsigned long snp_nr_leaked_pages; #undef pr_fmt #define pr_fmt(fmt) "SEV-SNP: " fmt -static int __mfd_enable(unsigned int cpu) -{ - u64 val; - - if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) - return 0; - - rdmsrq(MSR_AMD64_SYSCFG, val); - - val |= MSR_AMD64_SYSCFG_MFDM; - - wrmsrq(MSR_AMD64_SYSCFG, val); - - return 0; -} - static __init void mfd_enable(void *arg) { - __mfd_enable(smp_processor_id()); + if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) + return; + + msr_set_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); } -static int __snp_enable(unsigned int cpu) +static __init void snp_enable(void *arg) { u64 val; if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) - return 0; + return; rdmsrq(MSR_AMD64_SYSCFG, val); @@ -164,13 +151,6 @@ static int __snp_enable(unsigned int cpu) val |= MSR_AMD64_SYSCFG_SNP_VMPL_EN; wrmsrq(MSR_AMD64_SYSCFG, val); - - return 0; -} - -static __init void snp_enable(void *arg) -{ - __snp_enable(smp_processor_id()); } static void __init __snp_fixup_e820_tables(u64 pa) @@ -553,8 +533,6 @@ int __init snp_rmptable_init(void) on_each_cpu(snp_enable, NULL, 1); skip_enable: - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL); - /* * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic * notifier is invoked to do SNP IOMMU shutdown before kdump. From 99cf1fb58e68291d408b4c4484181cf88f081857 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 9 Mar 2026 12:00:44 -0600 Subject: [PATCH 02/11] x86/virt/sev: Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN WBINVD is required before SNP_INIT(_EX), but not before setting MSR_AMD64_SYSCFG_SNP_EN, since the ccp driver already does its own WBINVD before SNP_INIT (and this one would be too early for that anyway...). Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260309180053.2389118-3-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index f404c609582c..5e07f103c271 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -524,9 +524,6 @@ int __init snp_rmptable_init(void) memset(desc->rmp_entry, 0, desc->size); } - /* Flush the caches to ensure that data is written before SNP is enabled. */ - wbinvd_on_all_cpus(); - /* MtrrFixDramModEn must be enabled on all the CPUs prior to enabling SNP. */ on_each_cpu(mfd_enable, NULL, 1); From cca149429956745293ea94865ff8be6f77cc003d Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Mon, 9 Mar 2026 12:00:45 -0600 Subject: [PATCH 03/11] x86/virt/sev: Keep the RMP table bookkeeping area mapped In preparation for delayed SNP initialization and disablement on shutdown, the RMP will need to be cleared each time SNP is disabled. Maintain the mapping to the RMP bookkeeping area to avoid mapping and unmapping it each time and any possible errors that may arise from that. Signed-off-by: Tom Lendacky Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260309180053.2389118-4-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 5e07f103c271..e35fac0a8a3d 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -117,6 +117,8 @@ static u64 rmp_segment_mask; static u64 rmp_cfg; +static void *rmp_bookkeeping __ro_after_init; + /* Mask to apply to a PFN to get the first PFN of a 2MB page */ #define PFN_PMD_MASK GENMASK_ULL(63, PMD_SHIFT - PAGE_SHIFT) @@ -240,23 +242,6 @@ void __init snp_fixup_e820_tables(void) } } -static bool __init clear_rmptable_bookkeeping(void) -{ - void *bk; - - bk = memremap(probed_rmp_base, RMPTABLE_CPU_BOOKKEEPING_SZ, MEMREMAP_WB); - if (!bk) { - pr_err("Failed to map RMP bookkeeping area\n"); - return false; - } - - memset(bk, 0, RMPTABLE_CPU_BOOKKEEPING_SZ); - - memunmap(bk); - - return true; -} - static bool __init alloc_rmp_segment_desc(u64 segment_pa, u64 segment_size, u64 pa) { u64 rst_index, rmp_segment_size_max; @@ -474,10 +459,22 @@ e_free: static bool __init setup_rmptable(void) { if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) { - return setup_segmented_rmptable(); + if (!setup_segmented_rmptable()) + return false; } else { - return setup_contiguous_rmptable(); + if (!setup_contiguous_rmptable()) + return false; } + + rmp_bookkeeping = memremap(probed_rmp_base, RMPTABLE_CPU_BOOKKEEPING_SZ, MEMREMAP_WB); + if (!rmp_bookkeeping) { + pr_err("Failed to map RMP bookkeeping area\n"); + free_rmp_segment_table(); + + return false; + } + + return true; } /* @@ -508,10 +505,7 @@ int __init snp_rmptable_init(void) goto skip_enable; /* Zero out the RMP bookkeeping area */ - if (!clear_rmptable_bookkeeping()) { - free_rmp_segment_table(); - return -ENOSYS; - } + memset(rmp_bookkeeping, 0, RMPTABLE_CPU_BOOKKEEPING_SZ); /* Zero out the RMP entries */ for (i = 0; i < rst_max_index; i++) { From 531397a80360661b61fb69ab41c7359c1f7d35c1 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Tue, 3 Feb 2026 16:24:05 -0600 Subject: [PATCH 04/11] x86/sev: Rename SNP_FEATURES_PRESENT to SNP_FEATURES_IMPL Rename SNP_FEATURES_PRESENT to SNP_FEATURES_IMPL to denote its counterpart relationship with SNP_FEATURES_IMPL_REQ. [ bp: Drop stable@, massage commit message. ] Suggested-by: Borislav Petkov (AMD) Suggested-by: Tom Lendacky Signed-off-by: Kim Phillips Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260203222405.4065706-4-kim.phillips@amd.com --- arch/x86/boot/compressed/sev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c index e468476e9e4a..c6512f2ea31e 100644 --- a/arch/x86/boot/compressed/sev.c +++ b/arch/x86/boot/compressed/sev.c @@ -198,11 +198,11 @@ bool sev_es_check_ghcb_fault(unsigned long address) #endif /* - * SNP_FEATURES_PRESENT is the mask of SNP features that are implemented + * SNP_FEATURES_IMPL is the mask of SNP features that are implemented * by the guest kernel. As and when a new feature is implemented in the * guest kernel, a corresponding bit should be added to the mask. */ -#define SNP_FEATURES_PRESENT (MSR_AMD64_SNP_DEBUG_SWAP | \ +#define SNP_FEATURES_IMPL (MSR_AMD64_SNP_DEBUG_SWAP | \ MSR_AMD64_SNP_SECURE_TSC | \ SNP_FEATURE_SECURE_AVIC) @@ -211,7 +211,7 @@ u64 snp_get_unsupported_features(u64 status) if (!(status & MSR_AMD64_SEV_SNP_ENABLED)) return 0; - return status & SNP_FEATURES_IMPL_REQ & ~SNP_FEATURES_PRESENT; + return status & SNP_FEATURES_IMPL_REQ & ~SNP_FEATURES_IMPL; } void snp_check_features(void) From 9c016c3f4928f436b9ad20f30a5a9d538490fbb4 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Tue, 24 Mar 2026 10:12:55 -0600 Subject: [PATCH 05/11] x86/sev: Create a function to clear/zero the RMP In preparation for delayed SNP initialization and disablement on shutdown, create a function, clear_rmp(), that clears the RMP bookkeeping area and the RMP entries. Signed-off-by: Tom Lendacky Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260324161301.1353976-2-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index e35fac0a8a3d..025606969823 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -242,6 +242,32 @@ void __init snp_fixup_e820_tables(void) } } +static void clear_rmp(void) +{ + unsigned int i; + u64 val; + + if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) + return; + + /* Clearing the RMP while SNP is enabled will cause an exception */ + rdmsrq(MSR_AMD64_SYSCFG, val); + if (WARN_ON_ONCE(val & MSR_AMD64_SYSCFG_SNP_EN)) + return; + + memset(rmp_bookkeeping, 0, RMPTABLE_CPU_BOOKKEEPING_SZ); + + for (i = 0; i < rst_max_index; i++) { + struct rmp_segment_desc *desc; + + desc = rmp_segment_table[i]; + if (!desc) + continue; + + memset(desc->rmp_entry, 0, desc->size); + } +} + static bool __init alloc_rmp_segment_desc(u64 segment_pa, u64 segment_size, u64 pa) { u64 rst_index, rmp_segment_size_max; @@ -484,7 +510,6 @@ static bool __init setup_rmptable(void) */ int __init snp_rmptable_init(void) { - unsigned int i; u64 val; if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP))) @@ -504,19 +529,7 @@ int __init snp_rmptable_init(void) if (val & MSR_AMD64_SYSCFG_SNP_EN) goto skip_enable; - /* Zero out the RMP bookkeeping area */ - memset(rmp_bookkeeping, 0, RMPTABLE_CPU_BOOKKEEPING_SZ); - - /* Zero out the RMP entries */ - for (i = 0; i < rst_max_index; i++) { - struct rmp_segment_desc *desc; - - desc = rmp_segment_table[i]; - if (!desc) - continue; - - memset(desc->rmp_entry, 0, desc->size); - } + clear_rmp(); /* MtrrFixDramModEn must be enabled on all the CPUs prior to enabling SNP. */ on_each_cpu(mfd_enable, NULL, 1); From ca2ca373ec854d203c74e6ac1c4e08a70e8d2509 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Thu, 26 Mar 2026 10:11:05 -0600 Subject: [PATCH 06/11] x86/sev: Create snp_prepare() In preparation for delayed SNP initialization, create a function snp_prepare() that does the necessary architecture setup. Export this function for the ccp module to allow it to do the setup as necessary. Introduce a cpu_read_lock/unlock() wrapper around the MFDM and SNP enable. While CPU hotplug is not supported, this makes sure that the bit setting happens on the same set of CPUs in both cases. This improvement was suggested by Sashiko: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org Also move {mfd,snp}_enable() out of the __init section, since these will be called later. Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260326161110.1764303-3-tycho@kernel.org --- arch/x86/include/asm/sev.h | 2 ++ arch/x86/virt/svm/sev.c | 49 +++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index 0e6c0940100f..2140e26dec6c 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -661,6 +661,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages) { __snp_leak_pages(pfn, pages, true); } +void snp_prepare(void); #else static inline bool snp_probe_rmptable_info(void) { return false; } static inline int snp_rmptable_init(void) { return -ENOSYS; } @@ -677,6 +678,7 @@ static inline void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp) static inline void snp_leak_pages(u64 pfn, unsigned int npages) {} static inline void kdump_sev_callback(void) { } static inline void snp_fixup_e820_tables(void) {} +static inline void snp_prepare(void) {} #endif #endif diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 025606969823..ccec52952573 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -132,7 +132,7 @@ static unsigned long snp_nr_leaked_pages; #undef pr_fmt #define pr_fmt(fmt) "SEV-SNP: " fmt -static __init void mfd_enable(void *arg) +static void mfd_enable(void *arg) { if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) return; @@ -140,7 +140,7 @@ static __init void mfd_enable(void *arg) msr_set_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); } -static __init void snp_enable(void *arg) +static void snp_enable(void *arg) { u64 val; @@ -503,6 +503,33 @@ static bool __init setup_rmptable(void) return true; } +void snp_prepare(void) +{ + u64 val; + + /* + * Check if SEV-SNP is already enabled, this can happen in case of + * kexec boot. + */ + rdmsrq(MSR_AMD64_SYSCFG, val); + if (val & MSR_AMD64_SYSCFG_SNP_EN) + return; + + clear_rmp(); + + cpus_read_lock(); + + /* + * MtrrFixDramModEn is not shared between threads on a core, + * therefore it must be set on all CPUs prior to enabling SNP. + */ + on_each_cpu(mfd_enable, NULL, 1); + on_each_cpu(snp_enable, NULL, 1); + + cpus_read_unlock(); +} +EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp"); + /* * Do the necessary preparations which are verified by the firmware as * described in the SNP_INIT_EX firmware command description in the SNP @@ -510,8 +537,6 @@ static bool __init setup_rmptable(void) */ int __init snp_rmptable_init(void) { - u64 val; - if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP))) return -ENOSYS; @@ -521,22 +546,8 @@ int __init snp_rmptable_init(void) if (!setup_rmptable()) return -ENOSYS; - /* - * Check if SEV-SNP is already enabled, this can happen in case of - * kexec boot. - */ - rdmsrq(MSR_AMD64_SYSCFG, val); - if (val & MSR_AMD64_SYSCFG_SNP_EN) - goto skip_enable; + snp_prepare(); - clear_rmp(); - - /* MtrrFixDramModEn must be enabled on all the CPUs prior to enabling SNP. */ - on_each_cpu(mfd_enable, NULL, 1); - - on_each_cpu(snp_enable, NULL, 1); - -skip_enable: /* * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic * notifier is invoked to do SNP IOMMU shutdown before kdump. From b65546b14d273dde2a56c0313da36a6aeb5c3f32 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 24 Mar 2026 10:12:57 -0600 Subject: [PATCH 07/11] x86/sev: Create snp_shutdown() After SNP_SHUTDOWN, two things should be done: 1. clear the RMP table 2. disable MFDM to prevent the FW_WARN in k8_check_syscfg_dram_mod_en() in the event of a kexec Create and export to the CCP driver a function that does them. Also change the MFDM helper to allow for disabling the bit, since the SNP x86 shutdown path needs to disable MFDM. The comment for k8_check_syscfg_dram_mod_en() notes, the "BIOS" is supposed clear it, or the kernel in the case of module unload and shutdown followed by kexec. Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260324161301.1353976-4-tycho@kernel.org --- arch/x86/include/asm/sev.h | 2 ++ arch/x86/virt/svm/sev.c | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index 2140e26dec6c..09e605c85de4 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -662,6 +662,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages) __snp_leak_pages(pfn, pages, true); } void snp_prepare(void); +void snp_shutdown(void); #else static inline bool snp_probe_rmptable_info(void) { return false; } static inline int snp_rmptable_init(void) { return -ENOSYS; } @@ -679,6 +680,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int npages) {} static inline void kdump_sev_callback(void) { } static inline void snp_fixup_e820_tables(void) {} static inline void snp_prepare(void) {} +static inline void snp_shutdown(void) {} #endif #endif diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index ccec52952573..3b2273dca196 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -132,12 +132,15 @@ static unsigned long snp_nr_leaked_pages; #undef pr_fmt #define pr_fmt(fmt) "SEV-SNP: " fmt -static void mfd_enable(void *arg) +static void mfd_reconfigure(void *arg) { if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) return; - msr_set_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); + if (arg) + msr_set_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); + else + msr_clear_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); } static void snp_enable(void *arg) @@ -523,13 +526,26 @@ void snp_prepare(void) * MtrrFixDramModEn is not shared between threads on a core, * therefore it must be set on all CPUs prior to enabling SNP. */ - on_each_cpu(mfd_enable, NULL, 1); + on_each_cpu(mfd_reconfigure, (void *)1, 1); on_each_cpu(snp_enable, NULL, 1); cpus_read_unlock(); } EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp"); +void snp_shutdown(void) +{ + u64 syscfg; + + rdmsrq(MSR_AMD64_SYSCFG, syscfg); + if (syscfg & MSR_AMD64_SYSCFG_SNP_EN) + return; + + clear_rmp(); + on_each_cpu(mfd_reconfigure, NULL, 1); +} +EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); + /* * Do the necessary preparations which are verified by the firmware as * described in the SNP_INIT_EX firmware command description in the SNP From 299933b118d1bcf8b93d58076aed34582718b0dd Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 24 Mar 2026 10:12:58 -0600 Subject: [PATCH 08/11] x86/sev, crypto/ccp: Move SNP init to ccp driver Use the new snp_prepare() to initialize SNP from the ccp driver instead of at boot time. This means that SNP is not enabled unless it is really going to be used (i.e. kvm_amd loads the ccp driver automatically). Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260324161301.1353976-5-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 2 -- drivers/crypto/ccp/sev-dev.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 3b2273dca196..423fe77cc70f 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -562,8 +562,6 @@ int __init snp_rmptable_init(void) if (!setup_rmptable()) return -ENOSYS; - snp_prepare(); - /* * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic * notifier is invoked to do SNP IOMMU shutdown before kdump. diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 8b2dfc11289b..024eab243145 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1373,6 +1373,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) return -EOPNOTSUPP; } + snp_prepare(); + /* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */ on_each_cpu(snp_set_hsave_pa, NULL, 1); From 7b2bc5f0ab983a7aad7fa5180cede4b3e91fc164 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 24 Mar 2026 10:12:59 -0600 Subject: [PATCH 09/11] x86/sev, crypto/ccp: Move HSAVE_PA setup to arch/x86/ Now that there is snp_prepare() that indicates when the CCP driver wants to prepare the architecture for SNP_INIT(_EX), move this architecture-specific bit of code to a more sensible place. Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Link: https://patch.msgid.link/20260324161301.1353976-6-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 8 ++++++++ drivers/crypto/ccp/sev-dev.c | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 423fe77cc70f..41f76f15caa1 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -506,6 +506,11 @@ static bool __init setup_rmptable(void) return true; } +static void clear_hsave_pa(void *arg) +{ + wrmsrq(MSR_VM_HSAVE_PA, 0); +} + void snp_prepare(void) { u64 val; @@ -529,6 +534,9 @@ void snp_prepare(void) on_each_cpu(mfd_reconfigure, (void *)1, 1); on_each_cpu(snp_enable, NULL, 1); + /* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */ + on_each_cpu(clear_hsave_pa, NULL, 1); + cpus_read_unlock(); } EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp"); diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 024eab243145..0d0c09a3a0af 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1076,11 +1076,6 @@ static inline int __sev_do_init_locked(int *psp_ret) return __sev_init_locked(psp_ret); } -static void snp_set_hsave_pa(void *arg) -{ - wrmsrq(MSR_VM_HSAVE_PA, 0); -} - /* Hypervisor Fixed pages API interface */ static void snp_hv_fixed_pages_state_update(struct sev_device *sev, enum snp_hv_fixed_pages_state page_state) @@ -1375,9 +1370,6 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) snp_prepare(); - /* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */ - on_each_cpu(snp_set_hsave_pa, NULL, 1); - /* * Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list * of system physical address ranges to convert into HV-fixed page From f995fc377ac7d3757e1d94e6403940c4b8f3d76e Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 24 Mar 2026 10:13:00 -0600 Subject: [PATCH 10/11] crypto/ccp: Implement SNP x86 shutdown The SEV firmware has support to disable SNP during an SNP_SHUTDOWN_EX command. Verify that this support is available and set the flag so that SNP is disabled when it is not being used. In cases where SNP is disabled, skip the call to amd_iommu_snp_disable(), as all of the IOMMU pages have already been made shared. Also skip the panic case, since snp_shutdown() does IPIs. Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Acked-by: Herbert Xu Link: https://patch.msgid.link/20260324161301.1353976-7-tycho@kernel.org --- drivers/crypto/ccp/sev-dev.c | 41 +++++++++++++++++++++--------------- include/linux/psp-sev.h | 5 ++++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 0d0c09a3a0af..cc5c5b3ad66d 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2039,6 +2039,8 @@ static int __sev_snp_shutdown_locked(int *error, bool panic) memset(&data, 0, sizeof(data)); data.len = sizeof(data); data.iommu_snp_shutdown = 1; + if (sev->snp_feat_info_0.ecx & SNP_X86_SHUTDOWN_SUPPORTED) + data.x86_snp_shutdown = 1; /* * If invoked during panic handling, local interrupts are disabled @@ -2072,23 +2074,28 @@ static int __sev_snp_shutdown_locked(int *error, bool panic) return ret; } - /* - * SNP_SHUTDOWN_EX with IOMMU_SNP_SHUTDOWN set to 1 disables SNP - * enforcement by the IOMMU and also transitions all pages - * associated with the IOMMU to the Reclaim state. - * Firmware was transitioning the IOMMU pages to Hypervisor state - * before version 1.53. But, accounting for the number of assigned - * 4kB pages in a 2M page was done incorrectly by not transitioning - * to the Reclaim state. This resulted in RMP #PF when later accessing - * the 2M page containing those pages during kexec boot. Hence, the - * firmware now transitions these pages to Reclaim state and hypervisor - * needs to transition these pages to shared state. SNP Firmware - * version 1.53 and above are needed for kexec boot. - */ - ret = amd_iommu_snp_disable(); - if (ret) { - dev_err(sev->dev, "SNP IOMMU shutdown failed\n"); - return ret; + if (data.x86_snp_shutdown) { + if (!panic) + snp_shutdown(); + } else { + /* + * SNP_SHUTDOWN_EX with IOMMU_SNP_SHUTDOWN set to 1 disables SNP + * enforcement by the IOMMU and also transitions all pages + * associated with the IOMMU to the Reclaim state. + * Firmware was transitioning the IOMMU pages to Hypervisor state + * before version 1.53. But, accounting for the number of assigned + * 4kB pages in a 2M page was done incorrectly by not transitioning + * to the Reclaim state. This resulted in RMP #PF when later accessing + * the 2M page containing those pages during kexec boot. Hence, the + * firmware now transitions these pages to Reclaim state and hypervisor + * needs to transition these pages to shared state. SNP Firmware + * version 1.53 and above are needed for kexec boot. + */ + ret = amd_iommu_snp_disable(); + if (ret) { + dev_err(sev->dev, "SNP IOMMU shutdown failed\n"); + return ret; + } } snp_leak_hv_fixed_pages(); diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index 69ffa4b4d1fa..d5099a2baca5 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -829,12 +829,14 @@ struct sev_data_range_list { * * @len: length of the command buffer read by the PSP * @iommu_snp_shutdown: Disable enforcement of SNP in the IOMMU + * @x86_snp_shutdown: Disable SNP on all cores * @rsvd1: reserved */ struct sev_data_snp_shutdown_ex { u32 len; u32 iommu_snp_shutdown:1; - u32 rsvd1:31; + u32 x86_snp_shutdown:1; + u32 rsvd1:30; } __packed; /** @@ -891,6 +893,7 @@ struct snp_feature_info { } __packed; /* Feature bits in ECX */ +#define SNP_X86_SHUTDOWN_SUPPORTED BIT(1) #define SNP_RAPL_DISABLE_SUPPORTED BIT(2) #define SNP_CIPHER_TEXT_HIDING_SUPPORTED BIT(3) #define SNP_AES_256_XTS_POLICY_SUPPORTED BIT(4) From c3aa64ab6a5ea553ee70526579908936a3ff9392 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Tue, 24 Mar 2026 10:13:01 -0600 Subject: [PATCH 11/11] crypto/ccp: Update HV_FIXED page states to allow freeing of memory After SNP is disabled, any pages allocated as HV_FIXED can now be freed. Update the page state of these pages and the snp_leak_hv_fixed_pages() function to free pages on SNP_SHUTDOWN. Signed-off-by: Tom Lendacky Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Acked-by: Herbert Xu Link: https://patch.msgid.link/20260324161301.1353976-8-tycho@kernel.org --- drivers/crypto/ccp/sev-dev.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index cc5c5b3ad66d..57f354721cd5 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1219,7 +1219,7 @@ static void snp_add_hv_fixed_pages(struct sev_device *sev, struct sev_data_range static void snp_leak_hv_fixed_pages(void) { - struct snp_hv_fixed_pages_entry *entry; + struct snp_hv_fixed_pages_entry *entry, *nentry; /* List is protected by sev_cmd_mutex */ lockdep_assert_held(&sev_cmd_mutex); @@ -1227,10 +1227,16 @@ static void snp_leak_hv_fixed_pages(void) if (list_empty(&snp_hv_fixed_pages)) return; - list_for_each_entry(entry, &snp_hv_fixed_pages, list) - if (entry->page_state == HV_FIXED) + list_for_each_entry_safe(entry, nentry, &snp_hv_fixed_pages, list) { + if (entry->free && entry->page_state != HV_FIXED) + __free_pages(entry->page, entry->order); + else __snp_leak_pages(page_to_pfn(entry->page), 1 << entry->order, false); + + list_del(&entry->list); + kfree(entry); + } } bool sev_is_snp_ciphertext_hiding_supported(void) @@ -2077,6 +2083,7 @@ static int __sev_snp_shutdown_locked(int *error, bool panic) if (data.x86_snp_shutdown) { if (!panic) snp_shutdown(); + snp_hv_fixed_pages_state_update(sev, ALLOCATED); } else { /* * SNP_SHUTDOWN_EX with IOMMU_SNP_SHUTDOWN set to 1 disables SNP