Add RMPOPT support.
Subsystems (6)
Ashish Kalra
maintainerv16 cover Add RMPOPT support.
From: Ashish Kalra <[email protected]>
In the SEV-SNP architecture, hypervisor and non-SNP guests are subject to RMP checks on writes to provide integrity of SEV-SNP guest memory.
The RMPOPT architecture enables optimizations whereby the RMP checks can be skipped if 1GB regions of memory are known to not contain any SNP guest memory.
RMPOPT is a new instruction designed to minimize the performance overhead of RMP checks for the hypervisor and non-SNP guests.
RMPOPT instruction currently supports two functions. In case of the verify and report status function the CPU will read the RMP contents, verify the entire 1GB region starting at the provided SPA is HV-owned. For the entire 1GB region it checks that all RMP entries in this region are HV-owned (i.e, not in assigned state) and then accordingly updates the RMPOPT table to indicate if optimization has been enabled and provide indication to software if the optimization was successful.
In case of report status function, the CPU returns the optimization status for the 1GB region.
The RMPOPT table is managed by a combination of software and hardware. Software uses the RMPOPT instruction to set bits in the table, indicating that regions of memory are entirely HV-owned. Hardware automatically clears bits in the RMPOPT table when RMP contents are changed during RMPUPDATE instruction.
For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.
As SNP is enabled by default the hypervisor and non-SNP guests are subject to RMP write checks to provide integrity of SNP guest memory.
This patch-series adds support to enable RMP optimizations for up to 2TB of system RAM across the system and allow RMPUPDATE to disable those optimizations as SNP guests are launched.
Support for RAM larger than 2 TB will be added in follow-on series.
This series also adds support to disable CPU hotplug while SNP is active, as the SEV firmware enumerates CPUs at SNP initialization and is not aware of the OS bringing CPUs online or offline afterwards. This also keeps the set of CPUs stable for the asynchronous RMPOPT scan, so the per-core RMPOPT_BASE MSRs programmed during setup remain valid.
This series also introduces support to re-enable RMP optimizations during SNP guest termination, after guest pages have been converted back to shared.
RMP optimizations are performed asynchronously by queuing work on a dedicated workqueue after a 10 second delay.
Delaying work allows batching of multiple SNP guest terminations.
Once 1GB hugetlb guest_memfd support is merged, support for re-enabling RMPOPT optimizations during 1GB page cleanup will be added in follow-on series.
v16:
- 3/5 (Initialize RMPOPT MSRs): clear X86_FEATURE_RMPOPT when segmented RMP is enabled but its probe fails, so the initcall does not set up RMPOPT for a system that will not bring up SNP. Program the RMPOPT_BASE MSRs only when RMPOPT_EN is not already set on the local CPU, so guest teardown skips reprogramming rather than reprogramming the MSRs on every teardown.
Review feedback from Sashiko AI.
v15:
- 3/5 (Initialize RMPOPT MSRs): rename snp_setup_rmpopt() to snp_enable_rmpopt(); program each core's RMPOPT_BASE only when it is not already set.
- 4/5 (async RMPOPT): split setup from enable -- an initcall allocates the workqueue and computes the (fixed) optimization range once, and snp_enable_rmpopt() programs the RMPOPT_BASE MSRs and queues the optimization pass. Gate rmpopt_capable() on a static rmpopt_enabled bool instead of an if (rmpopt_wq) check and drop rmpopt_wq_mutex. Queue both the initial and the teardown pass with mod_delayed_work().
- 5/5 (Re-enable on guest shutdown): call snp_enable_rmpopt() on guest teardown instead of a separate snp_rmpopt_all_physmem().
This is a full resend rather than a 4/5-5/5 reply because patch 3 changed as part of the setup/enable split.
Review feedback from Borislav Petkov.
v14:
- 3/5 (Initialize RMPOPT MSRs): remove rmpopt_disable() and the RMPOPT_BASE teardown from this patch. RMPOPT is now set up once and not torn down, so the RMPOPT_BASE MSRs are left in place on shutdown; the (now minimal) disable path moves to 4/5.
- 4/5 (async RMPOPT): rework the setup/teardown -- initialize once and do not tear it down. rmpopt_disable() now only cancels the pending re-optimization pass. Use a dedicated per-CPU workqueue (WQ_PERCPU) and drop the migrate_disable()/migrate_enable() around the local warm-up scan. Add a binutils-version comment above the RMPOPT .byte encoding and trim redundant comments and the 2 TB pr_info(). Reword the subject to "Perform RMP optimizations asynchronously".
- 5/5 (Re-enable on guest shutdown): move the call-site comment onto the snp_rmpopt_all_physmem() definition and simplify the RMPOPT_WORK_TIMEOUT comment.
- Correct the Suggested-by/Reviewed-by tags across the series.
Review feedback from Borislav Petkov.
v13:
- 3/5 (Initialize RMPOPT MSRs): use cpu_primary_thread_mask directly instead of building a local rmpopt_cpumask -- all primary threads are online while SNP is active, so the two are equivalent. Rename snp_cleanup_rmpopt() to rmpopt_disable().
- 4/5 (async RMPOPT): drop the leader/follower bookkeeping. Warm the RMP scan cache once, then fan the RMPOPT out to cpu_primary_thread_mask; re-running RMPOPT on the warm-up CPU is a cache hit, so the separate follower cpumask and the cpumask_andnot() are no longer needed. Rename rmpopt_work_handler() to do_rmpopt_work() and drop the now-unused this_cpu.
- Add Reviewed-by: Tom Lendacky <[email protected]> to the series.
Review feedback from Borislav Petkov.
v12:
- Merged the former "Add interface to re-enable RMP optimizations" and "KVM: SEV: Perform RMP optimizations on SNP guest shutdown" patches into a single patch (now 5/5), since the interface has no user without the KVM caller. The series is now 5 patches.
- 2/5 (Disable CPU hotplug): drop a stray reference to later patches from the commit message and trim the snp_prepare() comment. Move cpu_hotplug_enable() to the end of snp_shutdown(), after clear_rmp() and the mfd_reconfigure() IPI, so no all-CPU operation runs after hotplug is re-enabled.
- 3/5 (Initialize RMPOPT MSRs): restructure snp_probe_rmptable_info() so the contiguous probe (which clears X86_FEATURE_RMPOPT) is the common fall-through, also covering X86_FEATURE_SEGMENTED_RMP advertised but disabled by firmware. Move snp_setup_rmpopt() to the end of __sev_snp_init_locked(). Give snp_cleanup_rmpopt() its own short comment in snp_shutdown().
- 4/5 (async RMPOPT): document the optimization trigger points in the commit message. Rename enum rmpopt_function to rmpopt_op_type. Merge __rmpopt() into rmpopt() and replace rmpopt_smp() with a new rmpopt_scan_range() that loops as the on_each_cpu() callback, so the follower scan issues one IPI per core rather than one per 1GB. Use u64 for physical addresses. Allocate the follower cpumask once at setup. Document rmpopt_wq as the setup sentinel. Move the RMPOPT_WORK_TIMEOUT define to the guest-shutdown patch (its only user) as (10 * MSEC_PER_SEC). Remove a spammy pr_info() and reword comments.
- 5/5 (Re-enable RMP optimizations on SNP guest shutdown): document why re-optimization is driven by guest teardown (the only event that returns guest memory to hypervisor ownership); reword the commit message (s/clear/disable/ for optimizations, drop "Conversely").
Review feedback from Borislav Petkov.
v11:
- Reordered so "Disable CPU hotplug while SNP is active" (2/6) precedes "Initialize RMPOPT configuration MSRs" (3/6): the RMPOPT setup/cleanup code is then introduced with CPU hotplug already disabled and never takes cpus_read_lock().
- 1/6 (cpufeatures): drop the tools/arch/x86/include/asm/cpufeatures.h change and adopt the commit message as applied by Boris.
- 2/6 (Disable CPU hotplug): reword the commit message. Drop the redundant cpus_read_lock()/cpus_read_unlock() in snp_prepare() in this same patch -- with hotplug disabled the online CPU mask is stable, so the read lock is not needed and hotplug handling has no hole when bisecting.
- 3/6 (Initialize RMPOPT MSRs): replace the rmpopt_capable bool with a small helper local to arch/x86/virt/svm/sev.c -- cpu_feature_enabled(X86_FEATURE_RMPOPT) && cc_platform_has(CC_ATTR_HOST_SEV_SNP) -- clearing X86_FEATURE_RMPOPT for a contiguous (non-segmented) RMP in snp_probe_rmptable_info() (runs at BSP init, before alternatives). Rename rmpopt_cleanup() to snp_cleanup_rmpopt() to match snp_setup_rmpopt(). Both are introduced without cpus_read_lock() (hotplug is already disabled by 2/6). Simplify the RMPOPT_BASE comments. Drop the now-unused <asm/sev.h> include from core.c.
- 4/6 (async RMPOPT): the follower scan is likewise introduced without cpus_read_lock(). Drop the cond_resched() calls (nops on x86). On re-initialization after a legacy SNP shutdown, re-queue the optimization pass instead of skipping it. pr_warn() on cpumask allocation failure.
Review feedback from Borislav Petkov and K Prateek Nayak.
v10:
- Rework the CPU-hotplug patch (3/6): disable CPU hotplug in snp_prepare(), before SnpEn is set, instead of late in __sev_snp_init_locked(), so no CPU can come online without SnpEn during SNP initialization (per upstream review). Tie hotplug to SnpEn: it stays disabled while SnpEn is set -- including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path -- and is re-enabled only once the firmware clears SnpEn on the x86_snp_shutdown path. Drop the separate idempotent flag: snp_prepare() re-enables hotplug on its own early failure, and a kexec target that boots with SnpEn already set disables hotplug once in snp_rmptable_init(). Reword the commit log and comments accordingly.
- Emit a pr_warn() in rmpopt_work_handler() (4/6) when the follower cpumask allocation fails, instead of silently skipping the optimization pass.
Sashiko AI upstream review identified several of the above issues.
v9:
- Rename rmpopt_configured to rmpopt_capable.
- Make rmpopt_cpumask a cpumask_var_t (allocated/freed at setup/cleanup) instead of a static cpumask_t.
- Drop the v8 WARN_ON_ONCE() on the RMPOPT_BASE writes; use a plain wrmsrq_on_cpu(), matching the SNP MSR-write convention in this file.
- Disable CPU hotplug with cpu_hotplug_disable()/cpu_hotplug_enable() (per tglx); re-enable only on the full x86_snp_shutdown path.
- Simplify rmpopt_work_handler() to a single leader-then-followers path:
with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the explicit-leader fallback is now unreachable. Drop it along with the v8 work_on_cpu()/rmpopt_leader_fn() helper.
- Drop the debugfs interface (was patch 7/7) and its report-only plumbing; observability will be revisited after this series is merged.
- Restrict snp_rmpopt_all_physmem()'s export to the kvm-amd module.
- Use scoped_guard(cpus_read_lock) for the per-CPU MSR and follower loops.
Sashiko AI upstream review identified several of the above issues.
v8:
- Add a new patch to disable CPU hotplug while SNP is active, keeping the CPU set stable for the RMPOPT work handler.
- Drop the setup_clear_cpu_cap(X86_FEATURE_RMPOPT) calls; the rmpopt_configured bool is the runtime guard.
- WARN_ON_ONCE() on the RMPOPT_BASE MSR writes that previously ignored their return value.
- Simplify rmpopt_work_handler() by removing the explicit-leader
fallback: with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the running CPU can always be the leader. This drops the smp_call_function_single() fallback (and with it the AB-BA deadlock and IRQ-latency concerns) and collapses the leader selection into a single leader-then-followers path.
- Use mod_delayed_work() in snp_rmpopt_all_physmem() so the batching delay tracks the last SNP guest termination.
Sashiko AI code review identified several of the above issues.
v7:
- Sync tools/arch/x86/include/asm/cpufeatures.h to mirror the kernel header for X86_FEATURE_RMPOPT.
- Fix commit title to use X86_FEATURE_RMPOPT to match the code (was X86_FEATURE_AMD_RMPOPT).
- Add static bool rmpopt_configured, set only when segmented RMP setup succeeds in setup_rmptable(). Check rmpopt_configured alongside cpu_feature_enabled(X86_FEATURE_RMPOPT) in snp_setup_rmpopt() and snp_rmpopt_all_physmem(), because setup_clear_cpu_cap() is unreliable after alternatives are patched. Add snp_clear_rmpopt_configured() called from amd_cc_platform_clear() when CC_ATTR_HOST_SEV_SNP is cleared. Do not use __ro_after_init on rmpopt_configured since the writer snp_clear_rmpopt_configured() is not __init.
- Add cond_resched() to all three leader loops in rmpopt_work_handler() to prevent soft lockups on systems with up to 2TB of RAM.
- Add comment above __rmpopt() documenting the RMPOPT instruction encoding (F2 0F 01 FC) and register interface (RAX = system physical address input, RCX = operation type input, RFLAGS.CF = output).
Note: RMPOPT does not modify RAX unlike PVALIDATE/RMPUPDATE, so the existing "a" (input-only) constraint is correct.
Sashiko AI code review identified several of the above issues.
v6:
- Drop wrmsrq_on_cpus() helper; use for_each_cpu() with wrmsrq_on_cpu() instead, as RMPOPT_BASE MSR programming is not performance-critical.
- Rewrite rmpopt_work_handler() leader selection to use a local follower_mask copy instead of modifying the global rmpopt_cpumask. This eliminates the current_cpu_cleared tracking and the restore at the end, and removes the need for synchronization comments about transient cpumask inconsistency.
- Add three-way leader selection in rmpopt_work_handler():
1. Current CPU is a primary thread in cpumask: run leader locally.
2. Current CPU is a sibling thread whose primary is in cpumask:
run leader locally (RMPOPT_BASE MSR is per-core), remove the primary from followers via cpumask_andnot(topology_sibling_cpumask).
3. Current CPU's core has no RMPOPT_BASE MSR programmed: pick an explicit leader via cpumask_first() + smp_call_function_single() to avoid #UD, with cpus_read_lock() around the IPI loop.
- Add WARN_ON_ONCE guard for empty cpumask in the explicit leader fallback path, with migrate_enable() before goto out.
- Add .llseek = seq_lseek to rmpopt_table_fops for consistency with other seq_file-based debugfs files and to support tools like "less".
- Change debugfs file permissions from 0444 to 0400 to restrict access to root only.
- Add comment in rmpopt_table_seq_show() explaining why cpu_online_mask is safe: RMPOPT_BASE MSR is per-core and snp_prepare() ensures all CPUs are online when the MSR is programmed.
Sashiko AI code review identified several of the above issues.
v5:
- Introduce rmpopt_cleanup() to tear down workqueue, debugfs, cpumask, and MSR state, called from snp_shutdown().
- Introduce rmpopt_wq_mutex to serialize snp_setup_rmpopt(), snp_rmpopt_all_physmem(), and rmpopt_cleanup().
- Introduce rmpopt_show_mutex to serialize debugfs reporting of rmpopt_report_cpumask.
- Move snp_rmpopt_all_physmem() call after SNP DECOMMISSION during guest shutdown.
- Use migrate_disable()/migrate_enable() for CPU pinning in the rmpopt_work_handler() leader loop to maintain CPU affinity without disabling preemption for the entire RMPOPT scan.
- Add cpus_read_lock()/cpus_read_unlock() around the follower on_each_cpu_mask() loop in rmpopt_work_handler().
- Guard snp_setup_rmpopt() against re-initialization when SNP_SHUTDOWN_EX with x86_snp_shutdown=0 skips rmpopt_cleanup() but clears snp_initialized, preventing workqueue and resource leaks on repeated init/shutdown cycles.
- Replace setup_clear_cpu_cap() with pr_err() on alloc_workqueue() failure in snp_setup_rmpopt(), as setup_clear_cpu_cap() cannot be used after alternatives are patched; callers check rmpopt_wq != NULL as the runtime guard instead.
- Add pr_info() when RMPOPT coverage is capped at 2TB.
- Add comments noting CPU hotplug is not supported with SNP enabled and only online primary threads are covered by rmpopt_cpumask.
- Add comment in setup_rmptable() noting Segmented RMP must be enabled to enable RMPOPT.
- Simplify cpumask setup loop to set if primary thread rather than skip if not primary.
- Improve grammar and clarity in snp_setup_rmpopt() comments.
- Added Reviewed-by's.
Sashiko AI code review identified several of the above issues.
v4:
- Add new wrmsrq_on_cpus() helper to write same u64 value to a per-CPU MSR across a cpumask without per-cpu struct allocation overhead.
- Rename configure_and_enable_rmpopt() to snp_setup_rmpopt().
- Use wrmsrq_on_cpus() instead of wrmsrq_on_cpu() loop for programming RMPOPT_BASE MSRs.
- Add setup_clear_cpu_cap(X86_FEATURE_RMPOPT) if segmented RMP setup fails or workqueue allocation fails.
- Add X86_FEATURE_RMPOPT feature clear logic in amd_cc_platform_clear() for CC_ATTR_HOST_SEV_SNP.
- All of the above allow checking for only X86_FEATURE_RMPOPT for both RMPOPT setup/enable and RMP re-optimizations.
- Rename snp_perform_rmp_optimization() to snp_rmpopt_all_physmem().
- Split rmpopt() into rmpopt() and rmpopt_smp() for SMP callback use.
- Introduce separate rmpopt_report_cpumask for debugfs reporting, distinct from rmpopt_cpumask used for primary thread tracking.
- Remove snp_perform_rmp_optimization() call from __sev_snp_init_locked() and instead setup and enable RMPOPT after SNP is enabled and initialized.
v3:
- Drop all RMPOPT kthread support and introduce adding custom and dedicated workqueue to schedule delayed and asynchronous RMPOPT work.
- Drop the guest_memfd inode cleanup interface and add support to re-enable RMP optimizations during guest shutdown using the asynchronous and delayed workqueue interface.
- Introduce new __rmpopt() helper and rmpopt() and rmpopt_report_status() wrappers on top which use rax and rcx parameters to closely match RMPOPT specs.
- Use new optimized RMPOPT loop to issue RMPOPT instructions on all system RAM upto 2TB and all CPUs, by optimizing each range on one CPU first, then let other CPUs execute RMPOPT in parallel so they can skip most work as the range has already been optimized.
- Also add support for running the optimized RMPOPT loop only on one thread per core.
- Replace all PUD_SIZE references with SZ_1G to conform to 1GB regions as specified by RMPOPT specifications and not be dependent on PUD_SIZE which makes the RMPOPT patch-set independent of x86 page table sizes.
- Use wrmsrq_on_cpu() to program the RMPOPT_BASE MSR registers on all CPUs that removes all ugly casting to use on_each_cpu_mask().
- Fix inline commits and patch commit messages
v2:
- Drop all NUMA and Socket configuration and enablement support and enable RMPOPT support for up to 2TB of system RAM.
- Drop get_cpumask_of_primary_threads() and enable per-core RMPOPT base MSRs and issue RMPOPT instruction on all CPUs.
- Drop the configfs interface to manually re-enable RMP optimizations.
- Add new guest_memfd cleanup interface to automatically re-enable RMP optimizations during guest shutdown.
- Include references to the public RMPOPT documentation.
- Move debugfs directory for RMPOPT under architecuture specific parent directory.
Ashish Kalra (5):
x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag x86/sev: Disable CPU hotplug while SNP is active x86/sev: Initialize RMPOPT configuration MSRs x86/sev: Perform RMP optimizations asynchronously x86/sev: Re-enable RMP optimizations on SNP guest shutdown
arch/x86/include/asm/cpufeatures.h | 2 +- arch/x86/include/asm/msr-index.h | 3 + arch/x86/include/asm/sev.h | 2 + arch/x86/kernel/cpu/scattered.c | 1 + arch/x86/kvm/svm/sev.c | 2 + arch/x86/virt/svm/sev.c | 193 ++++++++++++++++++++++++++--- drivers/crypto/ccp/sev-dev.c | 2 + 7 files changed, 189 insertions(+), 16 deletions(-)
Signature · 1 line
2.43.0
Ashish Kalra
maintainerPATCH v16 1/5 x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag
From: Ashish Kalra <[email protected]>
Add a flag indicating whether RMPOPT instruction is supported.
RMPOPT is a new instruction that reduces the performance overhead of RMP checks for the hypervisor and non-SNP guests by allowing those checks to be skipped when 1-GB memory regions are known to contain no SEV-SNP guest memory.
For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.
[ bp: Zap respective tools/ change. ]
--- arch/x86/include/asm/cpufeatures.h | 2 +- arch/x86/kernel/cpu/scattered.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-)
arch/x86/include/asm/cpufeatures.h +1 -1
@@ -76,7 +76,7 @@#define X86_FEATURE_K8 ( 3*32+ 4)/* Opteron, Athlon64 */ #define X86_FEATURE_ZEN5 ( 3*32+ 5)/* CPU based on Zen5 microarchitecture */ #define X86_FEATURE_ZEN6 ( 3*32+ 6)/* CPU based on Zen6 microarchitecture */ -/* Free ( 3*32+ 7) */ +#define X86_FEATURE_RMPOPT ( 3*32+ 7)/* Support for AMD RMPOPT instruction */ #define X86_FEATURE_CONSTANT_TSC ( 3*32+ 8)/* "constant_tsc" TSC ticks at a constant rate */ /* free: was #define X86_FEATURE_UP ( 3*32+ 9) * "up" SMP kernel running on UP */ #define X86_FEATURE_ART ( 3*32+10)/* "art" Always running timer (ART) */
arch/x86/kernel/cpu/scattered.c +1 -0
@@ -67,6 +67,7 @@ static const struct cpuid_bit cpuid_bits[] = { { X86_FEATURE_PERFMON_V2, CPUID_EAX, 0, 0x80000022, 0 }, { X86_FEATURE_AMD_LBR_V2, CPUID_EAX, 1, 0x80000022, 0 }, { X86_FEATURE_AMD_LBR_PMC_FREEZE, CPUID_EAX, 2, 0x80000022, 0 }, + { X86_FEATURE_RMPOPT, CPUID_EDX, 0, 0x80000025, 0 }, { X86_FEATURE_AMD_HTR_CORES, CPUID_EAX, 30, 0x80000026, 0 }, { 0, 0, 0, 0, 0 } };
Signature · 1 line
2.43.0
Ashish Kalra
maintainerPATCH v16 2/5 x86/sev: Disable CPU hotplug while SNP is active
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
While SNP is active, every memory write is checked against the RMP to protect SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization.
A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.
The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards.
SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline when SNP_INIT is issued, does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.
Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP.
If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled.
A kexec target that boots with SNP already enabled, disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled.
With CPU hotplug now disabled while SNP is active, the online CPU mask is stable, so the cpus_read_lock() previously taken in snp_prepare() to iterate it is redundant. Drop cpus_read_lock()/cpus_read_unlock() here.
--- arch/x86/virt/svm/sev.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-)
arch/x86/virt/svm/sev.c +26 -10
@@ -513,7 +513,6 @@ static void clear_hsave_pa(void *arg)int snp_prepare (void ) { -int ret;u64 val;/*
@@ -526,14 +525,18 @@ int snp_prepare(void)clear_rmp (); -cpus_read_lock (); +/* +* No CPU may come online without SnpEn while SNP is active; disable +* hotplug here and re-enable it in snp_shutdown(). +*/ +cpu_hotplug_disable ();if (!cpumask_equal (cpu_online_mask, cpu_present_mask)) { - ret = -EOPNOTSUPP ; +cpu_hotplug_enable ();pr_warn ("SNP init failed: not all CPUs online. (%*pbl online <-> %*pbl present masks).\n" ,cpumask_pr_args (cpu_online_mask),cpumask_pr_args (cpu_present_mask)); - goto unlock; +return -EOPNOTSUPP ; }wbinvd_on_all_cpus ();
@@ -548,12 +551,7 @@ int snp_prepare(void)/* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */ on_each_cpu (clear_hsave_pa,NULL ,1 ); - ret =0 ; - -unlock: -cpus_read_unlock (); - -return ret; +return 0 ; }EXPORT_SYMBOL_FOR_MODULES (snp_prepare,"ccp" );
@@ -567,6 +565,13 @@ void snp_shutdown(void)clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 ); + +/* +* The firmware has disabled SNP (SnpEn is clear), so re-enable CPU +* hotplug. A legacy SNP shutdown returns above with SnpEn still set and +* leaves hotplug disabled. +*/ +cpu_hotplug_enable (); }EXPORT_SYMBOL_FOR_MODULES (snp_shutdown,"ccp" );
@@ -577,6 +582,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");*/ int __init snp_rmptable_init (void ) { +u64 val; +if (WARN_ON_ONCE (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )))return -ENOSYS ;
@@ -586,6 +593,15 @@ int __init snp_rmptable_init(void)if (!setup_rmptable ())return -ENOSYS ; +/* +* On a kexec boot SNP may already be enabled (legacy firmware leaves +* SnpEn set across shutdown), in which case snp_prepare() bails without +* disabling CPU hotplug, so disable it here. +*/ +rdmsrq (MSR_AMD64_SYSCFG , val); +if (val &MSR_AMD64_SYSCFG_SNP_EN ) +cpu_hotplug_disable (); +/* * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic * notifier is invoked to do SNP IOMMU shutdown before kdump.
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v16 2/5] x86/sev: Disable CPU hotplug while SNP is active
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] x86/sev: CPU hotplug disablement bypassed on early kexec errors --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: CPU hotplug disablement bypassed on early kexec errors
--
commit ac084f2d7188c520f2ee3ced2ab76e4c068df91f
Author: Ashish Kalra <[email protected]>
x86/sev: Disable CPU hotplug while SNP is active
This commit disables CPU hotplug when SNP is enabled to prevent SNP guest memory from being broken by CPUs coming online without the SnpEn bit set. It ties CPU hotplug to the SNP-enable bit, disabling it in snp_prepare() and re-enabling it in snp_shutdown() when SNP is disabled. It also explicitly disables CPU hotplug during kexec if SNP is already enabled.
Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0
[ ... ]
Quoted 11 lines
arch/x86/virt/svm/sev.c +2
-0@@ -577,6 +582,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");*/ int __init snp_rmptable_init (void ) { +u64 val; +if (WARN_ON_ONCE (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )))return -ENOSYS ;@@ -586,6 +593,15 @@ int __init snp_rmptable_init(void)if (!setup_rmptable ())
arch/x86/virt/svm/sev.c int __init snp_rmptable_init(void) · line 603
Quoted 10 lines
arch/x86/virt/svm/sev.c +8
-0return -ENOSYS ; +/* +* On a kexec boot SNP may already be enabled (legacy firmware leaves +* SnpEn set across shutdown), in which case snp_prepare() bails without +* disabling CPU hotplug, so disable it here. +*/ +rdmsrq (MSR_AMD64_SYSCFG , val); +if (val &MSR_AMD64_SYSCFG_SNP_EN ) +cpu_hotplug_disable ();
[Severity: High]
If a kexec kernel boots into a system where the previous kernel left MSR_AMD64_SYSCFG_SNP_EN active, and an early initialization step fails (such as passing amd_iommu=off on the kernel command line resulting in !amd_iommu_snp_en, or setup_rmptable() failing), snp_rmptable_init() will return -ENOSYS before checking the physical MSR hardware state and disabling CPU hotplug.
Because hotplug remains enabled and the CCP driver skips initialization on failure (bypassing snp_prepare()), could a subsequently hotplugged CPU come online without the SNP_EN bit set?
If this results in a mixed hardware state, does it violate firmware constraints and cause fatal RMP faults when the new CPU accesses memory?
Should the hardware state be checked and hotplug disabled unconditionally before the software configuration checks?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Kalra, Ashish
maintainerRe: [PATCH v16 2/5] x86/sev: Disable CPU hotplug while SNP is active
Quoted 56 lines · On 9/16/2026 5:36 PM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: CPU hotplug disablement bypassed on early kexec errors
--commit
ac084f2d7188c520f2ee3ced2ab76e4c068df91f
Author: Ashish Kalra <[email protected]>x86/sev: Disable CPU hotplug while SNP is active
This commit disables CPU hotplug when SNP is enabled to prevent SNP guest memory from being broken by CPUs coming online without the SnpEn bit set. It ties CPU hotplug to the SNP-enable bit, disabling it in
snp_prepare()and re-enabling it insnp_shutdown()when SNP is disabled. It also explicitly disables CPU hotplug during kexec if SNP is already enabled.Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0[ ... ]
Quoted 11 lines
arch/x86/virt/svm/sev.c +2
-0@@ -577,6 +582,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");*/ int __init snp_rmptable_init (void ) { +u64 val; +if (WARN_ON_ONCE (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )))return -ENOSYS ;@@ -586,6 +593,15 @@ int __init snp_rmptable_init(void)if (!setup_rmptable ())arch/x86/virt/svm/sev.c int __init snp_rmptable_init(void) · line 603
Quoted 10 lines
arch/x86/virt/svm/sev.c +8
-0return -ENOSYS ; +/* +* On a kexec boot SNP may already be enabled (legacy firmware leaves +* SnpEn set across shutdown), in which case snp_prepare() bails without +* disabling CPU hotplug, so disable it here. +*/ +rdmsrq (MSR_AMD64_SYSCFG , val); +if (val &MSR_AMD64_SYSCFG_SNP_EN ) +cpu_hotplug_disable ();[Severity: High]
If a kexec kernel boots into a system where the previous kernel leftMSR_AMD64_SYSCFG_SNP_ENactive, and an early initialization step fails (such as passing amd_iommu=off on the kernel command line resulting in !amd_iommu_snp_en, orsetup_rmptable()failing),snp_rmptable_init()will return-ENOSYSbefore checking the physical MSR hardware state and disabling CPU hotplug.Because hotplug remains enabled and the CCP driver skips initialization on failure (bypassing
snp_prepare()), could a subsequently hotplugged CPU come online without theSNP_ENbit set?
Quoted 6 lines
If this results in a mixed hardware state, does it violate firmware constraints and cause fatal RMP faults when the new CPU accesses memory?
Should the hardware state be checked and hotplug disabled unconditionally before the software configuration checks?
The SnpEn‑already‑set branch handles only legacy firmware that leaves SnpEn set across kexec. Platforms supporting RMPOPT advertise SNP_X86_SHUTDOWN_SUPPORTED, so kexec clears SnpEn and a kexec boot leaves SnpEn=0 — the branch is never reached, and no CPU has SnpEn set to form a mixed state, whether or not amd_iommu=off or setup_rmptable() fails. On legacy firmware that does leave SnpEn set, such an early failure makes snp_rmptable_init() return -ENOSYS and SNP host support is not enabled, so the system doesn't run as an SNP host and RMPOPT never comes up; the resulting SnpEn‑set‑without‑RMP‑table state is a pre‑existing base‑SNP condition, not introduced by this patch. Disabling hotplug unconditionally before the checks would instead leave it permanently disabled when falling back to non‑SNP operation.
Thanks,
Ashish
Borislav Petkov
maintainerRe: [PATCH v16 2/5] x86/sev: Disable CPU hotplug while SNP is active
In reply to [email protected]Quoted 35 lines · On Wed, Sep 16, 2026 at 10:36:30PM +0000, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: CPU hotplug disablement bypassed on early kexec errors
--commit
ac084f2d7188c520f2ee3ced2ab76e4c068df91f
Author: Ashish Kalra <[email protected]>x86/sev: Disable CPU hotplug while SNP is active
This commit disables CPU hotplug when SNP is enabled to prevent SNP guest memory from being broken by CPUs coming online without the SnpEn bit set. It ties CPU hotplug to the SNP-enable bit, disabling it in
snp_prepare()and re-enabling it insnp_shutdown()when SNP is disabled. It also explicitly disables CPU hotplug during kexec if SNP is already enabled.Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0[ ... ]
Quoted 13 lines
arch/x86/virt/svm/sev.c +2
-0@@ -577,6 +582,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");*/ int __init snp_rmptable_init (void ) { +u64 val; +if (WARN_ON_ONCE (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )))return -ENOSYS ;@@ -586,6 +593,15 @@ int __init snp_rmptable_init(void)if (!setup_rmptable ())return -ENOSYS ;
Quoted 13 lines
arch/x86/virt/svm/sev.c int __init snp_rmptable_init(void) · line 603
Quoted 8 lines
arch/x86/virt/svm/sev.c +8
-0+/* +* On a kexec boot SNP may already be enabled (legacy firmware leaves +* SnpEn set across shutdown), in which case snp_prepare() bails without +* disabling CPU hotplug, so disable it here. +*/ +rdmsrq (MSR_AMD64_SYSCFG , val); +if (val &MSR_AMD64_SYSCFG_SNP_EN ) +cpu_hotplug_disable ();[Severity: High]
If a kexec kernel boots into a system where the previous kernel leftMSR_AMD64_SYSCFG_SNP_ENactive, and an early initialization step fails (such as passing amd_iommu=off
amd_iommu=off means no SNP.
Quoted 2 lines
on the kernel command line resulting in !amd_iommu_snp_en, or
setup_rmptable()failing),
The same happens in that case:
if (snp_rmptable_init()) {
pr_warn("SNP: RMP initialization failed, SNP cannot be supported.\n");
goto disable_snp;
^^^^^^^^^^^Does Sashiko not see that or somehow it ate a lot of credits so it cannot use enough now to "swap in" the whole context... if that is the AI that is going to destroy the world, then I'm not too worried. :)
Or is my AI failing me right now?
:-P
Leaving in the rest.
Quoted 7 lines
snp_rmptable_init()will
return-ENOSYSbefore checking the physical MSR hardware state and disabling CPU hotplug.Because hotplug remains enabled and the CCP driver skips initialization on failure (bypassing
snp_prepare()), could a subsequently hotplugged CPU come online without theSNP_ENbit set?
Yes, but the above disabled all our intention of running SNP in the first place...
Quoted 8 lines
If this results in a mixed hardware state, does it violate firmware constraints and cause fatal RMP faults when the new CPU accesses memory?
Should the hardware state be checked and hotplug disabled unconditionally before the software configuration checks?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteKalra, Ashish
maintainerRe: [PATCH v16 2/5] x86/sev: Disable CPU hotplug while SNP is active
Hello Boris,
Quoted 66 lines · Borislav Petkov
Quoted 35 lines · On Wed, Sep 16, 2026 at 10:36:30PM +0000, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: CPU hotplug disablement bypassed on early kexec errors
--commit
ac084f2d7188c520f2ee3ced2ab76e4c068df91f
Author: Ashish Kalra <[email protected]>x86/sev: Disable CPU hotplug while SNP is active
This commit disables CPU hotplug when SNP is enabled to prevent SNP guest memory from being broken by CPUs coming online without the SnpEn bit set. It ties CPU hotplug to the SNP-enable bit, disabling it in
snp_prepare()and re-enabling it insnp_shutdown()when SNP is disabled. It also explicitly disables CPU hotplug during kexec if SNP is already enabled.Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0[ ... ]
Quoted 13 lines
arch/x86/virt/svm/sev.c +2
-0@@ -577,6 +582,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");*/ int __init snp_rmptable_init (void ) { +u64 val; +if (WARN_ON_ONCE (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )))return -ENOSYS ;@@ -586,6 +593,15 @@ int __init snp_rmptable_init(void)if (!setup_rmptable ())return -ENOSYS ;Quoted 13 lines
arch/x86/virt/svm/sev.c int __init snp_rmptable_init(void) · line 603
Quoted 8 lines
arch/x86/virt/svm/sev.c +8
-0+/* +* On a kexec boot SNP may already be enabled (legacy firmware leaves +* SnpEn set across shutdown), in which case snp_prepare() bails without +* disabling CPU hotplug, so disable it here. +*/ +rdmsrq (MSR_AMD64_SYSCFG , val); +if (val &MSR_AMD64_SYSCFG_SNP_EN ) +cpu_hotplug_disable ();[Severity: High]
If a kexec kernel boots into a system where the previous kernel leftMSR_AMD64_SYSCFG_SNP_ENactive, and an early initialization step fails (such as passing amd_iommu=offamd_iommu=off means no SNP.
Quoted 2 lines
on the kernel command line resulting in !amd_iommu_snp_en, or
setup_rmptable()failing),The same happens in that case:
if (snp_rmptable_init()) { pr_warn("SNP: RMP initialization failed, SNP cannot be supported.\n"); goto disable_snp; ^^^^^^^^^^^Does Sashiko not see that or somehow it ate a lot of credits so it cannot use enough now to "swap in" the whole context... if that is the AI that is going to destroy the world, then I'm not too worried. :)
Quoted 5 lines
Or is my AI failing me right now?
:-P
Leaving in the rest.
Right, agreed —
As you have pointed out, on any snp_rmptable_init() failure — setup_rmptable() failing, !amd_iommu_snp_en (amd_iommu=off), etc. — the caller does goto disable_snp and SNP is disabled entirely.
Thanks,
Ashish
Quoted 21 lines
Quoted 8 lines
snp_rmptable_init()will
return-ENOSYSbefore checking the physical MSR hardware state and disabling CPU hotplug.Because hotplug remains enabled and the CCP driver skips initialization on failure (bypassing
snp_prepare()), could a subsequently hotplugged CPU come online without theSNP_ENbit set?Yes, but the above disabled all our intention of running SNP in the first place...
Quoted 8 lines
If this results in a mixed hardware state, does it violate firmware constraints and cause fatal RMP faults when the new CPU accesses memory?
Should the hardware state be checked and hotplug disabled unconditionally before the software configuration checks?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Ashish Kalra
maintainerPATCH v16 3/5 x86/sev: Initialize RMPOPT configuration MSRs
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
The new RMPOPT instruction helps manage per-CPU RMP optimization structures inside the CPU. It takes a 1GB-aligned physical address and either returns the status of the optimizations or tries to enable the optimizations.
Per-CPU RMPOPT tables support at most 2 TB of addressable memory for RMP optimizations.
Initialize the per-CPU RMPOPT table base to the starting physical address. This enables RMP optimization for up to 2 TB of system RAM on all CPUs.
Add snp_enable_rmpopt() to program RMPOPT once SNP is enabled and initialized.
---
Changes in v16:
- Clear X86_FEATURE_RMPOPT when segmented RMP is enabled but its probe fails, so the initcall does not set up RMPOPT for a system that will not bring up SNP.
- Program the RMPOPT_BASE MSRs (with wrmsrq_on_cpu()) only when RMPOPT_EN is not already set on the local CPU, so guest teardown skips reprogramming instead of reprogramming the MSRs on every teardown.
Changes in v15:
- Rename snp_setup_rmpopt() to snp_enable_rmpopt().
- Program each core's RMPOPT_BASE only when it is not already set.
arch/x86/include/asm/msr-index.h | 3 ++ arch/x86/include/asm/sev.h | 2 + arch/x86/virt/svm/sev.c | 66 +++++++++++++++++++++++++++++--- drivers/crypto/ccp/sev-dev.c | 2 + 4 files changed, 68 insertions(+), 5 deletions(-)
arch/x86/include/asm/msr-index.h +3 -0
@@ -761,6 +761,9 @@#define MSR_AMD64_SEG_RMP_ENABLED_BIT 0#define MSR_AMD64_SEG_RMP_ENABLED BIT_ULL(MSR_AMD64_SEG_RMP_ENABLED_BIT)#define MSR_AMD64_RMP_SEGMENT_SHIFT (x) (((x) & GENMASK_ULL(13, 8)) >> 8) +#define MSR_AMD64_RMPOPT_BASE 0xc0010139 +#define MSR_AMD64_RMPOPT_ENABLE_BIT 0 +#define MSR_AMD64_RMPOPT_ENABLE BIT_ULL(MSR_AMD64_RMPOPT_ENABLE_BIT)#define MSR_SVSM_CAA 0xc001f000
arch/x86/include/asm/sev.h +2 -0
@@ -662,6 +662,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages) __snp_leak_pages(pfn, pages, true); }int snp_prepare (void ); +void snp_enable_rmpopt (void );void snp_shutdown (void );#else static inline bool snp_probe_rmptable_info (void ) {return false; }
@@ -680,6 +681,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 int snp_prepare (void ) {return -ENODEV ; } +static inline void snp_enable_rmpopt (void ) {}static inline void snp_shutdown (void ) {}#endif
arch/x86/virt/svm/sev.c +61 -5
86 lines not on this page · raw message
drivers/crypto/ccp/sev-dev.c +2 -0
@@ -1663,6 +1663,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) sev_es_tmr_size =SNP_TMR_SIZE ; +snp_enable_rmpopt (); +return 0 ; }
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v16 3/5] x86/sev: Initialize RMPOPT configuration MSRs
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] x86/sev: missing MSR configuration on CPU hotplug cycle due to local check --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: missing MSR configuration on CPU hotplug cycle due to local check --
commit f73eea5b303a29d64f9efe740eeba6e570bac844
Author: Ashish Kalra <[email protected]>
x86/sev: Initialize RMPOPT configuration MSRs
The new RMPOPT instruction helps manage per-CPU RMP optimization structures inside the CPU. Initialize the per-CPU RMPOPT table base to the starting physical address to enable RMP optimization for up to 2 TB of system RAM on all CPUs.
Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0
[ ... ]
Quoted 27 lines
arch/x86/virt/svm/sev.c +23
-0@@ -575,6 +577,42 @@ void snp_shutdown(void) }EXPORT_SYMBOL_FOR_MODULES (snp_shutdown,"ccp" ); +static bool rmpopt_capable (void ) +{ +return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && +cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +} + +void snp_enable_rmpopt (void ) +{ +u64 base; +int cpu; + +if (!rmpopt_capable ()) +return ; + + rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); + +/* +* Per-CPU RMPOPT tables cover at most 2 TB. Program each core's +* RMPOPT_BASE with the start of RAM to optimize up to 2 TB. The MSR +* can only be written after SNP is enabled, so this runs from the ccp +* SNP init path (and again on guest teardown) rather than an initcall. +* +* The loop below programs RMPOPT_BASE on all primary threads. RMPOPT_EN
arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 612
Quoted 10 lines
arch/x86/virt/svm/sev.c +10
-0+* cannot be cleared while SNP is enabled, and CPU hotplug is disabled +* while SNP is active, so once programmed the MSRs stay set on all CPUs +* until SNP is disabled. A set RMPOPT_EN on the local CPU therefore +* means the programming has already been done, so skip it. +*/ +rdmsrq (MSR_AMD64_RMPOPT_BASE , base); +if (!(base &MSR_AMD64_RMPOPT_ENABLE )) +for_each_cpu (cpu, cpu_primary_thread_mask) +wrmsrq_on_cpu (cpu,MSR_AMD64_RMPOPT_BASE , + rmpopt_pa_start |MSR_AMD64_RMPOPT_ENABLE );
[Severity: High]
Could this local CPU check cause hotplugged CPUs to miss MSR configuration?
If SNP is shut down, CPU hotplug is re-enabled but the MSR remains intact on existing CPUs. If a CPU is offlined and brought back online, its MSR is reset to the default.
Later, when SNP is re-enabled, the local CPU executing snp_enable_rmpopt() might still have its own RMPOPT_ENABLE bit set. Will it erroneously assume all CPUs are programmed and skip the wrmsrq_on_cpu() loop, leaving the hotplugged CPU unconfigured?
Quoted 3 lines
arch/x86/virt/svm/sev.c +3
-0+} +EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" ); +
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Kalra, Ashish
maintainerRe: [PATCH v16 3/5] x86/sev: Initialize RMPOPT configuration MSRs
Quoted 62 lines · On 9/16/2026 5:26 PM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] x86/sev: missing MSR configuration on CPU hotplug cycle due to local check --commit
f73eea5b303a29d64f9efe740eeba6e570bac844
Author: Ashish Kalra <[email protected]>x86/sev: Initialize RMPOPT configuration MSRs
The new RMPOPT instruction helps manage per-CPU RMP optimization structures inside the CPU. Initialize the per-CPU RMPOPT table base to the starting physical address to enable RMP optimization for up to 2 TB of system RAM on all CPUs.
Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0[ ... ]
Quoted 27 lines
arch/x86/virt/svm/sev.c +23
-0@@ -575,6 +577,42 @@ void snp_shutdown(void) }EXPORT_SYMBOL_FOR_MODULES (snp_shutdown,"ccp" ); +static bool rmpopt_capable (void ) +{ +return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && +cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +} + +void snp_enable_rmpopt (void ) +{ +u64 base; +int cpu; + +if (!rmpopt_capable ()) +return ; + + rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); + +/* +* Per-CPU RMPOPT tables cover at most 2 TB. Program each core's +* RMPOPT_BASE with the start of RAM to optimize up to 2 TB. The MSR +* can only be written after SNP is enabled, so this runs from the ccp +* SNP init path (and again on guest teardown) rather than an initcall. +* +* The loop below programs RMPOPT_BASE on all primary threads. RMPOPT_EN arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 612
Quoted 10 lines
arch/x86/virt/svm/sev.c +10
-0+* cannot be cleared while SNP is enabled, and CPU hotplug is disabled +* while SNP is active, so once programmed the MSRs stay set on all CPUs +* until SNP is disabled. A set RMPOPT_EN on the local CPU therefore +* means the programming has already been done, so skip it. +*/ +rdmsrq (MSR_AMD64_RMPOPT_BASE , base); +if (!(base &MSR_AMD64_RMPOPT_ENABLE )) +for_each_cpu (cpu, cpu_primary_thread_mask) +wrmsrq_on_cpu (cpu,MSR_AMD64_RMPOPT_BASE , + rmpopt_pa_start |MSR_AMD64_RMPOPT_ENABLE );[Severity: High]
Could this local CPU check cause hotplugged CPUs to miss MSR configuration?
Quoted 9 lines
If SNP is shut down, CPU hotplug is re-enabled but the MSR remains intact on existing CPUs. If a CPU is offlined and brought back online, its MSR is reset to the default.
Later, when SNP is re-enabled, the local CPU executing
snp_enable_rmpopt()might still have its ownRMPOPT_ENABLEbit set. Will it erroneously assume all CPUs are programmed and skip thewrmsrq_on_cpu()loop, leaving the hotplugged CPU unconfigured?
This requires a runtime SNP teardown, a CPU offline/online cycle, and an SNP re‑init (rmmod/modprobe ccp with a hotplug in between). SNP hosts run with a fixed CPU set — CPU hotplug is disabled the entire time SNP is active, and the only window to cycle a CPU is a full runtime SNP shutdown. Bringing SNP back up with a CPU hotplugged while it was down is outside supported SNP host operation, so this sequence does not occur in practice.
Thanks,
Ashish
Quoted 4 lines
Quoted 3 lines
arch/x86/virt/svm/sev.c +3
-0+} +EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" ); +
Re: [PATCH v16 3/5] x86/sev: Initialize RMPOPT configuration MSRs
In reply to Ashish KalraQuoted 8 lines · Ashish Kalra
Changes in v15:
- Renamesnp_setup_rmpopt()tosnp_enable_rmpopt().
- Program each core'sRMPOPT_BASEonly when it is not already set.arch/x86/include/asm/msr-index.h | 3 ++ arch/x86/include/asm/sev.h | 2 + arch/x86/virt/svm/sev.c | 66 +++++++++++++++++++++++++++++--- drivers/crypto/ccp/sev-dev.c | 2 + 4 files changed, 68 insertions(+), 5 deletions(-)
Did some scrubbing:
commit 61132258c7a139a9533ae24b056a099184b70e50 (HEAD -> refs/heads/tip-x86-sev)
Author: Ashish Kalra <[email protected]> Date: Wed Sep 16 22:14:52 2026 +0000
x86/sev: Initialize RMPOPT configuration MSRs
The new RMPOPT instruction helps manage per-CPU RMP optimization structures inside the CPU. It takes a 1GB-aligned physical address and either returns the status of the optimizations or tries to enable the optimizations.
Initialize the per-CPU RMPOPT table base to the starting physical address. This enables RMP optimization for up to 2 TB of system RAM on all CPUs because this is the maximum the RMPOPT tables support.
The RMPOPT_BASE MSR can only be written after SNP is enabled, so the enabling runs from the ccp SNP init path (and again on guest teardown) rather than from an initcall. This is also in line with the intention to not enable SNP by default but enable it on demand, when the ccp module is loaded.
RMPOPT_BASE is programmed on all primary threads. RMPOPT_EN cannot be cleared while SNP is enabled, and CPU hotplug is disabled while SNP is active, so once programmed the MSRs stay set on all CPUs until SNP is disabled. A set RMPOPT_EN on the local CPU therefore means the programming has already been done and thus it can be skipped.
[ bp:
- Massage commit message
- move enabling logic into the commit message
- zap unnecessary comments
- cleanup ]
arch/x86/include/asm/msr-index.h +3 -0
@@ -761,6 +761,9 @@#define MSR_AMD64_SEG_RMP_ENABLED_BIT 0#define MSR_AMD64_SEG_RMP_ENABLED BIT_ULL(MSR_AMD64_SEG_RMP_ENABLED_BIT)#define MSR_AMD64_RMP_SEGMENT_SHIFT (x) (((x) & GENMASK_ULL(13, 8)) >> 8) +#define MSR_AMD64_RMPOPT_BASE 0xc0010139 +#define MSR_AMD64_RMPOPT_ENABLE_BIT 0 +#define MSR_AMD64_RMPOPT_ENABLE BIT_ULL(MSR_AMD64_RMPOPT_ENABLE_BIT)#define MSR_SVSM_CAA 0xc001f000
arch/x86/include/asm/sev.h +2 -0
@@ -662,6 +662,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages) __snp_leak_pages(pfn, pages, true); }int snp_prepare (void ); +void snp_enable_rmpopt (void );void snp_shutdown (void );#else static inline bool snp_probe_rmptable_info (void ) {return false; }
@@ -680,6 +681,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 int snp_prepare (void ) {return -ENODEV ; } +static inline void snp_enable_rmpopt (void ) {}static inline void snp_shutdown (void ) {}#endif
arch/x86/virt/svm/sev.c +43 -5
68 lines not on this page · raw message
drivers/crypto/ccp/sev-dev.c +2 -0
@@ -1663,6 +1663,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) sev_es_tmr_size =SNP_TMR_SIZE ; +snp_enable_rmpopt (); +return 0 ; }
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteAshish Kalra
maintainerPATCH v16 4/5 x86/sev: Perform RMP optimizations asynchronously
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
When SNP is enabled, all writes to memory are checked to ensure memory integrity. This imposes performance overhead on the whole system.
RMPOPT is a new instruction that minimizes the performance overhead of RMP checks on the hypervisor and on non-SNP guests by allowing RMP checks to be skipped for 1GB regions of memory that are known not to contain any SNP guest memory.
Add support for performing RMP optimizations asynchronously using a dedicated per-CPU workqueue. The workqueue is allocated from an initcall, and snp_enable_rmpopt() queues the optimization pass.
Shortly after SNP initialization, run an optimization pass over all physical memory (up to 2TB of system RAM, starting from the lowest physical memory address aligned down to a 1GB boundary), skipping RMP checks for 1GB regions that do not contain SNP guest memory (excluding preassigned pages such as the RMP table and firmware pages).
As SNP guests are launched, RMPUPDATE assigns their private pages to guest-owned state; when such a page falls within an optimized 1GB region, the hardware clears that region's RMPOPT optimization and RMP checks resume there to protect the guest memory.
Since launching SNP guests clears these optimizations, perform them again asynchronously using the dedicated workqueue.
---
Changes in v15:
- Move the workqueue allocation and the (fixed) optimization range computation to an initcall; snp_enable_rmpopt() now only programs the RMPOPT_BASE MSRs and queues the optimization pass.
- Gate rmpopt_capable() on a static rmpopt_enabled bool set when the workqueue is allocated, instead of an if (rmpopt_wq) check, and drop rmpopt_wq_mutex.
- Queue both the initial and the teardown pass with mod_delayed_work().
arch/x86/virt/svm/sev.c | 99 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)
arch/x86/virt/svm/sev.c +95 -4
@@ -19,6 +19,7 @@#include <linux/iommu.h> #include <linux/amd-iommu.h> #include <linux/nospec.h> +#include <linux/workqueue.h> #include <asm/sev.h> #include <asm/processor.h>
@@ -124,7 +125,25 @@ static void *rmp_bookkeeping __ro_after_init;static u64 probed_rmp_base, probed_rmp_size; -static phys_addr_t rmpopt_pa_start; +static u64 rmpopt_pa_start, rmpopt_pa_end; + +enum rmpopt_op_type { +RMPOPT_OP_VERIFY_AND_REPORT_STATUS , +RMPOPT_OP_REPORT_STATUS +}; + +static struct workqueue_struct *rmpopt_wq; +static struct delayed_work rmpopt_delayed_work; +static bool rmpopt_enabled; + +/* +* Delay, in milliseconds, before the RMP re-optimization pass runs after an SNP +* guest is torn down, passed as the delay to mod_delayed_work(). This coalesces +* a burst of teardowns into a single scan and gives each guest's pages time to +* be converted back to the shared, hypervisor-owned state. The 10 second value +* is a heuristic trading re-optimization latency against scanning too eagerly. +*/ +#define RMPOPT_WORK_TIMEOUT (10 * MSEC_PER_SEC)static LIST_HEAD (snp_leaked_pages_list);static DEFINE_SPINLOCK (snp_leaked_pages_list_lock);
@@ -557,6 +576,12 @@ int snp_prepare(void) }EXPORT_SYMBOL_FOR_MODULES (snp_prepare,"ccp" ); +static void rmpopt_disable (void ) +{ +if (rmpopt_wq) +cancel_delayed_work_sync (&rmpopt_delayed_work); +} +void snp_shutdown (void ) {u64 syscfg;
@@ -565,6 +590,8 @@ void snp_shutdown(void)if (syscfg &MSR_AMD64_SYSCFG_SNP_EN )return ; +rmpopt_disable (); +clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 );
@@ -580,8 +607,69 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");static bool rmpopt_capable (void ) {return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && -cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +cc_platform_has (CC_ATTR_HOST_SEV_SNP ) && rmpopt_enabled; +} + +/* +* RMPOPT optimizations skip RMP checks at 1GB granularity if this range of +* memory does not contain any SNP guest memory. +* +* @pa is a system physical address; RMPOPT operates on the containing 1GB. +*/ +static void rmpopt (u64 pa) +{ +enum rmpopt_op_type op =RMPOPT_OP_VERIFY_AND_REPORT_STATUS ; +u64 pa_start =ALIGN_DOWN (pa,SZ_1G ); + +/* Supported by binutils 2.48+ */ + asmvolatile (".byte 0xf2, 0x0f, 0x01, 0xfc" + ::"a" (pa_start),"c" (op) + :"memory" ,"cc" ); +} + +static void rmpopt_scan_range (void *arg) +{ +u64 pa; + +for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa +=SZ_1G ) +rmpopt (pa); +} + +static void do_rmpopt_work (struct work_struct *work) +{ +/* +* Warm up the RMPOPT cache on this pinned per-CPU worker with interrupts +* enabled, so the IRQ-disabled fan-out below only issues cache-hit RMPOPTs. +*/ +rmpopt_scan_range (NULL ); + +on_each_cpu_mask (cpu_primary_thread_mask, rmpopt_scan_range,NULL , true); +} + +static int __init rmpopt_init (void ) +{ +if (!cpu_feature_enabled (X86_FEATURE_RMPOPT )) +return 0 ; + + rmpopt_wq =alloc_workqueue ("rmpopt_wq" ,WQ_PERCPU ,1 ); +if (!rmpopt_wq) { +pr_err ("Failed to allocate RMPOPT workqueue\n" ); +return 0 ; + } + +INIT_DELAYED_WORK (&rmpopt_delayed_work, do_rmpopt_work); + +/* The optimization range is fixed at boot; compute it once. */ + rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); + rmpopt_pa_end =ALIGN (PFN_PHYS (max_pfn),SZ_1G ); +if ((rmpopt_pa_end - rmpopt_pa_start) >SZ_2T ) + rmpopt_pa_end = rmpopt_pa_start +SZ_2T ; + + rmpopt_enabled = true; + +return 0 ; } +device_initcall (rmpopt_init);void snp_enable_rmpopt (void ) {
@@ -591,8 +679,6 @@ void snp_enable_rmpopt(void)if (!rmpopt_capable ())return ; - rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); -/* * Per-CPU RMPOPT tables cover at most 2 TB. Program each core's * RMPOPT_BASE with the start of RAM to optimize up to 2 TB. The MSR
@@ -610,6 +696,11 @@ void snp_enable_rmpopt(void)for_each_cpu (cpu, cpu_primary_thread_mask)wrmsrq_on_cpu (cpu,MSR_AMD64_RMPOPT_BASE , rmpopt_pa_start |MSR_AMD64_RMPOPT_ENABLE ); + +mod_delayed_work (rmpopt_wq, &rmpopt_delayed_work, +msecs_to_jiffies (RMPOPT_WORK_TIMEOUT )); + +pr_info_once ("RMPOPT optimizations enabled\n" ); }EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" );
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v16 4/5] x86/sev: Perform RMP optimizations asynchronously
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] x86/sev: fatal #GP on blind RMPOPT scan across MMIO holes - [High] x86/sev: soft lockup via…
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] x86/sev: fatal #GP on blind RMPOPT scan across MMIO holes
- [High] x86/sev: soft lockup via IRQ-disabled IPI for cold RMPOPT cache
- [High] x86/sev: fatal #UD race between RMPOPT and firmware SNP shutdown --
commit 62479bc981175bad2d66a2cbd0e76b01cb89e84b
Author: Ashish Kalra <[email protected]>
x86/sev: Perform RMP optimizations asynchronously
This patch adds support for performing RMP optimizations asynchronously using a dedicated per-CPU workqueue. It scans physical memory up to 2TB and executes RMPOPT to skip RMP checks for 1GB regions that do not contain SNP guest memory.
Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0
[ ... ]
Quoted 14 lines
arch/x86/virt/svm/sev.c +6
-0@@ -557,6 +576,12 @@ int snp_prepare(void) }EXPORT_SYMBOL_FOR_MODULES (snp_prepare,"ccp" ); +static void rmpopt_disable (void ) +{ +if (rmpopt_wq) +cancel_delayed_work_sync (&rmpopt_delayed_work); +} +void snp_shutdown (void ) {u64 syscfg;
arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 593
Quoted 4 lines
arch/x86/virt/svm/sev.c +1
-0@@ -565,6 +590,8 @@ void snp_shutdown(void)if (syscfg &MSR_AMD64_SYSCFG_SNP_EN )return ; +rmpopt_disable ();
[Severity: High]
Is it safe to cancel the worker here?
When SNP is disabled, the CCP driver sends SEV_CMD_SNP_SHUTDOWN_EX to the firmware, which clears the SnpEn hardware state. The driver then calls snp_shutdown(), which calls cancel_delayed_work_sync() here.
Between the firmware clearing SnpEn and the work being cancelled, the background worker or an IPI can concurrently execute the RMPOPT instruction.
Executing RMPOPT while SnpEn is clear causes a #UD exception, leading to a kernel panic.
Quoted 4 lines
arch/x86/virt/svm/sev.c +1
-0+clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 );
[ ... ]
Quoted 21 lines
arch/x86/virt/svm/sev.c +16
-1@@ -580,8 +607,69 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");static bool rmpopt_capable (void ) {return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && -cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +cc_platform_has (CC_ATTR_HOST_SEV_SNP ) && rmpopt_enabled; +} + +/* +* RMPOPT optimizations skip RMP checks at 1GB granularity if this range of +* memory does not contain any SNP guest memory. +* +* @pa is a system physical address; RMPOPT operates on the containing 1GB. +*/ +static void rmpopt (u64 pa) +{ +enum rmpopt_op_type op =RMPOPT_OP_VERIFY_AND_REPORT_STATUS ; +u64 pa_start =ALIGN_DOWN (pa,SZ_1G ); + +/* Supported by binutils 2.48+ */ + asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"
arch/x86/virt/svm/sev.c EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); · line 635
Quoted 10 lines
arch/x86/virt/svm/sev.c +10
-0+ :: "a" (pa_start), "c" (op) + : "memory", "cc"); +} + +static void rmpopt_scan_range (void *arg) +{ +u64 pa; + +for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa +=SZ_1G ) +rmpopt (pa);
[Severity: High]
Does this iteration skip non-system RAM such as the PCI MMIO hole (e.g., 3GB-4GB)?
This iterates over the entire physical address range from min_low_pfn to max_pfn in 1GB steps. Executing the RMPOPT instruction on addresses not backed by valid system memory causes a #GP.
Since the rmpopt() inline assembly lacks an exception handler, will this result in a fatal kernel panic when executing on MMIO holes?
arch/x86/virt/svm/sev.c EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); · line 646
Quoted 11 lines
arch/x86/virt/svm/sev.c +11
-0+} + +static void do_rmpopt_work (struct work_struct *work) +{ +/* +* Warm up the RMPOPT cache on this pinned per-CPU worker with interrupts +* enabled, so the IRQ-disabled fan-out below only issues cache-hit RMPOPTs. +*/ +rmpopt_scan_range (NULL ); + +on_each_cpu_mask (cpu_primary_thread_mask, rmpopt_scan_range,NULL , true);
[Severity: High]
Could this IPI cause severe scheduling latency or potential soft lockups?
do_rmpopt_work() calls rmpopt_scan_range() locally to warm up the RMPOPT cache, then uses on_each_cpu_mask() to run it on all other CPUs via IPI.
Since the RMPOPT table is per-CPU, local warmup on the worker CPU does not affect remote CPUs. The IPI forces all other CPUs to execute the scan with a cold cache.
Executing up to 2048 cache-miss hardware instructions inside an IRQ-disabled IPI handler seems likely to cause severe latency issues.
Quoted 1 line
arch/x86/virt/svm/sev.c +1
-0+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Kalra, Ashish
maintainerRe: [PATCH v16 4/5] x86/sev: Perform RMP optimizations asynchronously
Quoted 47 lines · On 9/16/2026 5:29 PM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] x86/sev: fatal #GP on blind RMPOPT scan across MMIO holes
- [High] x86/sev: soft lockup via IRQ-disabled IPI for cold RMPOPT cache
- [High] x86/sev: fatal #UD race between RMPOPT and firmware SNP shutdown --commit
62479bc981175bad2d66a2cbd0e76b01cb89e84b
Author: Ashish Kalra <[email protected]>x86/sev: Perform RMP optimizations asynchronously
This patch adds support for performing RMP optimizations asynchronously using a dedicated per-CPU workqueue. It scans physical memory up to 2TB and executes RMPOPT to skip RMP checks for 1GB regions that do not contain SNP guest memory.
Quoted 4 lines
arch/x86/virt/svm/sev.c +0
-0[ ... ]
Quoted 14 lines
arch/x86/virt/svm/sev.c +6
-0@@ -557,6 +576,12 @@ int snp_prepare(void) }EXPORT_SYMBOL_FOR_MODULES (snp_prepare,"ccp" ); +static void rmpopt_disable (void ) +{ +if (rmpopt_wq) +cancel_delayed_work_sync (&rmpopt_delayed_work); +} +void snp_shutdown (void ) {u64 syscfg;arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 593
Quoted 4 lines
arch/x86/virt/svm/sev.c +1
-0@@ -565,6 +590,8 @@ void snp_shutdown(void)if (syscfg &MSR_AMD64_SYSCFG_SNP_EN )return ; +rmpopt_disable ();[Severity: High]
Is it safe to cancel the worker here?When SNP is disabled, the CCP driver sends
SEV_CMD_SNP_SHUTDOWN_EXto the firmware, which clears the SnpEn hardware state. The driver then callssnp_shutdown(), which callscancel_delayed_work_sync()here.
Quoted 6 lines
Between the firmware clearing SnpEn and the work being cancelled, the background worker or an IPI can concurrently execute the RMPOPT instruction.
Executing RMPOPT while SnpEn is clear causes a #UD exception, leading to a kernel panic.
snp_shutdown() runs only at full host SNP teardown (reboot, kexec, ccp removal), which is supported only after all SNP guests are torn down. RMP re‑optimization is a per‑guest‑teardown event, so in a supported configuration the two don't overlap: by the time the host disables SNP, guest teardown is complete and rmpopt_disable() drains any pending pass. We don't support disabling SNP underneath an in‑flight guest teardown.
Quoted 41 lines
Quoted 4 lines
arch/x86/virt/svm/sev.c +1
-0+clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 );[ ... ]
Quoted 21 lines
arch/x86/virt/svm/sev.c +16
-1@@ -580,8 +607,69 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");static bool rmpopt_capable (void ) {return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && -cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +cc_platform_has (CC_ATTR_HOST_SEV_SNP ) && rmpopt_enabled; +} + +/* +* RMPOPT optimizations skip RMP checks at 1GB granularity if this range of +* memory does not contain any SNP guest memory. +* +* @pa is a system physical address; RMPOPT operates on the containing 1GB. +*/ +static void rmpopt (u64 pa) +{ +enum rmpopt_op_type op =RMPOPT_OP_VERIFY_AND_REPORT_STATUS ; +u64 pa_start =ALIGN_DOWN (pa,SZ_1G ); + +/* Supported by binutils 2.48+ */ + asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"arch/x86/virt/svm/sev.c EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); · line 635
Quoted 10 lines
arch/x86/virt/svm/sev.c +10
-0+ :: "a" (pa_start), "c" (op) + : "memory", "cc"); +} + +static void rmpopt_scan_range (void *arg) +{ +u64 pa; + +for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa +=SZ_1G ) +rmpopt (pa);[Severity: High]
Does this iteration skip non-system RAM such as the PCI MMIO hole (e.g., 3GB-4GB)?
Quoted 7 lines
This iterates over the entire physical address range from
min_low_pfntomax_pfnin 1GB steps. Executing the RMPOPT instruction on addresses not backed by valid system memory causes a #GP.Since the
rmpopt()inline assembly lacks an exception handler, will this result in a fatal kernel panic when executing on MMIO holes?
RMPOPT does not fault on physical addresses not backed by system RAM. It inspects the RMP entries for the 1 GB region — which reside in the RMP table (itself RAM), not the target memory — so an MMIO hole is never accessed. The RMP covers the entire physical range up to the top of RAM, so holes below that top (e.g. the 3–4 GB PCI hole on systems with RAM above 4 GB) have default hypervisor‑owned RMP entries and RMPOPT reports them optimizable; there is no #GP/#MC/OOB for non‑RAM regions in the scanned range. Confirmed on hardware: the test host has RAM above 4 GB (hence a 3–4 GB hole inside the 0..2 TB scan) and the full pass completes with guests booting normally.
Quoted 18 lines
arch/x86/virt/svm/sev.c EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); · line 646
Quoted 11 lines
arch/x86/virt/svm/sev.c +11
-0+} + +static void do_rmpopt_work (struct work_struct *work) +{ +/* +* Warm up the RMPOPT cache on this pinned per-CPU worker with interrupts +* enabled, so the IRQ-disabled fan-out below only issues cache-hit RMPOPTs. +*/ +rmpopt_scan_range (NULL ); + +on_each_cpu_mask (cpu_primary_thread_mask, rmpopt_scan_range,NULL , true);[Severity: High]
Could this IPI cause severe scheduling latency or potential soft lockups?
do_rmpopt_work()callsrmpopt_scan_range()locally to warm up the RMPOPT cache, then useson_each_cpu_mask()to run it on all other CPUs via IPI.
Quoted 6 lines
Since the RMPOPT table is per-CPU, local warmup on the worker CPU does not affect remote CPUs. The IPI forces all other CPUs to execute the scan with a cold cache.
Executing up to 2048 cache-miss hardware instructions inside an IRQ-disabled IPI handler seems likely to cause severe latency issues.
The RMPOPT optimization state is a shared in‑memory table (all cores program RMPOPT_BASE to the same address), not a per‑CPU cache. The warm‑up scan on the worker populates it, so the IRQ‑disabled fan‑out issues only cache‑hit RMPOPTs. Measured worst‑case follower callback is 1.34 ms — well under the soft‑lockup/NMI‑watchdog thresholds.
Thanks,
Ashish
Quoted 3 lines
Quoted 2 lines
arch/x86/virt/svm/sev.c +1
-0+}
Borislav Petkov
maintainerRe: [PATCH v16 4/5] x86/sev: Perform RMP optimizations asynchronously
In reply to Ashish KalraQuoted 10 lines · Ashish Kalra
Changes in v15:
- Move the workqueue allocation and the (fixed) optimization range computation to an initcall;snp_enable_rmpopt()now only programs theRMPOPT_BASEMSRs and queues the optimization pass.
- Gatermpopt_capable()on a staticrmpopt_enabledbool set when the workqueue is allocated, instead of an if (rmpopt_wq) check, and droprmpopt_wq_mutex.
- Queue both the initial and the teardown pass withmod_delayed_work().arch/x86/virt/svm/sev.c | 99 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)
Some scrubbing to this one too:
Author: Ashish Kalra <[email protected]> Date: Wed Sep 16 22:15:14 2026 +0000
x86/sev: Perform RMP optimizations asynchronously
When SNP is enabled, all writes to memory are checked to ensure memory integrity. This imposes performance overhead on the whole system.
RMPOPT is a new instruction that minimizes the performance overhead of RMP checks on the hypervisor and on non-SNP guests by allowing such checks to be skipped for 1GB regions of memory that are known not to contain any SNP guest memory.
Add support for performing RMP optimizations asynchronously using a dedicated per-CPU workqueue. Shortly after SNP initialization, run an optimization pass over all physical memory.
As SNP guests are launched, RMPUPDATE assigns their private pages to guest-owned state; when such a page falls within an optimized 1GB region, the hardware clears that region's RMPOPT optimization and RMP checks resume there to protect the guest memory.
Since launching SNP guests clears these optimizations, perform them again asynchronously using the dedicated workqueue.
[ bp:
- Massage commit message and comments
- simplify code
- redo some of the logic ]
arch/x86/virt/svm/sev.c +94 -6
@@ -19,6 +19,7 @@#include <linux/iommu.h> #include <linux/amd-iommu.h> #include <linux/nospec.h> +#include <linux/workqueue.h> #include <asm/sev.h> #include <asm/processor.h>
@@ -124,7 +125,27 @@ static void *rmp_bookkeeping __ro_after_init;static u64 probed_rmp_base, probed_rmp_size; -static phys_addr_t rmpopt_pa_start; +static u64 rmpopt_pa_start, rmpopt_pa_end; + +enum rmpopt_op_type { +RMPOPT_OP_VERIFY_AND_REPORT_STATUS , +RMPOPT_OP_REPORT_STATUS +}; + +static struct workqueue_struct *rmpopt_wq; +static struct delayed_work rmpopt_delayed_work; + +/* Software RMPOPT facilities initialized */ +static bool rmpopt_soft_init; + +/* +* Delay, in milliseconds, before the RMP re-optimization pass runs after an +* SNP guest is torn down. This coalesces a burst of teardowns into a single +* scan and gives each guest's pages time to be converted back to the shared, +* hypervisor-owned state. The 10 second value is a heuristic trading +* re-optimization latency against scanning too eagerly. +*/ +#define RMPOPT_WORK_TIMEOUT (10 * MSEC_PER_SEC)static LIST_HEAD (snp_leaked_pages_list);static DEFINE_SPINLOCK (snp_leaked_pages_list_lock);
@@ -565,6 +586,9 @@ void snp_shutdown(void)if (syscfg &MSR_AMD64_SYSCFG_SNP_EN )return ; +if (rmpopt_soft_init) +cancel_delayed_work_sync (&rmpopt_delayed_work); +clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 );
@@ -577,21 +601,80 @@ void snp_shutdown(void) }EXPORT_SYMBOL_FOR_MODULES (snp_shutdown,"ccp" ); -static bool rmpopt_capable (void ) +/* +* RMPOPT optimizations skip RMP checks at 1GB granularity if this range of +* memory does not contain any SNP guest memory. +* +* @pa is a system physical address; RMPOPT operates on the containing 1GB. +*/ +static void rmpopt (u64 pa) { -return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && -cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +enum rmpopt_op_type op =RMPOPT_OP_VERIFY_AND_REPORT_STATUS ; +u64 pa_start =ALIGN_DOWN (pa,SZ_1G ); + +/* Supported by binutils 2.48+ */ + asmvolatile (".byte 0xf2, 0x0f, 0x01, 0xfc" + ::"a" (pa_start),"c" (op) + :"memory" ,"cc" ); +} + +static void rmpopt_scan_range (void *arg) +{ +u64 pa; + +for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa +=SZ_1G ) +rmpopt (pa); } +static void do_rmpopt_work (struct work_struct *work) +{ +/* +* Warm up the RMPOPT cache on this pinned per-CPU worker with interrupts +* enabled, so the IRQ-disabled fan-out below only issues cache-hit RMPOPTs. +*/ +rmpopt_scan_range (NULL ); + +on_each_cpu_mask (cpu_primary_thread_mask, rmpopt_scan_range,NULL , true); +} + +static int __init rmpopt_init (void ) +{ +if (!cpu_feature_enabled (X86_FEATURE_RMPOPT )) +return -ENODEV ; + + rmpopt_wq =alloc_workqueue ("rmpopt_wq" ,WQ_PERCPU ,1 ); +if (!rmpopt_wq) { +pr_err ("Failed to allocate RMPOPT workqueue\n" ); +return -ENOMEM ; + } + +INIT_DELAYED_WORK (&rmpopt_delayed_work, do_rmpopt_work); + +/* The optimization range is fixed at boot; compute it once. */ + rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); + rmpopt_pa_end =ALIGN (PFN_PHYS (max_pfn),SZ_1G ); +if ((rmpopt_pa_end - rmpopt_pa_start) >SZ_2T ) + rmpopt_pa_end = rmpopt_pa_start +SZ_2T ; + + rmpopt_soft_init = true; + +return 0 ; +} +device_initcall (rmpopt_init); +void snp_enable_rmpopt (void ) {u64 base;int cpu; -if (!rmpopt_capable ()) +if (!cpu_feature_enabled (X86_FEATURE_RMPOPT ))return ; - rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )) +return ; + +if (!rmpopt_soft_init) +return ;/* * Per-CPU RMPOPT tables cover at most 2 TB. Program each core's
@@ -602,6 +685,11 @@ void snp_enable_rmpopt(void)for_each_cpu (cpu, cpu_primary_thread_mask)wrmsrq_on_cpu (cpu,MSR_AMD64_RMPOPT_BASE , rmpopt_pa_start |MSR_AMD64_RMPOPT_ENABLE ); + +mod_delayed_work (rmpopt_wq, &rmpopt_delayed_work, +msecs_to_jiffies (RMPOPT_WORK_TIMEOUT )); + +pr_info ("RMPOPT optimizations enabled\n" ); }EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" );
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteKalra, Ashish
maintainerRe: [PATCH v16 4/5] x86/sev: Perform RMP optimizations asynchronously
Hello Boris,
Quoted 188 lines · Borislav Petkov
Quoted 10 lines · Ashish Kalra
Changes in v15:
- Move the workqueue allocation and the (fixed) optimization range computation to an initcall;snp_enable_rmpopt()now only programs theRMPOPT_BASEMSRs and queues the optimization pass.
- Gatermpopt_capable()on a staticrmpopt_enabledbool set when the workqueue is allocated, instead of an if (rmpopt_wq) check, and droprmpopt_wq_mutex.
- Queue both the initial and the teardown pass withmod_delayed_work().arch/x86/virt/svm/sev.c | 99 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)Some scrubbing to this one too:
Author: Ashish Kalra <[email protected]> Date: Wed Sep 16 22:15:14 2026 +0000x86/sev: Perform RMP optimizations asynchronously
When SNP is enabled, all writes to memory are checked to ensure memory integrity. This imposes performance overhead on the whole system.
RMPOPT is a new instruction that minimizes the performance overhead of RMP checks on the hypervisor and on non-SNP guests by allowing such checks to be skipped for 1GB regions of memory that are known not to contain any SNP guest memory.
Add support for performing RMP optimizations asynchronously using a dedicated per-CPU workqueue. Shortly after SNP initialization, run an optimization pass over all physical memory.
As SNP guests are launched, RMPUPDATE assigns their private pages to guest-owned state; when such a page falls within an optimized 1GB region, the hardware clears that region's RMPOPT optimization and RMP checks resume there to protect the guest memory.
Since launching SNP guests clears these optimizations, perform them again asynchronously using the dedicated workqueue.
[ bp:
- Massage commit message and comments
- simplify code
- redo some of the logic ]Suggested-by Thomas Lendacky <[email protected]>Suggested-by Dave Hansen <[email protected]>Signed-off-by Ashish Kalra <[email protected]>Signed-off-by Borislav Petkov (AMD) <[email protected]>arch/x86/virt/svm/sev.c +89
-6@@ -19,6 +19,7 @@#include <linux/iommu.h> #include <linux/amd-iommu.h> #include <linux/nospec.h> +#include <linux/workqueue.h> #include <asm/sev.h> #include <asm/processor.h> @@ -124,7 +125,27 @@ static void *rmp_bookkeeping __ro_after_init;static u64 probed_rmp_base, probed_rmp_size; -static phys_addr_t rmpopt_pa_start; +static u64 rmpopt_pa_start, rmpopt_pa_end; + +enum rmpopt_op_type { +RMPOPT_OP_VERIFY_AND_REPORT_STATUS , +RMPOPT_OP_REPORT_STATUS +}; + +static struct workqueue_struct *rmpopt_wq; +static struct delayed_work rmpopt_delayed_work; + +/* Software RMPOPT facilities initialized */ +static bool rmpopt_soft_init; + +/* +* Delay, in milliseconds, before the RMP re-optimization pass runs after an +* SNP guest is torn down. This coalesces a burst of teardowns into a single +* scan and gives each guest's pages time to be converted back to the shared, +* hypervisor-owned state. The 10 second value is a heuristic trading +* re-optimization latency against scanning too eagerly. +*/ +#define RMPOPT_WORK_TIMEOUT (10 * MSEC_PER_SEC)static LIST_HEAD (snp_leaked_pages_list);static DEFINE_SPINLOCK (snp_leaked_pages_list_lock);@@ -565,6 +586,9 @@ void snp_shutdown(void)if (syscfg &MSR_AMD64_SYSCFG_SNP_EN )return ; +if (rmpopt_soft_init) +cancel_delayed_work_sync (&rmpopt_delayed_work); +clear_rmp ();on_each_cpu (mfd_reconfigure,NULL ,1 );@@ -577,21 +601,80 @@ void snp_shutdown(void) }EXPORT_SYMBOL_FOR_MODULES (snp_shutdown,"ccp" ); -static bool rmpopt_capable (void ) +/* +* RMPOPT optimizations skip RMP checks at 1GB granularity if this range of +* memory does not contain any SNP guest memory. +* +* @pa is a system physical address; RMPOPT operates on the containing 1GB. +*/ +static void rmpopt (u64 pa) { -return cpu_feature_enabled (X86_FEATURE_RMPOPT ) && -cc_platform_has (CC_ATTR_HOST_SEV_SNP ); +enum rmpopt_op_type op =RMPOPT_OP_VERIFY_AND_REPORT_STATUS ; +u64 pa_start =ALIGN_DOWN (pa,SZ_1G ); + +/* Supported by binutils 2.48+ */ + asmvolatile (".byte 0xf2, 0x0f, 0x01, 0xfc" + ::"a" (pa_start),"c" (op) + :"memory" ,"cc" ); +} + +static void rmpopt_scan_range (void *arg) +{ +u64 pa; + +for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa +=SZ_1G ) +rmpopt (pa); } +static void do_rmpopt_work (struct work_struct *work) +{ +/* +* Warm up the RMPOPT cache on this pinned per-CPU worker with interrupts +* enabled, so the IRQ-disabled fan-out below only issues cache-hit RMPOPTs. +*/ +rmpopt_scan_range (NULL ); + +on_each_cpu_mask (cpu_primary_thread_mask, rmpopt_scan_range,NULL , true); +} + +static int __init rmpopt_init (void ) +{ +if (!cpu_feature_enabled (X86_FEATURE_RMPOPT )) +return -ENODEV ; + + rmpopt_wq =alloc_workqueue ("rmpopt_wq" ,WQ_PERCPU ,1 ); +if (!rmpopt_wq) { +pr_err ("Failed to allocate RMPOPT workqueue\n" ); +return -ENOMEM ; + } + +INIT_DELAYED_WORK (&rmpopt_delayed_work, do_rmpopt_work); + +/* The optimization range is fixed at boot; compute it once. */ + rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); + rmpopt_pa_end =ALIGN (PFN_PHYS (max_pfn),SZ_1G ); +if ((rmpopt_pa_end - rmpopt_pa_start) >SZ_2T ) + rmpopt_pa_end = rmpopt_pa_start +SZ_2T ; + + rmpopt_soft_init = true; + +return 0 ; +} +device_initcall (rmpopt_init); +void snp_enable_rmpopt (void ) {u64 base;int cpu; -if (!rmpopt_capable ()) +if (!cpu_feature_enabled (X86_FEATURE_RMPOPT ))return ; - rmpopt_pa_start =ALIGN_DOWN (PFN_PHYS (min_low_pfn),SZ_1G ); +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP )) +return ; + +if (!rmpopt_soft_init) +return ;/* * Per-CPU RMPOPT tables cover at most 2 TB. Program each core's
arch/x86/virt/svm/sev.c void snp_enable_rmpopt(void) · line 692
Quoted 8 lines
arch/x86/virt/svm/sev.c +5
-0@@ -602,6 +685,11 @@ void snp_enable_rmpopt(void)for_each_cpu (cpu, cpu_primary_thread_mask)wrmsrq_on_cpu (cpu,MSR_AMD64_RMPOPT_BASE , rmpopt_pa_start |MSR_AMD64_RMPOPT_ENABLE ); + +mod_delayed_work (rmpopt_wq, &rmpopt_delayed_work, +msecs_to_jiffies (RMPOPT_WORK_TIMEOUT )); + +pr_info ("RMPOPT optimizations enabled\n" );
Looks good.
One comment here, i originally had pr_info_once() for printing this once during SNP init, if you change it to pr_info(), it will get printed again at every SNP guest teardown too, i am fine with it and actually it keeps the user aware that RMPOPT optimizations are being re-applied/re-enabled, but there may be just too many kernel messages dumped.
Thanks,
Ashish
Quoted 5 lines
arch/x86/virt/svm/sev.c +0
-0}EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" );
Borislav Petkov
maintainerRe: [PATCH v16 4/5] x86/sev: Perform RMP optimizations asynchronously
Quoted 1 line · Ashish
Looks good.
Thx.
Quoted 4 lines
One comment here, i originally had
pr_info_once()for printing this once during SNP init, if you change it topr_info(), it will get printed again at every SNP guest teardown too, i am fine with it and actually it keeps the user aware that RMPOPT optimizations are being re-applied/re-enabled, but there may be just too many kernel messages dumped.
Yeah, I had the exact same internal conflict :)
I guess we should do the below instead. This tells us that all the necessary stuff needed for RMP optimizations has been set up and we can assume that those optimizations will happen from now on.
We can later add some more non-screaming reporting to user - ratelimited or whatnot - which gives more info.
But let's do that when we cross that bridge and we decide that they're actually necessary.
Thx.
--
arch/x86/virt/svm/sev.c +2 -2
@@ -656,6 +656,8 @@ static int __init rmpopt_init(void)if ((rmpopt_pa_end - rmpopt_pa_start) >SZ_2T ) rmpopt_pa_end = rmpopt_pa_start +SZ_2T ; +pr_info ("RMPOPT optimizations enabled\n" ); + rmpopt_soft_init = true;return 0 ;
@@ -688,8 +690,6 @@ void snp_enable_rmpopt(void)mod_delayed_work (rmpopt_wq, &rmpopt_delayed_work,msecs_to_jiffies (RMPOPT_WORK_TIMEOUT )); - -pr_info ("RMPOPT optimizations enabled\n" ); }EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp,kvm-amd" );
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteAshish Kalra
maintainerPATCH v16 5/5 x86/sev: Re-enable RMP optimizations on SNP guest shutdown
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
The RMPOPT table is a per-CPU table which indicates whether 1GB regions of physical memory are entirely hypervisor-owned.
When performing host memory accesses in hypervisor mode as well as non-SNP guest mode, the processor may consult the RMPOPT table to potentially skip an RMP access and improve performance.
Normal guest events disable RMP optimizations: pages are converted from shared to private as SNP guests are launched, and large pages are split and collapsed during guest operation -- both disable the RMPOPT optimizations for the affected 1GB regions.
When guests are torn down, their pages are converted back to shared, so those regions may become eligible for RMPOPT optimization again. Without some intervention, all RMP optimizations would eventually be lost, so re-optimize all of physical memory on SNP guest teardown by calling snp_enable_rmpopt().
snp_enable_rmpopt() performs the re-optimization after a delay, using mod_delayed_work() so that the delay timer is reset on each call. This batches multiple guest terminations into a single pass: the re-optimization runs 10 seconds after the *last* termination rather than after the first. mod_delayed_work() also re-queues work that is already in-flight, so a re-scan request during an active scan is not silently dropped.
Guest teardown is currently the only event that returns guest memory to hypervisor ownership: SNP guests do not support ballooning or memory hotplug, so pages freed during a guest's lifetime remain guest-owned. It is therefore the only point at which memory becomes eligible for RMP re-optimization, which is why re-optimization is driven by guest teardown rather than by a periodic scan.
---
Changes in v15:
- Call snp_enable_rmpopt() on guest teardown instead of a separate snp_rmpopt_all_physmem(); the delayed re-optimization now lives in snp_enable_rmpopt().
arch/x86/kvm/svm/sev.c | 2 ++ arch/x86/virt/svm/sev.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-)
arch/x86/kvm/svm/sev.c +2 -0
@@ -3032,6 +3032,8 @@ void sev_vm_destroy(struct kvm *kvm)*/ if (snp_decommission_context (kvm))return ; + +snp_enable_rmpopt (); }else {sev_unbind_asid (kvm, sev->handle); }
arch/x86/virt/svm/sev.c +1 -1
@@ -702,7 +702,7 @@ void snp_enable_rmpopt(void)pr_info_once ("RMPOPT optimizations enabled\n" ); } -EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp" ); +EXPORT_SYMBOL_FOR_MODULES (snp_enable_rmpopt,"ccp,kvm-amd" );/* * Do the necessary preparations which are verified by the firmware as
Signature · 1 line
2.43.0
Tom Lendacky
maintainerQuoted 1 line · Ashish Kalra
From: Ashish Kalra <[email protected]>
For the series:
Quoted 390 lines
In the SEV-SNP architecture, hypervisor and non-SNP guests are subject to RMP checks on writes to provide integrity of SEV-SNP guest memory.
The RMPOPT architecture enables optimizations whereby the RMP checks can be skipped if 1GB regions of memory are known to not contain any SNP guest memory.
RMPOPT is a new instruction designed to minimize the performance overhead of RMP checks for the hypervisor and non-SNP guests.
RMPOPT instruction currently supports two functions. In case of the verify and report status function the CPU will read the RMP contents, verify the entire 1GB region starting at the provided SPA is HV-owned. For the entire 1GB region it checks that all RMP entries in this region are HV-owned (i.e, not in assigned state) and then accordingly updates the RMPOPT table to indicate if optimization has been enabled and provide indication to software if the optimization was successful.
In case of report status function, the CPU returns the optimization status for the 1GB region.
The RMPOPT table is managed by a combination of software and hardware. Software uses the RMPOPT instruction to set bits in the table, indicating that regions of memory are entirely HV-owned. Hardware automatically clears bits in the RMPOPT table when RMP contents are changed during RMPUPDATE instruction.
For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.
As SNP is enabled by default the hypervisor and non-SNP guests are subject to RMP write checks to provide integrity of SNP guest memory.
This patch-series adds support to enable RMP optimizations for up to 2TB of system RAM across the system and allow RMPUPDATE to disable those optimizations as SNP guests are launched.
Support for RAM larger than 2 TB will be added in follow-on series.
This series also adds support to disable CPU hotplug while SNP is active, as the SEV firmware enumerates CPUs at SNP initialization and is not aware of the OS bringing CPUs online or offline afterwards. This also keeps the set of CPUs stable for the asynchronous RMPOPT scan, so the per-core
RMPOPT_BASEMSRs programmed during setup remain valid.This series also introduces support to re-enable RMP optimizations during SNP guest termination, after guest pages have been converted back to shared.
RMP optimizations are performed asynchronously by queuing work on a dedicated workqueue after a 10 second delay.
Delaying work allows batching of multiple SNP guest terminations.
Once 1GB hugetlb
guest_memfdsupport is merged, support for re-enabling RMPOPT optimizations during 1GB page cleanup will be added in follow-on series.v16:
- 3/5 (Initialize RMPOPT MSRs): clearX86_FEATURE_RMPOPTwhen segmented RMP is enabled but its probe fails, so the initcall does not set up RMPOPT for a system that will not bring up SNP. Program theRMPOPT_BASEMSRs only whenRMPOPT_ENis not already set on the local CPU, so guest teardown skips reprogramming rather than reprogramming the MSRs on every teardown.Review feedback from Sashiko AI.
v15:
- 3/5 (Initialize RMPOPT MSRs): renamesnp_setup_rmpopt()tosnp_enable_rmpopt(); program each core'sRMPOPT_BASEonly when it is not already set.
- 4/5 (async RMPOPT): split setup from enable -- an initcall allocates the workqueue and computes the (fixed) optimization range once, andsnp_enable_rmpopt()programs theRMPOPT_BASEMSRs and queues the optimization pass. Gatermpopt_capable()on a staticrmpopt_enabledbool instead of an if (rmpopt_wq) check and droprmpopt_wq_mutex. Queue both the initial and the teardown pass withmod_delayed_work().
- 5/5 (Re-enable on guest shutdown): callsnp_enable_rmpopt()on guest teardown instead of a separatesnp_rmpopt_all_physmem().This is a full resend rather than a 4/5-5/5 reply because patch 3 changed as part of the setup/enable split.
Review feedback from Borislav Petkov.
v14:
- 3/5 (Initialize RMPOPT MSRs): removermpopt_disable()and theRMPOPT_BASEteardown from this patch. RMPOPT is now set up once and not torn down, so theRMPOPT_BASEMSRs are left in place on shutdown; the (now minimal) disable path moves to 4/5.
- 4/5 (async RMPOPT): rework the setup/teardown -- initialize once and do not tear it down.rmpopt_disable()now only cancels the pending re-optimization pass. Use a dedicated per-CPU workqueue (WQ_PERCPU) and drop the migrate_disable()/migrate_enable() around the local warm-up scan. Add a binutils-version comment above the RMPOPT .byte encoding and trim redundant comments and the 2 TBpr_info(). Reword the subject to "Perform RMP optimizations asynchronously".
- 5/5 (Re-enable on guest shutdown): move the call-site comment onto thesnp_rmpopt_all_physmem()definition and simplify theRMPOPT_WORK_TIMEOUTcomment.
- Correct the Suggested-by/Reviewed-by tags across the series.Review feedback from Borislav Petkov.
v13:
- 3/5 (Initialize RMPOPT MSRs): usecpu_primary_thread_maskdirectly instead of building a localrmpopt_cpumask-- all primary threads are online while SNP is active, so the two are equivalent. Renamesnp_cleanup_rmpopt()tormpopt_disable().
- 4/5 (async RMPOPT): drop the leader/follower bookkeeping. Warm the RMP scan cache once, then fan the RMPOPT out tocpu_primary_thread_mask; re-running RMPOPT on the warm-up CPU is a cache hit, so the separate follower cpumask and thecpumask_andnot()are no longer needed. Renamermpopt_work_handler()todo_rmpopt_work()and drop the now-unusedthis_cpu.
- Add Reviewed-by: Tom Lendacky <[email protected]> to the series.Review feedback from Borislav Petkov.
v12:
- Merged the former "Add interface to re-enable RMP optimizations" and "KVM: SEV: Perform RMP optimizations on SNP guest shutdown" patches into a single patch (now 5/5), since the interface has no user without the KVM caller. The series is now 5 patches.
- 2/5 (Disable CPU hotplug): drop a stray reference to later patches from the commit message and trim thesnp_prepare()comment. Movecpu_hotplug_enable()to the end ofsnp_shutdown(), afterclear_rmp()and themfd_reconfigure()IPI, so no all-CPU operation runs after hotplug is re-enabled.
- 3/5 (Initialize RMPOPT MSRs): restructuresnp_probe_rmptable_info()so the contiguous probe (which clearsX86_FEATURE_RMPOPT) is the common fall-through, also coveringX86_FEATURE_SEGMENTED_RMPadvertised but disabled by firmware. Movesnp_setup_rmpopt()to the end of__sev_snp_init_locked(). Givesnp_cleanup_rmpopt()its own short comment insnp_shutdown().
- 4/5 (async RMPOPT): document the optimization trigger points in the commit message. Rename enumrmpopt_functiontormpopt_op_type. Merge__rmpopt()intormpopt()and replacermpopt_smp()with a newrmpopt_scan_range()that loops as theon_each_cpu()callback, so the follower scan issues one IPI per core rather than one per 1GB. Use u64 for physical addresses. Allocate the follower cpumask once at setup. Documentrmpopt_wqas the setup sentinel. Move theRMPOPT_WORK_TIMEOUTdefine to the guest-shutdown patch (its only user) as (10 *MSEC_PER_SEC). Remove a spammypr_info()and reword comments.
- 5/5 (Re-enable RMP optimizations on SNP guest shutdown): document why re-optimization is driven by guest teardown (the only event that returns guest memory to hypervisor ownership); reword the commit message (s/clear/disable/ for optimizations, drop "Conversely").Review feedback from Borislav Petkov.
v11:
- Reordered so "Disable CPU hotplug while SNP is active" (2/6) precedes "Initialize RMPOPT configuration MSRs" (3/6): the RMPOPT setup/cleanup code is then introduced with CPU hotplug already disabled and never takescpus_read_lock().
- 1/6 (cpufeatures): drop thetools/arch/x86/include/asm/cpufeatures.hchange and adopt the commit message as applied by Boris.
- 2/6 (Disable CPU hotplug): reword the commit message. Drop the redundant cpus_read_lock()/cpus_read_unlock() insnp_prepare()in this same patch -- with hotplug disabled the online CPU mask is stable, so the read lock is not needed and hotplug handling has no hole when bisecting.
- 3/6 (Initialize RMPOPT MSRs): replace thermpopt_capablebool with a small helper local toarch/x86/virt/svm/sev.c--cpu_feature_enabled(X86_FEATURE_RMPOPT)&&cc_platform_has(CC_ATTR_HOST_SEV_SNP)-- clearingX86_FEATURE_RMPOPTfor a contiguous (non-segmented) RMP insnp_probe_rmptable_info()(runs at BSP init, before alternatives). Renamermpopt_cleanup()tosnp_cleanup_rmpopt()to matchsnp_setup_rmpopt(). Both are introduced withoutcpus_read_lock()(hotplug is already disabled by 2/6). Simplify theRMPOPT_BASEcomments. Drop the now-unused <asm/sev.h> include fromcore.c.
- 4/6 (async RMPOPT): the follower scan is likewise introduced withoutcpus_read_lock(). Drop thecond_resched()calls (nops on x86). On re-initialization after a legacy SNP shutdown, re-queue the optimization pass instead of skipping it.pr_warn()on cpumask allocation failure.Review feedback from Borislav Petkov and K Prateek Nayak.
v10:
- Rework the CPU-hotplug patch (3/6): disable CPU hotplug insnp_prepare(), before SnpEn is set, instead of late in__sev_snp_init_locked(), so no CPU can come online without SnpEn during SNP initialization (per upstream review). Tie hotplug to SnpEn: it stays disabled while SnpEn is set -- including across a failedSNP_INITand across the legacySNP_SHUTDOWN_EXpath -- and is re-enabled only once the firmware clears SnpEn on thex86_snp_shutdownpath. Drop the separate idempotent flag:snp_prepare()re-enables hotplug on its own early failure, and a kexec target that boots with SnpEn already set disables hotplug once insnp_rmptable_init(). Reword the commit log and comments accordingly.
- Emit apr_warn()inrmpopt_work_handler()(4/6) when the follower cpumask allocation fails, instead of silently skipping the optimization pass.Sashiko AI upstream review identified several of the above issues.
v9:
- Renamermpopt_configuredtormpopt_capable.
- Makermpopt_cpumaskacpumask_var_t(allocated/freed at setup/cleanup) instead of a staticcpumask_t.
- Drop the v8WARN_ON_ONCE()on theRMPOPT_BASEwrites; use a plainwrmsrq_on_cpu(), matching the SNP MSR-write convention in this file.
- Disable CPU hotplug with cpu_hotplug_disable()/cpu_hotplug_enable() (per tglx); re-enable only on the fullx86_snp_shutdownpath.
- Simplifyrmpopt_work_handler()to a single leader-then-followers path:
with CPU hotplug disabled while SNP is active andsnp_prepare()requiring all CPUs online whenRMPOPT_BASEis programmed, every core is always programmed, so the explicit-leader fallback is now unreachable. Drop it along with the v8 work_on_cpu()/rmpopt_leader_fn() helper.
- Drop the debugfs interface (was patch 7/7) and its report-only plumbing; observability will be revisited after this series is merged.
- Restrict snp_rmpopt_all_physmem()'s export to the kvm-amd module.
- Usescoped_guard(cpus_read_lock)for the per-CPU MSR and follower loops.Sashiko AI upstream review identified several of the above issues.
v8:
- Add a new patch to disable CPU hotplug while SNP is active, keeping the CPU set stable for the RMPOPT work handler.
- Drop thesetup_clear_cpu_cap(X86_FEATURE_RMPOPT)calls; thermpopt_configuredbool is the runtime guard.
-WARN_ON_ONCE()on theRMPOPT_BASEMSR writes that previously ignored their return value.
- Simplifyrmpopt_work_handler()by removing the explicit-leader
fallback: with CPU hotplug disabled while SNP is active andsnp_prepare()requiring all CPUs online whenRMPOPT_BASEis programmed, every core is always programmed, so the running CPU can always be the leader. This drops thesmp_call_function_single()fallback (and with it the AB-BA deadlock and IRQ-latency concerns) and collapses the leader selection into a single leader-then-followers path.
- Usemod_delayed_work()insnp_rmpopt_all_physmem()so the batching delay tracks the last SNP guest termination.Sashiko AI code review identified several of the above issues.
v7:
- Synctools/arch/x86/include/asm/cpufeatures.hto mirror the kernel header forX86_FEATURE_RMPOPT.
- Fix commit title to useX86_FEATURE_RMPOPTto match the code (wasX86_FEATURE_AMD_RMPOPT).
- Add static boolrmpopt_configured, set only when segmented RMP setup succeeds insetup_rmptable(). Checkrmpopt_configuredalongsidecpu_feature_enabled(X86_FEATURE_RMPOPT)insnp_setup_rmpopt()andsnp_rmpopt_all_physmem(), becausesetup_clear_cpu_cap()is unreliable after alternatives are patched. Addsnp_clear_rmpopt_configured()called fromamd_cc_platform_clear()whenCC_ATTR_HOST_SEV_SNPis cleared. Do not use__ro_after_initonrmpopt_configuredsince the writersnp_clear_rmpopt_configured()is not__init.
- Addcond_resched()to all three leader loops inrmpopt_work_handler()to prevent soft lockups on systems with up to 2TB of RAM.
- Add comment above__rmpopt()documenting the RMPOPT instruction encoding (F2 0F 01 FC) and register interface (RAX = system physical address input, RCX = operation type input, RFLAGS.CF = output).
Note: RMPOPT does not modify RAX unlike PVALIDATE/RMPUPDATE, so the existing "a" (input-only) constraint is correct.Sashiko AI code review identified several of the above issues.
v6:
- Dropwrmsrq_on_cpus()helper; usefor_each_cpu()withwrmsrq_on_cpu()instead, asRMPOPT_BASEMSR programming is not performance-critical.
- Rewritermpopt_work_handler()leader selection to use a localfollower_maskcopy instead of modifying the globalrmpopt_cpumask. This eliminates thecurrent_cpu_clearedtracking and the restore at the end, and removes the need for synchronization comments about transient cpumask inconsistency.
- Add three-way leader selection inrmpopt_work_handler():
1. Current CPU is a primary thread in cpumask: run leader locally.
2. Current CPU is a sibling thread whose primary is in cpumask:
run leader locally (RMPOPT_BASEMSR is per-core), remove the primary from followers viacpumask_andnot(topology_sibling_cpumask).
3. Current CPU's core has noRMPOPT_BASEMSR programmed: pick an explicit leader viacpumask_first()+smp_call_function_single()to avoid #UD, withcpus_read_lock()around the IPI loop.
- AddWARN_ON_ONCEguard for empty cpumask in the explicit leader fallback path, withmigrate_enable()before goto out.
- Add .llseek =seq_lseektormpopt_table_fopsfor consistency with other seq_file-based debugfs files and to support tools like "less".
- Change debugfs file permissions from 0444 to 0400 to restrict access to root only.
- Add comment inrmpopt_table_seq_show()explaining whycpu_online_maskis safe:RMPOPT_BASEMSR is per-core andsnp_prepare()ensures all CPUs are online when the MSR is programmed.Sashiko AI code review identified several of the above issues.
v5:
- Introducermpopt_cleanup()to tear down workqueue, debugfs, cpumask, and MSR state, called fromsnp_shutdown().
- Introducermpopt_wq_mutexto serializesnp_setup_rmpopt(),snp_rmpopt_all_physmem(), andrmpopt_cleanup().
- Introducermpopt_show_mutexto serialize debugfs reporting ofrmpopt_report_cpumask.
- Movesnp_rmpopt_all_physmem()call after SNP DECOMMISSION during guest shutdown.
- Use migrate_disable()/migrate_enable() for CPU pinning in thermpopt_work_handler()leader loop to maintain CPU affinity without disabling preemption for the entire RMPOPT scan.
- Add cpus_read_lock()/cpus_read_unlock() around the followeron_each_cpu_mask()loop inrmpopt_work_handler().
- Guardsnp_setup_rmpopt()against re-initialization whenSNP_SHUTDOWN_EXwith x86_snp_shutdown=0 skipsrmpopt_cleanup()but clearssnp_initialized, preventing workqueue and resource leaks on repeated init/shutdown cycles.
- Replacesetup_clear_cpu_cap()withpr_err()onalloc_workqueue()failure insnp_setup_rmpopt(), assetup_clear_cpu_cap()cannot be used after alternatives are patched; callers checkrmpopt_wq!= NULL as the runtime guard instead.
- Addpr_info()when RMPOPT coverage is capped at 2TB.
- Add comments noting CPU hotplug is not supported with SNP enabled and only online primary threads are covered byrmpopt_cpumask.
- Add comment insetup_rmptable()noting Segmented RMP must be enabled to enable RMPOPT.
- Simplify cpumask setup loop to set if primary thread rather than skip if not primary.
- Improve grammar and clarity insnp_setup_rmpopt()comments.
- Added Reviewed-by's.Sashiko AI code review identified several of the above issues.
v4:
- Add newwrmsrq_on_cpus()helper to write same u64 value to a per-CPU MSR across a cpumask without per-cpu struct allocation overhead.
- Renameconfigure_and_enable_rmpopt()tosnp_setup_rmpopt().
- Usewrmsrq_on_cpus()instead ofwrmsrq_on_cpu()loop for programmingRMPOPT_BASEMSRs.
- Addsetup_clear_cpu_cap(X86_FEATURE_RMPOPT)if segmented RMP setup fails or workqueue allocation fails.
- AddX86_FEATURE_RMPOPTfeature clear logic inamd_cc_platform_clear()forCC_ATTR_HOST_SEV_SNP.
- All of the above allow checking for onlyX86_FEATURE_RMPOPTfor both RMPOPT setup/enable and RMP re-optimizations.
- Renamesnp_perform_rmp_optimization()tosnp_rmpopt_all_physmem().
- Splitrmpopt()intormpopt()andrmpopt_smp()for SMP callback use.
- Introduce separatermpopt_report_cpumaskfor debugfs reporting, distinct fromrmpopt_cpumaskused for primary thread tracking.
- Removesnp_perform_rmp_optimization()call from__sev_snp_init_locked()and instead setup and enable RMPOPT after SNP is enabled and initialized.v3:
- Drop all RMPOPT kthread support and introduce adding custom and dedicated workqueue to schedule delayed and asynchronous RMPOPT work.
- Drop theguest_memfdinode cleanup interface and add support to re-enable RMP optimizations during guest shutdown using the asynchronous and delayed workqueue interface.
- Introduce new__rmpopt()helper andrmpopt()andrmpopt_report_status()wrappers on top which use rax and rcx parameters to closely match RMPOPT specs.
- Use new optimized RMPOPT loop to issue RMPOPT instructions on all system RAM upto 2TB and all CPUs, by optimizing each range on one CPU first, then let other CPUs execute RMPOPT in parallel so they can skip most work as the range has already been optimized.
- Also add support for running the optimized RMPOPT loop only on one thread per core.
- Replace allPUD_SIZEreferences withSZ_1Gto conform to 1GB regions as specified by RMPOPT specifications and not be dependent onPUD_SIZEwhich makes the RMPOPT patch-set independent of x86 page table sizes.
- Usewrmsrq_on_cpu()to program theRMPOPT_BASEMSR registers on all CPUs that removes all ugly casting to useon_each_cpu_mask().
- Fix inline commits and patch commit messagesv2:
- Drop all NUMA and Socket configuration and enablement support and enable RMPOPT support for up to 2TB of system RAM.
- Dropget_cpumask_of_primary_threads()and enable per-core RMPOPT base MSRs and issue RMPOPT instruction on all CPUs.
- Drop the configfs interface to manually re-enable RMP optimizations.
- Add newguest_memfdcleanup interface to automatically re-enable RMP optimizations during guest shutdown.
- Include references to the public RMPOPT documentation.
- Move debugfs directory for RMPOPT under architecuture specific parent directory.Ashish Kalra (5):
x86/cpufeatures: AddX86_FEATURE_RMPOPTfeature flag x86/sev: Disable CPU hotplug while SNP is active x86/sev: Initialize RMPOPT configuration MSRs x86/sev: Perform RMP optimizations asynchronously x86/sev: Re-enable RMP optimizations on SNP guest shutdownarch/x86/include/asm/cpufeatures.h | 2 +- arch/x86/include/asm/msr-index.h | 3 + arch/x86/include/asm/sev.h | 2 + arch/x86/kernel/cpu/scattered.c | 1 + arch/x86/kvm/svm/sev.c | 2 + arch/x86/virt/svm/sev.c | 193 ++++++++++++++++++++++++++--- drivers/crypto/ccp/sev-dev.c | 2 + 7 files changed, 189 insertions(+), 16 deletions(-)