Skip to content

Commit 8ae3291

Browse files
tlendackybp3tk0v
authored andcommitted
x86/sev: Add full support for a segmented RMP table
A segmented RMP table allows for improved locality of reference between the memory protected by the RMP and the RMP entries themselves. Add support to detect and initialize a segmented RMP table with multiple segments as configured by the system BIOS. While the RMPREAD instruction will be used to read an RMP entry in a segmented RMP, initialization and debugging capabilities will require the mapping of the segments. The RMP_CFG MSR indicates if segmented RMP support is enabled and, if enabled, the amount of memory that an RMP segment covers. When segmented RMP support is enabled, the RMP_BASE MSR points to the start of the RMP bookkeeping area, which is 16K in size. The RMP Segment Table (RST) is located immediately after the bookkeeping area and is 4K in size. The RST contains up to 512 8-byte entries that identify the location of the RMP segment and amount of memory mapped by the segment (which must be less than or equal to the configured segment size). The physical address that is covered by a segment is based on the segment size and the index of the segment in the RST. The RMP entry for a physical address is based on the offset within the segment. For example, if the segment size is 64GB (0x1000000000 or 1 << 36), then physical address 0x9000800000 is RST entry 9 (0x9000800000 >> 36) and RST entry 9 covers physical memory 0x9000000000 to 0x9FFFFFFFFF. The RMP entry index within the RMP segment is the physical address AND-ed with the segment mask, 64GB - 1 (0xFFFFFFFFF), and then right-shifted 12 bits or PHYS_PFN(0x9000800000 & 0xFFFFFFFFF), which is 0x800. CPUID 0x80000025_EBX[9:0] describes the number of RMP segments that can be cached by the hardware. Additionally, if CPUID 0x80000025_EBX[10] is set, then the number of actual RMP segments defined cannot exceed the number of RMP segments that can be cached and can be used as a maximum RST index. [ bp: Unify printk hex format specifiers. ] Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Nikunj A Dadhania <[email protected]> Reviewed-by: Neeraj Upadhyay <[email protected]> Link: https://lore.kernel.org/r/02afd0ffd097a19cb6e5fb1bb76eb110496c5b11.1734101742.git.thomas.lendacky@amd.com
1 parent 0f14af0 commit 8ae3291

File tree

3 files changed

+245
-24
lines changed

3 files changed

+245
-24
lines changed

arch/x86/include/asm/cpufeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@
452452
#define X86_FEATURE_SME_COHERENT (19*32+10) /* AMD hardware-enforced cache coherency */
453453
#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* "debug_swap" AMD SEV-ES full debug state swap support */
454454
#define X86_FEATURE_RMPREAD (19*32+21) /* RMPREAD instruction */
455+
#define X86_FEATURE_SEGMENTED_RMP (19*32+23) /* Segmented RMP support */
455456
#define X86_FEATURE_SVSM (19*32+28) /* "svsm" SVSM present */
456457

457458
/* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */

arch/x86/include/asm/msr-index.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@
644644
#define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */
645645
#define MSR_AMD64_SVM_AVIC_DOORBELL 0xc001011b
646646
#define MSR_AMD64_VM_PAGE_FLUSH 0xc001011e
647+
#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
647648
#define MSR_AMD64_SEV_ES_GHCB 0xc0010130
648649
#define MSR_AMD64_SEV 0xc0010131
649650
#define MSR_AMD64_SEV_ENABLED_BIT 0
@@ -682,11 +683,12 @@
682683
#define MSR_AMD64_SNP_SMT_PROT BIT_ULL(MSR_AMD64_SNP_SMT_PROT_BIT)
683684
#define MSR_AMD64_SNP_RESV_BIT 18
684685
#define MSR_AMD64_SNP_RESERVED_MASK GENMASK_ULL(63, MSR_AMD64_SNP_RESV_BIT)
685-
686-
#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
687-
688686
#define MSR_AMD64_RMP_BASE 0xc0010132
689687
#define MSR_AMD64_RMP_END 0xc0010133
688+
#define MSR_AMD64_RMP_CFG 0xc0010136
689+
#define MSR_AMD64_SEG_RMP_ENABLED_BIT 0
690+
#define MSR_AMD64_SEG_RMP_ENABLED BIT_ULL(MSR_AMD64_SEG_RMP_ENABLED_BIT)
691+
#define MSR_AMD64_RMP_SEGMENT_SHIFT(x) (((x) & GENMASK_ULL(13, 8)) >> 8)
690692

691693
#define MSR_SVSM_CAA 0xc001f000
692694

arch/x86/virt/svm/sev.c

Lines changed: 239 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ struct rmp_segment_desc {
100100
* a specific portion of memory. There can be up to 512 8-byte entries,
101101
* one pages worth.
102102
*/
103+
#define RST_ENTRY_MAPPED_SIZE(x) ((x) & GENMASK_ULL(19, 0))
104+
#define RST_ENTRY_SEGMENT_BASE(x) ((x) & GENMASK_ULL(51, 20))
105+
106+
#define RST_SIZE SZ_4K
103107
static struct rmp_segment_desc **rmp_segment_table __ro_after_init;
104108
static unsigned int rst_max_index __ro_after_init = 512;
105109

@@ -110,6 +114,8 @@ static u64 rmp_segment_mask;
110114
#define RST_ENTRY_INDEX(x) ((x) >> rmp_segment_shift)
111115
#define RMP_ENTRY_INDEX(x) ((u64)(PHYS_PFN((x) & rmp_segment_mask)))
112116

117+
static u64 rmp_cfg;
118+
113119
/* Mask to apply to a PFN to get the first PFN of a 2MB page */
114120
#define PFN_PMD_MASK GENMASK_ULL(63, PMD_SHIFT - PAGE_SHIFT)
115121

@@ -198,12 +204,62 @@ static void __init __snp_fixup_e820_tables(u64 pa)
198204
}
199205
}
200206

201-
void __init snp_fixup_e820_tables(void)
207+
static void __init fixup_e820_tables_for_segmented_rmp(void)
208+
{
209+
u64 pa, *rst, size, mapped_size;
210+
unsigned int i;
211+
212+
__snp_fixup_e820_tables(probed_rmp_base);
213+
214+
pa = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ;
215+
216+
__snp_fixup_e820_tables(pa + RST_SIZE);
217+
218+
rst = early_memremap(pa, RST_SIZE);
219+
if (!rst)
220+
return;
221+
222+
for (i = 0; i < rst_max_index; i++) {
223+
pa = RST_ENTRY_SEGMENT_BASE(rst[i]);
224+
mapped_size = RST_ENTRY_MAPPED_SIZE(rst[i]);
225+
if (!mapped_size)
226+
continue;
227+
228+
__snp_fixup_e820_tables(pa);
229+
230+
/*
231+
* Mapped size in GB. Mapped size is allowed to exceed
232+
* the segment coverage size, but gets reduced to the
233+
* segment coverage size.
234+
*/
235+
mapped_size <<= 30;
236+
if (mapped_size > rmp_segment_size)
237+
mapped_size = rmp_segment_size;
238+
239+
/* Calculate the RMP segment size (16 bytes/page mapped) */
240+
size = PHYS_PFN(mapped_size) << 4;
241+
242+
__snp_fixup_e820_tables(pa + size);
243+
}
244+
245+
early_memunmap(rst, RST_SIZE);
246+
}
247+
248+
static void __init fixup_e820_tables_for_contiguous_rmp(void)
202249
{
203250
__snp_fixup_e820_tables(probed_rmp_base);
204251
__snp_fixup_e820_tables(probed_rmp_base + probed_rmp_size);
205252
}
206253

254+
void __init snp_fixup_e820_tables(void)
255+
{
256+
if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
257+
fixup_e820_tables_for_segmented_rmp();
258+
} else {
259+
fixup_e820_tables_for_contiguous_rmp();
260+
}
261+
}
262+
207263
static bool __init clear_rmptable_bookkeeping(void)
208264
{
209265
void *bk;
@@ -307,29 +363,17 @@ static bool __init alloc_rmp_segment_table(void)
307363
return true;
308364
}
309365

310-
/*
311-
* Do the necessary preparations which are verified by the firmware as
312-
* described in the SNP_INIT_EX firmware command description in the SNP
313-
* firmware ABI spec.
314-
*/
315-
static int __init snp_rmptable_init(void)
366+
static bool __init setup_contiguous_rmptable(void)
316367
{
317-
u64 max_rmp_pfn, calc_rmp_sz, rmptable_segment, rmptable_size, rmp_end, val;
318-
unsigned int i;
319-
320-
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
321-
return 0;
322-
323-
if (!amd_iommu_snp_en)
324-
goto nosnp;
368+
u64 max_rmp_pfn, calc_rmp_sz, rmptable_segment, rmptable_size, rmp_end;
325369

326370
if (!probed_rmp_size)
327-
goto nosnp;
371+
return false;
328372

329373
rmp_end = probed_rmp_base + probed_rmp_size - 1;
330374

331375
/*
332-
* Calculate the amount the memory that must be reserved by the BIOS to
376+
* Calculate the amount of memory that must be reserved by the BIOS to
333377
* address the whole RAM, including the bookkeeping area. The RMP itself
334378
* must also be covered.
335379
*/
@@ -341,21 +385,140 @@ static int __init snp_rmptable_init(void)
341385
if (calc_rmp_sz > probed_rmp_size) {
342386
pr_err("Memory reserved for the RMP table does not cover full system RAM (expected 0x%llx got 0x%llx)\n",
343387
calc_rmp_sz, probed_rmp_size);
344-
goto nosnp;
388+
return false;
345389
}
346390

347391
if (!alloc_rmp_segment_table())
348-
goto nosnp;
392+
return false;
349393

350394
/* Map only the RMP entries */
351395
rmptable_segment = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ;
352396
rmptable_size = probed_rmp_size - RMPTABLE_CPU_BOOKKEEPING_SZ;
353397

354398
if (!alloc_rmp_segment_desc(rmptable_segment, rmptable_size, 0)) {
355399
free_rmp_segment_table();
356-
goto nosnp;
400+
return false;
357401
}
358402

403+
return true;
404+
}
405+
406+
static bool __init setup_segmented_rmptable(void)
407+
{
408+
u64 rst_pa, *rst, pa, ram_pa_end, ram_pa_max;
409+
unsigned int i, max_index;
410+
411+
if (!probed_rmp_base)
412+
return false;
413+
414+
if (!alloc_rmp_segment_table())
415+
return false;
416+
417+
rst_pa = probed_rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ;
418+
rst = memremap(rst_pa, RST_SIZE, MEMREMAP_WB);
419+
if (!rst) {
420+
pr_err("Failed to map RMP segment table addr 0x%llx\n", rst_pa);
421+
goto e_free;
422+
}
423+
424+
pr_info("Segmented RMP using %lluGB segments\n", rmp_segment_size >> 30);
425+
426+
ram_pa_max = max_pfn << PAGE_SHIFT;
427+
428+
max_index = 0;
429+
ram_pa_end = 0;
430+
for (i = 0; i < rst_max_index; i++) {
431+
u64 rmp_segment, rmp_size, mapped_size;
432+
433+
mapped_size = RST_ENTRY_MAPPED_SIZE(rst[i]);
434+
if (!mapped_size)
435+
continue;
436+
437+
max_index = i;
438+
439+
/*
440+
* Mapped size in GB. Mapped size is allowed to exceed the
441+
* segment coverage size, but gets reduced to the segment
442+
* coverage size.
443+
*/
444+
mapped_size <<= 30;
445+
if (mapped_size > rmp_segment_size) {
446+
pr_info("RMP segment %u mapped size (0x%llx) reduced to 0x%llx\n",
447+
i, mapped_size, rmp_segment_size);
448+
mapped_size = rmp_segment_size;
449+
}
450+
451+
rmp_segment = RST_ENTRY_SEGMENT_BASE(rst[i]);
452+
453+
/* Calculate the RMP segment size (16 bytes/page mapped) */
454+
rmp_size = PHYS_PFN(mapped_size) << 4;
455+
456+
pa = (u64)i << rmp_segment_shift;
457+
458+
/*
459+
* Some segments may be for MMIO mapped above system RAM. These
460+
* segments are used for Trusted I/O.
461+
*/
462+
if (pa < ram_pa_max)
463+
ram_pa_end = pa + mapped_size;
464+
465+
if (!alloc_rmp_segment_desc(rmp_segment, rmp_size, pa))
466+
goto e_unmap;
467+
468+
pr_info("RMP segment %u physical address [0x%llx - 0x%llx] covering [0x%llx - 0x%llx]\n",
469+
i, rmp_segment, rmp_segment + rmp_size - 1, pa, pa + mapped_size - 1);
470+
}
471+
472+
if (ram_pa_max > ram_pa_end) {
473+
pr_err("Segmented RMP does not cover full system RAM (expected 0x%llx got 0x%llx)\n",
474+
ram_pa_max, ram_pa_end);
475+
goto e_unmap;
476+
}
477+
478+
/* Adjust the maximum index based on the found segments */
479+
rst_max_index = max_index + 1;
480+
481+
memunmap(rst);
482+
483+
return true;
484+
485+
e_unmap:
486+
memunmap(rst);
487+
488+
e_free:
489+
free_rmp_segment_table();
490+
491+
return false;
492+
}
493+
494+
static bool __init setup_rmptable(void)
495+
{
496+
if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
497+
return setup_segmented_rmptable();
498+
} else {
499+
return setup_contiguous_rmptable();
500+
}
501+
}
502+
503+
/*
504+
* Do the necessary preparations which are verified by the firmware as
505+
* described in the SNP_INIT_EX firmware command description in the SNP
506+
* firmware ABI spec.
507+
*/
508+
static int __init snp_rmptable_init(void)
509+
{
510+
unsigned int i;
511+
u64 val;
512+
513+
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
514+
return 0;
515+
516+
if (!amd_iommu_snp_en)
517+
goto nosnp;
518+
519+
if (!setup_rmptable())
520+
goto nosnp;
521+
359522
/*
360523
* Check if SEV-SNP is already enabled, this can happen in case of
361524
* kexec boot.
@@ -419,7 +582,7 @@ static void set_rmp_segment_info(unsigned int segment_shift)
419582

420583
#define RMP_ADDR_MASK GENMASK_ULL(51, 13)
421584

422-
bool snp_probe_rmptable_info(void)
585+
static bool probe_contiguous_rmptable_info(void)
423586
{
424587
u64 rmp_sz, rmp_base, rmp_end;
425588

@@ -452,6 +615,61 @@ bool snp_probe_rmptable_info(void)
452615
return true;
453616
}
454617

618+
static bool probe_segmented_rmptable_info(void)
619+
{
620+
unsigned int eax, ebx, segment_shift, segment_shift_min, segment_shift_max;
621+
u64 rmp_base, rmp_end;
622+
623+
rdmsrl(MSR_AMD64_RMP_BASE, rmp_base);
624+
if (!(rmp_base & RMP_ADDR_MASK)) {
625+
pr_err("Memory for the RMP table has not been reserved by BIOS\n");
626+
return false;
627+
}
628+
629+
rdmsrl(MSR_AMD64_RMP_END, rmp_end);
630+
WARN_ONCE(rmp_end & RMP_ADDR_MASK,
631+
"Segmented RMP enabled but RMP_END MSR is non-zero\n");
632+
633+
/* Obtain the min and max supported RMP segment size */
634+
eax = cpuid_eax(0x80000025);
635+
segment_shift_min = eax & GENMASK(5, 0);
636+
segment_shift_max = (eax & GENMASK(11, 6)) >> 6;
637+
638+
/* Verify the segment size is within the supported limits */
639+
segment_shift = MSR_AMD64_RMP_SEGMENT_SHIFT(rmp_cfg);
640+
if (segment_shift > segment_shift_max || segment_shift < segment_shift_min) {
641+
pr_err("RMP segment size (%u) is not within advertised bounds (min=%u, max=%u)\n",
642+
segment_shift, segment_shift_min, segment_shift_max);
643+
return false;
644+
}
645+
646+
/* Override the max supported RST index if a hardware limit exists */
647+
ebx = cpuid_ebx(0x80000025);
648+
if (ebx & BIT(10))
649+
rst_max_index = ebx & GENMASK(9, 0);
650+
651+
set_rmp_segment_info(segment_shift);
652+
653+
probed_rmp_base = rmp_base;
654+
probed_rmp_size = 0;
655+
656+
pr_info("Segmented RMP base table physical range [0x%016llx - 0x%016llx]\n",
657+
rmp_base, rmp_base + RMPTABLE_CPU_BOOKKEEPING_SZ + RST_SIZE);
658+
659+
return true;
660+
}
661+
662+
bool snp_probe_rmptable_info(void)
663+
{
664+
if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP))
665+
rdmsrl(MSR_AMD64_RMP_CFG, rmp_cfg);
666+
667+
if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)
668+
return probe_segmented_rmptable_info();
669+
else
670+
return probe_contiguous_rmptable_info();
671+
}
672+
455673
/*
456674
* About the array_index_nospec() usage below:
457675
*

0 commit comments

Comments
 (0)