| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * AMD SVM-SEV Host Support. | |
| * | |
| * Copyright (C) 2023 Advanced Micro Devices, Inc. | |
| * | |
| * Author: Ashish Kalra <ashish.kalra@amd.com> | |
| * | |
| */ | |
| #include <linux/cc_platform.h> | |
| #include <linux/printk.h> | |
| #include <linux/mm_types.h> | |
| #include <linux/set_memory.h> | |
| #include <linux/memblock.h> | |
| #include <linux/kernel.h> | |
| #include <linux/mm.h> | |
| #include <linux/cpumask.h> | |
| #include <linux/iommu.h> | |
| #include <linux/amd-iommu.h> | |
| #include <linux/nospec.h> | |
| #include <asm/sev.h> | |
| #include <asm/processor.h> | |
| #include <asm/setup.h> | |
| #include <asm/svm.h> | |
| #include <asm/smp.h> | |
| #include <asm/cpu.h> | |
| #include <asm/apic.h> | |
| #include <asm/cpuid/api.h> | |
| #include <asm/cmdline.h> | |
| #include <asm/iommu.h> | |
| #include <asm/msr.h> | |
| /* | |
| * The RMP entry information as returned by the RMPREAD instruction. | |
| */ | |
| struct rmpentry { | |
| u64 gpa; | |
| u8 assigned :1, | |
| rsvd1 :7; | |
| u8 pagesize :1, | |
| hpage_region_status :1, | |
| rsvd2 :6; | |
| u8 immutable :1, | |
| rsvd3 :7; | |
| u8 rsvd4; | |
| u32 asid; | |
| } __packed; | |
| /* | |
| * The raw RMP entry format is not architectural. The format is defined in PPR | |
| * Family 19h Model 01h, Rev B1 processor. This format represents the actual | |
| * entry in the RMP table memory. The bitfield definitions are used for machines | |
| * without the RMPREAD instruction (Zen3 and Zen4), otherwise the "hi" and "lo" | |
| * fields are only used for dumping the raw data. | |
| */ | |
| struct rmpentry_raw { | |
| union { | |
| struct { | |
| u64 assigned : 1, | |
| pagesize : 1, | |
| immutable : 1, | |
| rsvd1 : 9, | |
| gpa : 39, | |
| asid : 10, | |
| vmsa : 1, | |
| validated : 1, | |
| rsvd2 : 1; | |
| }; | |
| u64 lo; | |
| }; | |
| u64 hi; | |
| } __packed; | |
| /* | |
| * The first 16KB from the RMP_BASE is used by the processor for the | |
| * bookkeeping, the range needs to be added during the RMP entry lookup. | |
| */ | |
| #define RMPTABLE_CPU_BOOKKEEPING_SZ 0x4000 | |
| /* | |
| * For a non-segmented RMP table, use the maximum physical addressing as the | |
| * segment size in order to always arrive at index 0 in the table. | |
| */ | |
| #define RMPTABLE_NON_SEGMENTED_SHIFT 52 | |
| struct rmp_segment_desc { | |
| struct rmpentry_raw *rmp_entry; | |
| u64 max_index; | |
| u64 size; | |
| }; | |
| /* | |
| * Segmented RMP Table support. | |
| * - The segment size is used for two purposes: | |
| * - Identify the amount of memory covered by an RMP segment | |
| * - Quickly locate an RMP segment table entry for a physical address | |
| * | |
| * - The RMP segment table contains pointers to an RMP table that covers | |
| * a specific portion of memory. There can be up to 512 8-byte entries, | |
| * one pages worth. | |
| */ | |
| #define RST_ENTRY_MAPPED_SIZE(x) ((x) & GENMASK_ULL(19, 0)) | |
| #define RST_ENTRY_SEGMENT_BASE(x) ((x) & GENMASK_ULL(51, 20)) | |
| #define RST_SIZE SZ_4K | |
| static struct rmp_segment_desc **rmp_segment_table __ro_after_init; | |
| static unsigned int rst_max_index __ro_after_init = 512; | |
| static unsigned int rmp_segment_shift; | |
| static u64 rmp_segment_size; | |
| static u64 rmp_segment_mask; | |
| #define RST_ENTRY_INDEX(x) ((x) >> rmp_segment_shift) | |
| #define RMP_ENTRY_INDEX(x) ((u64)(PHYS_PFN((x) & rmp_segment_mask))) | |
| static u64 rmp_cfg; | |
| static void *rmp_bookkeeping __ro_after_init; | |
| /* Mask to apply to a PFN to get the first PFN of a 2MB page */ | |
| #define PFN_PMD_MASK GENMASK_ULL(63, PMD_SHIFT - PAGE_SHIFT) | |
| static u64 probed_rmp_base, probed_rmp_size; | |
| static LIST_HEAD(snp_leaked_pages_list); | |
| static DEFINE_SPINLOCK(snp_leaked_pages_list_lock); | |
| static unsigned long snp_nr_leaked_pages; | |
| #undef pr_fmt | |
| #define pr_fmt(fmt) "SEV-SNP: " fmt | |
| static void mfd_reconfigure(void *arg) | |
| { | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return; | |
| if (arg) | |
| msr_set_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); | |
| else | |
| msr_clear_bit(MSR_AMD64_SYSCFG, MSR_AMD64_SYSCFG_MFDM_BIT); | |
| } | |
| static void snp_enable(void *arg) | |
| { | |
| u64 val; | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return; | |
| rdmsrq(MSR_AMD64_SYSCFG, val); | |
| val |= MSR_AMD64_SYSCFG_SNP_EN; | |
| val |= MSR_AMD64_SYSCFG_SNP_VMPL_EN; | |
| wrmsrq(MSR_AMD64_SYSCFG, val); | |
| } | |
| static void __init __snp_fixup_e820_tables(u64 pa) | |
| { | |
| if (IS_ALIGNED(pa, PMD_SIZE)) | |
| return; | |
| /* | |
| * Handle cases where the RMP table placement by the BIOS is not | |
| * 2M aligned and the kexec kernel could try to allocate | |
| * from within that chunk which then causes a fatal RMP fault. | |
| * | |
| * The e820_table needs to be updated as it is converted to | |
| * kernel memory resources and used by KEXEC_FILE_LOAD syscall | |
| * to load kexec segments. | |
| * | |
| * The e820_table_firmware needs to be updated as it is exposed | |
| * to sysfs and used by the KEXEC_LOAD syscall to load kexec | |
| * segments. | |
| * | |
| * The e820_table_kexec needs to be updated as it passed to | |
| * the kexec-ed kernel. | |
| */ | |
| pa = ALIGN_DOWN(pa, PMD_SIZE); | |
| if (e820__mapped_any(pa, pa + PMD_SIZE, E820_TYPE_RAM)) { | |
| pr_info("Reserving start/end of RMP table on a 2MB boundary [0x%016llx]\n", pa); | |
| e820__range_update(pa, PMD_SIZE, E820_TYPE_RAM, E820_TYPE_RESERVED); | |
| e820__range_update_table(e820_table_kexec, pa, PMD_SIZE, E820_TYPE_RAM, E820_TYPE_RESERVED); | |
| if (!memblock_is_region_reserved(pa, PMD_SIZE)) | |
| memblock_reserve(pa, PMD_SIZE); | |
| } | |
| } | |
| static void __init fixup_e820_tables_for_segmented_rmp(void) | |
| { | |
| u64 pa, *rst, size, mapped_size; | |
| unsigned int i; | |
| __snp_fixup_e820_tables(probed_rmp_base); | |
| pa = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ; | |
| __snp_fixup_e820_tables(pa + RST_SIZE); | |
| rst = early_memremap(pa, RST_SIZE); | |
| if (!rst) | |
| return; | |
| for (i = 0; i < rst_max_index; i++) { | |
| pa = RST_ENTRY_SEGMENT_BASE(rst[i]); | |
| mapped_size = RST_ENTRY_MAPPED_SIZE(rst[i]); | |
| if (!mapped_size) | |
| continue; | |
| __snp_fixup_e820_tables(pa); | |
| /* | |
| * Mapped size in GB. Mapped size is allowed to exceed | |
| * the segment coverage size, but gets reduced to the | |
| * segment coverage size. | |
| */ | |
| mapped_size <<= 30; | |
| if (mapped_size > rmp_segment_size) | |
| mapped_size = rmp_segment_size; | |
| /* Calculate the RMP segment size (16 bytes/page mapped) */ | |
| size = PHYS_PFN(mapped_size) << 4; | |
| __snp_fixup_e820_tables(pa + size); | |
| } | |
| early_memunmap(rst, RST_SIZE); | |
| } | |
| static void __init fixup_e820_tables_for_contiguous_rmp(void) | |
| { | |
| __snp_fixup_e820_tables(probed_rmp_base); | |
| __snp_fixup_e820_tables(probed_rmp_base + probed_rmp_size); | |
| } | |
| void __init snp_fixup_e820_tables(void) | |
| { | |
| if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) { | |
| fixup_e820_tables_for_segmented_rmp(); | |
| } else { | |
| fixup_e820_tables_for_contiguous_rmp(); | |
| } | |
| } | |
| static void clear_rmp(void) | |
| { | |
| unsigned int i; | |
| u64 val; | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return; | |
| /* Clearing the RMP while SNP is enabled will cause an exception */ | |
| rdmsrq(MSR_AMD64_SYSCFG, val); | |
| if (WARN_ON_ONCE(val & MSR_AMD64_SYSCFG_SNP_EN)) | |
| return; | |
| memset(rmp_bookkeeping, 0, RMPTABLE_CPU_BOOKKEEPING_SZ); | |
| for (i = 0; i < rst_max_index; i++) { | |
| struct rmp_segment_desc *desc; | |
| desc = rmp_segment_table[i]; | |
| if (!desc) | |
| continue; | |
| memset(desc->rmp_entry, 0, desc->size); | |
| } | |
| } | |
| static bool __init alloc_rmp_segment_desc(u64 segment_pa, u64 segment_size, u64 pa) | |
| { | |
| u64 rst_index, rmp_segment_size_max; | |
| struct rmp_segment_desc *desc; | |
| void *rmp_segment; | |
| /* Calculate the maximum size an RMP can be (16 bytes/page mapped) */ | |
| rmp_segment_size_max = PHYS_PFN(rmp_segment_size) << 4; | |
| /* Validate the RMP segment size */ | |
| if (segment_size > rmp_segment_size_max) { | |
| pr_err("Invalid RMP size 0x%llx for configured segment size 0x%llx\n", | |
| segment_size, rmp_segment_size_max); | |
| return false; | |
| } | |
| /* Validate the RMP segment table index */ | |
| rst_index = RST_ENTRY_INDEX(pa); | |
| if (rst_index >= rst_max_index) { | |
| pr_err("Invalid RMP segment base address 0x%llx for configured segment size 0x%llx\n", | |
| pa, rmp_segment_size); | |
| return false; | |
| } | |
| if (rmp_segment_table[rst_index]) { | |
| pr_err("RMP segment descriptor already exists at index %llu\n", rst_index); | |
| return false; | |
| } | |
| rmp_segment = memremap(segment_pa, segment_size, MEMREMAP_WB); | |
| if (!rmp_segment) { | |
| pr_err("Failed to map RMP segment addr 0x%llx size 0x%llx\n", | |
| segment_pa, segment_size); | |
| return false; | |
| } | |
| desc = kzalloc_obj(*desc); | |
| if (!desc) { | |
| memunmap(rmp_segment); | |
| return false; | |
| } | |
| desc->rmp_entry = rmp_segment; | |
| desc->max_index = segment_size / sizeof(*desc->rmp_entry); | |
| desc->size = segment_size; | |
| rmp_segment_table[rst_index] = desc; | |
| return true; | |
| } | |
| static void __init free_rmp_segment_table(void) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < rst_max_index; i++) { | |
| struct rmp_segment_desc *desc; | |
| desc = rmp_segment_table[i]; | |
| if (!desc) | |
| continue; | |
| memunmap(desc->rmp_entry); | |
| kfree(desc); | |
| } | |
| free_page((unsigned long)rmp_segment_table); | |
| rmp_segment_table = NULL; | |
| } | |
| /* Allocate the table used to index into the RMP segments */ | |
| static bool __init alloc_rmp_segment_table(void) | |
| { | |
| struct page *page; | |
| page = alloc_page(__GFP_ZERO); | |
| if (!page) | |
| return false; | |
| rmp_segment_table = page_address(page); | |
| return true; | |
| } | |
| static bool __init setup_contiguous_rmptable(void) | |
| { | |
| u64 max_rmp_pfn, calc_rmp_sz, rmptable_segment, rmptable_size, rmp_end; | |
| if (!probed_rmp_size) | |
| return false; | |
| rmp_end = probed_rmp_base + probed_rmp_size - 1; | |
| /* | |
| * Calculate the amount of memory that must be reserved by the BIOS to | |
| * address the whole RAM, including the bookkeeping area. The RMP itself | |
| * must also be covered. | |
| */ | |
| max_rmp_pfn = max_pfn; | |
| if (PFN_UP(rmp_end) > max_pfn) | |
| max_rmp_pfn = PFN_UP(rmp_end); | |
| calc_rmp_sz = (max_rmp_pfn << 4) + RMPTABLE_CPU_BOOKKEEPING_SZ; | |
| if (calc_rmp_sz > probed_rmp_size) { | |
| pr_err("Memory reserved for the RMP table does not cover full system RAM (expected 0x%llx got 0x%llx)\n", | |
| calc_rmp_sz, probed_rmp_size); | |
| return false; | |
| } | |
| if (!alloc_rmp_segment_table()) | |
| return false; | |
| /* Map only the RMP entries */ | |
| rmptable_segment = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ; | |
| rmptable_size = probed_rmp_size - RMPTABLE_CPU_BOOKKEEPING_SZ; | |
| if (!alloc_rmp_segment_desc(rmptable_segment, rmptable_size, 0)) { | |
| free_rmp_segment_table(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool __init setup_segmented_rmptable(void) | |
| { | |
| u64 rst_pa, *rst, pa, ram_pa_end, ram_pa_max; | |
| unsigned int i, max_index; | |
| if (!probed_rmp_base) | |
| return false; | |
| if (!alloc_rmp_segment_table()) | |
| return false; | |
| rst_pa = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ; | |
| rst = memremap(rst_pa, RST_SIZE, MEMREMAP_WB); | |
| if (!rst) { | |
| pr_err("Failed to map RMP segment table addr 0x%llx\n", rst_pa); | |
| goto e_free; | |
| } | |
| pr_info("Segmented RMP using %lluGB segments\n", rmp_segment_size >> 30); | |
| ram_pa_max = max_pfn << PAGE_SHIFT; | |
| max_index = 0; | |
| ram_pa_end = 0; | |
| for (i = 0; i < rst_max_index; i++) { | |
| u64 rmp_segment, rmp_size, mapped_size; | |
| mapped_size = RST_ENTRY_MAPPED_SIZE(rst[i]); | |
| if (!mapped_size) | |
| continue; | |
| max_index = i; | |
| /* | |
| * Mapped size in GB. Mapped size is allowed to exceed the | |
| * segment coverage size, but gets reduced to the segment | |
| * coverage size. | |
| */ | |
| mapped_size <<= 30; | |
| if (mapped_size > rmp_segment_size) { | |
| pr_info("RMP segment %u mapped size (0x%llx) reduced to 0x%llx\n", | |
| i, mapped_size, rmp_segment_size); | |
| mapped_size = rmp_segment_size; | |
| } | |
| rmp_segment = RST_ENTRY_SEGMENT_BASE(rst[i]); | |
| /* Calculate the RMP segment size (16 bytes/page mapped) */ | |
| rmp_size = PHYS_PFN(mapped_size) << 4; | |
| pa = (u64)i << rmp_segment_shift; | |
| /* | |
| * Some segments may be for MMIO mapped above system RAM. These | |
| * segments are used for Trusted I/O. | |
| */ | |
| if (pa < ram_pa_max) | |
| ram_pa_end = pa + mapped_size; | |
| if (!alloc_rmp_segment_desc(rmp_segment, rmp_size, pa)) | |
| goto e_unmap; | |
| pr_info("RMP segment %u physical address [0x%llx - 0x%llx] covering [0x%llx - 0x%llx]\n", | |
| i, rmp_segment, rmp_segment + rmp_size - 1, pa, pa + mapped_size - 1); | |
| } | |
| if (ram_pa_max > ram_pa_end) { | |
| pr_err("Segmented RMP does not cover full system RAM (expected 0x%llx got 0x%llx)\n", | |
| ram_pa_max, ram_pa_end); | |
| goto e_unmap; | |
| } | |
| /* Adjust the maximum index based on the found segments */ | |
| rst_max_index = max_index + 1; | |
| memunmap(rst); | |
| return true; | |
| e_unmap: | |
| memunmap(rst); | |
| e_free: | |
| free_rmp_segment_table(); | |
| return false; | |
| } | |
| static bool __init setup_rmptable(void) | |
| { | |
| if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) { | |
| if (!setup_segmented_rmptable()) | |
| return false; | |
| } else { | |
| if (!setup_contiguous_rmptable()) | |
| return false; | |
| } | |
| rmp_bookkeeping = memremap(probed_rmp_base, RMPTABLE_CPU_BOOKKEEPING_SZ, MEMREMAP_WB); | |
| if (!rmp_bookkeeping) { | |
| pr_err("Failed to map RMP bookkeeping area\n"); | |
| free_rmp_segment_table(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void clear_hsave_pa(void *arg) | |
| { | |
| wrmsrq(MSR_VM_HSAVE_PA, 0); | |
| } | |
| int snp_prepare(void) | |
| { | |
| int ret; | |
| u64 val; | |
| /* | |
| * Check if SEV-SNP is already enabled, this can happen in case of | |
| * kexec boot. | |
| */ | |
| rdmsrq(MSR_AMD64_SYSCFG, val); | |
| if (val & MSR_AMD64_SYSCFG_SNP_EN) | |
| return 0; | |
| clear_rmp(); | |
| cpus_read_lock(); | |
| if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) { | |
| ret = -EOPNOTSUPP; | |
| pr_warn("SNP init failed: not all CPUs online. (%*pbl online <-> %*pbl present masks).\n", | |
| cpumask_pr_args(cpu_online_mask), | |
| cpumask_pr_args(cpu_present_mask)); | |
| goto unlock; | |
| } | |
| wbinvd_on_all_cpus(); | |
| /* | |
| * MtrrFixDramModEn is not shared between threads on a core, | |
| * therefore it must be set on all CPUs prior to enabling SNP. | |
| */ | |
| on_each_cpu(mfd_reconfigure, (void *)1, 1); | |
| on_each_cpu(snp_enable, NULL, 1); | |
| /* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */ | |
| on_each_cpu(clear_hsave_pa, NULL, 1); | |
| ret = 0; | |
| unlock: | |
| cpus_read_unlock(); | |
| return ret; | |
| } | |
| EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp"); | |
| void snp_shutdown(void) | |
| { | |
| u64 syscfg; | |
| rdmsrq(MSR_AMD64_SYSCFG, syscfg); | |
| if (syscfg & MSR_AMD64_SYSCFG_SNP_EN) | |
| return; | |
| clear_rmp(); | |
| on_each_cpu(mfd_reconfigure, NULL, 1); | |
| } | |
| EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp"); | |
| /* | |
| * Do the necessary preparations which are verified by the firmware as | |
| * described in the SNP_INIT_EX firmware command description in the SNP | |
| * firmware ABI spec. | |
| */ | |
| int __init snp_rmptable_init(void) | |
| { | |
| if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP))) | |
| return -ENOSYS; | |
| if (WARN_ON_ONCE(!amd_iommu_snp_en)) | |
| return -ENOSYS; | |
| if (!setup_rmptable()) | |
| return -ENOSYS; | |
| /* | |
| * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic | |
| * notifier is invoked to do SNP IOMMU shutdown before kdump. | |
| */ | |
| crash_kexec_post_notifiers = true; | |
| return 0; | |
| } | |
| static void set_rmp_segment_info(unsigned int segment_shift) | |
| { | |
| rmp_segment_shift = segment_shift; | |
| rmp_segment_size = 1ULL << rmp_segment_shift; | |
| rmp_segment_mask = rmp_segment_size - 1; | |
| } | |
| #define RMP_ADDR_MASK GENMASK_ULL(51, 13) | |
| static bool probe_contiguous_rmptable_info(void) | |
| { | |
| u64 rmp_sz, rmp_base, rmp_end; | |
| rdmsrq(MSR_AMD64_RMP_BASE, rmp_base); | |
| rdmsrq(MSR_AMD64_RMP_END, rmp_end); | |
| if (!(rmp_base & RMP_ADDR_MASK) || !(rmp_end & RMP_ADDR_MASK)) { | |
| pr_err("Memory for the RMP table has not been reserved by BIOS\n"); | |
| return false; | |
| } | |
| if (rmp_base > rmp_end) { | |
| pr_err("RMP configuration not valid: base=%#llx, end=%#llx\n", rmp_base, rmp_end); | |
| return false; | |
| } | |
| rmp_sz = rmp_end - rmp_base + 1; | |
| /* Treat the contiguous RMP table as a single segment */ | |
| rst_max_index = 1; | |
| set_rmp_segment_info(RMPTABLE_NON_SEGMENTED_SHIFT); | |
| probed_rmp_base = rmp_base; | |
| probed_rmp_size = rmp_sz; | |
| pr_info("RMP table physical range [0x%016llx - 0x%016llx]\n", | |
| rmp_base, rmp_end); | |
| return true; | |
| } | |
| static bool probe_segmented_rmptable_info(void) | |
| { | |
| unsigned int eax, ebx, segment_shift, segment_shift_min, segment_shift_max; | |
| u64 rmp_base, rmp_end; | |
| rdmsrq(MSR_AMD64_RMP_BASE, rmp_base); | |
| if (!(rmp_base & RMP_ADDR_MASK)) { | |
| pr_err("Memory for the RMP table has not been reserved by BIOS\n"); | |
| return false; | |
| } | |
| rdmsrq(MSR_AMD64_RMP_END, rmp_end); | |
| WARN_ONCE(rmp_end & RMP_ADDR_MASK, | |
| "Segmented RMP enabled but RMP_END MSR is non-zero\n"); | |
| /* Obtain the min and max supported RMP segment size */ | |
| eax = cpuid_eax(0x80000025); | |
| segment_shift_min = eax & GENMASK(5, 0); | |
| segment_shift_max = (eax & GENMASK(11, 6)) >> 6; | |
| /* Verify the segment size is within the supported limits */ | |
| segment_shift = MSR_AMD64_RMP_SEGMENT_SHIFT(rmp_cfg); | |
| if (segment_shift > segment_shift_max || segment_shift < segment_shift_min) { | |
| pr_err("RMP segment size (%u) is not within advertised bounds (min=%u, max=%u)\n", | |
| segment_shift, segment_shift_min, segment_shift_max); | |
| return false; | |
| } | |
| /* Override the max supported RST index if a hardware limit exists */ | |
| ebx = cpuid_ebx(0x80000025); | |
| if (ebx & BIT(10)) | |
| rst_max_index = ebx & GENMASK(9, 0); | |
| set_rmp_segment_info(segment_shift); | |
| probed_rmp_base = rmp_base; | |
| probed_rmp_size = 0; | |
| pr_info("Segmented RMP base table physical range [0x%016llx - 0x%016llx]\n", | |
| rmp_base, rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ + RST_SIZE); | |
| return true; | |
| } | |
| bool snp_probe_rmptable_info(void) | |
| { | |
| if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP)) | |
| rdmsrq(MSR_AMD64_RMP_CFG, rmp_cfg); | |
| if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) | |
| return probe_segmented_rmptable_info(); | |
| else | |
| return probe_contiguous_rmptable_info(); | |
| } | |
| /* | |
| * About the array_index_nospec() usage below: | |
| * | |
| * This function can get called by exported functions like | |
| * snp_lookup_rmpentry(), which is used by the KVM #PF handler, among | |
| * others, and since the @pfn passed in cannot always be trusted, | |
| * speculation should be stopped as a protective measure. | |
| */ | |
| static struct rmpentry_raw *get_raw_rmpentry(u64 pfn) | |
| { | |
| u64 paddr, rst_index, segment_index; | |
| struct rmp_segment_desc *desc; | |
| if (!rmp_segment_table) | |
| return ERR_PTR(-ENODEV); | |
| paddr = pfn << PAGE_SHIFT; | |
| rst_index = RST_ENTRY_INDEX(paddr); | |
| if (unlikely(rst_index >= rst_max_index)) | |
| return ERR_PTR(-EFAULT); | |
| rst_index = array_index_nospec(rst_index, rst_max_index); | |
| desc = rmp_segment_table[rst_index]; | |
| if (unlikely(!desc)) | |
| return ERR_PTR(-EFAULT); | |
| segment_index = RMP_ENTRY_INDEX(paddr); | |
| if (unlikely(segment_index >= desc->max_index)) | |
| return ERR_PTR(-EFAULT); | |
| segment_index = array_index_nospec(segment_index, desc->max_index); | |
| return desc->rmp_entry + segment_index; | |
| } | |
| static int get_rmpentry(u64 pfn, struct rmpentry *e) | |
| { | |
| struct rmpentry_raw *e_raw; | |
| if (cpu_feature_enabled(X86_FEATURE_RMPREAD)) { | |
| int ret; | |
| /* Binutils version 2.44 supports the RMPREAD mnemonic. */ | |
| asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfd" | |
| : "=a" (ret) | |
| : "a" (pfn << PAGE_SHIFT), "c" (e) | |
| : "memory", "cc"); | |
| return ret; | |
| } | |
| e_raw = get_raw_rmpentry(pfn); | |
| if (IS_ERR(e_raw)) | |
| return PTR_ERR(e_raw); | |
| /* | |
| * Map the raw RMP table entry onto the RMPREAD output format. | |
| * The 2MB region status indicator (hpage_region_status field) is not | |
| * calculated, since the overhead could be significant and the field | |
| * is not used. | |
| */ | |
| memset(e, 0, sizeof(*e)); | |
| e->gpa = e_raw->gpa << PAGE_SHIFT; | |
| e->asid = e_raw->asid; | |
| e->assigned = e_raw->assigned; | |
| e->pagesize = e_raw->pagesize; | |
| e->immutable = e_raw->immutable; | |
| return 0; | |
| } | |
| static int __snp_lookup_rmpentry(u64 pfn, struct rmpentry *e, int *level) | |
| { | |
| struct rmpentry e_large; | |
| int ret; | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return -ENODEV; | |
| ret = get_rmpentry(pfn, e); | |
| if (ret) | |
| return ret; | |
| /* | |
| * Find the authoritative RMP entry for a PFN. This can be either a 4K | |
| * RMP entry or a special large RMP entry that is authoritative for a | |
| * whole 2M area. | |
| */ | |
| ret = get_rmpentry(pfn & PFN_PMD_MASK, &e_large); | |
| if (ret) | |
| return ret; | |
| *level = RMP_TO_PG_LEVEL(e_large.pagesize); | |
| return 0; | |
| } | |
| int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level) | |
| { | |
| struct rmpentry e; | |
| int ret; | |
| ret = __snp_lookup_rmpentry(pfn, &e, level); | |
| if (ret) | |
| return ret; | |
| *assigned = !!e.assigned; | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(snp_lookup_rmpentry); | |
| /* | |
| * Dump the raw RMP entry for a particular PFN. These bits are documented in the | |
| * PPR for a particular CPU model and provide useful information about how a | |
| * particular PFN is being utilized by the kernel/firmware at the time certain | |
| * unexpected events occur, such as RMP faults. | |
| */ | |
| static void dump_rmpentry(u64 pfn) | |
| { | |
| struct rmpentry_raw *e_raw; | |
| u64 pfn_i, pfn_end; | |
| struct rmpentry e; | |
| int level, ret; | |
| ret = __snp_lookup_rmpentry(pfn, &e, &level); | |
| if (ret) { | |
| pr_err("Failed to read RMP entry for PFN 0x%llx, error %d\n", | |
| pfn, ret); | |
| return; | |
| } | |
| if (e.assigned) { | |
| e_raw = get_raw_rmpentry(pfn); | |
| if (IS_ERR(e_raw)) { | |
| pr_err("Failed to read RMP contents for PFN 0x%llx, error %ld\n", | |
| pfn, PTR_ERR(e_raw)); | |
| return; | |
| } | |
| pr_info("PFN 0x%llx, RMP entry: [0x%016llx - 0x%016llx]\n", | |
| pfn, e_raw->lo, e_raw->hi); | |
| return; | |
| } | |
| /* | |
| * If the RMP entry for a particular PFN is not in an assigned state, | |
| * then it is sometimes useful to get an idea of whether or not any RMP | |
| * entries for other PFNs within the same 2MB region are assigned, since | |
| * those too can affect the ability to access a particular PFN in | |
| * certain situations, such as when the PFN is being accessed via a 2MB | |
| * mapping in the host page table. | |
| */ | |
| pfn_i = ALIGN_DOWN(pfn, PTRS_PER_PMD); | |
| pfn_end = pfn_i + PTRS_PER_PMD; | |
| pr_info("PFN 0x%llx unassigned, dumping non-zero entries in 2M PFN region: [0x%llx - 0x%llx]\n", | |
| pfn, pfn_i, pfn_end); | |
| while (pfn_i < pfn_end) { | |
| e_raw = get_raw_rmpentry(pfn_i); | |
| if (IS_ERR(e_raw)) { | |
| pr_err("Error %ld reading RMP contents for PFN 0x%llx\n", | |
| PTR_ERR(e_raw), pfn_i); | |
| pfn_i++; | |
| continue; | |
| } | |
| if (e_raw->lo || e_raw->hi) | |
| pr_info("PFN: 0x%llx, [0x%016llx - 0x%016llx]\n", pfn_i, e_raw->lo, e_raw->hi); | |
| pfn_i++; | |
| } | |
| } | |
| void snp_dump_hva_rmpentry(unsigned long hva) | |
| { | |
| unsigned long paddr; | |
| unsigned int level; | |
| pgd_t *pgd; | |
| pte_t *pte; | |
| pgd = __va(read_cr3_pa()); | |
| pgd += pgd_index(hva); | |
| pte = lookup_address_in_pgd(pgd, hva, &level); | |
| if (!pte) { | |
| pr_err("Can't dump RMP entry for HVA %lx: no PTE/PFN found\n", hva); | |
| return; | |
| } | |
| paddr = PFN_PHYS(pte_pfn(*pte)) | (hva & ~page_level_mask(level)); | |
| dump_rmpentry(PHYS_PFN(paddr)); | |
| } | |
| /* | |
| * PSMASH a 2MB aligned page into 4K pages in the RMP table while preserving the | |
| * Validated bit. | |
| */ | |
| int psmash(u64 pfn) | |
| { | |
| unsigned long paddr = pfn << PAGE_SHIFT; | |
| int ret; | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return -ENODEV; | |
| if (!pfn_valid(pfn)) | |
| return -EINVAL; | |
| /* Binutils version 2.36 supports the PSMASH mnemonic. */ | |
| asm volatile(".byte 0xF3, 0x0F, 0x01, 0xFF" | |
| : "=a" (ret) | |
| : "a" (paddr) | |
| : "memory", "cc"); | |
| return ret; | |
| } | |
| EXPORT_SYMBOL_GPL(psmash); | |
| /* | |
| * If the kernel uses a 2MB or larger directmap mapping to write to an address, | |
| * and that mapping contains any 4KB pages that are set to private in the RMP | |
| * table, an RMP #PF will trigger and cause a host crash. Hypervisor code that | |
| * owns the PFNs being transitioned will never attempt such a write, but other | |
| * kernel tasks writing to other PFNs in the range may trigger these checks | |
| * inadvertently due a large directmap mapping that happens to overlap such a | |
| * PFN. | |
| * | |
| * Prevent this by splitting any 2MB+ mappings that might end up containing a | |
| * mix of private/shared PFNs as a result of a subsequent RMPUPDATE for the | |
| * PFN/rmp_level passed in. | |
| * | |
| * Note that there is no attempt here to scan all the RMP entries for the 2MB | |
| * physical range, since it would only be worthwhile in determining if a | |
| * subsequent RMPUPDATE for a 4KB PFN would result in all the entries being of | |
| * the same shared/private state, thus avoiding the need to split the mapping. | |
| * But that would mean the entries are currently in a mixed state, and so the | |
| * mapping would have already been split as a result of prior transitions. | |
| * And since the 4K split is only done if the mapping is 2MB+, and there isn't | |
| * currently a mechanism in place to restore 2MB+ mappings, such a check would | |
| * not provide any usable benefit. | |
| * | |
| * More specifics on how these checks are carried out can be found in APM | |
| * Volume 2, "RMP and VMPL Access Checks". | |
| */ | |
| static int adjust_direct_map(u64 pfn, int rmp_level) | |
| { | |
| unsigned long vaddr; | |
| unsigned int level; | |
| int npages, ret; | |
| pte_t *pte; | |
| /* | |
| * pfn_to_kaddr() will return a vaddr only within the direct | |
| * map range. | |
| */ | |
| vaddr = (unsigned long)pfn_to_kaddr(pfn); | |
| /* Only 4KB/2MB RMP entries are supported by current hardware. */ | |
| if (WARN_ON_ONCE(rmp_level > PG_LEVEL_2M)) | |
| return -EINVAL; | |
| if (!pfn_valid(pfn)) | |
| return -EINVAL; | |
| if (rmp_level == PG_LEVEL_2M && | |
| (!IS_ALIGNED(pfn, PTRS_PER_PMD) || !pfn_valid(pfn + PTRS_PER_PMD - 1))) | |
| return -EINVAL; | |
| /* | |
| * If an entire 2MB physical range is being transitioned, then there is | |
| * no risk of RMP #PFs due to write accesses from overlapping mappings, | |
| * since even accesses from 1GB mappings will be treated as 2MB accesses | |
| * as far as RMP table checks are concerned. | |
| */ | |
| if (rmp_level == PG_LEVEL_2M) | |
| return 0; | |
| pte = lookup_address(vaddr, &level); | |
| if (!pte || pte_none(*pte)) | |
| return 0; | |
| if (level == PG_LEVEL_4K) | |
| return 0; | |
| npages = page_level_size(rmp_level) / PAGE_SIZE; | |
| ret = set_memory_4k(vaddr, npages); | |
| if (ret) | |
| pr_warn("Failed to split direct map for PFN 0x%llx, ret: %d\n", | |
| pfn, ret); | |
| return ret; | |
| } | |
| /* | |
| * It is expected that those operations are seldom enough so that no mutual | |
| * exclusion of updaters is needed and thus the overlap error condition below | |
| * should happen very rarely and would get resolved relatively quickly by | |
| * the firmware. | |
| * | |
| * If not, one could consider introducing a mutex or so here to sync concurrent | |
| * RMP updates and thus diminish the amount of cases where firmware needs to | |
| * lock 2M ranges to protect against concurrent updates. | |
| * | |
| * The optimal solution would be range locking to avoid locking disjoint | |
| * regions unnecessarily but there's no support for that yet. | |
| */ | |
| static int rmpupdate(u64 pfn, struct rmp_state *state) | |
| { | |
| unsigned long paddr = pfn << PAGE_SHIFT; | |
| int ret, level; | |
| if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| return -ENODEV; | |
| level = RMP_TO_PG_LEVEL(state->pagesize); | |
| if (adjust_direct_map(pfn, level)) | |
| return -EFAULT; | |
| do { | |
| /* Binutils version 2.36 supports the RMPUPDATE mnemonic. */ | |
| asm volatile(".byte 0xF2, 0x0F, 0x01, 0xFE" | |
| : "=a" (ret) | |
| : "a" (paddr), "c" ((unsigned long)state) | |
| : "memory", "cc"); | |
| } while (ret == RMPUPDATE_FAIL_OVERLAP); | |
| if (ret) { | |
| pr_err("RMPUPDATE failed for PFN %llx, pg_level: %d, ret: %d\n", | |
| pfn, level, ret); | |
| dump_rmpentry(pfn); | |
| dump_stack(); | |
| return -EFAULT; | |
| } | |
| return 0; | |
| } | |
| /* Transition a page to guest-owned/private state in the RMP table. */ | |
| int rmp_make_private(u64 pfn, u64 gpa, enum pg_level level, u32 asid, bool immutable) | |
| { | |
| struct rmp_state state; | |
| memset(&state, 0, sizeof(state)); | |
| state.assigned = 1; | |
| state.asid = asid; | |
| state.immutable = immutable; | |
| state.gpa = gpa; | |
| state.pagesize = PG_LEVEL_TO_RMP(level); | |
| return rmpupdate(pfn, &state); | |
| } | |
| EXPORT_SYMBOL_GPL(rmp_make_private); | |
| /* Transition a page to hypervisor-owned/shared state in the RMP table. */ | |
| int rmp_make_shared(u64 pfn, enum pg_level level) | |
| { | |
| struct rmp_state state; | |
| memset(&state, 0, sizeof(state)); | |
| state.pagesize = PG_LEVEL_TO_RMP(level); | |
| return rmpupdate(pfn, &state); | |
| } | |
| EXPORT_SYMBOL_GPL(rmp_make_shared); | |
| void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp) | |
| { | |
| struct page *page = pfn_to_page(pfn); | |
| pr_warn("Leaking PFN range 0x%llx-0x%llx\n", pfn, pfn + npages); | |
| spin_lock(&snp_leaked_pages_list_lock); | |
| while (npages--) { | |
| /* | |
| * Reuse the page's buddy list for chaining into the leaked | |
| * pages list. This page should not be on a free list currently | |
| * and is also unsafe to be added to a free list. | |
| */ | |
| if (likely(!PageCompound(page)) || | |
| /* | |
| * Skip inserting tail pages of compound page as | |
| * page->buddy_list of tail pages is not usable. | |
| */ | |
| (PageHead(page) && compound_nr(page) <= npages)) | |
| list_add_tail(&page->buddy_list, &snp_leaked_pages_list); | |
| if (dump_rmp) | |
| dump_rmpentry(pfn); | |
| snp_nr_leaked_pages++; | |
| pfn++; | |
| page++; | |
| } | |
| spin_unlock(&snp_leaked_pages_list_lock); | |
| } | |
| EXPORT_SYMBOL_GPL(__snp_leak_pages); | |
| void kdump_sev_callback(void) | |
| { | |
| /* | |
| * Do wbinvd() on remote CPUs when SNP is enabled in order to | |
| * safely do SNP_SHUTDOWN on the local CPU. | |
| */ | |
| if (cc_platform_has(CC_ATTR_HOST_SEV_SNP)) | |
| wbinvd(); | |
| } |