1*d4077e6aSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0-only 2*d4077e6aSArd Biesheuvel /* 3*d4077e6aSArd Biesheuvel * AMD Memory Encryption Support 4*d4077e6aSArd Biesheuvel * 5*d4077e6aSArd Biesheuvel * Copyright (C) 2019 SUSE 6*d4077e6aSArd Biesheuvel * 7*d4077e6aSArd Biesheuvel * Author: Joerg Roedel <jroedel@suse.de> 8*d4077e6aSArd Biesheuvel */ 9*d4077e6aSArd Biesheuvel 10*d4077e6aSArd Biesheuvel #define pr_fmt(fmt) "SEV: " fmt 11*d4077e6aSArd Biesheuvel 12*d4077e6aSArd Biesheuvel #include <linux/bug.h> 13*d4077e6aSArd Biesheuvel #include <linux/kernel.h> 14*d4077e6aSArd Biesheuvel 15*d4077e6aSArd Biesheuvel #include <asm/cpu_entry_area.h> 16*d4077e6aSArd Biesheuvel #include <asm/msr.h> 17*d4077e6aSArd Biesheuvel #include <asm/ptrace.h> 18*d4077e6aSArd Biesheuvel #include <asm/sev.h> 19*d4077e6aSArd Biesheuvel #include <asm/sev-internal.h> 20*d4077e6aSArd Biesheuvel 21*d4077e6aSArd Biesheuvel static __always_inline bool on_vc_stack(struct pt_regs *regs) 22*d4077e6aSArd Biesheuvel { 23*d4077e6aSArd Biesheuvel unsigned long sp = regs->sp; 24*d4077e6aSArd Biesheuvel 25*d4077e6aSArd Biesheuvel /* User-mode RSP is not trusted */ 26*d4077e6aSArd Biesheuvel if (user_mode(regs)) 27*d4077e6aSArd Biesheuvel return false; 28*d4077e6aSArd Biesheuvel 29*d4077e6aSArd Biesheuvel /* SYSCALL gap still has user-mode RSP */ 30*d4077e6aSArd Biesheuvel if (ip_within_syscall_gap(regs)) 31*d4077e6aSArd Biesheuvel return false; 32*d4077e6aSArd Biesheuvel 33*d4077e6aSArd Biesheuvel return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC))); 34*d4077e6aSArd Biesheuvel } 35*d4077e6aSArd Biesheuvel 36*d4077e6aSArd Biesheuvel /* 37*d4077e6aSArd Biesheuvel * This function handles the case when an NMI is raised in the #VC 38*d4077e6aSArd Biesheuvel * exception handler entry code, before the #VC handler has switched off 39*d4077e6aSArd Biesheuvel * its IST stack. In this case, the IST entry for #VC must be adjusted, 40*d4077e6aSArd Biesheuvel * so that any nested #VC exception will not overwrite the stack 41*d4077e6aSArd Biesheuvel * contents of the interrupted #VC handler. 42*d4077e6aSArd Biesheuvel * 43*d4077e6aSArd Biesheuvel * The IST entry is adjusted unconditionally so that it can be also be 44*d4077e6aSArd Biesheuvel * unconditionally adjusted back in __sev_es_ist_exit(). Otherwise a 45*d4077e6aSArd Biesheuvel * nested sev_es_ist_exit() call may adjust back the IST entry too 46*d4077e6aSArd Biesheuvel * early. 47*d4077e6aSArd Biesheuvel * 48*d4077e6aSArd Biesheuvel * The __sev_es_ist_enter() and __sev_es_ist_exit() functions always run 49*d4077e6aSArd Biesheuvel * on the NMI IST stack, as they are only called from NMI handling code 50*d4077e6aSArd Biesheuvel * right now. 51*d4077e6aSArd Biesheuvel */ 52*d4077e6aSArd Biesheuvel void noinstr __sev_es_ist_enter(struct pt_regs *regs) 53*d4077e6aSArd Biesheuvel { 54*d4077e6aSArd Biesheuvel unsigned long old_ist, new_ist; 55*d4077e6aSArd Biesheuvel 56*d4077e6aSArd Biesheuvel /* Read old IST entry */ 57*d4077e6aSArd Biesheuvel new_ist = old_ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); 58*d4077e6aSArd Biesheuvel 59*d4077e6aSArd Biesheuvel /* 60*d4077e6aSArd Biesheuvel * If NMI happened while on the #VC IST stack, set the new IST 61*d4077e6aSArd Biesheuvel * value below regs->sp, so that the interrupted stack frame is 62*d4077e6aSArd Biesheuvel * not overwritten by subsequent #VC exceptions. 63*d4077e6aSArd Biesheuvel */ 64*d4077e6aSArd Biesheuvel if (on_vc_stack(regs)) 65*d4077e6aSArd Biesheuvel new_ist = regs->sp; 66*d4077e6aSArd Biesheuvel 67*d4077e6aSArd Biesheuvel /* 68*d4077e6aSArd Biesheuvel * Reserve additional 8 bytes and store old IST value so this 69*d4077e6aSArd Biesheuvel * adjustment can be unrolled in __sev_es_ist_exit(). 70*d4077e6aSArd Biesheuvel */ 71*d4077e6aSArd Biesheuvel new_ist -= sizeof(old_ist); 72*d4077e6aSArd Biesheuvel *(unsigned long *)new_ist = old_ist; 73*d4077e6aSArd Biesheuvel 74*d4077e6aSArd Biesheuvel /* Set new IST entry */ 75*d4077e6aSArd Biesheuvel this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], new_ist); 76*d4077e6aSArd Biesheuvel } 77*d4077e6aSArd Biesheuvel 78*d4077e6aSArd Biesheuvel void noinstr __sev_es_ist_exit(void) 79*d4077e6aSArd Biesheuvel { 80*d4077e6aSArd Biesheuvel unsigned long ist; 81*d4077e6aSArd Biesheuvel 82*d4077e6aSArd Biesheuvel /* Read IST entry */ 83*d4077e6aSArd Biesheuvel ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); 84*d4077e6aSArd Biesheuvel 85*d4077e6aSArd Biesheuvel if (WARN_ON(ist == __this_cpu_ist_top_va(VC))) 86*d4077e6aSArd Biesheuvel return; 87*d4077e6aSArd Biesheuvel 88*d4077e6aSArd Biesheuvel /* Read back old IST entry and write it to the TSS */ 89*d4077e6aSArd Biesheuvel this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], *(unsigned long *)ist); 90*d4077e6aSArd Biesheuvel } 91*d4077e6aSArd Biesheuvel 92*d4077e6aSArd Biesheuvel void noinstr __sev_es_nmi_complete(void) 93*d4077e6aSArd Biesheuvel { 94*d4077e6aSArd Biesheuvel struct ghcb_state state; 95*d4077e6aSArd Biesheuvel struct ghcb *ghcb; 96*d4077e6aSArd Biesheuvel 97*d4077e6aSArd Biesheuvel ghcb = __sev_get_ghcb(&state); 98*d4077e6aSArd Biesheuvel 99*d4077e6aSArd Biesheuvel vc_ghcb_invalidate(ghcb); 100*d4077e6aSArd Biesheuvel ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_NMI_COMPLETE); 101*d4077e6aSArd Biesheuvel ghcb_set_sw_exit_info_1(ghcb, 0); 102*d4077e6aSArd Biesheuvel ghcb_set_sw_exit_info_2(ghcb, 0); 103*d4077e6aSArd Biesheuvel 104*d4077e6aSArd Biesheuvel sev_es_wr_ghcb_msr(__pa_nodebug(ghcb)); 105*d4077e6aSArd Biesheuvel VMGEXIT(); 106*d4077e6aSArd Biesheuvel 107*d4077e6aSArd Biesheuvel __sev_put_ghcb(&state); 108*d4077e6aSArd Biesheuvel } 109*d4077e6aSArd Biesheuvel 110*d4077e6aSArd Biesheuvel /* 111*d4077e6aSArd Biesheuvel * Nothing shall interrupt this code path while holding the per-CPU 112*d4077e6aSArd Biesheuvel * GHCB. The backup GHCB is only for NMIs interrupting this path. 113*d4077e6aSArd Biesheuvel * 114*d4077e6aSArd Biesheuvel * Callers must disable local interrupts around it. 115*d4077e6aSArd Biesheuvel */ 116*d4077e6aSArd Biesheuvel noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state) 117*d4077e6aSArd Biesheuvel { 118*d4077e6aSArd Biesheuvel struct sev_es_runtime_data *data; 119*d4077e6aSArd Biesheuvel struct ghcb *ghcb; 120*d4077e6aSArd Biesheuvel 121*d4077e6aSArd Biesheuvel WARN_ON(!irqs_disabled()); 122*d4077e6aSArd Biesheuvel 123*d4077e6aSArd Biesheuvel data = this_cpu_read(runtime_data); 124*d4077e6aSArd Biesheuvel ghcb = &data->ghcb_page; 125*d4077e6aSArd Biesheuvel 126*d4077e6aSArd Biesheuvel if (unlikely(data->ghcb_active)) { 127*d4077e6aSArd Biesheuvel /* GHCB is already in use - save its contents */ 128*d4077e6aSArd Biesheuvel 129*d4077e6aSArd Biesheuvel if (unlikely(data->backup_ghcb_active)) { 130*d4077e6aSArd Biesheuvel /* 131*d4077e6aSArd Biesheuvel * Backup-GHCB is also already in use. There is no way 132*d4077e6aSArd Biesheuvel * to continue here so just kill the machine. To make 133*d4077e6aSArd Biesheuvel * panic() work, mark GHCBs inactive so that messages 134*d4077e6aSArd Biesheuvel * can be printed out. 135*d4077e6aSArd Biesheuvel */ 136*d4077e6aSArd Biesheuvel data->ghcb_active = false; 137*d4077e6aSArd Biesheuvel data->backup_ghcb_active = false; 138*d4077e6aSArd Biesheuvel 139*d4077e6aSArd Biesheuvel instrumentation_begin(); 140*d4077e6aSArd Biesheuvel panic("Unable to handle #VC exception! GHCB and Backup GHCB are already in use"); 141*d4077e6aSArd Biesheuvel instrumentation_end(); 142*d4077e6aSArd Biesheuvel } 143*d4077e6aSArd Biesheuvel 144*d4077e6aSArd Biesheuvel /* Mark backup_ghcb active before writing to it */ 145*d4077e6aSArd Biesheuvel data->backup_ghcb_active = true; 146*d4077e6aSArd Biesheuvel 147*d4077e6aSArd Biesheuvel state->ghcb = &data->backup_ghcb; 148*d4077e6aSArd Biesheuvel 149*d4077e6aSArd Biesheuvel /* Backup GHCB content */ 150*d4077e6aSArd Biesheuvel *state->ghcb = *ghcb; 151*d4077e6aSArd Biesheuvel } else { 152*d4077e6aSArd Biesheuvel state->ghcb = NULL; 153*d4077e6aSArd Biesheuvel data->ghcb_active = true; 154*d4077e6aSArd Biesheuvel } 155*d4077e6aSArd Biesheuvel 156*d4077e6aSArd Biesheuvel return ghcb; 157*d4077e6aSArd Biesheuvel } 158*d4077e6aSArd Biesheuvel 159*d4077e6aSArd Biesheuvel noinstr void __sev_put_ghcb(struct ghcb_state *state) 160*d4077e6aSArd Biesheuvel { 161*d4077e6aSArd Biesheuvel struct sev_es_runtime_data *data; 162*d4077e6aSArd Biesheuvel struct ghcb *ghcb; 163*d4077e6aSArd Biesheuvel 164*d4077e6aSArd Biesheuvel WARN_ON(!irqs_disabled()); 165*d4077e6aSArd Biesheuvel 166*d4077e6aSArd Biesheuvel data = this_cpu_read(runtime_data); 167*d4077e6aSArd Biesheuvel ghcb = &data->ghcb_page; 168*d4077e6aSArd Biesheuvel 169*d4077e6aSArd Biesheuvel if (state->ghcb) { 170*d4077e6aSArd Biesheuvel /* Restore GHCB from Backup */ 171*d4077e6aSArd Biesheuvel *ghcb = *state->ghcb; 172*d4077e6aSArd Biesheuvel data->backup_ghcb_active = false; 173*d4077e6aSArd Biesheuvel state->ghcb = NULL; 174*d4077e6aSArd Biesheuvel } else { 175*d4077e6aSArd Biesheuvel /* 176*d4077e6aSArd Biesheuvel * Invalidate the GHCB so a VMGEXIT instruction issued 177*d4077e6aSArd Biesheuvel * from userspace won't appear to be valid. 178*d4077e6aSArd Biesheuvel */ 179*d4077e6aSArd Biesheuvel vc_ghcb_invalidate(ghcb); 180*d4077e6aSArd Biesheuvel data->ghcb_active = false; 181*d4077e6aSArd Biesheuvel } 182*d4077e6aSArd Biesheuvel } 183