xref: /openbsd-src/usr.sbin/vmd/psp.c (revision 7f22b52a24eeebfc8cbfddf21a32770f3db6c5e9)
1 /*	$OpenBSD: psp.c,v 1.5 2024/11/06 23:04:45 bluhm Exp $	*/
2 
3 /*
4  * Copyright (c) 2023, 2024 Hans-Joerg Hoexer <hshoexer@genua.de>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 
22 #include <dev/ic/pspvar.h>
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <string.h>
27 
28 #include "vmd.h"
29 
30 extern struct vmd	*env;
31 
32 /* Guest policy */
33 #define GPOL_NODBG	(1ULL << 0)	/* no debuggin */
34 #define GPOL_NOKS	(1ULL << 1)	/* no key sharing */
35 #define GPOL_ES		(1ULL << 2)	/* SEV-ES required */
36 #define GPOL_NOSEND	(1ULL << 3)	/* no guest migration */
37 #define GPOL_DOMAIN	(1ULL << 4)	/* no migration to other domain */
38 #define GPOL_SEV	(1ULL << 5)	/* no migration to non-SEV platform */
39 
40 
41 /*
42  * Retrieve platform state.
43  */
44 int
45 psp_get_pstate(uint16_t *state, uint8_t *major, uint8_t *minor,
46     uint8_t *build, uint8_t *seves)
47 {
48 	struct psp_platform_status pst;
49 
50 	if (ioctl(env->vmd_psp_fd, PSP_IOC_GET_PSTATUS, &pst) < 0) {
51 		log_warn("%s: ioctl", __func__);
52 		return (-1);
53 	}
54 
55 	if (state)
56 		*state = pst.state;
57 	if (major)
58 		*major = pst.api_major;
59 	if (minor)
60 		*minor = pst.api_minor;
61 	if (build)
62 		*build = (pst.cfges_build >> 24) & 0xff;
63 	if (seves)
64 		*seves = pst.cfges_build & 0x1;
65 
66 	return (0);
67 }
68 
69 
70 /*
71  * Flush data fabrics of all cores.
72  *
73  * This ensures all data of a SEV enabled guest is committed to
74  * memory.  This needs to be done before an ASID is assigend to
75  * guest using psp_activate().
76  */
77 int
78 psp_df_flush(void)
79 {
80 	if (ioctl(env->vmd_psp_fd, PSP_IOC_DF_FLUSH) < 0) {
81 		log_warn("%s: ioctl", __func__);
82 		return (-1);
83 	}
84 
85 	return (0);
86 }
87 
88 
89 /*
90  * Retrieve guest state.
91  */
92 int
93 psp_get_gstate(uint32_t handle, uint32_t *policy, uint32_t *asid,
94     uint8_t *state)
95 {
96 	struct psp_guest_status gst;
97 
98 	memset(&gst, 0, sizeof(gst));
99 	gst.handle = handle;
100 
101 	if (ioctl(env->vmd_psp_fd, PSP_IOC_GET_GSTATUS, &gst) < 0) {
102 		log_warn("%s: ioctl", __func__);
103 		return (-1);
104 	}
105 
106 	if (policy)
107 		*policy = gst.policy;
108 	if (asid)
109 		*asid = gst.asid;
110 	if (state)
111 		*state = gst.state;
112 
113 	return (0);
114 }
115 
116 
117 /*
118  * Start the launch sequence of a guest.
119  */
120 int
121 psp_launch_start(uint32_t *handle)
122 {
123 	struct psp_launch_start ls;
124 
125 	memset(&ls, 0, sizeof(ls));
126 
127 	/* Set guest policy. */
128 	ls.policy = (GPOL_NODBG | GPOL_NOKS | GPOL_NOSEND | GPOL_DOMAIN |
129 	    GPOL_SEV);
130 
131 	if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_START, &ls) < 0) {
132 		log_warn("%s: ioctl", __func__);
133 		return (-1);
134 	}
135 
136 	if (handle)
137 		*handle = ls.handle;
138 
139 	return (0);
140 }
141 
142 
143 /*
144  * Encrypt and measure a memory range.
145  */
146 int
147 psp_launch_update(uint32_t handle, vaddr_t v, size_t len)
148 {
149 	struct psp_launch_update_data lud;
150 
151 	memset(&lud, 0, sizeof(lud));
152 	lud.handle = handle;
153 	lud.paddr = v;			/* will be converted to paddr */
154 	lud.length = len;
155 
156 	if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_UPDATE_DATA, &lud) < 0) {
157 		log_warn("%s: ioctl", __func__);
158 		return (-1);
159 	}
160 
161 	return (0);
162 }
163 
164 
165 /*
166  * Finalize and return memory measurement.
167  *
168  * We ask the PSP to provide a measurement (HMAC) over the encrypted
169  * memory.  As we do not yet negotiate a shared integrity key with
170  * the PSP, the measurement is not really meaningful.  Thus we just
171  * log it for now.
172  */
173 int
174 psp_launch_measure(uint32_t handle)
175 {
176 	struct psp_launch_measure lm;
177 	char *p, buf[256];
178 	size_t len;
179 	unsigned int i;
180 
181 	memset(&lm, 0, sizeof(lm));
182 	lm.handle = handle;
183 	lm.measure_len = sizeof(lm.psp_measure);
184 	memset(lm.measure, 0, sizeof(lm.measure));
185 	memset(lm.measure_nonce, 0, sizeof(lm.measure_nonce));
186 
187 	if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_MEASURE, &lm) < 0) {
188 		log_warn("%s: ioctl", __func__);
189 		return (-1);
190 	}
191 
192 	/*
193 	 * We can not verify the measurement, yet. Therefore just
194 	 * log it.
195 	 */
196 	len = sizeof(buf);
197 	memset(buf, 0, len);
198 	p = buf;
199 	for (i = 0; i < sizeof(lm.measure) && len >= 2;
200 	    i++, p += 2, len -= 2) {
201 		snprintf(p, len, "%02x", lm.measure[i]);
202 	}
203 	log_info("%s: measurement 0x%s", __func__, buf);
204 
205 	len = sizeof(buf);
206 	memset(buf, 0, len);
207 	p = buf;
208 	for (i = 0; i < sizeof(lm.measure_nonce) && len >= 2;
209 	    i++, p += 2, len -= 2) {
210 		snprintf(p, len, "%02x", lm.measure_nonce[i]);
211 	}
212 	log_info("%s: nonce 0x%s", __func__, buf);
213 
214 	return (0);
215 }
216 
217 
218 /*
219  * Finalize launch sequence.
220  */
221 int
222 psp_launch_finish(uint32_t handle)
223 {
224 	struct psp_launch_finish lf;
225 
226 	lf.handle = handle;
227 
228 	if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_FINISH, &lf) < 0) {
229 		log_warn("%s: ioctl", __func__);
230 		return (-1);
231 	}
232 
233 	return (0);
234 }
235 
236 
237 /*
238  * Activate a guest.
239  *
240  * This associates the guest's ASID with the handle used to identify
241  * crypto contexts managed by the PSP.
242  */
243 int
244 psp_activate(uint32_t handle, uint32_t asid)
245 {
246 	struct psp_activate act;
247 
248 	act.handle = handle;
249 	act.asid = asid;
250 
251 	if (ioctl(env->vmd_psp_fd, PSP_IOC_ACTIVATE, &act) < 0) {
252 		log_warn("%s: ioctl", __func__);
253 		return (-1);
254 	}
255 
256 	return (0);
257 }
258 
259 
260 /*
261  * Deactivate and decommission a guest.
262  *
263  * This deassociates the guest's ASID from the crypto contexts in
264  * the PSP.  Then the PSP releases the crypto contexts (i.e. deletes
265  * keys).
266  */
267 int
268 psp_guest_shutdown(uint32_t handle)
269 {
270 	struct psp_guest_shutdown gshutdown;
271 
272 	gshutdown.handle = handle;
273 
274 	if (ioctl(env->vmd_psp_fd, PSP_IOC_GUEST_SHUTDOWN, &gshutdown) < 0) {
275 		log_warn("%s: ioctl", __func__);
276 		return (-1);
277 	}
278 
279 	return (0);
280 }
281 
282 /*
283  * Initialize PSP.
284  */
285 static int
286 psp_init(void)
287 {
288 	if (ioctl(env->vmd_psp_fd, PSP_IOC_INIT) < 0) {
289 		log_warn("%s: ioctl", __func__);
290 		return (-1);
291 	}
292 
293 	return (0);
294 }
295 
296 /*
297  * Shutdown PSP.
298  */
299 static int
300 psp_shutdown(void)
301 {
302 	if (ioctl(env->vmd_psp_fd, PSP_IOC_SHUTDOWN) < 0) {
303 		log_warn("%s: ioctl", __func__);
304 		return (-1);
305 	}
306 
307 	return (0);
308 }
309 
310 /*
311  * Reset PSP.
312  *
313  * Shut PSP down, then re-initialize it.  This clears and resets
314  * all active contexts.
315  */
316 static int
317 psp_reset(void)
318 {
319 	int	ret;
320 
321 	if ((ret = psp_shutdown()) < 0 || (ret = psp_init()) < 0)
322 		return (ret);
323 
324 	return (0);
325 }
326 
327 void
328 psp_setup(void)
329 {
330 	uint8_t	major, minor, build;
331 
332 	env->vmd_psp_fd = open(PSP_NODE, O_RDWR);
333 	if (env->vmd_psp_fd == -1) {
334 		if (errno != ENXIO)
335 			log_debug("%s: failed to open %s", __func__, PSP_NODE);
336 		return;
337 	}
338 
339 	if (psp_reset() < 0)
340 		fatalx("%s: failed to reset PSP", __func__);
341 	if (psp_get_pstate(NULL, &major, &minor, &build, NULL) < 0)
342 		fatalx("%s: failed to get platform state", __func__);
343 	log_info("PSP api %hhu.%hhu, build %hhu", major, minor, build);
344 }
345