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
on_vc_stack(struct pt_regs * regs)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 */
__sev_es_ist_enter(struct pt_regs * regs)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
__sev_es_ist_exit(void)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
__sev_es_nmi_complete(void)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 */
__sev_get_ghcb(struct ghcb_state * state)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
__sev_put_ghcb(struct ghcb_state * state)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