* Re: [PATCH v2 2/7] x86/sev: add support for enabling RMPOPT
From: Dave Hansen @ 2026-03-02 22:33 UTC (permalink / raw)
To: Ashish Kalra, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
peterz, thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <85aec55af41957678d214e9629eb6249b064fa87.1772486459.git.ashish.kalra@amd.com>
Oh, and:
[PATCH v2 2/7] x86/sev: add support for enabling RMPOPT
^ Capitalize this, please
^ permalink raw reply
* Re: [PATCH v2 2/7] x86/sev: add support for enabling RMPOPT
From: Dave Hansen @ 2026-03-02 22:32 UTC (permalink / raw)
To: Ashish Kalra, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
peterz, thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <85aec55af41957678d214e9629eb6249b064fa87.1772486459.git.ashish.kalra@amd.com>
On 3/2/26 13:35, Ashish Kalra wrote:
> The new RMPOPT instruction sets bits in a per-CPU RMPOPT table, which
> indicates whether specific 1GB physical memory regions contain SEV-SNP
> guest memory.
Honestly, this is an implementation detail that we don't need to know
about in the kernel. It's also not even factually correct. The
instruction _might_ not set any bits, either because there is SEV-SNP
memory or because it's being run in query mode.
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.
The reset looks good.
> diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
> index da5275d8eda6..8e7da03abd5b 100644
> --- a/arch/x86/include/asm/msr-index.h
> +++ b/arch/x86/include/asm/msr-index.h
> @@ -753,6 +753,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/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
> index a4f3a364fb65..405199c2f563 100644
> --- a/arch/x86/virt/svm/sev.c
> +++ b/arch/x86/virt/svm/sev.c
> @@ -500,6 +500,41 @@ static bool __init setup_rmptable(void)
> }
> }
>
> +static void __configure_rmpopt(void *val)
> +{
> + u64 rmpopt_base = ((u64)val & PUD_MASK) | MSR_AMD64_RMPOPT_ENABLE;
> +
> + wrmsrq(MSR_AMD64_RMPOPT_BASE, rmpopt_base);
> +}
> +
> +static __init void configure_and_enable_rmpopt(void)
> +{
> + phys_addr_t pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), PUD_SIZE);
> +
> + if (!cpu_feature_enabled(X86_FEATURE_RMPOPT)) {
> + pr_debug("RMPOPT not supported on this platform\n");
> + return;
> + }
> +
> + if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) {
> + pr_debug("RMPOPT optimizations not enabled as SNP support is not enabled\n");
> + return;
> + }
To be honest, I think those two are just plain noise ^^.
> + if (!(rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)) {
> + pr_info("RMPOPT optimizations not enabled, segmented RMP required\n");
> + return;
> + }
> +
> + /*
> + * Per-CPU RMPOPT tables support at most 2 TB of addressable memory for RMP optimizations.
> + *
> + * Set per-core RMPOPT base to min_low_pfn to enable RMP optimization for
> + * up to 2TB of system RAM on all CPUs.
> + */
Please at least be consistent with your comments. This is both over 80
columns *and* not even consistent in the two sentences.
> + on_each_cpu_mask(cpu_online_mask, __configure_rmpopt, (void *)pa_start, true);
> +}
What's wrong with:
u64 rmpopt_base = pa_start | MSR_AMD64_RMPOPT_ENABLE;
...
for_each_online_cpu(cpu)
wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, rmpopt_base);
Then there's at least no ugly casting.
^ permalink raw reply
* [PATCH v2 7/7] x86/sev: Add debugfs support for RMPOPT
From: Ashish Kalra @ 2026-03-02 21:37 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
Add a debugfs interface to report per-CPU RMPOPT status across all
system RAM.
To dump the per-CPU RMPOPT status for all system RAM:
/sys/kernel/debug/rmpopt# cat rmpopt-table
Memory @ 0GB: CPU(s): none
Memory @ 1GB: CPU(s): none
Memory @ 2GB: CPU(s): 0-1023
Memory @ 3GB: CPU(s): 0-1023
Memory @ 4GB: CPU(s): none
Memory @ 5GB: CPU(s): 0-1023
Memory @ 6GB: CPU(s): 0-1023
Memory @ 7GB: CPU(s): 0-1023
...
Memory @1025GB: CPU(s): 0-1023
Memory @1026GB: CPU(s): 0-1023
Memory @1027GB: CPU(s): 0-1023
Memory @1028GB: CPU(s): 0-1023
Memory @1029GB: CPU(s): 0-1023
Memory @1030GB: CPU(s): 0-1023
Memory @1031GB: CPU(s): 0-1023
Memory @1032GB: CPU(s): 0-1023
Memory @1033GB: CPU(s): 0-1023
Memory @1034GB: CPU(s): 0-1023
Memory @1035GB: CPU(s): 0-1023
Memory @1036GB: CPU(s): 0-1023
Memory @1037GB: CPU(s): 0-1023
Memory @1038GB: CPU(s): none
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/virt/svm/sev.c | 101 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 100 insertions(+), 1 deletion(-)
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index 4dd5a525ad32..49bd7ba76169 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -20,6 +20,8 @@
#include <linux/amd-iommu.h>
#include <linux/nospec.h>
#include <linux/kthread.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <asm/sev.h>
#include <asm/processor.h>
@@ -135,6 +137,13 @@ static DEFINE_SPINLOCK(snp_leaked_pages_list_lock);
static unsigned long snp_nr_leaked_pages;
+static cpumask_t rmpopt_cpumask;
+static struct dentry *rmpopt_debugfs;
+
+struct seq_paddr {
+ phys_addr_t next_seq_paddr;
+};
+
#undef pr_fmt
#define pr_fmt(fmt) "SEV-SNP: " fmt
@@ -515,9 +524,14 @@ static bool __init setup_rmptable(void)
*/
static void rmpopt(void *val)
{
+ bool optimized;
+
asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"
- : : "a" ((u64)val & PUD_MASK), "c" ((u64)val & 0x1)
+ : "=@ccc" (optimized)
+ : "a" ((u64)val & PUD_MASK), "c" ((u64)val & 0x1)
: "memory", "cc");
+
+ assign_cpu(smp_processor_id(), &rmpopt_cpumask, optimized);
}
static int rmpopt_kthread(void *__unused)
@@ -563,6 +577,89 @@ static void rmpopt_all_physmem(void)
wake_up_process(rmpopt_task);
}
+/*
+ * start() can be called multiple times if allocated buffer has overflowed
+ * and bigger buffer is allocated.
+ */
+static void *rmpopt_table_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ phys_addr_t end_paddr = ALIGN(PFN_PHYS(max_pfn), PUD_SIZE);
+ struct seq_paddr *p = seq->private;
+
+ if (*pos == 0) {
+ p->next_seq_paddr = ALIGN_DOWN(PFN_PHYS(min_low_pfn), PUD_SIZE);
+ return &p->next_seq_paddr;
+ }
+
+ if (p->next_seq_paddr == end_paddr)
+ return NULL;
+
+ return &p->next_seq_paddr;
+}
+
+static void *rmpopt_table_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ phys_addr_t end_paddr = ALIGN(PFN_PHYS(max_pfn), PUD_SIZE);
+ phys_addr_t *curr_paddr = v;
+
+ (*pos)++;
+ if (*curr_paddr == end_paddr)
+ return NULL;
+ *curr_paddr += PUD_SIZE;
+
+ return curr_paddr;
+}
+
+static void rmpopt_table_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int rmpopt_table_seq_show(struct seq_file *seq, void *v)
+{
+ phys_addr_t *curr_paddr = v;
+
+ seq_printf(seq, "Memory @%3lluGB: ", *curr_paddr >> PUD_SHIFT);
+
+ cpumask_clear(&rmpopt_cpumask);
+ on_each_cpu_mask(cpu_online_mask, rmpopt,
+ (void *)(*curr_paddr | RMPOPT_FUNC_REPORT_STATUS),
+ true);
+
+ if (cpumask_empty(&rmpopt_cpumask))
+ seq_puts(seq, "CPU(s): none\n");
+ else
+ seq_printf(seq, "CPU(s): %*pbl\n", cpumask_pr_args(&rmpopt_cpumask));
+
+ return 0;
+}
+
+static const struct seq_operations rmpopt_table_seq_ops = {
+ .start = rmpopt_table_seq_start,
+ .next = rmpopt_table_seq_next,
+ .stop = rmpopt_table_seq_stop,
+ .show = rmpopt_table_seq_show
+};
+
+static int rmpopt_table_open(struct inode *inode, struct file *file)
+{
+ return seq_open_private(file, &rmpopt_table_seq_ops, sizeof(struct seq_paddr));
+}
+
+static const struct file_operations rmpopt_table_fops = {
+ .open = rmpopt_table_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
+static void rmpopt_debugfs_setup(void)
+{
+ rmpopt_debugfs = debugfs_create_dir("rmpopt", arch_debugfs_dir);
+
+ debugfs_create_file("rmpopt-table", 0444, rmpopt_debugfs,
+ NULL, &rmpopt_table_fops);
+}
+
static void __configure_rmpopt(void *val)
{
u64 rmpopt_base = ((u64)val & PUD_MASK) | MSR_AMD64_RMPOPT_ENABLE;
@@ -611,6 +708,8 @@ static __init void configure_and_enable_rmpopt(void)
* optimizations on all physical memory.
*/
rmpopt_all_physmem();
+
+ rmpopt_debugfs_setup();
}
/*
--
2.43.0
^ permalink raw reply related
* [PATCH v2 6/7] KVM: SEV: Implement SEV-SNP specific guest cleanup
From: Ashish Kalra @ 2026-03-02 21:37 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
Implement the arch-specific cleanup for SEV-SNP via the
kvm_gmem_cleanup() hook. Use this interface to re-enable RMP
optimizations during guest shutdown.
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/kvm/Kconfig | 1 +
arch/x86/kvm/svm/sev.c | 9 +++++++++
arch/x86/kvm/svm/svm.c | 1 +
arch/x86/kvm/svm/svm.h | 2 ++
4 files changed, 13 insertions(+)
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index d916bd766c94..fdfdb7ac6a45 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -164,6 +164,7 @@ config KVM_AMD_SEV
select HAVE_KVM_ARCH_GMEM_PREPARE
select HAVE_KVM_ARCH_GMEM_INVALIDATE
select HAVE_KVM_ARCH_GMEM_POPULATE
+ select HAVE_KVM_ARCH_GMEM_CLEANUP
help
Provides support for launching encrypted VMs which use Secure
Encrypted Virtualization (SEV), Secure Encrypted Virtualization with
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 3f9c1aa39a0a..4c206e9f70cd 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5109,6 +5109,15 @@ int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private)
return level;
}
+void sev_gmem_cleanup(void)
+{
+ /*
+ * Re-enable RMP optimizations once all guest pages are
+ * converted back to shared following guest shutdown.
+ */
+ snp_perform_rmp_optimization();
+}
+
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 8f8bc863e214..46526ab9ab92 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5260,6 +5260,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.gmem_prepare = sev_gmem_prepare,
.gmem_invalidate = sev_gmem_invalidate,
.gmem_max_mapping_level = sev_gmem_max_mapping_level,
+ .gmem_cleanup = sev_gmem_cleanup,
};
/*
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index ebd7b36b1ceb..443c29c23a6a 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -896,6 +896,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
+void sev_gmem_cleanup(void);
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);
#else
@@ -928,6 +929,7 @@ static inline int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, boo
{
return 0;
}
+static inline void sev_gmem_cleanup(void) {}
static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu)
{
--
2.43.0
^ permalink raw reply related
* [PATCH v2 5/7] KVM: guest_memfd: Add cleanup interface for guest teardown
From: Ashish Kalra @ 2026-03-02 21:36 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
Introduce kvm_arch_gmem_cleanup() to perform architecture-specific
cleanups when the last file descriptor for the guest_memfd inode is
closed. This typically occurs during guest shutdown and termination
and allows for final resource release.
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/x86.c | 7 +++++++
include/linux/kvm_host.h | 4 ++++
virt/kvm/Kconfig | 4 ++++
virt/kvm/guest_memfd.c | 8 ++++++++
6 files changed, 25 insertions(+)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index de709fb5bd76..ebbecd0c9e4f 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -148,6 +148,7 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
KVM_X86_OP_OPTIONAL_RET0(gmem_prepare)
KVM_X86_OP_OPTIONAL_RET0(gmem_max_mapping_level)
KVM_X86_OP_OPTIONAL(gmem_invalidate)
+KVM_X86_OP_OPTIONAL(gmem_cleanup)
#undef KVM_X86_OP
#undef KVM_X86_OP_OPTIONAL
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index ff07c45e3c73..7894cf791fef 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1962,6 +1962,7 @@ struct kvm_x86_ops {
int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void (*gmem_invalidate)(kvm_pfn_t start, kvm_pfn_t end);
int (*gmem_max_mapping_level)(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
+ void (*gmem_cleanup)(void);
};
struct kvm_x86_nested_ops {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3fb64905d190..d992848942c3 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -14080,6 +14080,13 @@ void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
kvm_x86_call(gmem_invalidate)(start, end);
}
#endif
+
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CLEANUP
+void kvm_arch_gmem_cleanup(void)
+{
+ kvm_x86_call(gmem_cleanup)();
+}
+#endif
#endif
int kvm_spec_ctrl_test_value(u64 value)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index dde605cb894e..b14143c427eb 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2607,6 +2607,10 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages
void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
#endif
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CLEANUP
+void kvm_arch_gmem_cleanup(void);
+#endif
+
#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
struct kvm_pre_fault_memory *range);
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 267c7369c765..9072ec12d5e7 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -125,3 +125,7 @@ config HAVE_KVM_ARCH_GMEM_INVALIDATE
config HAVE_KVM_ARCH_GMEM_POPULATE
bool
depends on KVM_GUEST_MEMFD
+
+config HAVE_KVM_ARCH_GMEM_CLEANUP
+ bool
+ depends on KVM_GUEST_MEMFD
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 017d84a7adf3..2724dd1099f2 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -955,6 +955,14 @@ static void kvm_gmem_destroy_inode(struct inode *inode)
static void kvm_gmem_free_inode(struct inode *inode)
{
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CLEANUP
+ /*
+ * Finalize cleanup for the inode once the last guest_memfd
+ * reference is released. This usually occurs after guest
+ * termination.
+ */
+ kvm_arch_gmem_cleanup();
+#endif
kmem_cache_free(kvm_gmem_inode_cachep, GMEM_I(inode));
}
--
2.43.0
^ permalink raw reply related
* [PATCH v2 4/7] x86/sev: Add interface to re-enable RMP optimizations.
From: Ashish Kalra @ 2026-03-02 21:36 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
RMPOPT table is a per-processor table which indicates if 1GB regions of
physical memory are entirely hypervisor-owned or not.
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.
Events such as RMPUPDATE or SNP_INIT can clear RMP optimizations. Add
an interface to re-enable those optimizations.
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/sev.h | 2 ++
arch/x86/virt/svm/sev.c | 17 +++++++++++++++++
drivers/crypto/ccp/sev-dev.c | 4 ++++
3 files changed, 23 insertions(+)
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 0e6c0940100f..451fb2b2a0f7 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -657,6 +657,7 @@ int rmp_make_shared(u64 pfn, enum pg_level level);
void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp);
void kdump_sev_callback(void);
void snp_fixup_e820_tables(void);
+int snp_perform_rmp_optimization(void);
static inline void snp_leak_pages(u64 pfn, unsigned int pages)
{
__snp_leak_pages(pfn, pages, true);
@@ -677,6 +678,7 @@ static inline void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp)
static inline void snp_leak_pages(u64 pfn, unsigned int npages) {}
static inline void kdump_sev_callback(void) { }
static inline void snp_fixup_e820_tables(void) {}
+static inline int snp_perform_rmp_optimization(void) { return 0; }
#endif
#endif
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index c99270dfe3b3..4dd5a525ad32 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -1144,6 +1144,23 @@ int rmp_make_shared(u64 pfn, enum pg_level level)
}
EXPORT_SYMBOL_GPL(rmp_make_shared);
+int snp_perform_rmp_optimization(void)
+{
+ if (!cpu_feature_enabled(X86_FEATURE_RMPOPT))
+ return -EINVAL;
+
+ if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
+ return -EINVAL;
+
+ if (!(rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED))
+ return -EINVAL;
+
+ rmpopt_all_physmem();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snp_perform_rmp_optimization);
+
void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp)
{
struct page *page = pfn_to_page(pfn);
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index 096f993974d1..d84178a232e0 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -1478,6 +1478,10 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
}
snp_hv_fixed_pages_state_update(sev, HV_FIXED);
+
+ /* SNP_INIT clears the RMPOPT table, re-enable RMP optimizations */
+ snp_perform_rmp_optimization();
+
sev->snp_initialized = true;
dev_dbg(sev->dev, "SEV-SNP firmware initialized, SEV-TIO is %s\n",
data.tio_en ? "enabled" : "disabled");
--
2.43.0
^ permalink raw reply related
* [PATCH v2 3/7] x86/sev: add support for RMPOPT instruction
From: Ashish Kalra @ 2026-03-02 21:36 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
As SEV-SNP is enabled by default on boot when an RMP table is
allocated by BIOS, the hypervisor and non-SNP guests are subject to
RMP write checks to provide integrity of SNP guest memory.
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 SEV-SNP guest memory.
Enable RMPOPT optimizations globally for all system RAM at RMP
initialization time. RMP checks can initially be skipped for 1GB memory
ranges that do not contain SEV-SNP guest memory (excluding preassigned
pages such as the RMP table and firmware pages). As SNP guests are
launched, RMPUPDATE will disable the corresponding RMPOPT optimizations.
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/virt/svm/sev.c | 78 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index 405199c2f563..c99270dfe3b3 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -19,6 +19,7 @@
#include <linux/iommu.h>
#include <linux/amd-iommu.h>
#include <linux/nospec.h>
+#include <linux/kthread.h>
#include <asm/sev.h>
#include <asm/processor.h>
@@ -122,6 +123,13 @@ static u64 rmp_cfg;
static u64 probed_rmp_base, probed_rmp_size;
+enum rmpopt_function {
+ RMPOPT_FUNC_VERIFY_AND_REPORT_STATUS,
+ RMPOPT_FUNC_REPORT_STATUS
+};
+
+static struct task_struct *rmpopt_task;
+
static LIST_HEAD(snp_leaked_pages_list);
static DEFINE_SPINLOCK(snp_leaked_pages_list_lock);
@@ -500,6 +508,61 @@ static bool __init setup_rmptable(void)
}
}
+/*
+ * 'val' is a system physical address aligned to 1GB OR'ed with
+ * a function selection. Currently supported functions are 0
+ * (verify and report status) and 1 (report status).
+ */
+static void rmpopt(void *val)
+{
+ asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"
+ : : "a" ((u64)val & PUD_MASK), "c" ((u64)val & 0x1)
+ : "memory", "cc");
+}
+
+static int rmpopt_kthread(void *__unused)
+{
+ phys_addr_t pa_start, pa_end;
+
+ pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), PUD_SIZE);
+ pa_end = ALIGN(PFN_PHYS(max_pfn), PUD_SIZE);
+
+ /* Limit memory scanning to the first 2 TB of RAM */
+ pa_end = (pa_end - pa_start) <= SZ_2T ? pa_end : pa_start + SZ_2T;
+
+ while (!kthread_should_stop()) {
+ phys_addr_t pa;
+
+ pr_info("RMP optimizations enabled on physical address range @1GB alignment [0x%016llx - 0x%016llx]\n",
+ pa_start, pa_end);
+
+ /*
+ * RMPOPT optimizations skip RMP checks at 1GB granularity if this range of
+ * memory does not contain any SNP guest memory.
+ */
+ for (pa = pa_start; pa < pa_end; pa += PUD_SIZE) {
+ /* Bit zero passes the function to the RMPOPT instruction. */
+ on_each_cpu_mask(cpu_online_mask, rmpopt,
+ (void *)(pa | RMPOPT_FUNC_VERIFY_AND_REPORT_STATUS),
+ true);
+
+ /* Give a chance for other threads to run */
+ cond_resched();
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+
+ return 0;
+}
+
+static void rmpopt_all_physmem(void)
+{
+ if (rmpopt_task)
+ wake_up_process(rmpopt_task);
+}
+
static void __configure_rmpopt(void *val)
{
u64 rmpopt_base = ((u64)val & PUD_MASK) | MSR_AMD64_RMPOPT_ENABLE;
@@ -533,6 +596,21 @@ static __init void configure_and_enable_rmpopt(void)
* up to 2TB of system RAM on all CPUs.
*/
on_each_cpu_mask(cpu_online_mask, __configure_rmpopt, (void *)pa_start, true);
+
+ rmpopt_task = kthread_create(rmpopt_kthread, NULL, "rmpopt_kthread");
+ if (IS_ERR(rmpopt_task)) {
+ pr_warn("Unable to start RMPOPT kernel thread\n");
+ rmpopt_task = NULL;
+ return;
+ }
+
+ pr_info("RMPOPT worker thread created with PID %d\n", task_pid_nr(rmpopt_task));
+
+ /*
+ * Once all per-CPU RMPOPT tables have been configured, enable RMPOPT
+ * optimizations on all physical memory.
+ */
+ rmpopt_all_physmem();
}
/*
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/7] x86/sev: add support for enabling RMPOPT
From: Ashish Kalra @ 2026-03-02 21:35 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
The new RMPOPT instruction sets bits in a per-CPU RMPOPT table, which
indicates whether specific 1GB physical memory regions contain SEV-SNP
guest memory.
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.
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/msr-index.h | 3 +++
arch/x86/virt/svm/sev.c | 37 ++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index da5275d8eda6..8e7da03abd5b 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -753,6 +753,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/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index a4f3a364fb65..405199c2f563 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -500,6 +500,41 @@ static bool __init setup_rmptable(void)
}
}
+static void __configure_rmpopt(void *val)
+{
+ u64 rmpopt_base = ((u64)val & PUD_MASK) | MSR_AMD64_RMPOPT_ENABLE;
+
+ wrmsrq(MSR_AMD64_RMPOPT_BASE, rmpopt_base);
+}
+
+static __init void configure_and_enable_rmpopt(void)
+{
+ phys_addr_t pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), PUD_SIZE);
+
+ if (!cpu_feature_enabled(X86_FEATURE_RMPOPT)) {
+ pr_debug("RMPOPT not supported on this platform\n");
+ return;
+ }
+
+ if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) {
+ pr_debug("RMPOPT optimizations not enabled as SNP support is not enabled\n");
+ return;
+ }
+
+ if (!(rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)) {
+ pr_info("RMPOPT optimizations not enabled, segmented RMP required\n");
+ return;
+ }
+
+ /*
+ * Per-CPU RMPOPT tables support at most 2 TB of addressable memory for RMP optimizations.
+ *
+ * Set per-core RMPOPT base to min_low_pfn to enable RMP optimization for
+ * up to 2TB of system RAM on all CPUs.
+ */
+ on_each_cpu_mask(cpu_online_mask, __configure_rmpopt, (void *)pa_start, true);
+}
+
/*
* Do the necessary preparations which are verified by the firmware as
* described in the SNP_INIT_EX firmware command description in the SNP
@@ -555,6 +590,8 @@ int __init snp_rmptable_init(void)
skip_enable:
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL);
+ configure_and_enable_rmpopt();
+
/*
* Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic
* notifier is invoked to do SNP IOMMU shutdown before kdump.
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/7] x86/cpufeatures: Add X86_FEATURE_AMD_RMPOPT feature flag
From: Ashish Kalra @ 2026-03-02 21:35 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <cover.1772486459.git.ashish.kalra@amd.com>
From: Ashish Kalra <ashish.kalra@amd.com>
Add a flag indicating whether RMPOPT instruction is supported.
RMPOPT is a new instruction designed to minimize the performance
overhead of RMP checks on the hypervisor and on non-SNP guests by
allowing RMP checks to be skipped when 1G regions of memory are known
not to contain any SEV-SNP guest memory.
For more information on the RMPOPT instruction, see the AMD64 RMPOPT
technical documentation. [1]
Link: https://docs.amd.com/v/u/en-US/69201_1.00_AMD64_RMPOPT_PUB [1]
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/cpufeatures.h | 2 +-
arch/x86/kernel/cpu/scattered.c | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index dbe104df339b..bce1b2e2a35c 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -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 */
#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) */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 42c7eac0c387..7ac3818c4502 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -65,6 +65,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 }
};
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/7] Add RMPOPT support.
From: Ashish Kalra @ 2026-03-02 21:35 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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. [1]
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 introduces a new guest_memfd cleanup interface for
guest teardown, in case of SEV-SNP this interface is used to re-enable
RMP optimizations during guest shutdown and/or termination.
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.
Additionally add debugfs interface to report per-CPU RMPOPT status
across all system RAM.
[1] https://docs.amd.com/v/u/en-US/69201_1.00_AMD64_RMPOPT_PUB
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 (7):
x86/cpufeatures: Add X86_FEATURE_AMD_RMPOPT feature flag
x86/sev: add support for enabling RMPOPT
x86/sev: add support for RMPOPT instruction
x86/sev: Add interface to re-enable RMP optimizations.
KVM: guest_memfd: Add cleanup interface for guest teardown
KVM: SEV: Implement SEV-SNP specific guest cleanup
x86/sev: Add debugfs support for RMPOPT
arch/x86/include/asm/cpufeatures.h | 2 +-
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 1 +
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/Kconfig | 1 +
arch/x86/kvm/svm/sev.c | 9 ++
arch/x86/kvm/svm/svm.c | 1 +
arch/x86/kvm/svm/svm.h | 2 +
arch/x86/kvm/x86.c | 7 +
arch/x86/virt/svm/sev.c | 231 +++++++++++++++++++++++++++++
drivers/crypto/ccp/sev-dev.c | 4 +
include/linux/kvm_host.h | 4 +
virt/kvm/Kconfig | 4 +
virt/kvm/guest_memfd.c | 8 +
16 files changed, 280 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Suzuki K Poulose @ 2026-03-02 17:13 UTC (permalink / raw)
To: Steven Price, Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <33053e22-6cc6-4d55-bc7f-01f873a15d28@arm.com>
On 02/03/2026 15:23, Steven Price wrote:
> Hi Marc,
>
> On 02/03/2026 14:25, Marc Zyngier wrote:
>> On Wed, 17 Dec 2025 10:10:43 +0000,
>> Steven Price <steven.price@arm.com> wrote:
>>>
>>> There is one CAP which identified the presence of CCA, and two ioctls.
>>> One ioctl is used to populate memory and the other is used when user
>>> space is providing the PSCI implementation to identify the target of the
>>> operation.
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> ---
>>> Changes since v11:
>>> * Completely reworked to be more implicit. Rather than having explicit
>>> CAP operations to progress the realm construction these operations
>>> are done when needed (on populating and on first vCPU run).
>>> * Populate and PSCI complete are promoted to proper ioctls.
>>> Changes since v10:
>>> * Rename symbols from RME to RMI.
>>> Changes since v9:
>>> * Improvements to documentation.
>>> * Bump the magic number for KVM_CAP_ARM_RME to avoid conflicts.
>>> Changes since v8:
>>> * Minor improvements to documentation following review.
>>> * Bump the magic numbers to avoid conflicts.
>>> Changes since v7:
>>> * Add documentation of new ioctls
>>> * Bump the magic numbers to avoid conflicts
>>> Changes since v6:
>>> * Rename some of the symbols to make their usage clearer and avoid
>>> repetition.
>>> Changes from v5:
>>> * Actually expose the new VCPU capability (KVM_ARM_VCPU_REC) by bumping
>>> KVM_VCPU_MAX_FEATURES - note this also exposes KVM_ARM_VCPU_HAS_EL2!
>>> ---
>>> Documentation/virt/kvm/api.rst | 57 ++++++++++++++++++++++++++++++++++
>>> include/uapi/linux/kvm.h | 23 ++++++++++++++
>>> 2 files changed, 80 insertions(+)
>>>
>>> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
>>> index 01a3abef8abb..2d5dc7e48954 100644
>>> --- a/Documentation/virt/kvm/api.rst
>>> +++ b/Documentation/virt/kvm/api.rst
>>> @@ -6517,6 +6517,54 @@ the capability to be present.
>>>
>>> `flags` must currently be zero.
>>>
>>> +4.144 KVM_ARM_VCPU_RMI_PSCI_COMPLETE
>>> +------------------------------------
>>> +
>>> +:Capability: KVM_CAP_ARM_RMI
>>> +:Architectures: arm64
>>> +:Type: vcpu ioctl
>>> +:Parameters: struct kvm_arm_rmi_psci_complete (in)
>>> +:Returns: 0 if successful, < 0 on error
>>> +
>>> +::
>>> +
>>> + struct kvm_arm_rmi_psci_complete {
>>> + __u64 target_mpidr;
>>> + __u32 psci_status;
>>> + __u32 padding[3];
>>> + };
>>> +
>>> +Where PSCI functions are handled by user space, the RMM needs to be informed of
>>> +the target of the operation using `target_mpidr`, along with the status
>>> +(`psci_status`). The RMM v1.0 specification defines two functions that require
>>> +this call: PSCI_CPU_ON and PSCI_AFFINITY_INFO.
>>> +
>>> +If the kernel is handling PSCI then this is done automatically and the VMM
>>> +doesn't need to call this ioctl.
>>
>> Shouldn't we make handling of PSCI mandatory for VMMs that deal with
>> CCA? I suspect it would simplify the implementation significantly.
>
> What do you mean by making it "mandatory for VMMs"? If you mean PSCI is
> always forwarded to user space then I don't think it's going to make
> much difference. Patch 27 handles the PSCI changes (72 lines added), and
> some of that is adding this uAPI for the VMM to handle it.
>
> Removing the functionality to allow the VMM to handle it would obviously
> simplify things a bit (we can drop this uAPI), but I think the desire is
> to push this onto user space.
>
>> What vcpu fd does this apply to? The vcpu calling the PSCI function?
>> Or the target? This is pretty important for PSCI_ON. My guess is that
>> this is setting the return value for the caller?
>
> Yes the fd is the vcpu calling PSCI. As you say, this is for the return
> value to be set correctly.
>
>> Assuming this is indeed for the caller, why do we have a different
>> flow from anything else that returns a result from a hypercall?
>
> I'm not entirely sure what you are suggesting. Do you mean why are we
> not just writing to the GPRS that would contain the result? The issue
> here is that the RMM needs to know the PA of the target REC structure -
> this isn't a return to the guest, but information for the RMM itself to
> complete the PSCI call.
>
> Ultimately even in the case where the VMM is handling PSCI, it's
> actually a combination of the VMM and the RMM - with the RMM validating
> the responses.
>
More importantly, we have to make sure that the "RMI_PSCI_COMPLETE" is
invoked before both of the following:
1. The "source" vCPU is run again
2. More importantly the "target" vCPU is run.
The latter part makes it difficult to issue this RMI_PSCI_COMPLETE on
the way back from servicing the SMCCC call.
Suzuki
>>> +
>>> +4.145 KVM_ARM_RMI_POPULATE
>>> +--------------------------
>>> +
>>> +:Capability: KVM_CAP_ARM_RMI
>>> +:Architectures: arm64
>>> +:Type: vm ioctl
>>> +:Parameters: struct kvm_arm_rmi_populate (in)
>>> +:Returns: number of bytes populated, < 0 on error
>>> +
>>> +::
>>> +
>>> + struct kvm_arm_rmi_populate {
>>> + __u64 base;
>>> + __u64 size;
>>> + __u64 source_uaddr;
>>> + __u32 flags;
>>> + __u32 reserved;
>>> + };
>>> +
>>> +Populate a region of protected address space by copying the data from the user
>>> +space pointer provided. This is only valid before any VCPUs have been run.
>>> +The ioctl might not populate the entire region and user space may have to
>>> +repeatedly call it (with updated pointers) to populate the entire region.
>>
>> size as a __u64 is odd, as the return value from the ioctl is a signed
>> int. This implies that you can't really report how many bytes you have
>> copied. Some form of consistency wouldn't hurt.
>
> Good spot. In practice this works because >2GB in one operation is
> highly unlikely to be processed in one go. But I guess I'll change this
> to have an output size argument. I guess I could make the kernel update
> all of base,size,source_uaddr which would simplify user space.
>
> Thanks,
> Steve
>
^ permalink raw reply
* Re: [PATCH v12 20/46] arm64: RMI: Allow populating initial contents
From: Steven Price @ 2026-03-02 16:46 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <86qzq28elj.wl-maz@kernel.org>
On 02/03/2026 14:56, Marc Zyngier wrote:
> On Wed, 17 Dec 2025 10:10:57 +0000,
> Steven Price <steven.price@arm.com> wrote:
>>
>> The VMM needs to populate the realm with some data before starting (e.g.
>> a kernel and initrd). This is measured by the RMM and used as part of
>> the attestation later on.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v11:
>> * The multiplex CAP is gone and there's a new ioctl which makes use of
>> the generic kvm_gmem_populate() functionality.
>> Changes since v7:
>> * Improve the error codes.
>> * Other minor changes from review.
>> Changes since v6:
>> * Handle host potentially having a larger page size than the RMM
>> granule.
>> * Drop historic "par" (protected address range) from
>> populate_par_region() - it doesn't exist within the current
>> architecture.
>> * Add a cond_resched() call in kvm_populate_realm().
>> Changes since v5:
>> * Refactor to use PFNs rather than tracking struct page in
>> realm_create_protected_data_page().
>> * Pull changes from a later patch (in the v5 series) for accessing
>> pages from a guest memfd.
>> * Do the populate in chunks to avoid holding locks for too long and
>> triggering RCU stall warnings.
>> ---
>> arch/arm64/include/asm/kvm_rmi.h | 4 +
>> arch/arm64/kvm/Kconfig | 1 +
>> arch/arm64/kvm/arm.c | 9 ++
>> arch/arm64/kvm/rmi.c | 175 +++++++++++++++++++++++++++++++
>> 4 files changed, 189 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> index 8a862fc1a99d..b5e36344975c 100644
>> --- a/arch/arm64/include/asm/kvm_rmi.h
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -99,6 +99,10 @@ int kvm_rec_enter(struct kvm_vcpu *vcpu);
>> int kvm_rec_pre_enter(struct kvm_vcpu *vcpu);
>> int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_status);
>>
>> +struct kvm_arm_rmi_populate;
>> +
>> +int kvm_arm_rmi_populate(struct kvm *kvm,
>> + struct kvm_arm_rmi_populate *arg);
>> void kvm_realm_unmap_range(struct kvm *kvm,
>> unsigned long ipa,
>> unsigned long size,
>> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
>> index 1cac6dfc0972..b495dfd3a8b4 100644
>> --- a/arch/arm64/kvm/Kconfig
>> +++ b/arch/arm64/kvm/Kconfig
>> @@ -39,6 +39,7 @@ menuconfig KVM
>> select GUEST_PERF_EVENTS if PERF_EVENTS
>> select KVM_GUEST_MEMFD
>> select KVM_GENERIC_MEMORY_ATTRIBUTES
>> + select HAVE_KVM_ARCH_GMEM_POPULATE
>> help
>> Support hosting virtualized guest machines.
>>
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 7927181887cf..0a06ed9d1a64 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -2037,6 +2037,15 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
>> return -EFAULT;
>> return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range);
>> }
>> + case KVM_ARM_RMI_POPULATE: {
>> + struct kvm_arm_rmi_populate req;
>> +
>> + if (!kvm_is_realm(kvm))
>> + return -EPERM;
>
> EPERM is odd. It isn't that the VMM doesn't have the right to do it,
> it is that it shouldn't have called that, because the ioctl doesn't
> exist for a normal VM. -ENOSYS?
Ack
>> + if (copy_from_user(&req, argp, sizeof(req)))
>> + return -EFAULT;
>> + return kvm_arm_rmi_populate(kvm, &req);
>> + }
>> default:
>> return -EINVAL;
>> }
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index fe15b400091c..39577e956a59 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -558,6 +558,150 @@ void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,
>> realm_unmap_private_range(kvm, start, end, may_block);
>> }
>>
>> +static int realm_create_protected_data_granule(struct realm *realm,
>> + unsigned long ipa,
>> + phys_addr_t dst_phys,
>> + phys_addr_t src_phys,
>> + unsigned long flags)
>> +{
>> + phys_addr_t rd = virt_to_phys(realm->rd);
>> + int ret;
>> +
>> + if (rmi_granule_delegate(dst_phys))
>> + return -ENXIO;
>> +
>> + ret = rmi_data_create(rd, dst_phys, ipa, src_phys, flags);
>> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
>> + /* Create missing RTTs and retry */
>> + int level = RMI_RETURN_INDEX(ret);
>> +
>> + WARN_ON(level == RMM_RTT_MAX_LEVEL);
>
> If this is unexpected, why do we still try to handle it? We should
> abort hard on anything that doesn't seem 100% correct, and mark the
> realm dead.
Well this is a "should never happen - the RMM (or Linux kerne) is buggy"
situation - so it's not specifically the realm's fault. The "do nothing"
error handling deals with things quite reasonably - the following
realm_create_rtt_levels() call is a no-op, so we'll retry the
rmi_data_create() call and bubble the error up.
I'll change this to KVM_BUG_ON so that the guest is killed just in case
it turns out the guest can somehow trigger this maliciously.
>> +
>> + ret = realm_create_rtt_levels(realm, ipa, level,
>> + RMM_RTT_MAX_LEVEL, NULL);
>> + if (ret)
>> + return -EIO;
>> +
>> + ret = rmi_data_create(rd, dst_phys, ipa, src_phys, flags);
>> + }
>> + if (ret)
>> + return -EIO;
>> +
>> + return 0;
>> +}
>> +
>> +static int realm_create_protected_data_page(struct realm *realm,
>> + unsigned long ipa,
>> + kvm_pfn_t dst_pfn,
>> + kvm_pfn_t src_pfn,
>> + unsigned long flags)
>> +{
>> + unsigned long rd = virt_to_phys(realm->rd);
>> + phys_addr_t dst_phys, src_phys;
>> + bool undelegate_failed = false;
>> + int ret, offset;
>> +
>> + dst_phys = __pfn_to_phys(dst_pfn);
>> + src_phys = __pfn_to_phys(src_pfn);
>> +
>> + for (offset = 0; offset < PAGE_SIZE; offset += RMM_PAGE_SIZE) {
>> + ret = realm_create_protected_data_granule(realm,
>> + ipa,
>> + dst_phys,
>> + src_phys,
>> + flags);
>> + if (ret)
>> + goto err;
>> +
>> + ipa += RMM_PAGE_SIZE;
>> + dst_phys += RMM_PAGE_SIZE;
>> + src_phys += RMM_PAGE_SIZE;
>> + }
>> +
>> + return 0;
>> +
>> +err:
>> + if (ret == -EIO) {
>> + /* current offset needs undelegating */
>> + if (WARN_ON(rmi_granule_undelegate(dst_phys)))
>> + undelegate_failed = true;
>> + }
>> + while (offset > 0) {
>> + ipa -= RMM_PAGE_SIZE;
>> + offset -= RMM_PAGE_SIZE;
>> + dst_phys -= RMM_PAGE_SIZE;
>> +
>> + rmi_data_destroy(rd, ipa, NULL, NULL);
>> +
>> + if (WARN_ON(rmi_granule_undelegate(dst_phys)))
>> + undelegate_failed = true;
>> + }
>> +
>> + if (undelegate_failed) {
>> + /*
>> + * A granule could not be undelegated,
>> + * so the page has to be leaked
>> + */
>> + get_page(pfn_to_page(dst_pfn));
>> + }
>> +
>> + return -ENXIO;
>> +}
>> +
>> +static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
>> + void __user *src, int order, void *opaque)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + unsigned long data_flags = *(unsigned long *)opaque;
>> + phys_addr_t ipa = gfn_to_gpa(gfn);
>> + int npages = (1 << order);
>> + int i;
>> +
>> + for (i = 0; i < npages; i++) {
>> + struct page *src_page;
>> + int ret;
>> +
>> + ret = get_user_pages((unsigned long)src, 1, 0, &src_page);
>> + if (ret < 0)
>> + return ret;
>> + if (ret != 1)
>> + return -ENOMEM;
>> +
>> + ret = realm_create_protected_data_page(realm, ipa, pfn,
>> + page_to_pfn(src_page),
>> + data_flags);
>> +
>> + put_page(src_page);
>> +
>> + if (ret)
>> + return ret;
>> +
>> + ipa += PAGE_SIZE;
>> + pfn++;
>> + src += PAGE_SIZE;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static long populate_region(struct kvm *kvm,
>> + gfn_t base_gfn,
>> + unsigned long pages,
>> + u64 uaddr,
>> + unsigned long data_flags)
>> +{
>> + long ret = 0;
>> +
>> + mutex_lock(&kvm->slots_lock);
>> + mmap_read_lock(current->mm);
>> + ret = kvm_gmem_populate(kvm, base_gfn, u64_to_user_ptr(uaddr), pages,
>> + populate_region_cb, &data_flags);
>> + mmap_read_unlock(current->mm);
>> + mutex_unlock(&kvm->slots_lock);
>> +
>> + return ret;
>> +}
>> +
>> enum ripas_action {
>> RIPAS_INIT,
>> RIPAS_SET,
>> @@ -655,6 +799,37 @@ static int realm_ensure_created(struct kvm *kvm)
>> return -ENXIO;
>> }
>>
>> +int kvm_arm_rmi_populate(struct kvm *kvm,
>> + struct kvm_arm_rmi_populate *args)
>> +{
>> + unsigned long data_flags = 0;
>> + unsigned long ipa_start = args->base;
>> + unsigned long ipa_end = ipa_start + args->size;
>> + int ret;
>> +
>> + if (args->reserved ||
>> + (args->flags & ~KVM_ARM_RMI_POPULATE_FLAGS_MEASURE) ||
>> + !IS_ALIGNED(ipa_start, PAGE_SIZE) ||
>> + !IS_ALIGNED(ipa_end, PAGE_SIZE))
>> + return -EINVAL;
>> +
>> + ret = realm_ensure_created(kvm);
>> + if (ret)
>> + return ret;
>> +
>> + if (args->flags & KVM_ARM_RMI_POPULATE_FLAGS_MEASURE)
>
> This flag isn't documented.
Indeed - that's an oversight! I'll add the following to the docs:
`flags` can be set to `KVM_ARM_RMI_POPULATE_FLAGS_MEASURE` to request that the
populated data is hashed and added to the guest's Realm Initial Measurement
(RIM).
>> + data_flags |= RMI_MEASURE_CONTENT;
>> +
>> + ret = populate_region(kvm, gpa_to_gfn(ipa_start),
>> + args->size >> PAGE_SHIFT,
>> + args->source_uaddr, args->flags);
>> +
>> + if (ret < 0)
>> + return ret;
>> +
>> + return ret * PAGE_SIZE;
>
> Bits of the code works on PAGE_SIZE, other bits on RMM_PAGE_SIZE. It
> is pretty confusing. Are you in the middle of reworking this?
Yes, sorry about that - RMM_PAGE_SIZE will be completely gone when
this is updated to RMM v2.0.
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH v12 27/46] KVM: arm64: Handle Realm PSCI requests
From: Marc Zyngier @ 2026-03-02 16:39 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20251217101125.91098-28-steven.price@arm.com>
On Wed, 17 Dec 2025 10:11:04 +0000,
Steven Price <steven.price@arm.com> wrote:
>
> The RMM needs to be informed of the target REC when a PSCI call is made
> with an MPIDR argument. Expose an ioctl to the userspace in case the PSCI
> is handled by it.
>
> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
> ---
> Changes since v11:
> * RMM->RMI renaming.
> Changes since v6:
> * Use vcpu_is_rec() rather than kvm_is_realm(vcpu->kvm).
> * Minor renaming/formatting fixes.
> ---
> arch/arm64/include/asm/kvm_rmi.h | 3 +++
> arch/arm64/kvm/arm.c | 25 +++++++++++++++++++++++++
> arch/arm64/kvm/psci.c | 30 ++++++++++++++++++++++++++++++
> arch/arm64/kvm/rmi.c | 14 ++++++++++++++
> 4 files changed, 72 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index bfe6428eaf16..77da297ca09d 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -118,6 +118,9 @@ int realm_map_non_secure(struct realm *realm,
> kvm_pfn_t pfn,
> unsigned long size,
> struct kvm_mmu_memory_cache *memcache);
> +int realm_psci_complete(struct kvm_vcpu *source,
> + struct kvm_vcpu *target,
> + unsigned long status);
>
> static inline bool kvm_realm_is_private_address(struct realm *realm,
> unsigned long addr)
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 06070bc47ee3..fb04d032504e 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1797,6 +1797,22 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> return __kvm_arm_vcpu_set_events(vcpu, events);
> }
>
> +static int kvm_arm_vcpu_rmi_psci_complete(struct kvm_vcpu *vcpu,
> + struct kvm_arm_rmi_psci_complete *arg)
> +{
> + struct kvm_vcpu *target = kvm_mpidr_to_vcpu(vcpu->kvm, arg->target_mpidr);
> +
> + if (!target)
> + return -EINVAL;
> +
> + /*
> + * RMM v1.0 only supports PSCI_RET_SUCCESS or PSCI_RET_DENIED
> + * for the status. But, let us leave it to the RMM to filter
> + * for making this future proof.
> + */
> + return realm_psci_complete(vcpu, target, arg->psci_status);
> +}
> +
> long kvm_arch_vcpu_ioctl(struct file *filp,
> unsigned int ioctl, unsigned long arg)
> {
> @@ -1925,6 +1941,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>
> return kvm_arm_vcpu_finalize(vcpu, what);
> }
> + case KVM_ARM_VCPU_RMI_PSCI_COMPLETE: {
> + struct kvm_arm_rmi_psci_complete req;
> +
> + if (!vcpu_is_rec(vcpu))
> + return -EPERM;
Same remark as for the other ioctl: EPERM is not quite describing the
problem.
> + if (copy_from_user(&req, argp, sizeof(req)))
> + return -EFAULT;
> + return kvm_arm_vcpu_rmi_psci_complete(vcpu, &req);
> + }
> default:
> r = -EINVAL;
> }
> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> index 3b5dbe9a0a0e..a68f3c1878a5 100644
> --- a/arch/arm64/kvm/psci.c
> +++ b/arch/arm64/kvm/psci.c
> @@ -103,6 +103,12 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
>
> reset_state->reset = true;
> kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
> + /*
> + * Make sure we issue PSCI_COMPLETE before the VCPU can be
> + * scheduled.
> + */
> + if (vcpu_is_rec(vcpu))
> + realm_psci_complete(source_vcpu, vcpu, PSCI_RET_SUCCESS);
>
I really think in-kernel PSCI should be for NS VMs only. The whole
reason for moving to userspace support was to stop adding features to
an already complex infrastructure, and CCA is exactly the sort of
things we want userspace to deal with.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v12 11/46] arm64: RMI: Activate realm on first VCPU run
From: Steven Price @ 2026-03-02 16:31 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <86seai8fbd.wl-maz@kernel.org>
On 02/03/2026 14:40, Marc Zyngier wrote:
> On Wed, 17 Dec 2025 10:10:48 +0000,
> Steven Price <steven.price@arm.com> wrote:
>>
>> When a VCPU migrates to another physical CPU check
>
> To another physical CPU?
>
> That's not what kvm_arch_vcpu_run_pid_change() tracks. It really is
> limited to a new PID being associated to the vpcu thread. Which is
> indeed the case when the vpcu runs for the first time, but that's
> about it.
>
> If you need to track the physical CPU, we have some tracking for it in
> vcpu_load(), but that'd need some rework.
Sorry that was just a complete brainfart when I wrote that commit
message. We don't care about physical CPUs - this is just catching the
first VCPU run. Thanks for catching that.
>> if this is the first
>> time the guest has run, and if so activate the realm.
>>
>> Before the realm can be activated it must first be created, this is a
>> stub in this patch and will be filled in by a later patch.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> New patch for v12
>> ---
>> arch/arm64/include/asm/kvm_rmi.h | 1 +
>> arch/arm64/kvm/arm.c | 6 +++++
>> arch/arm64/kvm/rmi.c | 42 ++++++++++++++++++++++++++++++++
>> 3 files changed, 49 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> index cb7350f8a01a..e4534af06d96 100644
>> --- a/arch/arm64/include/asm/kvm_rmi.h
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -69,6 +69,7 @@ void kvm_init_rmi(void);
>> u32 kvm_realm_ipa_limit(void);
>>
>> int kvm_init_realm_vm(struct kvm *kvm);
>> +int kvm_activate_realm(struct kvm *kvm);
>> void kvm_destroy_realm(struct kvm *kvm);
>> void kvm_realm_destroy_rtts(struct kvm *kvm);
>>
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 941d1bec8e77..542df37b9e82 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -951,6 +951,12 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
>> return ret;
>> }
>>
>> + if (kvm_is_realm(vcpu->kvm)) {
>> + ret = kvm_activate_realm(kvm);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> mutex_lock(&kvm->arch.config_lock);
>> set_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags);
>> mutex_unlock(&kvm->arch.config_lock);
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index e57e8b7eafa9..98929382c365 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -223,6 +223,48 @@ void kvm_realm_destroy_rtts(struct kvm *kvm)
>> WARN_ON(realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits)));
>> }
>>
>> +static int realm_ensure_created(struct kvm *kvm)
>> +{
>> + /* Provided in later patch */
>> + return -ENXIO;
>> +}
>> +
>> +int kvm_activate_realm(struct kvm *kvm)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + int ret;
>> +
>> + if (!kvm_is_realm(kvm))
>> + return -ENXIO;
>
> nit: you already checked for this in caller.
Ack
>> +
>> + if (kvm_realm_state(kvm) == REALM_STATE_ACTIVE)
>> + return 0;
>
> You probably also want to return early once the realm has been marked
> as dead -- it shouldn't be able to be a zombie and die twice.
Indeed, >= would be a better check.
Thanks,
Steve
>> +
>> + guard(mutex)(&kvm->arch.config_lock);
>> + /* Check again with the lock held */
>> + if (kvm_realm_state(kvm) == REALM_STATE_ACTIVE)
>> + return 0;
>> +
>> + ret = realm_ensure_created(kvm);
>> + if (ret)
>> + return ret;
>> +
>> + /* Mark state as dead in case we fail */
>> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
>> +
>> + if (!irqchip_in_kernel(kvm)) {
>> + /* Userspace irqchip not yet supported with realms */
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + ret = rmi_realm_activate(virt_to_phys(realm->rd));
>> + if (ret)
>> + return -ENXIO;
>> +
>> + WRITE_ONCE(realm->state, REALM_STATE_ACTIVE);
>> + return 0;
>> +}
>> +
>> void kvm_destroy_realm(struct kvm *kvm)
>> {
>> struct realm *realm = &kvm->arch.realm;
>
> Thanks,
>
> M.
>
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Steven Price @ 2026-03-02 15:23 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <86tsuy8g0u.wl-maz@kernel.org>
Hi Marc,
On 02/03/2026 14:25, Marc Zyngier wrote:
> On Wed, 17 Dec 2025 10:10:43 +0000,
> Steven Price <steven.price@arm.com> wrote:
>>
>> There is one CAP which identified the presence of CCA, and two ioctls.
>> One ioctl is used to populate memory and the other is used when user
>> space is providing the PSCI implementation to identify the target of the
>> operation.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v11:
>> * Completely reworked to be more implicit. Rather than having explicit
>> CAP operations to progress the realm construction these operations
>> are done when needed (on populating and on first vCPU run).
>> * Populate and PSCI complete are promoted to proper ioctls.
>> Changes since v10:
>> * Rename symbols from RME to RMI.
>> Changes since v9:
>> * Improvements to documentation.
>> * Bump the magic number for KVM_CAP_ARM_RME to avoid conflicts.
>> Changes since v8:
>> * Minor improvements to documentation following review.
>> * Bump the magic numbers to avoid conflicts.
>> Changes since v7:
>> * Add documentation of new ioctls
>> * Bump the magic numbers to avoid conflicts
>> Changes since v6:
>> * Rename some of the symbols to make their usage clearer and avoid
>> repetition.
>> Changes from v5:
>> * Actually expose the new VCPU capability (KVM_ARM_VCPU_REC) by bumping
>> KVM_VCPU_MAX_FEATURES - note this also exposes KVM_ARM_VCPU_HAS_EL2!
>> ---
>> Documentation/virt/kvm/api.rst | 57 ++++++++++++++++++++++++++++++++++
>> include/uapi/linux/kvm.h | 23 ++++++++++++++
>> 2 files changed, 80 insertions(+)
>>
>> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
>> index 01a3abef8abb..2d5dc7e48954 100644
>> --- a/Documentation/virt/kvm/api.rst
>> +++ b/Documentation/virt/kvm/api.rst
>> @@ -6517,6 +6517,54 @@ the capability to be present.
>>
>> `flags` must currently be zero.
>>
>> +4.144 KVM_ARM_VCPU_RMI_PSCI_COMPLETE
>> +------------------------------------
>> +
>> +:Capability: KVM_CAP_ARM_RMI
>> +:Architectures: arm64
>> +:Type: vcpu ioctl
>> +:Parameters: struct kvm_arm_rmi_psci_complete (in)
>> +:Returns: 0 if successful, < 0 on error
>> +
>> +::
>> +
>> + struct kvm_arm_rmi_psci_complete {
>> + __u64 target_mpidr;
>> + __u32 psci_status;
>> + __u32 padding[3];
>> + };
>> +
>> +Where PSCI functions are handled by user space, the RMM needs to be informed of
>> +the target of the operation using `target_mpidr`, along with the status
>> +(`psci_status`). The RMM v1.0 specification defines two functions that require
>> +this call: PSCI_CPU_ON and PSCI_AFFINITY_INFO.
>> +
>> +If the kernel is handling PSCI then this is done automatically and the VMM
>> +doesn't need to call this ioctl.
>
> Shouldn't we make handling of PSCI mandatory for VMMs that deal with
> CCA? I suspect it would simplify the implementation significantly.
What do you mean by making it "mandatory for VMMs"? If you mean PSCI is
always forwarded to user space then I don't think it's going to make
much difference. Patch 27 handles the PSCI changes (72 lines added), and
some of that is adding this uAPI for the VMM to handle it.
Removing the functionality to allow the VMM to handle it would obviously
simplify things a bit (we can drop this uAPI), but I think the desire is
to push this onto user space.
> What vcpu fd does this apply to? The vcpu calling the PSCI function?
> Or the target? This is pretty important for PSCI_ON. My guess is that
> this is setting the return value for the caller?
Yes the fd is the vcpu calling PSCI. As you say, this is for the return
value to be set correctly.
> Assuming this is indeed for the caller, why do we have a different
> flow from anything else that returns a result from a hypercall?
I'm not entirely sure what you are suggesting. Do you mean why are we
not just writing to the GPRS that would contain the result? The issue
here is that the RMM needs to know the PA of the target REC structure -
this isn't a return to the guest, but information for the RMM itself to
complete the PSCI call.
Ultimately even in the case where the VMM is handling PSCI, it's
actually a combination of the VMM and the RMM - with the RMM validating
the responses.
>> +
>> +4.145 KVM_ARM_RMI_POPULATE
>> +--------------------------
>> +
>> +:Capability: KVM_CAP_ARM_RMI
>> +:Architectures: arm64
>> +:Type: vm ioctl
>> +:Parameters: struct kvm_arm_rmi_populate (in)
>> +:Returns: number of bytes populated, < 0 on error
>> +
>> +::
>> +
>> + struct kvm_arm_rmi_populate {
>> + __u64 base;
>> + __u64 size;
>> + __u64 source_uaddr;
>> + __u32 flags;
>> + __u32 reserved;
>> + };
>> +
>> +Populate a region of protected address space by copying the data from the user
>> +space pointer provided. This is only valid before any VCPUs have been run.
>> +The ioctl might not populate the entire region and user space may have to
>> +repeatedly call it (with updated pointers) to populate the entire region.
>
> size as a __u64 is odd, as the return value from the ioctl is a signed
> int. This implies that you can't really report how many bytes you have
> copied. Some form of consistency wouldn't hurt.
Good spot. In practice this works because >2GB in one operation is
highly unlikely to be processed in one go. But I guess I'll change this
to have an output size argument. I guess I could make the kernel update
all of base,size,source_uaddr which would simplify user space.
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH v2 2/3] KVM: SEV: Add support for IBPB-on-Entry
From: Sean Christopherson @ 2026-03-02 15:10 UTC (permalink / raw)
To: Borislav Petkov
Cc: Kim Phillips, linux-kernel, kvm, linux-coco, x86, Paolo Bonzini,
K Prateek Nayak, Nikunj A Dadhania, Tom Lendacky, Michael Roth,
Naveen Rao, David Kaplan
In-Reply-To: <20260228165506.GAaaMd6nQ56E7i5Cqg@fat_crate.local>
On Sat, Feb 28, 2026, Borislav Petkov wrote:
> Sean, ack for the KVM bits and me taking them thru tip?
Ya, should be fine for this to go through tip.
> On Tue, Feb 03, 2026 at 04:24:04PM -0600, Kim Phillips wrote:
> > AMD EPYC 5th generation and above processors support IBPB-on-Entry
> > for SNP guests. By invoking an Indirect Branch Prediction Barrier
> > (IBPB) on VMRUN, old indirect branch predictions are prevented
> > from influencing indirect branches within the guest.
> >
> > SNP guests may choose to enable IBPB-on-Entry by setting
> > SEV_FEATURES bit 21 (IbpbOnEntry).
> >
> > Host support for IBPB on Entry is indicated by CPUID
> > Fn8000_001F[IbpbOnEntry], bit 31.
> >
> > If supported, indicate support for IBPB on Entry in
> > sev_supported_vmsa_features bit 23 (IbpbOnEntry).
> >
> > For more info, refer to page 615, Section 15.36.17 "Side-Channel
> > Protection", AMD64 Architecture Programmer's Manual Volume 2: System
> > Programming Part 2, Pub. 24593 Rev. 3.42 - March 2024 (see Link).
> >
> > Link: https://bugzilla.kernel.org/attachment.cgi?id=306250
> > Signed-off-by: Kim Phillips <kim.phillips@amd.com>
> > Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
> > ---
...
> > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> > index ea515cf41168..8a6d25db0c00 100644
> > --- a/arch/x86/kvm/svm/sev.c
> > +++ b/arch/x86/kvm/svm/sev.c
> > @@ -3165,8 +3165,15 @@ void __init sev_hardware_setup(void)
> > cpu_feature_enabled(X86_FEATURE_NO_NESTED_DATA_BP))
> > sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
> >
> > - if (sev_snp_enabled && tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
> > + if (!sev_snp_enabled)
> > + return;
> > + /* the following feature bit checks are SNP specific */
> > +
> > + if (tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
> > sev_supported_vmsa_features |= SVM_SEV_FEAT_SECURE_TSC;
> > +
> > + if (cpu_feature_enabled(X86_FEATURE_IBPB_ON_ENTRY))
> > + sev_supported_vmsa_features |= SVM_SEV_FEAT_IBPB_ON_ENTRY;
> > }
I think I'd prefer to nest the if-statement, e.g.
if (sev_snp_enabled) {
if (tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
sev_supported_vmsa_features |= SVM_SEV_FEAT_SECURE_TSC;
if (cpu_feature_enabled(X86_FEATURE_IBPB_ON_ENTRY))
sev_supported_vmsa_features |= SVM_SEV_FEAT_IBPB_ON_ENTRY;
}
I'm mildly concerned that'll we'll overlook the early return and unintentionally
bury common code in the SNP-section tail.
More importantly, this patch is buggy. __sev_guest_init() needs to disallow
setting SVM_SEV_FEAT_IBPB_ON_ENTRY for non-SNP guests.
As a follow-up, I also think we should advertise SVM_SEV_FEAT_SNP_ACTIVE and
allow userspace to set the flag in kvm_sev_init.flags. KVM still needs to set
the flag for backwards compatibility, but disallowing SVM_SEV_FEAT_SNP_ACTIVE
for an SNP guest is bizarre.
E.g. across 2 or 3 patches:
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index edde36097ddc..7db1bfce4cca 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -307,6 +307,10 @@ static_assert((X2AVIC_4K_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AV
#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5)
#define SVM_SEV_FEAT_SECURE_TSC BIT(9)
+#define SVM_SEV_FEAT_SNP_ONLY_MASK (SVM_SEV_FEAT_SNP_ACTIVE | \
+ SVM_SEV_FEAT_SECURE_TSC | \
+ SVM_SEV_FEAT_IBPB_ON_ENTRY)
+
#define VMCB_ALLOWED_SEV_FEATURES_VALID BIT_ULL(63)
struct vmcb_seg {
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 41385573629e..b2fe0fa11f90 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -500,7 +500,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
return -EINVAL;
if (!snp_active)
- valid_vmsa_features &= ~SVM_SEV_FEAT_SECURE_TSC;
+ valid_vmsa_features &= ~SVM_SEV_FEAT_SNP_ONLY_MASK;
if (data->vmsa_features & ~valid_vmsa_features)
return -EINVAL;
@@ -3218,8 +3218,15 @@ void __init sev_hardware_setup(void)
cpu_feature_enabled(X86_FEATURE_NO_NESTED_DATA_BP))
sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
- if (sev_snp_enabled && tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
- sev_supported_vmsa_features |= SVM_SEV_FEAT_SECURE_TSC;
+ if (sev_snp_enabled) {
+ sev_supported_vmsa_features |= SVM_SEV_FEAT_SNP_ACTIVE;
+
+ if (tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
+ sev_supported_vmsa_features |= SVM_SEV_FEAT_SECURE_TSC;
+
+ if (cpu_feature_enabled(X86_FEATURE_IBPB_ON_ENTRY))
+ sev_supported_vmsa_features |= SVM_SEV_FEAT_IBPB_ON_ENTRY;
+ }
}
void sev_hardware_unsetup(void)
^ permalink raw reply related
* Re: [PATCH v12 20/46] arm64: RMI: Allow populating initial contents
From: Marc Zyngier @ 2026-03-02 14:56 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20251217101125.91098-21-steven.price@arm.com>
On Wed, 17 Dec 2025 10:10:57 +0000,
Steven Price <steven.price@arm.com> wrote:
>
> The VMM needs to populate the realm with some data before starting (e.g.
> a kernel and initrd). This is measured by the RMM and used as part of
> the attestation later on.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v11:
> * The multiplex CAP is gone and there's a new ioctl which makes use of
> the generic kvm_gmem_populate() functionality.
> Changes since v7:
> * Improve the error codes.
> * Other minor changes from review.
> Changes since v6:
> * Handle host potentially having a larger page size than the RMM
> granule.
> * Drop historic "par" (protected address range) from
> populate_par_region() - it doesn't exist within the current
> architecture.
> * Add a cond_resched() call in kvm_populate_realm().
> Changes since v5:
> * Refactor to use PFNs rather than tracking struct page in
> realm_create_protected_data_page().
> * Pull changes from a later patch (in the v5 series) for accessing
> pages from a guest memfd.
> * Do the populate in chunks to avoid holding locks for too long and
> triggering RCU stall warnings.
> ---
> arch/arm64/include/asm/kvm_rmi.h | 4 +
> arch/arm64/kvm/Kconfig | 1 +
> arch/arm64/kvm/arm.c | 9 ++
> arch/arm64/kvm/rmi.c | 175 +++++++++++++++++++++++++++++++
> 4 files changed, 189 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 8a862fc1a99d..b5e36344975c 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -99,6 +99,10 @@ int kvm_rec_enter(struct kvm_vcpu *vcpu);
> int kvm_rec_pre_enter(struct kvm_vcpu *vcpu);
> int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_status);
>
> +struct kvm_arm_rmi_populate;
> +
> +int kvm_arm_rmi_populate(struct kvm *kvm,
> + struct kvm_arm_rmi_populate *arg);
> void kvm_realm_unmap_range(struct kvm *kvm,
> unsigned long ipa,
> unsigned long size,
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index 1cac6dfc0972..b495dfd3a8b4 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -39,6 +39,7 @@ menuconfig KVM
> select GUEST_PERF_EVENTS if PERF_EVENTS
> select KVM_GUEST_MEMFD
> select KVM_GENERIC_MEMORY_ATTRIBUTES
> + select HAVE_KVM_ARCH_GMEM_POPULATE
> help
> Support hosting virtualized guest machines.
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 7927181887cf..0a06ed9d1a64 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -2037,6 +2037,15 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
> return -EFAULT;
> return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range);
> }
> + case KVM_ARM_RMI_POPULATE: {
> + struct kvm_arm_rmi_populate req;
> +
> + if (!kvm_is_realm(kvm))
> + return -EPERM;
EPERM is odd. It isn't that the VMM doesn't have the right to do it,
it is that it shouldn't have called that, because the ioctl doesn't
exist for a normal VM. -ENOSYS?
> + if (copy_from_user(&req, argp, sizeof(req)))
> + return -EFAULT;
> + return kvm_arm_rmi_populate(kvm, &req);
> + }
> default:
> return -EINVAL;
> }
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index fe15b400091c..39577e956a59 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -558,6 +558,150 @@ void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,
> realm_unmap_private_range(kvm, start, end, may_block);
> }
>
> +static int realm_create_protected_data_granule(struct realm *realm,
> + unsigned long ipa,
> + phys_addr_t dst_phys,
> + phys_addr_t src_phys,
> + unsigned long flags)
> +{
> + phys_addr_t rd = virt_to_phys(realm->rd);
> + int ret;
> +
> + if (rmi_granule_delegate(dst_phys))
> + return -ENXIO;
> +
> + ret = rmi_data_create(rd, dst_phys, ipa, src_phys, flags);
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + WARN_ON(level == RMM_RTT_MAX_LEVEL);
If this is unexpected, why do we still try to handle it? We should
abort hard on anything that doesn't seem 100% correct, and mark the
realm dead.
> +
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + RMM_RTT_MAX_LEVEL, NULL);
> + if (ret)
> + return -EIO;
> +
> + ret = rmi_data_create(rd, dst_phys, ipa, src_phys, flags);
> + }
> + if (ret)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int realm_create_protected_data_page(struct realm *realm,
> + unsigned long ipa,
> + kvm_pfn_t dst_pfn,
> + kvm_pfn_t src_pfn,
> + unsigned long flags)
> +{
> + unsigned long rd = virt_to_phys(realm->rd);
> + phys_addr_t dst_phys, src_phys;
> + bool undelegate_failed = false;
> + int ret, offset;
> +
> + dst_phys = __pfn_to_phys(dst_pfn);
> + src_phys = __pfn_to_phys(src_pfn);
> +
> + for (offset = 0; offset < PAGE_SIZE; offset += RMM_PAGE_SIZE) {
> + ret = realm_create_protected_data_granule(realm,
> + ipa,
> + dst_phys,
> + src_phys,
> + flags);
> + if (ret)
> + goto err;
> +
> + ipa += RMM_PAGE_SIZE;
> + dst_phys += RMM_PAGE_SIZE;
> + src_phys += RMM_PAGE_SIZE;
> + }
> +
> + return 0;
> +
> +err:
> + if (ret == -EIO) {
> + /* current offset needs undelegating */
> + if (WARN_ON(rmi_granule_undelegate(dst_phys)))
> + undelegate_failed = true;
> + }
> + while (offset > 0) {
> + ipa -= RMM_PAGE_SIZE;
> + offset -= RMM_PAGE_SIZE;
> + dst_phys -= RMM_PAGE_SIZE;
> +
> + rmi_data_destroy(rd, ipa, NULL, NULL);
> +
> + if (WARN_ON(rmi_granule_undelegate(dst_phys)))
> + undelegate_failed = true;
> + }
> +
> + if (undelegate_failed) {
> + /*
> + * A granule could not be undelegated,
> + * so the page has to be leaked
> + */
> + get_page(pfn_to_page(dst_pfn));
> + }
> +
> + return -ENXIO;
> +}
> +
> +static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> + void __user *src, int order, void *opaque)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + unsigned long data_flags = *(unsigned long *)opaque;
> + phys_addr_t ipa = gfn_to_gpa(gfn);
> + int npages = (1 << order);
> + int i;
> +
> + for (i = 0; i < npages; i++) {
> + struct page *src_page;
> + int ret;
> +
> + ret = get_user_pages((unsigned long)src, 1, 0, &src_page);
> + if (ret < 0)
> + return ret;
> + if (ret != 1)
> + return -ENOMEM;
> +
> + ret = realm_create_protected_data_page(realm, ipa, pfn,
> + page_to_pfn(src_page),
> + data_flags);
> +
> + put_page(src_page);
> +
> + if (ret)
> + return ret;
> +
> + ipa += PAGE_SIZE;
> + pfn++;
> + src += PAGE_SIZE;
> + }
> +
> + return 0;
> +}
> +
> +static long populate_region(struct kvm *kvm,
> + gfn_t base_gfn,
> + unsigned long pages,
> + u64 uaddr,
> + unsigned long data_flags)
> +{
> + long ret = 0;
> +
> + mutex_lock(&kvm->slots_lock);
> + mmap_read_lock(current->mm);
> + ret = kvm_gmem_populate(kvm, base_gfn, u64_to_user_ptr(uaddr), pages,
> + populate_region_cb, &data_flags);
> + mmap_read_unlock(current->mm);
> + mutex_unlock(&kvm->slots_lock);
> +
> + return ret;
> +}
> +
> enum ripas_action {
> RIPAS_INIT,
> RIPAS_SET,
> @@ -655,6 +799,37 @@ static int realm_ensure_created(struct kvm *kvm)
> return -ENXIO;
> }
>
> +int kvm_arm_rmi_populate(struct kvm *kvm,
> + struct kvm_arm_rmi_populate *args)
> +{
> + unsigned long data_flags = 0;
> + unsigned long ipa_start = args->base;
> + unsigned long ipa_end = ipa_start + args->size;
> + int ret;
> +
> + if (args->reserved ||
> + (args->flags & ~KVM_ARM_RMI_POPULATE_FLAGS_MEASURE) ||
> + !IS_ALIGNED(ipa_start, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa_end, PAGE_SIZE))
> + return -EINVAL;
> +
> + ret = realm_ensure_created(kvm);
> + if (ret)
> + return ret;
> +
> + if (args->flags & KVM_ARM_RMI_POPULATE_FLAGS_MEASURE)
This flag isn't documented.
> + data_flags |= RMI_MEASURE_CONTENT;
> +
> + ret = populate_region(kvm, gpa_to_gfn(ipa_start),
> + args->size >> PAGE_SHIFT,
> + args->source_uaddr, args->flags);
> +
> + if (ret < 0)
> + return ret;
> +
> + return ret * PAGE_SIZE;
Bits of the code works on PAGE_SIZE, other bits on RMM_PAGE_SIZE. It
is pretty confusing. Are you in the middle of reworking this?
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v12 11/46] arm64: RMI: Activate realm on first VCPU run
From: Marc Zyngier @ 2026-03-02 14:40 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20251217101125.91098-12-steven.price@arm.com>
On Wed, 17 Dec 2025 10:10:48 +0000,
Steven Price <steven.price@arm.com> wrote:
>
> When a VCPU migrates to another physical CPU check
To another physical CPU?
That's not what kvm_arch_vcpu_run_pid_change() tracks. It really is
limited to a new PID being associated to the vpcu thread. Which is
indeed the case when the vpcu runs for the first time, but that's
about it.
If you need to track the physical CPU, we have some tracking for it in
vcpu_load(), but that'd need some rework.
> if this is the first
> time the guest has run, and if so activate the realm.
>
> Before the realm can be activated it must first be created, this is a
> stub in this patch and will be filled in by a later patch.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> New patch for v12
> ---
> arch/arm64/include/asm/kvm_rmi.h | 1 +
> arch/arm64/kvm/arm.c | 6 +++++
> arch/arm64/kvm/rmi.c | 42 ++++++++++++++++++++++++++++++++
> 3 files changed, 49 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index cb7350f8a01a..e4534af06d96 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -69,6 +69,7 @@ void kvm_init_rmi(void);
> u32 kvm_realm_ipa_limit(void);
>
> int kvm_init_realm_vm(struct kvm *kvm);
> +int kvm_activate_realm(struct kvm *kvm);
> void kvm_destroy_realm(struct kvm *kvm);
> void kvm_realm_destroy_rtts(struct kvm *kvm);
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 941d1bec8e77..542df37b9e82 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -951,6 +951,12 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
> return ret;
> }
>
> + if (kvm_is_realm(vcpu->kvm)) {
> + ret = kvm_activate_realm(kvm);
> + if (ret)
> + return ret;
> + }
> +
> mutex_lock(&kvm->arch.config_lock);
> set_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags);
> mutex_unlock(&kvm->arch.config_lock);
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index e57e8b7eafa9..98929382c365 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -223,6 +223,48 @@ void kvm_realm_destroy_rtts(struct kvm *kvm)
> WARN_ON(realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits)));
> }
>
> +static int realm_ensure_created(struct kvm *kvm)
> +{
> + /* Provided in later patch */
> + return -ENXIO;
> +}
> +
> +int kvm_activate_realm(struct kvm *kvm)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + int ret;
> +
> + if (!kvm_is_realm(kvm))
> + return -ENXIO;
nit: you already checked for this in caller.
> +
> + if (kvm_realm_state(kvm) == REALM_STATE_ACTIVE)
> + return 0;
You probably also want to return early once the realm has been marked
as dead -- it shouldn't be able to be a zombie and die twice.
> +
> + guard(mutex)(&kvm->arch.config_lock);
> + /* Check again with the lock held */
> + if (kvm_realm_state(kvm) == REALM_STATE_ACTIVE)
> + return 0;
> +
> + ret = realm_ensure_created(kvm);
> + if (ret)
> + return ret;
> +
> + /* Mark state as dead in case we fail */
> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
> +
> + if (!irqchip_in_kernel(kvm)) {
> + /* Userspace irqchip not yet supported with realms */
> + return -EOPNOTSUPP;
> + }
> +
> + ret = rmi_realm_activate(virt_to_phys(realm->rd));
> + if (ret)
> + return -ENXIO;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_ACTIVE);
> + return 0;
> +}
> +
> void kvm_destroy_realm(struct kvm *kvm)
> {
> struct realm *realm = &kvm->arch.realm;
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Marc Zyngier @ 2026-03-02 14:25 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20251217101125.91098-7-steven.price@arm.com>
On Wed, 17 Dec 2025 10:10:43 +0000,
Steven Price <steven.price@arm.com> wrote:
>
> There is one CAP which identified the presence of CCA, and two ioctls.
> One ioctl is used to populate memory and the other is used when user
> space is providing the PSCI implementation to identify the target of the
> operation.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v11:
> * Completely reworked to be more implicit. Rather than having explicit
> CAP operations to progress the realm construction these operations
> are done when needed (on populating and on first vCPU run).
> * Populate and PSCI complete are promoted to proper ioctls.
> Changes since v10:
> * Rename symbols from RME to RMI.
> Changes since v9:
> * Improvements to documentation.
> * Bump the magic number for KVM_CAP_ARM_RME to avoid conflicts.
> Changes since v8:
> * Minor improvements to documentation following review.
> * Bump the magic numbers to avoid conflicts.
> Changes since v7:
> * Add documentation of new ioctls
> * Bump the magic numbers to avoid conflicts
> Changes since v6:
> * Rename some of the symbols to make their usage clearer and avoid
> repetition.
> Changes from v5:
> * Actually expose the new VCPU capability (KVM_ARM_VCPU_REC) by bumping
> KVM_VCPU_MAX_FEATURES - note this also exposes KVM_ARM_VCPU_HAS_EL2!
> ---
> Documentation/virt/kvm/api.rst | 57 ++++++++++++++++++++++++++++++++++
> include/uapi/linux/kvm.h | 23 ++++++++++++++
> 2 files changed, 80 insertions(+)
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 01a3abef8abb..2d5dc7e48954 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -6517,6 +6517,54 @@ the capability to be present.
>
> `flags` must currently be zero.
>
> +4.144 KVM_ARM_VCPU_RMI_PSCI_COMPLETE
> +------------------------------------
> +
> +:Capability: KVM_CAP_ARM_RMI
> +:Architectures: arm64
> +:Type: vcpu ioctl
> +:Parameters: struct kvm_arm_rmi_psci_complete (in)
> +:Returns: 0 if successful, < 0 on error
> +
> +::
> +
> + struct kvm_arm_rmi_psci_complete {
> + __u64 target_mpidr;
> + __u32 psci_status;
> + __u32 padding[3];
> + };
> +
> +Where PSCI functions are handled by user space, the RMM needs to be informed of
> +the target of the operation using `target_mpidr`, along with the status
> +(`psci_status`). The RMM v1.0 specification defines two functions that require
> +this call: PSCI_CPU_ON and PSCI_AFFINITY_INFO.
> +
> +If the kernel is handling PSCI then this is done automatically and the VMM
> +doesn't need to call this ioctl.
Shouldn't we make handling of PSCI mandatory for VMMs that deal with
CCA? I suspect it would simplify the implementation significantly.
What vcpu fd does this apply to? The vcpu calling the PSCI function?
Or the target? This is pretty important for PSCI_ON. My guess is that
this is setting the return value for the caller?
Assuming this is indeed for the caller, why do we have a different
flow from anything else that returns a result from a hypercall?
> +
> +4.145 KVM_ARM_RMI_POPULATE
> +--------------------------
> +
> +:Capability: KVM_CAP_ARM_RMI
> +:Architectures: arm64
> +:Type: vm ioctl
> +:Parameters: struct kvm_arm_rmi_populate (in)
> +:Returns: number of bytes populated, < 0 on error
> +
> +::
> +
> + struct kvm_arm_rmi_populate {
> + __u64 base;
> + __u64 size;
> + __u64 source_uaddr;
> + __u32 flags;
> + __u32 reserved;
> + };
> +
> +Populate a region of protected address space by copying the data from the user
> +space pointer provided. This is only valid before any VCPUs have been run.
> +The ioctl might not populate the entire region and user space may have to
> +repeatedly call it (with updated pointers) to populate the entire region.
size as a __u64 is odd, as the return value from the ioctl is a signed
int. This implies that you can't really report how many bytes you have
copied. Some form of consistency wouldn't hurt.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH kernel 6/9] x86/dma-direct: Stop changing encrypted page state for TDISP devices
From: Jason Gunthorpe @ 2026-03-02 13:35 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Robin Murphy, x86, linux-kernel, kvm, linux-pci, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, H. Peter Anvin,
Sean Christopherson, Paolo Bonzini, Andy Lutomirski,
Peter Zijlstra, Bjorn Helgaas, Dan Williams, Marek Szyprowski,
Andrew Morton, Catalin Marinas, Michael Ellerman, Mike Rapoport,
Tom Lendacky, Ard Biesheuvel, Neeraj Upadhyay, Ashish Kalra,
Stefano Garzarella, Melody Wang, Seongman Lee, Joerg Roedel,
Nikunj A Dadhania, Michael Roth, Suravee Suthikulpanit,
Andi Kleen, Kuppuswamy Sathyanarayanan, Tony Luck,
David Woodhouse, Greg Kroah-Hartman, Denis Efremov, Geliang Tang,
Piotr Gregor, Michael S. Tsirkin, Alex Williamson, Arnd Bergmann,
Jesse Barnes, Jacob Pan, Yinghai Lu, Kevin Brodsky,
Jonathan Cameron, Aneesh Kumar K.V (Arm), Xu Yilun, Herbert Xu,
Kim Phillips, Konrad Rzeszutek Wilk, Stefano Stabellini,
Claire Chang, linux-coco, iommu, Jiri Pirko
In-Reply-To: <500e3174-9aa1-464a-b933-f0bcc2ddde68@amd.com>
On Mon, Mar 02, 2026 at 04:26:58PM +1100, Alexey Kardashevskiy wrote:
> > > Without secure vIOMMU, no Cbit in the S2 table (==host) for any
> > > VM. SDTE (==IOMMU) decides on shared/private for the device,
> > > i.e. (device_cc_accepted()?private:shared).
> >
> > Is this "Cbit" part of the CPU S2 page table address space or is it
> > actually some PTE bit that says it is "encrypted" ?
> >
> > It is confusing when you say it would start working with a vIOMMU.
>
> When I mention vIOMMU, I mean the S1 table which is guest owned and
> which has Cbit in PTEs.
Yes, I understand this.
It seems from your email that the CPU S2 has the Cbit as part of the
address and the S1 feeds it through to the S2, so it is genuinely has
two addres spaces?
While the IOMMU S1 does not and instead needs a PTE bit which is
emphatically not an address bit because it does not feed through the
S2?
> > If 1<<51 is a valid IOPTE, and it is an actually address, then it
> > should be mapped into the IOMMU S2, shouldn't it? If it is in the
> > IOMMU S2 then shouldn't it work as a dma_addr_t?
>
> It should (and checked with the HW folks), I just have not tried it as, like, whyyy.
Well, I think things work more sensibly if you don't have to mangle
the address..
> > But in this case I would expect the vIOMMU to also use the same GPA
> > space starting from 0 and also remove the C bit, as the S2 shouldn't
> > have mappings starting at 1<<51.
>
> How would then IOMMU know if DMA targets private or shared memory?
> The Cbit does not participate in the S2 translation as an address
> bit but IOMMU still knows what it is.
Same way it knows if there is no S1? Why does the S1 change anything?
> > > There is vTOM in SDTE which is "every phys_addr_t above vTOM is no
> > > Cbit, below - with Cbit" (and there is the same thing for the CPU
> > > side in SEV) but this not it, right?
> >
> > That seems like the IOMMU HW is specially handling the address bits in
> > some way?
>
> Yeah there is this capability. Except everything below vTOM is
> private and every above is shared so SME mask for it would be
> reverse than the CPU SME mask :) Not using this thing though (not
> sure why we have it). Thanks,
Weird!!
Jason
^ permalink raw reply
* Re: [PATCH v4 01/24] x86/virt/tdx: Move low level SEAMCALL helpers out of <asm/tdx.h>
From: Chao Gao @ 2026-03-02 12:24 UTC (permalink / raw)
To: linux-coco, linux-kernel, kvm, x86
Cc: reinette.chatre, ira.weiny, kai.huang, dan.j.williams, yilun.xu,
sagis, vannapurve, paulmck, nik.borisov, zhenzhong.duan, seanjc,
rick.p.edgecombe, kas, dave.hansen, vishal.l.verma, binbin.wu,
tony.lindgren, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
H. Peter Anvin
In-Reply-To: <20260212143606.534586-2-chao.gao@intel.com>
>-static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
>- struct tdx_module_args *args)
>-{
>- int retry = RDRAND_RETRY_LOOPS;
>- u64 ret;
>-
>- do {
>- preempt_disable();
>- ret = __seamcall_dirty_cache(func, fn, args);
>- preempt_enable();
...
>- } while (ret == TDX_RND_NO_ENTROPY && --retry);
>-
>- return ret;
>-}
>-
<snip>
>+
>+static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
>+ struct tdx_module_args *args)
>+{
>+ int retry = RDRAND_RETRY_LOOPS;
>+ u64 ret;
>+
>+ do {
>+ ret = func(fn, args);
Here should be:
preempt_disable();
ret = __seamcall_dirty_cache(func, fn, args);
preempt_enable();
This looks like a bug I introduced when resolving conflicts with
commit 10df8607bf1a ("x86/virt/tdx: Mark memory cache state incoherent when making SEAMCALL")
Sorry for this issue. I will fix it in the next version.
>+ } while (ret == TDX_RND_NO_ENTROPY && --retry);
>+
>+ return ret;
>+}
^ permalink raw reply
* Re: [PATCH kernel 9/9] pci: Allow encrypted MMIO mapping via sysfs
From: Alexey Kardashevskiy @ 2026-03-02 8:59 UTC (permalink / raw)
To: Aneesh Kumar K.V, x86
Cc: linux-kernel, kvm, linux-pci, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Sean Christopherson,
Paolo Bonzini, Andy Lutomirski, Peter Zijlstra, Bjorn Helgaas,
Dan Williams, Marek Szyprowski, Robin Murphy, Andrew Morton,
Catalin Marinas, Michael Ellerman, Mike Rapoport, Tom Lendacky,
Ard Biesheuvel, Neeraj Upadhyay, Ashish Kalra, Stefano Garzarella,
Melody Wang, Seongman Lee, Joerg Roedel, Nikunj A Dadhania,
Michael Roth, Suravee Suthikulpanit, Andi Kleen,
Kuppuswamy Sathyanarayanan, Tony Luck, David Woodhouse,
Greg Kroah-Hartman, Denis Efremov, Geliang Tang, Piotr Gregor,
Michael S. Tsirkin, Alex Williamson, Arnd Bergmann, Jesse Barnes,
Jacob Pan, Yinghai Lu, Kevin Brodsky, Jonathan Cameron, Xu Yilun,
Herbert Xu, Kim Phillips, Konrad Rzeszutek Wilk,
Stefano Stabellini, Claire Chang, linux-coco, iommu
In-Reply-To: <yq5aa4wqtzfz.fsf@kernel.org>
On 2/3/26 19:20, Aneesh Kumar K.V wrote:
> Alexey Kardashevskiy <aik@amd.com> writes:
>
>> Add another resource#d_enc to allow mapping MMIO as
>> an encrypted/private region.
>>
>> Unlike resourceN_wc, the node is added always as ability to
>> map MMIO as private depends on negotiation with the TSM which
>> happens quite late.
>
> Why is this needed? Is this for a specific tool?
It is not _needed_ but (as the cover letter says) since one of my test devices does not use private MMIO for the main function, here it is to allow https://github.com/billfarrow/pcimem.git to map MMIO as private and do simple reads/writes. Useful for validation, can stop in gdb and inspect tables and whatever. Thanks,
>
> -aneesh
--
Alexey
^ permalink raw reply
* Re: [PATCH kernel 9/9] pci: Allow encrypted MMIO mapping via sysfs
From: Aneesh Kumar K.V @ 2026-03-02 8:20 UTC (permalink / raw)
To: Alexey Kardashevskiy, x86
Cc: linux-kernel, kvm, linux-pci, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Sean Christopherson,
Paolo Bonzini, Andy Lutomirski, Peter Zijlstra, Bjorn Helgaas,
Dan Williams, Marek Szyprowski, Robin Murphy, Andrew Morton,
Catalin Marinas, Michael Ellerman, Mike Rapoport, Tom Lendacky,
Ard Biesheuvel, Neeraj Upadhyay, Ashish Kalra, Stefano Garzarella,
Melody Wang, Seongman Lee, Joerg Roedel, Nikunj A Dadhania,
Michael Roth, Suravee Suthikulpanit, Andi Kleen,
Kuppuswamy Sathyanarayanan, Tony Luck, David Woodhouse,
Greg Kroah-Hartman, Denis Efremov, Geliang Tang, Piotr Gregor,
Michael S. Tsirkin, Alex Williamson, Arnd Bergmann, Jesse Barnes,
Jacob Pan, Yinghai Lu, Kevin Brodsky, Jonathan Cameron, Xu Yilun,
Herbert Xu, Kim Phillips, Konrad Rzeszutek Wilk,
Stefano Stabellini, Claire Chang, linux-coco, iommu,
Alexey Kardashevskiy
In-Reply-To: <20260225053806.3311234-10-aik@amd.com>
Alexey Kardashevskiy <aik@amd.com> writes:
> Add another resource#d_enc to allow mapping MMIO as
> an encrypted/private region.
>
> Unlike resourceN_wc, the node is added always as ability to
> map MMIO as private depends on negotiation with the TSM which
> happens quite late.
Why is this needed? Is this for a specific tool?
-aneesh
^ permalink raw reply
* Re: [PATCH kernel 4/9] dma/swiotlb: Stop forcing SWIOTLB for TDISP devices
From: Aneesh Kumar K.V @ 2026-03-02 7:54 UTC (permalink / raw)
To: Alexey Kardashevskiy, x86
Cc: linux-kernel, kvm, linux-pci, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Sean Christopherson,
Paolo Bonzini, Andy Lutomirski, Peter Zijlstra, Bjorn Helgaas,
Dan Williams, Marek Szyprowski, Robin Murphy, Andrew Morton,
Catalin Marinas, Michael Ellerman, Mike Rapoport, Tom Lendacky,
Ard Biesheuvel, Neeraj Upadhyay, Ashish Kalra, Stefano Garzarella,
Melody Wang, Seongman Lee, Joerg Roedel, Nikunj A Dadhania,
Michael Roth, Suravee Suthikulpanit, Andi Kleen,
Kuppuswamy Sathyanarayanan, Tony Luck, David Woodhouse,
Greg Kroah-Hartman, Denis Efremov, Geliang Tang, Piotr Gregor,
Michael S. Tsirkin, Alex Williamson, Arnd Bergmann, Jesse Barnes,
Jacob Pan, Yinghai Lu, Kevin Brodsky, Jonathan Cameron, Xu Yilun,
Herbert Xu, Kim Phillips, Konrad Rzeszutek Wilk,
Stefano Stabellini, Claire Chang, linux-coco, iommu,
Alexey Kardashevskiy
In-Reply-To: <20260225053806.3311234-5-aik@amd.com>
Alexey Kardashevskiy <aik@amd.com> writes:
> SWIOTLB is enforced when encrypted guest memory is detected
> in pci_swiotlb_detect() which is required for legacy devices.
>
> Skip SWIOTLB for TDISP devices.
>
> Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
> ---
> include/linux/swiotlb.h | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
> index 3dae0f592063..119c25d639a7 100644
> --- a/include/linux/swiotlb.h
> +++ b/include/linux/swiotlb.h
> @@ -173,6 +173,15 @@ static inline bool is_swiotlb_force_bounce(struct device *dev)
> {
> struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
>
> + /*
> + * CC_ATTR_GUEST_MEM_ENCRYPT enforces SWIOTLB_FORCE in
> + * swiotlb_init_remap() to allow legacy devices access arbitrary
> + * VM encrypted memory.
> + * Skip it for TDISP devices capable of DMA-ing the encrypted memory.
> + */
> + if (device_cc_accepted(dev))
> + return false;
> +
> return mem && mem->force_bounce;
> }
>
I’m wondering whether we need more than that. Perhaps we could start
with a simpler assumption: a TDISP-capable device will never require
SWIOTLB bouncing. That would significantly simplify the DMA allocation
path for T=1.
Without this assumption, we might need to implement a private
io_tlb_mem.
We should also avoid supporting TDISP mode on devices that require
things like restricted-memory SWIOTLB pool.
Something like:
modified arch/arm64/mm/mem_encrypt.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/mem_encrypt.h>
+#include <linux/device.h>
static const struct arm64_mem_crypt_ops *crypt_ops;
@@ -53,3 +54,12 @@ int set_memory_decrypted(unsigned long addr, int numpages)
return crypt_ops->decrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_decrypted);
+
+bool force_dma_unencrypted(struct device *dev)
+{
+ if (device_cc_accepted(dev))
+ return false;
+
+ return is_realm_world();
+}
+EXPORT_SYMBOL_GPL(force_dma_unencrypted);
modified include/linux/swiotlb.h
@@ -173,6 +173,11 @@ static inline bool is_swiotlb_force_bounce(struct device *dev)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+ if (device_cc_accepted(dev)) {
+ dev_warn_once(dev, "(TIO) Disable SWIOTLB");
+ return false;
+ }
+
return mem && mem->force_bounce;
}
@@ -287,6 +292,9 @@ bool swiotlb_free(struct device *dev, struct page *page, size_t size);
static inline bool is_swiotlb_for_alloc(struct device *dev)
{
+ if (device_cc_accepted(dev))
+ return false;
+
return dev->dma_io_tlb_mem->for_alloc;
}
#else
modified kernel/dma/direct.c
@@ -159,6 +159,14 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
*/
static bool dma_direct_use_pool(struct device *dev, gfp_t gfp)
{
+ /*
+ * Atomic pools are marked decrypted and are used if we require require
+ * updation of pfn mem encryption attributes or for DMA non-coherent
+ * device allocation. Both is not true for trusted device.
+ */
+ if (device_cc_accepted(dev))
+ return false;
+
return !gfpflags_allow_blocking(gfp) && !is_swiotlb_for_alloc(dev);
}
modified kernel/dma/swiotlb.c
@@ -1643,6 +1643,9 @@ bool is_swiotlb_active(struct device *dev)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+ if (device_cc_accepted(dev))
+ return false;
+
return mem && mem->nslabs;
}
^ permalink raw reply
* Re: [PATCH kernel 2/9] pci/tsm: Add tsm_tdi_status
From: Aneesh Kumar K.V @ 2026-03-02 6:58 UTC (permalink / raw)
To: dan.j.williams, Alexey Kardashevskiy, x86
Cc: linux-kernel, kvm, linux-pci, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Sean Christopherson,
Paolo Bonzini, Andy Lutomirski, Peter Zijlstra, Bjorn Helgaas,
Dan Williams, Marek Szyprowski, Robin Murphy, Andrew Morton,
Catalin Marinas, Michael Ellerman, Mike Rapoport, Tom Lendacky,
Ard Biesheuvel, Neeraj Upadhyay, Ashish Kalra, Stefano Garzarella,
Melody Wang, Seongman Lee, Joerg Roedel, Nikunj A Dadhania,
Michael Roth, Suravee Suthikulpanit, Andi Kleen,
Kuppuswamy Sathyanarayanan, Tony Luck, David Woodhouse,
Greg Kroah-Hartman, Denis Efremov, Geliang Tang, Piotr Gregor,
Michael S. Tsirkin, Alex Williamson, Arnd Bergmann, Jesse Barnes,
Jacob Pan, Yinghai Lu, Kevin Brodsky, Jonathan Cameron, Xu Yilun,
Herbert Xu, Kim Phillips, Konrad Rzeszutek Wilk,
Stefano Stabellini, Claire Chang, linux-coco, iommu,
Alexey Kardashevskiy
In-Reply-To: <699e97d6e8be7_1cc51003c@dwillia2-mobl4.notmuch>
<dan.j.williams@intel.com> writes:
> Alexey Kardashevskiy wrote:
>> Define a structure with all info about a TDI such as TDISP status,
>> bind state, used START_INTERFACE options and the report digest.
>>
>> This will be extended and shared to the userspace.
>>
>> Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
>> ---
>>
>> Make it uapi? We might want a sysfs node per a field so probably not.
>> For now its only user is AMD SEV TIO with a plan to expose this struct
>> as a whole via sysfs.
>
> Say more about what this uapi when sysfs already has lock+accept
> indications?
>
> Or are you just talking about exporting the TDISP report as a binary
> blob?
>
> I think the kernel probably wants a generic abstraction for asserting
> that the tsm layer believes the report remains valid between fetch and
> run. In other words I am not sure arch features like intf_report_counter
> ever show up anywhere in uapi outside of debugfs.
>
Agreed. For CCA, we use rsi_vdev_info, but we need a generic mechanism
to associate this with the report that the guest has attested.
In CCA, we call rsi_vdev_get_info(vdev_id, dev_info) and later use that
information in rsi_vdev_enable_dma(vdev_id, dev_info).
Perhaps we could add a generation number (or meas_nonce) to the TSM
netlink response and use it when accepting the device, so we can
reliably bind the device measurement to the attested one?
-aneesh
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox