File Index
Files and directories
Sign in
FILE

noinstr.c

File preview with syntax highlighting.

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