From: Ashish Kalra 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 --- 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 From: Ashish Kalra 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 Signed-off-by: Ashish Kalra --- 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 From: Ashish Kalra 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 Signed-off-by: Ashish Kalra --- 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 #include #include +#include #include #include @@ -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 From: Ashish Kalra 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 --- 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 From: Ashish Kalra 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 --- 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 From: Ashish Kalra 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 --- 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 From: Ashish Kalra 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 Signed-off-by: Ashish Kalra --- 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 #include #include +#include +#include #include #include @@ -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