The SEV-SNP guest is required to perform GHCB GPA registration. This is
because the hypervisor may prefer that a guest use a consistent and/or
specific GPA for the GHCB associated with a vCPU. For more information,
see the GHCB specification section 2.5.2.

During the boot, init_ghcb() allocates a per-cpu GHCB page. On very first
VC exception, the exception handler switch to using the per-cpu GHCB page
allocated during the init_ghcb(). The GHCB page must be registered in
the current vcpu context.

Cc: Thomas Gleixner <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Tony Luck <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: "Peter Zijlstra (Intel)" <[email protected]>
Cc: Paolo Bonzini <[email protected]>
Cc: Tom Lendacky <[email protected]>
Cc: David Rientjes <[email protected]>
Cc: Sean Christopherson <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Brijesh Singh <[email protected]>
---
 arch/x86/kernel/Makefile  |  3 ++
 arch/x86/kernel/sev-es.c  | 19 +++++++++++++
 arch/x86/kernel/sev-snp.c | 58 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)
 create mode 100644 arch/x86/kernel/sev-snp.c

diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 5eeb808eb024..2fb24c49d2e3 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -21,6 +21,7 @@ CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_early_printk.o = -pg
 CFLAGS_REMOVE_head64.o = -pg
 CFLAGS_REMOVE_sev-es.o = -pg
+CFLAGS_REMOVE_sev-snp.o = -pg
 endif
 
 KASAN_SANITIZE_head$(BITS).o                           := n
@@ -29,6 +30,7 @@ KASAN_SANITIZE_dumpstack_$(BITS).o                    := n
 KASAN_SANITIZE_stacktrace.o                            := n
 KASAN_SANITIZE_paravirt.o                              := n
 KASAN_SANITIZE_sev-es.o                                        := n
+KASAN_SANITIZE_sev-snp.o                               := n
 
 # With some compiler versions the generated code results in boot hangs, caused
 # by several compilation units. To be safe, disable all instrumentation.
@@ -151,6 +153,7 @@ obj-$(CONFIG_UNWINDER_FRAME_POINTER)        += 
unwind_frame.o
 obj-$(CONFIG_UNWINDER_GUESS)           += unwind_guess.o
 
 obj-$(CONFIG_AMD_MEM_ENCRYPT)          += sev-es.o
+obj-$(CONFIG_AMD_MEM_ENCRYPT)          += sev-snp.o
 ###
 # 64 bit specific files
 ifeq ($(CONFIG_X86_64),y)
diff --git a/arch/x86/kernel/sev-es.c b/arch/x86/kernel/sev-es.c
index 0bd1a0fc587e..004bf1102dc1 100644
--- a/arch/x86/kernel/sev-es.c
+++ b/arch/x86/kernel/sev-es.c
@@ -23,6 +23,7 @@
 #include <asm/cpu_entry_area.h>
 #include <asm/stacktrace.h>
 #include <asm/sev-es.h>
+#include <asm/sev-snp.h>
 #include <asm/insn-eval.h>
 #include <asm/fpu/internal.h>
 #include <asm/processor.h>
@@ -88,6 +89,13 @@ struct sev_es_runtime_data {
         * is currently unsupported in SEV-ES guests.
         */
        unsigned long dr7;
+
+       /*
+        * SEV-SNP requires that the GHCB must be registered before using it.
+        * The flag below will indicate whether the GHCB is registered, if its
+        * not registered then sev_es_get_ghcb() will perform the registration.
+        */
+       bool ghcb_registered;
 };
 
 struct ghcb_state {
@@ -196,6 +204,12 @@ static __always_inline struct ghcb *sev_es_get_ghcb(struct 
ghcb_state *state)
                data->ghcb_active = true;
        }
 
+       /* SEV-SNP guest requires that GHCB must be registered before using it. 
*/
+       if (sev_snp_active() && !data->ghcb_registered) {
+               sev_snp_register_ghcb(__pa(ghcb));
+               data->ghcb_registered = true;
+       }
+
        return ghcb;
 }
 
@@ -569,6 +583,10 @@ static bool __init sev_es_setup_ghcb(void)
        /* Alright - Make the boot-ghcb public */
        boot_ghcb = &boot_ghcb_page;
 
+       /* SEV-SNP guest requires that GHCB GPA must be registered */
+       if (sev_snp_active())
+               sev_snp_register_ghcb(__pa(&boot_ghcb_page));
+
        return true;
 }
 
@@ -658,6 +676,7 @@ static void __init init_ghcb(int cpu)
 
        data->ghcb_active = false;
        data->backup_ghcb_active = false;
+       data->ghcb_registered = false;
 }
 
 void __init sev_es_init_vc_handling(void)
diff --git a/arch/x86/kernel/sev-snp.c b/arch/x86/kernel/sev-snp.c
new file mode 100644
index 000000000000..d32225c2b653
--- /dev/null
+++ b/arch/x86/kernel/sev-snp.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD Memory Encryption Support
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ *
+ * Author: Brijesh Singh <[email protected]>
+ */
+
+#define pr_fmt(fmt)    "SEV-SNP: " fmt
+
+#include <linux/mem_encrypt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/sev-es.h>
+#include <asm/sev-snp.h>
+
+static inline u64 sev_es_rd_ghcb_msr(void)
+{
+       return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
+}
+
+static 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);
+}
+
+/* Provides sev_es_terminate() */
+#include "sev-common-shared.c"
+
+void sev_snp_register_ghcb(unsigned long paddr)
+{
+       u64 pfn = paddr >> PAGE_SHIFT;
+       u64 old, val;
+
+       /* save the old GHCB MSR */
+       old = sev_es_rd_ghcb_msr();
+
+       /* Issue VMGEXIT */
+       sev_es_wr_ghcb_msr(GHCB_REGISTER_GPA_REQ_VAL(pfn));
+       VMGEXIT();
+
+       val = sev_es_rd_ghcb_msr();
+
+       /* If the response GPA is not ours then abort the guest */
+       if ((GHCB_SEV_GHCB_RESP_CODE(val) != GHCB_REGISTER_GPA_RESP) ||
+           (GHCB_REGISTER_GPA_RESP_VAL(val) != pfn))
+               sev_es_terminate(GHCB_SEV_ES_REASON_GENERAL_REQUEST);
+
+       /* Restore the GHCB MSR value */
+       sev_es_wr_ghcb_msr(old);
+}
-- 
2.17.1

Reply via email to