[PATCH 60/62] x86/sev-es: Support CPU offline/online

From: Joerg Roedel
Date: Tue Feb 11 2020 - 08:54:13 EST


From: Joerg Roedel <jroedel@xxxxxxx>

Add a play_dead handler when running under SEV-ES. This is needed
because the hypervisor can't deliver an SIPI request to restart the AP.
Instead the kernel has to issue a VMGEXIT to halt the VCPU. When the
hypervisor would deliver and SIPI is wakes up the VCPU instead.

Signed-off-by: Joerg Roedel <jroedel@xxxxxxx>
---
arch/x86/include/uapi/asm/svm.h | 1 +
arch/x86/kernel/sev-es.c | 46 +++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)

diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index a19ce9681ec2..20a05839dd9a 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -84,6 +84,7 @@
/* SEV-ES software-defined VMGEXIT events */
#define SVM_VMGEXIT_MMIO_READ 0x80000001
#define SVM_VMGEXIT_MMIO_WRITE 0x80000002
+#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004
#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1
diff --git a/arch/x86/kernel/sev-es.c b/arch/x86/kernel/sev-es.c
index d8193d37ed2b..755708f72824 100644
--- a/arch/x86/kernel/sev-es.c
+++ b/arch/x86/kernel/sev-es.c
@@ -22,6 +22,8 @@
#include <asm/processor.h>
#include <asm/traps.h>
#include <asm/svm.h>
+#include <asm/smp.h>
+#include <asm/cpu.h>

#define DR7_RESET_VALUE 0x400

@@ -252,6 +254,48 @@ static bool __init setup_ghcb(void)
return true;
}

+#ifdef CONFIG_HOTPLUG_CPU
+static void sev_es_ap_hlt_loop(void)
+{
+ struct ghcb *ghcb;
+
+ ghcb = this_cpu_ptr(&ghcb_page);
+
+ while (true) {
+ ghcb_invalidate(ghcb);
+ ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_AP_HLT_LOOP);
+ ghcb_set_sw_exit_info_1(ghcb, 0);
+ ghcb_set_sw_exit_info_2(ghcb, 0);
+
+ write_ghcb_msr(__pa(ghcb));
+ VMGEXIT();
+
+ /* Wakup Signal? */
+ if (ghcb_is_valid_sw_exit_info_2(ghcb) &&
+ ghcb->save.sw_exit_info_2 != 0)
+ break;
+ }
+}
+
+void sev_es_play_dead(void)
+{
+ play_dead_common();
+
+ /* IRQs now disabled */
+
+ sev_es_ap_hlt_loop();
+
+ /*
+ * If we get here, the VCPU was woken up again. Jump to CPU
+ * startup code to get it back online.
+ */
+
+ start_cpu();
+}
+#else /* CONFIG_HOTPLUG_CPU */
+#define sev_es_play_dead native_play_dead
+#endif /* CONFIG_HOTPLUG_CPU */
+
void encrypted_state_init_ghcbs(void)
{
int cpu;
@@ -267,6 +311,8 @@ void encrypted_state_init_ghcbs(void)
sizeof(ghcb_page) >> PAGE_SHIFT);
memset(ghcb, 0, sizeof(*ghcb));
}
+
+ smp_ops.play_dead = sev_es_play_dead;
}

static void __init early_forward_exception(struct es_em_ctxt *ctxt)
--
2.17.1