x86/sev: Use firmware-validated CPUID for SEV-SNP guests
authorMichael Roth <michael.roth@amd.com>
Mon, 7 Mar 2022 21:33:49 +0000 (15:33 -0600)
committerBorislav Petkov <bp@suse.de>
Thu, 7 Apr 2022 14:47:12 +0000 (16:47 +0200)
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 boot_params 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@amd.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lore.kernel.org/r/20220307213356.2797205-40-brijesh.singh@amd.com
arch/x86/boot/compressed/sev.c
arch/x86/kernel/sev-shared.c
arch/x86/kernel/sev.c

index 82079ce7be06957a7a91f05143bde67ca224a99b..52f989f6acc281f95815bc76e0976348b5b8f635 100644 (file)
@@ -381,43 +381,6 @@ found_cc_info:
        return cc_info;
 }
 
-/*
- * Initialize the kernel's copy of the SNP CPUID table, and set up the
- * pointer that will be used to access it.
- *
- * Maintaining a direct mapping of the 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 setup_cpuid_table(const struct cc_blob_sev_info *cc_info)
-{
-       const struct snp_cpuid_table *cpuid_table_fw, *cpuid_table;
-       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_table_fw = (const struct snp_cpuid_table *)cc_info->cpuid_phys;
-       if (!cpuid_table_fw->count || cpuid_table_fw->count > SNP_CPUID_COUNT_MAX)
-               sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID);
-
-       cpuid_table = snp_cpuid_get_table();
-       memcpy((void *)cpuid_table, cpuid_table_fw, sizeof(*cpuid_table));
-
-       /* Initialize CPUID ranges for range-checking. */
-       for (i = 0; i < cpuid_table->count; i++) {
-               const struct snp_cpuid_fn *fn = &cpuid_table->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;
-       }
-}
-
 /*
  * Indicate SNP based on presence of SNP-specific CC blob. Subsequent checks
  * will verify the SNP CPUID/MSR bits.
index a7a1c0fb298e018644d946b9a6f37234d5a93626..2b4270d5559e546b5fc5302885fd73be7fbc74ec 100644 (file)
@@ -964,3 +964,40 @@ static struct cc_blob_sev_info *find_cc_blob_setup_data(struct boot_params *bp)
 
        return NULL;
 }
+
+/*
+ * Initialize the kernel's copy of the SNP CPUID table, and set up the
+ * pointer that will be used to access it.
+ *
+ * Maintaining a direct mapping of the 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 setup_cpuid_table(const struct cc_blob_sev_info *cc_info)
+{
+       const struct snp_cpuid_table *cpuid_table_fw, *cpuid_table;
+       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_table_fw = (const struct snp_cpuid_table *)cc_info->cpuid_phys;
+       if (!cpuid_table_fw->count || cpuid_table_fw->count > SNP_CPUID_COUNT_MAX)
+               sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID);
+
+       cpuid_table = snp_cpuid_get_table();
+       memcpy((void *)cpuid_table, cpuid_table_fw, sizeof(*cpuid_table));
+
+       /* Initialize CPUID ranges for range-checking. */
+       for (i = 0; i < cpuid_table->count; i++) {
+               const struct snp_cpuid_fn *fn = &cpuid_table->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;
+       }
+}
index 692da7b291276314db13131dabe89a951bb53387..c8733725d8bf90afaf3d6f6c0099cace5c2fb6a6 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/cpu.h>
 #include <asm/apic.h>
 #include <asm/cpuid.h>
+#include <asm/cmdline.h>
 
 #define DR7_RESET_VALUE        0x400
 
@@ -2025,6 +2026,8 @@ bool __init snp_init(struct boot_params *bp)
        if (!cc_info)
                return false;
 
+       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.
@@ -2038,3 +2041,24 @@ void __init snp_abort(void)
 {
        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
 }
+
+/*
+ * 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.
+ */
+static int __init report_cpuid_table(void)
+{
+       const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table();
+
+       if (!cpuid_table->count)
+               return 0;
+
+       pr_info("Using SNP CPUID table, %d entries present.\n",
+               cpuid_table->count);
+
+       return 0;
+}
+arch_initcall(report_cpuid_table);