xref: /linux/arch/x86/coco/sev/noinstr.c (revision 22bdd6e68bbe270a916233ec5f34a13ae5e80ed9)
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