Skip to content

Commit 4ca68e0

Browse files
joergroedelsuryasaimadhu
authored andcommitted
x86/sev-es: Handle NMI State
When running under SEV-ES, the kernel has to tell the hypervisor when to open the NMI window again after an NMI was injected. This is done with an NMI-complete message to the hypervisor. Add code to the kernel's NMI handler to send this message right at the beginning of do_nmi(). This always allows nesting NMIs. [ bp: Mark __sev_es_nmi_complete() noinstr: vmlinux.o: warning: objtool: exc_nmi()+0x17: call to __sev_es_nmi_complete() leaves .noinstr.text section While at it, use __pa_nodebug() for the same reason due to CONFIG_DEBUG_VIRTUAL=y: vmlinux.o: warning: objtool: __sev_es_nmi_complete()+0xd9: call to __phys_addr() leaves .noinstr.text section ] Signed-off-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lkml.kernel.org/r/20200907131613.12703-71-joro@8bytes.org
1 parent 094794f commit 4ca68e0

4 files changed

Lines changed: 32 additions & 0 deletions

File tree

arch/x86/include/asm/sev-es.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,17 @@ static __always_inline void sev_es_ist_exit(void)
9696
__sev_es_ist_exit();
9797
}
9898
extern int sev_es_setup_ap_jump_table(struct real_mode_header *rmh);
99+
extern void __sev_es_nmi_complete(void);
100+
static __always_inline void sev_es_nmi_complete(void)
101+
{
102+
if (static_branch_unlikely(&sev_es_enable_key))
103+
__sev_es_nmi_complete();
104+
}
99105
#else
100106
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
101107
static inline void sev_es_ist_exit(void) { }
102108
static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; }
109+
static inline void sev_es_nmi_complete(void) { }
103110
#endif
104111

105112
#endif

arch/x86/include/uapi/asm/svm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
/* SEV-ES software-defined VMGEXIT events */
8585
#define SVM_VMGEXIT_MMIO_READ 0x80000001
8686
#define SVM_VMGEXIT_MMIO_WRITE 0x80000002
87+
#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003
8788
#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004
8889
#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005
8990
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0

arch/x86/kernel/nmi.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,12 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
478478
{
479479
bool irq_state;
480480

481+
/*
482+
* Re-enable NMIs right here when running as an SEV-ES guest. This might
483+
* cause nested NMIs, but those can be handled safely.
484+
*/
485+
sev_es_nmi_complete();
486+
481487
if (IS_ENABLED(CONFIG_SMP) && arch_cpu_is_offline(smp_processor_id()))
482488
return;
483489

arch/x86/kernel/sev-es.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,24 @@ static bool vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
408408
/* Include code shared with pre-decompression boot stage */
409409
#include "sev-es-shared.c"
410410

411+
void noinstr __sev_es_nmi_complete(void)
412+
{
413+
struct ghcb_state state;
414+
struct ghcb *ghcb;
415+
416+
ghcb = sev_es_get_ghcb(&state);
417+
418+
vc_ghcb_invalidate(ghcb);
419+
ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_NMI_COMPLETE);
420+
ghcb_set_sw_exit_info_1(ghcb, 0);
421+
ghcb_set_sw_exit_info_2(ghcb, 0);
422+
423+
sev_es_wr_ghcb_msr(__pa_nodebug(ghcb));
424+
VMGEXIT();
425+
426+
sev_es_put_ghcb(&state);
427+
}
428+
411429
static u64 get_jump_table_addr(void)
412430
{
413431
struct ghcb_state state;

0 commit comments

Comments
 (0)