[PATCH v7 37/45] x86/compressed: use firmware-validated CPUID for SEV-SNP guests
From: Brijesh Singh
Date: Wed Nov 10 2021 - 17:10:59 EST
From: Michael Roth <michael.roth@xxxxxxx>
SEV-SNP guests will be provided the location of special 'secrets'
'CPUID' pages via the Confidential Computing blob. This blob is
provided to the boot kernel either through an EFI config table entry,
or via a setup_data structure as defined by the Linux Boot Protocol.
Locate the Confidential Computing from these sources and, if found,
use the provided CPUID page/table address to create a copy that the
boot 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 | 13 +++++++++
arch/x86/include/asm/sev.h | 1 +
arch/x86/kernel/sev-shared.c | 48 ++++++++++++++++++++++++++++++++++
3 files changed, 62 insertions(+)
diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index a41e7d29f328..d109ec982961 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -411,6 +411,19 @@ bool snp_init(struct boot_params *bp)
if (!cc_info)
return false;
+ /*
+ * If SEV-SNP-specific Confidential Computing blob is present, then
+ * firmware/bootloader have indicated SEV-SNP support. Verifying this
+ * involves CPUID checks which will be more reliable if the SEV-SNP
+ * CPUID table is used. See comments for snp_cpuid_info_create() for
+ * more details.
+ */
+ snp_cpuid_info_create(cc_info);
+
+ /* SEV-SNP CPUID table should be set up now. */
+ if (!snp_cpuid_active())
+ sev_es_terminate(1, GHCB_TERM_CPUID);
+
/*
* Pass run-time kernel a pointer to CC info via boot_params so EFI
* config table doesn't need to be searched again during early startup
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index cd189c20bcc4..b6a97863b71f 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -157,6 +157,7 @@ bool snp_init(struct boot_params *bp);
* sev-shared.c via #include and these declarations can be dropped.
*/
struct cc_blob_sev_info *snp_find_cc_blob_setup_data(struct boot_params *bp);
+void __init snp_cpuid_info_create(const struct cc_blob_sev_info *cc_info);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
index 05cb88fa1437..4189d2808ff4 100644
--- a/arch/x86/kernel/sev-shared.c
+++ b/arch/x86/kernel/sev-shared.c
@@ -65,6 +65,11 @@ static u64 sev_hv_features __ro_after_init;
static struct snp_cpuid_info cpuid_info_copy __ro_after_init;
static bool snp_cpuid_initialized __ro_after_init;
+/* Copy of the SNP firmware's CPUID page. */
+static struct snp_cpuid_info cpuid_info_copy __ro_after_init;
+
+static bool snp_cpuid_initialized __ro_after_init;
+
/*
* These will be initialized based on CPUID table so that non-present
* all-zero leaves (for sparse tables) can be differentiated from
@@ -413,6 +418,23 @@ snp_cpuid_find_validated_func(u32 func, u32 subfunc, u32 *eax, u32 *ebx,
return false;
}
+static void __init snp_cpuid_set_ranges(void)
+{
+ const struct snp_cpuid_info *cpuid_info = snp_cpuid_info_get_ptr();
+ int i;
+
+ 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;
+ }
+}
+
static bool snp_cpuid_check_range(u32 func)
{
if (func <= cpuid_std_range_max ||
@@ -978,3 +1000,29 @@ snp_find_cc_blob_setup_data(struct boot_params *bp)
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.
+ */
+void __init snp_cpuid_info_create(const struct cc_blob_sev_info *cc_info)
+{
+ const struct snp_cpuid_info *cpuid_info_fw, *cpuid_info;
+
+ if (!cc_info || !cc_info->cpuid_phys || cc_info->cpuid_len < PAGE_SIZE)
+ sev_es_terminate(1, 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(1, GHCB_TERM_CPUID);
+
+ cpuid_info = snp_cpuid_info_get_ptr();
+ memcpy((void *)cpuid_info, cpuid_info_fw, sizeof(*cpuid_info));
+ snp_cpuid_initialized = true;
+ snp_cpuid_set_ranges();
+}
--
2.25.1