]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/sev: Prepare for splitting off early SEV code
authorArd Biesheuvel <ardb@kernel.org>
Thu, 10 Apr 2025 13:41:25 +0000 (15:41 +0200)
committerIngo Molnar <mingo@kernel.org>
Sat, 12 Apr 2025 09:13:05 +0000 (11:13 +0200)
Prepare for splitting off parts of the SEV core.c source file into a
file that carries code that must tolerate being called from the early
1:1 mapping. This will allow special build-time handling of thise code,
to ensure that it gets generated in a way that is compatible with the
early execution context.

So create a de-facto internal SEV API and put the definitions into
sev-internal.h. No attempt is made to allow this header file to be
included in arbitrary other sources - this is explicitly not the intent.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Dionna Amalie Glaze <dionnaglaze@google.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Kevin Loughlin <kevinloughlin@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: linux-efi@vger.kernel.org
Link: https://lore.kernel.org/r/20250410134117.3713574-20-ardb+git@google.com
arch/x86/boot/compressed/sev.c
arch/x86/coco/sev/core.c
arch/x86/coco/sev/shared.c
arch/x86/include/asm/sev-internal.h [new file with mode: 0644]
arch/x86/include/asm/sev.h

index bb55934c1cee7053726629e90e82b5b5b5825d21..6eadd790f4e58e6802127a95e482e9b5394dc499 100644 (file)
@@ -126,20 +126,25 @@ static bool fault_in_kernel_space(unsigned long address)
 #include "../../lib/inat.c"
 #include "../../lib/insn.c"
 
-/* Include code for early handlers */
-#include "../../coco/sev/shared.c"
+extern struct svsm_ca *boot_svsm_caa;
+extern u64 boot_svsm_caa_pa;
 
-static struct svsm_ca *svsm_get_caa(void)
+struct svsm_ca *svsm_get_caa(void)
 {
        return boot_svsm_caa;
 }
 
-static u64 svsm_get_caa_pa(void)
+u64 svsm_get_caa_pa(void)
 {
        return boot_svsm_caa_pa;
 }
 
-static int svsm_perform_call_protocol(struct svsm_call *call)
+int svsm_perform_call_protocol(struct svsm_call *call);
+
+/* Include code for early handlers */
+#include "../../coco/sev/shared.c"
+
+int svsm_perform_call_protocol(struct svsm_call *call)
 {
        struct ghcb *ghcb;
        int ret;
index 832f7a7b10b23139dc2edd30f7e13afacf328ff6..aeb7731862c0dc709e45ba1483d5f70491b8669c 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/cpu_entry_area.h>
 #include <asm/stacktrace.h>
 #include <asm/sev.h>
+#include <asm/sev-internal.h>
 #include <asm/insn-eval.h>
 #include <asm/fpu/xcr.h>
 #include <asm/processor.h>
@@ -44,8 +45,6 @@
 #include <asm/cpuid.h>
 #include <asm/cmdline.h>
 
-#define DR7_RESET_VALUE        0x400
-
 /* AP INIT values as documented in the APM2  section "Processor Initialization State" */
 #define AP_INIT_CS_LIMIT               0xffff
 #define AP_INIT_DS_LIMIT               0xffff
@@ -82,16 +81,16 @@ static const char * const sev_status_feat_names[] = {
 };
 
 /* For early boot hypervisor communication in SEV-ES enabled guests */
-static struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE);
+struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE);
 
 /*
  * Needs to be in the .data section because we need it NULL before bss is
  * cleared
  */
-static struct ghcb *boot_ghcb __section(".data");
+struct ghcb *boot_ghcb __section(".data");
 
 /* Bitmap of SEV features supported by the hypervisor */
-static u64 sev_hv_features __ro_after_init;
+u64 sev_hv_features __ro_after_init;
 
 /* Secrets page physical address from the CC blob */
 static u64 secrets_pa __ro_after_init;
@@ -105,54 +104,14 @@ static u64 snp_tsc_scale __ro_after_init;
 static u64 snp_tsc_offset __ro_after_init;
 static u64 snp_tsc_freq_khz __ro_after_init;
 
-/* #VC handler runtime per-CPU data */
-struct sev_es_runtime_data {
-       struct ghcb ghcb_page;
-
-       /*
-        * Reserve one page per CPU as backup storage for the unencrypted GHCB.
-        * It is needed when an NMI happens while the #VC handler uses the real
-        * GHCB, and the NMI handler itself is causing another #VC exception. In
-        * that case the GHCB content of the first handler needs to be backed up
-        * and restored.
-        */
-       struct ghcb backup_ghcb;
-
-       /*
-        * Mark the per-cpu GHCBs as in-use to detect nested #VC exceptions.
-        * There is no need for it to be atomic, because nothing is written to
-        * the GHCB between the read and the write of ghcb_active. So it is safe
-        * to use it when a nested #VC exception happens before the write.
-        *
-        * This is necessary for example in the #VC->NMI->#VC case when the NMI
-        * happens while the first #VC handler uses the GHCB. When the NMI code
-        * raises a second #VC handler it might overwrite the contents of the
-        * GHCB written by the first handler. To avoid this the content of the
-        * GHCB is saved and restored when the GHCB is detected to be in use
-        * already.
-        */
-       bool ghcb_active;
-       bool backup_ghcb_active;
-
-       /*
-        * Cached DR7 value - write it on DR7 writes and return it on reads.
-        * That value will never make it to the real hardware DR7 as debugging
-        * is currently unsupported in SEV-ES guests.
-        */
-       unsigned long dr7;
-};
-
-struct ghcb_state {
-       struct ghcb *ghcb;
-};
 
 /* For early boot SVSM communication */
-static struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
+struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
 
-static DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
-static DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
-static DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
-static DEFINE_PER_CPU(u64, svsm_caa_pa);
+DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
+DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
+DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
+DEFINE_PER_CPU(u64, svsm_caa_pa);
 
 static __always_inline bool on_vc_stack(struct pt_regs *regs)
 {
@@ -231,7 +190,7 @@ void noinstr __sev_es_ist_exit(void)
  *
  * Callers must disable local interrupts around it.
  */
-static noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
+noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
 {
        struct sev_es_runtime_data *data;
        struct ghcb *ghcb;
@@ -274,21 +233,6 @@ static noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
        return ghcb;
 }
 
-static inline u64 sev_es_rd_ghcb_msr(void)
-{
-       return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
-}
-
-static __always_inline void sev_es_wr_ghcb_msr(u64 val)
-{
-       u32 low, high;
-
-       low  = (u32)(val);
-       high = (u32)(val >> 32);
-
-       native_wrmsr(MSR_AMD64_SEV_ES_GHCB, low, high);
-}
-
 static int vc_fetch_insn_kernel(struct es_em_ctxt *ctxt,
                                unsigned char *buffer)
 {
@@ -601,33 +545,7 @@ static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt)
 /* Include code shared with pre-decompression boot stage */
 #include "shared.c"
 
-static inline struct svsm_ca *svsm_get_caa(void)
-{
-       /*
-        * Use rIP-relative references when called early in the boot. If
-        * ->use_cas is set, then it is late in the boot and no need
-        * to worry about rIP-relative references.
-        */
-       if (RIP_REL_REF(sev_cfg).use_cas)
-               return this_cpu_read(svsm_caa);
-       else
-               return RIP_REL_REF(boot_svsm_caa);
-}
-
-static u64 svsm_get_caa_pa(void)
-{
-       /*
-        * Use rIP-relative references when called early in the boot. If
-        * ->use_cas is set, then it is late in the boot and no need
-        * to worry about rIP-relative references.
-        */
-       if (RIP_REL_REF(sev_cfg).use_cas)
-               return this_cpu_read(svsm_caa_pa);
-       else
-               return RIP_REL_REF(boot_svsm_caa_pa);
-}
-
-static noinstr void __sev_put_ghcb(struct ghcb_state *state)
+noinstr void __sev_put_ghcb(struct ghcb_state *state)
 {
        struct sev_es_runtime_data *data;
        struct ghcb *ghcb;
@@ -652,7 +570,7 @@ static noinstr void __sev_put_ghcb(struct ghcb_state *state)
        }
 }
 
-static int svsm_perform_call_protocol(struct svsm_call *call)
+int svsm_perform_call_protocol(struct svsm_call *call)
 {
        struct ghcb_state state;
        unsigned long flags;
@@ -761,7 +679,7 @@ static u64 __init get_jump_table_addr(void)
        return ret;
 }
 
-static void __head
+void __head
 early_set_pages_state(unsigned long vaddr, unsigned long paddr,
                      unsigned long npages, enum psc_op op)
 {
index 04982d3568033dfdbab46b58aa8611c0d79b7fa0..a7c94020e3847c26c9dde7ca2585a44134dc6784 100644 (file)
  */
 u8 snp_vmpl __ro_after_init;
 EXPORT_SYMBOL_GPL(snp_vmpl);
-static struct svsm_ca *boot_svsm_caa __ro_after_init;
-static u64 boot_svsm_caa_pa __ro_after_init;
-
-static struct svsm_ca *svsm_get_caa(void);
-static u64 svsm_get_caa_pa(void);
-static int svsm_perform_call_protocol(struct svsm_call *call);
+struct svsm_ca *boot_svsm_caa __ro_after_init;
+u64 boot_svsm_caa_pa __ro_after_init;
 
 /* I/O parameters for CPUID-related helpers */
 struct cpuid_leaf {
@@ -55,36 +51,6 @@ struct cpuid_leaf {
        u32 edx;
 };
 
-/*
- * Individual entries of the SNP CPUID table, as defined by the SNP
- * Firmware ABI, Revision 0.9, Section 7.1, Table 14.
- */
-struct snp_cpuid_fn {
-       u32 eax_in;
-       u32 ecx_in;
-       u64 xcr0_in;
-       u64 xss_in;
-       u32 eax;
-       u32 ebx;
-       u32 ecx;
-       u32 edx;
-       u64 __reserved;
-} __packed;
-
-/*
- * SNP CPUID table, as defined by the SNP Firmware ABI, Revision 0.9,
- * Section 8.14.2.6. Also noted there is the SNP firmware-enforced limit
- * of 64 entries per CPUID table.
- */
-#define SNP_CPUID_COUNT_MAX 64
-
-struct snp_cpuid_table {
-       u32 count;
-       u32 __reserved1;
-       u64 __reserved2;
-       struct snp_cpuid_fn fn[SNP_CPUID_COUNT_MAX];
-} __packed;
-
 /*
  * Since feature negotiation related variables are set early in the boot
  * process they must reside in the .data section so as not to be zeroed
@@ -107,7 +73,7 @@ static u32 cpuid_std_range_max __ro_after_init;
 static u32 cpuid_hyp_range_max __ro_after_init;
 static u32 cpuid_ext_range_max __ro_after_init;
 
-static bool __init sev_es_check_cpu_features(void)
+bool __init sev_es_check_cpu_features(void)
 {
        if (!has_cpuflag(X86_FEATURE_RDRAND)) {
                error("RDRAND instruction not supported - no trusted source of randomness available\n");
@@ -117,7 +83,7 @@ static bool __init sev_es_check_cpu_features(void)
        return true;
 }
 
-static void __head __noreturn
+void __head __noreturn
 sev_es_terminate(unsigned int set, unsigned int reason)
 {
        u64 val = GHCB_MSR_TERM_REQ;
@@ -136,7 +102,7 @@ sev_es_terminate(unsigned int set, unsigned int reason)
 /*
  * The hypervisor features are available from GHCB version 2 onward.
  */
-static u64 get_hv_features(void)
+u64 get_hv_features(void)
 {
        u64 val;
 
@@ -153,7 +119,7 @@ static u64 get_hv_features(void)
        return GHCB_MSR_HV_FT_RESP_VAL(val);
 }
 
-static void snp_register_ghcb_early(unsigned long paddr)
+void snp_register_ghcb_early(unsigned long paddr)
 {
        unsigned long pfn = paddr >> PAGE_SHIFT;
        u64 val;
@@ -169,7 +135,7 @@ static void snp_register_ghcb_early(unsigned long paddr)
                sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_REGISTER);
 }
 
-static bool sev_es_negotiate_protocol(void)
+bool sev_es_negotiate_protocol(void)
 {
        u64 val;
 
@@ -190,12 +156,6 @@ static bool sev_es_negotiate_protocol(void)
        return true;
 }
 
-static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
-{
-       ghcb->save.sw_exit_code = 0;
-       __builtin_memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
-}
-
 static bool vc_decoding_needed(unsigned long exit_code)
 {
        /* Exceptions don't require to decode the instruction */
@@ -371,10 +331,10 @@ static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call)
        return svsm_process_result_codes(call);
 }
 
-static enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb,
-                                         struct es_em_ctxt *ctxt,
-                                         u64 exit_code, u64 exit_info_1,
-                                         u64 exit_info_2)
+enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb,
+                                  struct es_em_ctxt *ctxt,
+                                  u64 exit_code, u64 exit_info_1,
+                                  u64 exit_info_2)
 {
        /* Fill in protocol and format specifiers */
        ghcb->protocol_version = ghcb_version;
@@ -473,7 +433,7 @@ static int sev_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid
  * while running with the initial identity mapping as well as the
  * switch-over to kernel virtual addresses later.
  */
-static const struct snp_cpuid_table *snp_cpuid_get_table(void)
+const struct snp_cpuid_table *snp_cpuid_get_table(void)
 {
        return rip_rel_ptr(&cpuid_table_copy);
 }
diff --git a/arch/x86/include/asm/sev-internal.h b/arch/x86/include/asm/sev-internal.h
new file mode 100644 (file)
index 0000000..73cb774
--- /dev/null
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define DR7_RESET_VALUE        0x400
+
+extern struct ghcb boot_ghcb_page;
+extern struct ghcb *boot_ghcb;
+extern u64 sev_hv_features;
+
+/* #VC handler runtime per-CPU data */
+struct sev_es_runtime_data {
+       struct ghcb ghcb_page;
+
+       /*
+        * Reserve one page per CPU as backup storage for the unencrypted GHCB.
+        * It is needed when an NMI happens while the #VC handler uses the real
+        * GHCB, and the NMI handler itself is causing another #VC exception. In
+        * that case the GHCB content of the first handler needs to be backed up
+        * and restored.
+        */
+       struct ghcb backup_ghcb;
+
+       /*
+        * Mark the per-cpu GHCBs as in-use to detect nested #VC exceptions.
+        * There is no need for it to be atomic, because nothing is written to
+        * the GHCB between the read and the write of ghcb_active. So it is safe
+        * to use it when a nested #VC exception happens before the write.
+        *
+        * This is necessary for example in the #VC->NMI->#VC case when the NMI
+        * happens while the first #VC handler uses the GHCB. When the NMI code
+        * raises a second #VC handler it might overwrite the contents of the
+        * GHCB written by the first handler. To avoid this the content of the
+        * GHCB is saved and restored when the GHCB is detected to be in use
+        * already.
+        */
+       bool ghcb_active;
+       bool backup_ghcb_active;
+
+       /*
+        * Cached DR7 value - write it on DR7 writes and return it on reads.
+        * That value will never make it to the real hardware DR7 as debugging
+        * is currently unsupported in SEV-ES guests.
+        */
+       unsigned long dr7;
+};
+
+struct ghcb_state {
+       struct ghcb *ghcb;
+};
+
+extern struct svsm_ca boot_svsm_ca_page;
+
+struct ghcb *__sev_get_ghcb(struct ghcb_state *state);
+void __sev_put_ghcb(struct ghcb_state *state);
+
+DECLARE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
+DECLARE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
+
+void early_set_pages_state(unsigned long vaddr, unsigned long paddr,
+                          unsigned long npages, enum psc_op op);
+
+void __noreturn sev_es_terminate(unsigned int set, unsigned int reason);
+
+DECLARE_PER_CPU(struct svsm_ca *, svsm_caa);
+DECLARE_PER_CPU(u64, svsm_caa_pa);
+
+extern struct svsm_ca *boot_svsm_caa;
+extern u64 boot_svsm_caa_pa;
+
+static __always_inline struct svsm_ca *svsm_get_caa(void)
+{
+       /*
+        * Use rIP-relative references when called early in the boot. If
+        * ->use_cas is set, then it is late in the boot and no need
+        * to worry about rIP-relative references.
+        */
+       if (RIP_REL_REF(sev_cfg).use_cas)
+               return this_cpu_read(svsm_caa);
+       else
+               return RIP_REL_REF(boot_svsm_caa);
+}
+
+static __always_inline u64 svsm_get_caa_pa(void)
+{
+       /*
+        * Use rIP-relative references when called early in the boot. If
+        * ->use_cas is set, then it is late in the boot and no need
+        * to worry about rIP-relative references.
+        */
+       if (RIP_REL_REF(sev_cfg).use_cas)
+               return this_cpu_read(svsm_caa_pa);
+       else
+               return RIP_REL_REF(boot_svsm_caa_pa);
+}
+
+int svsm_perform_call_protocol(struct svsm_call *call);
+
+static inline u64 sev_es_rd_ghcb_msr(void)
+{
+       return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
+}
+
+static __always_inline void sev_es_wr_ghcb_msr(u64 val)
+{
+       u32 low, high;
+
+       low  = (u32)(val);
+       high = (u32)(val >> 32);
+
+       native_wrmsr(MSR_AMD64_SEV_ES_GHCB, low, high);
+}
+
+enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb,
+                                  struct es_em_ctxt *ctxt,
+                                  u64 exit_code, u64 exit_info_1,
+                                  u64 exit_info_2);
+
+void snp_register_ghcb_early(unsigned long paddr);
+bool sev_es_negotiate_protocol(void);
+bool sev_es_check_cpu_features(void);
+u64 get_hv_features(void);
+
+const struct snp_cpuid_table *snp_cpuid_get_table(void);
index ba7999f66abe6de599b3e5bc19fcf8ca2dd9131d..a8661dfc9a9a38040332d5d76132efd1b7895759 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/sev-common.h>
 #include <asm/coco.h>
 #include <asm/set_memory.h>
+#include <asm/svm.h>
 
 #define GHCB_PROTOCOL_MIN      1ULL
 #define GHCB_PROTOCOL_MAX      2ULL
@@ -83,6 +84,36 @@ extern void vc_no_ghcb(void);
 extern void vc_boot_ghcb(void);
 extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
 
+/*
+ * Individual entries of the SNP CPUID table, as defined by the SNP
+ * Firmware ABI, Revision 0.9, Section 7.1, Table 14.
+ */
+struct snp_cpuid_fn {
+       u32 eax_in;
+       u32 ecx_in;
+       u64 xcr0_in;
+       u64 xss_in;
+       u32 eax;
+       u32 ebx;
+       u32 ecx;
+       u32 edx;
+       u64 __reserved;
+} __packed;
+
+/*
+ * SNP CPUID table, as defined by the SNP Firmware ABI, Revision 0.9,
+ * Section 8.14.2.6. Also noted there is the SNP firmware-enforced limit
+ * of 64 entries per CPUID table.
+ */
+#define SNP_CPUID_COUNT_MAX 64
+
+struct snp_cpuid_table {
+       u32 count;
+       u32 __reserved1;
+       u64 __reserved2;
+       struct snp_cpuid_fn fn[SNP_CPUID_COUNT_MAX];
+} __packed;
+
 /* PVALIDATE return codes */
 #define PVALIDATE_FAIL_SIZEMISMATCH    6
 
@@ -484,6 +515,12 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req
 void __init snp_secure_tsc_prepare(void);
 void __init snp_secure_tsc_init(void);
 
+static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
+{
+       ghcb->save.sw_exit_code = 0;
+       __builtin_memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
+}
+
 #else  /* !CONFIG_AMD_MEM_ENCRYPT */
 
 #define snp_vmpl 0