[tip: x86/sev] x86/sev: Initialize RMPOPT configuration MSRs

From: tip-bot2 for Ashish Kalra

Date: Fri Sep 18 2026 - 20:12:49 EST


The following commit has been merged into the x86/sev branch of tip:

Commit-ID: 15d974be0a3ba37091083028d16237ff49eb05b5
Gitweb: https://git.kernel.org/tip/15d974be0a3ba37091083028d16237ff49eb05b5
Author: Ashish Kalra <ashish.kalra@xxxxxxx>
AuthorDate: Wed, 16 Sep 2026 22:14:52
Committer: Borislav Petkov (AMD) <bp@xxxxxxxxx>
CommitterDate: Fri, 18 Sep 2026 15:51:34 -07:00

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 ]

Suggested-by: Thomas Lendacky <thomas.lendacky@xxxxxxx>
Suggested-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Signed-off-by: Ashish Kalra <ashish.kalra@xxxxxxx>
Signed-off-by: Borislav Petkov (AMD) <bp@xxxxxxxxx>
Reviewed-by: Tom Lendacky <thomas.lendacky@xxxxxxx>
Link: https://patch.msgid.link/7fdf0f5b1be4b561c884c5200d6606a947cbc09d.1789594774.git.ashish.kalra@xxxxxxx
---
arch/x86/include/asm/msr-index.h | 3 ++-
arch/x86/include/asm/sev.h | 2 +-
arch/x86/virt/svm/sev.c | 48 +++++++++++++++++++++++++++----
drivers/crypto/ccp/sev-dev.c | 2 +-
4 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3a8e51a..1635e2e 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -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

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 9e7a077..fa81aa0 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -662,6 +662,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages)
__snp_leak_pages(pfn, pages, true);
}
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

diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index 558f792..1fecd24 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -124,6 +124,8 @@ static void *rmp_bookkeeping __ro_after_init;

static u64 probed_rmp_base, probed_rmp_size;

+static phys_addr_t rmpopt_pa_start;
+
static LIST_HEAD(snp_leaked_pages_list);
static DEFINE_SPINLOCK(snp_leaked_pages_list_lock);

@@ -575,6 +577,34 @@ 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.
+ */
+ 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);
+}
+EXPORT_SYMBOL_FOR_MODULES(snp_enable_rmpopt, "ccp");
+
/*
* Do the necessary preparations which are verified by the firmware as
* described in the SNP_INIT_EX firmware command description in the SNP
@@ -699,13 +729,21 @@ static bool probe_segmented_rmptable_info(void)

bool snp_probe_rmptable_info(void)
{
- if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP))
+ if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP)) {
rdmsrq(MSR_AMD64_RMP_CFG, rmp_cfg);

- if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)
- return probe_segmented_rmptable_info();
- else
- return probe_contiguous_rmptable_info();
+ if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
+ if (probe_segmented_rmptable_info())
+ return true;
+
+ setup_clear_cpu_cap(X86_FEATURE_RMPOPT);
+ return false;
+ }
+ } else {
+ setup_clear_cpu_cap(X86_FEATURE_RMPOPT);
+ }
+
+ return probe_contiguous_rmptable_info();
}

/*
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index f833cb7..5c996ab 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -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;
}