From: Michael Roth <michael.roth@xxxxxxx> SEV-SNP guests will be provided the location of special 'secrets' and 'CPUID' pages via the Confidential Computing blob. This blob is provided to the run-time kernel either through a bootparams field that was initialized by the boot/compressed kernel, or via a setup_data structure as defined by the Linux Boot Protocol. Locate the Confidential Computing blob from these sources and, if found, use the provided CPUID page/table address to create a copy that the run-time kernel will use when servicing CPUID instructions via a #VC handler. Signed-off-by: Michael Roth <michael.roth@xxxxxxx> Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx> --- arch/x86/boot/compressed/sev.c | 37 ----------------- arch/x86/kernel/sev-shared.c | 37 +++++++++++++++++ arch/x86/kernel/sev.c | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 37 deletions(-) diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c index cde5658e1007..21cdafac71e3 100644 --- a/arch/x86/boot/compressed/sev.c +++ b/arch/x86/boot/compressed/sev.c @@ -393,43 +393,6 @@ static struct cc_blob_sev_info *snp_find_cc_blob(struct boot_params *bp) return cc_info; } -/* - * Initialize the kernel's copy of the SEV-SNP CPUID table, and set up the - * pointer that will be used to access it. - * - * Maintaining a direct mapping of the SEV-SNP CPUID table used by firmware - * would be possible as an alternative, but the approach is brittle since the - * mapping needs to be updated in sync with all the changes to virtual memory - * layout and related mapping facilities throughout the boot process. - */ -static void snp_setup_cpuid_table(const struct cc_blob_sev_info *cc_info) -{ - const struct snp_cpuid_info *cpuid_info_fw, *cpuid_info; - int i; - - if (!cc_info || !cc_info->cpuid_phys || cc_info->cpuid_len < PAGE_SIZE) - sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); - - cpuid_info_fw = (const struct snp_cpuid_info *)cc_info->cpuid_phys; - if (!cpuid_info_fw->count || cpuid_info_fw->count > SNP_CPUID_COUNT_MAX) - sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); - - cpuid_info = snp_cpuid_info_get_ptr(); - memcpy((void *)cpuid_info, cpuid_info_fw, sizeof(*cpuid_info)); - - /* Initialize CPUID ranges for range-checking. */ - for (i = 0; i < cpuid_info->count; i++) { - const struct snp_cpuid_fn *fn = &cpuid_info->fn[i]; - - if (fn->eax_in == 0x0) - cpuid_std_range_max = fn->eax; - else if (fn->eax_in == 0x40000000) - cpuid_hyp_range_max = fn->eax; - else if (fn->eax_in == 0x80000000) - cpuid_ext_range_max = fn->eax; - } -} - bool snp_init(struct boot_params *bp) { struct cc_blob_sev_info *cc_info; diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c index 2bddbfea079b..795426ee6108 100644 --- a/arch/x86/kernel/sev-shared.c +++ b/arch/x86/kernel/sev-shared.c @@ -961,3 +961,40 @@ static struct cc_blob_sev_info *snp_find_cc_blob_setup_data(struct boot_params * return (struct cc_blob_sev_info *)(unsigned long)sd->cc_blob_address; } + +/* + * Initialize the kernel's copy of the SEV-SNP CPUID table, and set up the + * pointer that will be used to access it. + * + * Maintaining a direct mapping of the SEV-SNP CPUID table used by firmware + * would be possible as an alternative, but the approach is brittle since the + * mapping needs to be updated in sync with all the changes to virtual memory + * layout and related mapping facilities throughout the boot process. + */ +static void __init snp_setup_cpuid_table(const struct cc_blob_sev_info *cc_info) +{ + const struct snp_cpuid_info *cpuid_info_fw, *cpuid_info; + int i; + + if (!cc_info || !cc_info->cpuid_phys || cc_info->cpuid_len < PAGE_SIZE) + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); + + cpuid_info_fw = (const struct snp_cpuid_info *)cc_info->cpuid_phys; + if (!cpuid_info_fw->count || cpuid_info_fw->count > SNP_CPUID_COUNT_MAX) + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); + + cpuid_info = snp_cpuid_info_get_ptr(); + memcpy((void *)cpuid_info, cpuid_info_fw, sizeof(*cpuid_info)); + + /* Initialize CPUID ranges for range-checking. */ + for (i = 0; i < cpuid_info->count; i++) { + const struct snp_cpuid_fn *fn = &cpuid_info->fn[i]; + + if (fn->eax_in == 0x0) + cpuid_std_range_max = fn->eax; + else if (fn->eax_in == 0x40000000) + cpuid_hyp_range_max = fn->eax; + else if (fn->eax_in == 0x80000000) + cpuid_ext_range_max = fn->eax; + } +} diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c index 5c72b21c7e7b..cb97200bfda7 100644 --- a/arch/x86/kernel/sev.c +++ b/arch/x86/kernel/sev.c @@ -2034,6 +2034,8 @@ bool __init snp_init(struct boot_params *bp) if (!cc_info) return false; + snp_setup_cpuid_table(cc_info); + /* * The CC blob will be used later to access the secrets page. Cache * it here like the boot kernel does. @@ -2047,3 +2049,76 @@ void __init snp_abort(void) { sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED); } + +static void snp_dump_cpuid_table(void) +{ + const struct snp_cpuid_info *cpuid_info = snp_cpuid_info_get_ptr(); + int i = 0; + + pr_info("count=%d reserved=0x%x reserved2=0x%llx\n", + cpuid_info->count, cpuid_info->__reserved1, cpuid_info->__reserved2); + + for (i = 0; i < SNP_CPUID_COUNT_MAX; i++) { + const struct snp_cpuid_fn *fn = &cpuid_info->fn[i]; + + pr_info("index=%03d fn=0x%08x subfn=0x%08x: eax=0x%08x ebx=0x%08x ecx=0x%08x edx=0x%08x xcr0_in=0x%016llx xss_in=0x%016llx reserved=0x%016llx\n", + i, fn->eax_in, fn->ecx_in, fn->eax, fn->ebx, fn->ecx, + fn->edx, fn->xcr0_in, fn->xss_in, fn->__reserved); + } +} + +/* + * It is useful from an auditing/testing perspective to provide an easy way + * for the guest owner to know that the CPUID table has been initialized as + * expected, but that initialization happens too early in boot to print any + * sort of indicator, and there's not really any other good place to do it. + * So do it here. This is also a good place to flag unexpected usage of the + * CPUID table that does not violate the SNP specification, but may still + * be worth noting to the user. + */ +static int __init snp_check_cpuid_table(void) +{ + const struct snp_cpuid_info *cpuid_info = snp_cpuid_info_get_ptr(); + bool dump_table = false; + int i; + + if (!cpuid_info->count) + return 0; + + pr_info("Using SEV-SNP CPUID table, %d entries present.\n", + cpuid_info->count); + + if (cpuid_info->__reserved1 || cpuid_info->__reserved2) { + pr_warn("Unexpected use of reserved fields in CPUID table header.\n"); + dump_table = true; + } + + for (i = 0; i < cpuid_info->count; i++) { + const struct snp_cpuid_fn *fn = &cpuid_info->fn[i]; + struct snp_cpuid_fn zero_fn = {0}; + + /* + * Though allowed, an all-zero entry is not a valid leaf for linux + * guests, and likely the result of an incorrect entry count. + */ + if (!memcmp(fn, &zero_fn, sizeof(struct snp_cpuid_fn))) { + pr_warn("CPUID table contains all-zero entry at index=%d\n", i); + dump_table = true; + } + + if (fn->__reserved) { + pr_warn("Unexpected use of reserved fields in CPUID table entry at index=%d\n", + i); + dump_table = true; + } + } + + if (dump_table) { + pr_warn("Dumping CPUID table:\n"); + snp_dump_cpuid_table(); + } + + return 0; +} + +arch_initcall(snp_check_cpuid_table); -- 2.25.1