xref: /openbsd/usr.sbin/vmd/sev.c (revision 06d0d098)
1 /*	$OpenBSD: sev.c,v 1.5 2024/11/06 22:06:16 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/param.h>	/* roundup */
21 
22 #include <crypto/xform.h>
23 #include <dev/ic/pspvar.h>
24 
25 #include <string.h>
26 
27 #include "vmd.h"
28 
29 extern struct vmd_vm	*current_vm;
30 
31 /*
32  * Prepare guest to use SEV.
33  *
34  * This asks the PSP to create a new crypto context including a
35  * memory encryption key and assign a handle to the context.
36  *
37  * When the PSP driver psp(4) attaches, it initializes the platform.
38  * If this fails for whatever reason we can not run a guest using SEV.
39  */
40 int
sev_init(struct vmd_vm * vm)41 sev_init(struct vmd_vm *vm)
42 {
43 	struct vmop_create_params *vmc = &vm->vm_params;
44 	struct vm_create_params	*vcp = &vmc->vmc_params;
45 	uint32_t		 handle;
46 	uint16_t		 pstate;
47 	uint8_t			 gstate;
48 
49 	if (!vcp->vcp_sev)
50 		return (0);
51 
52 	if (psp_get_pstate(&pstate, NULL, NULL, NULL, NULL)) {
53 		log_warnx("%s: failed to get platform state", __func__);
54 		return (-1);
55 	}
56 	if (pstate == PSP_PSTATE_UNINIT) {
57 		log_warnx("%s: platform uninitialized", __func__);
58 		return (-1);
59 	}
60 
61 	if (psp_launch_start(&handle) < 0) {
62 		log_warnx("%s: launch failed", __func__);
63 		return (-1);
64 	}
65 	vm->vm_sev_handle = handle;
66 
67 	if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
68 		log_warnx("%s: failed to get guest state", __func__);
69 		return (-1);
70 	}
71 	if (gstate != PSP_GSTATE_LUPDATE) {
72 		log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
73 		return (-1);
74 	}
75 
76 	return (0);
77 }
78 
79 /*
80  * Record memory segments to be encrypted for SEV.
81  */
82 int
sev_register_encryption(vaddr_t addr,size_t size)83 sev_register_encryption(vaddr_t addr, size_t size)
84 {
85 	struct vmop_create_params *vmc;
86 	struct vm_create_params *vcp;
87 	struct vm_mem_range	*vmr;
88 	size_t			 off;
89 	int			 i;
90 
91 	vmc = &current_vm->vm_params;
92 	vcp = &vmc->vmc_params;
93 
94 	if (!vcp->vcp_sev)
95 		return (0);
96 
97 	if (size == 0)
98 		return (0);
99 
100 	/* Adjust address and size to be aligend to AES_XTS_BLOCKSIZE. */
101 	if (addr & (AES_XTS_BLOCKSIZE - 1)) {
102 		size += (addr & (AES_XTS_BLOCKSIZE - 1));
103 		addr &= ~(AES_XTS_BLOCKSIZE - 1);
104 	}
105 
106 	vmr = find_gpa_range(&current_vm->vm_params.vmc_params, addr, size);
107 	if (vmr == NULL) {
108 		log_warnx("%s: failed - invalid memory range addr = 0x%lx, "
109 		    "len = 0x%zx", __func__, addr, size);
110 		return (-1);
111 	}
112 	if (current_vm->vm_sev_nmemsegments ==
113 	    nitems(current_vm->vm_sev_memsegments)) {
114 		log_warnx("%s: failed - out of SEV memory segments", __func__);
115 		return (-1);
116 	}
117 	i = current_vm->vm_sev_nmemsegments++;
118 
119 	off = addr - vmr->vmr_gpa;
120 
121 	current_vm->vm_sev_memsegments[i].vmr_va = vmr->vmr_va + off;
122 	current_vm->vm_sev_memsegments[i].vmr_size = size;
123 	current_vm->vm_sev_memsegments[i].vmr_gpa = vmr->vmr_gpa + off;
124 
125 	log_debug("%s: i %d addr 0x%lx size 0x%lx vmr_va 0x%lx vmr_gpa 0x%lx "
126 	    "vmr_size 0x%lx", __func__, i, addr, size,
127 	    current_vm->vm_sev_memsegments[i].vmr_va,
128 	    current_vm->vm_sev_memsegments[i].vmr_gpa,
129 	    current_vm->vm_sev_memsegments[i].vmr_size);
130 
131 	return (0);
132 }
133 
134 /*
135  * Encrypt and measure previously recorded memroy segments.
136  *
137  * This encrypts the memory initially used by the guest.  This
138  * includes the kernel or BIOS image, initial stack, boot arguments
139  * and page tables.
140  *
141  * We also ask the PSP to provide a measurement.  However, right
142  * now we can not really verify it.
143  */
144 int
sev_encrypt_memory(struct vmd_vm * vm)145 sev_encrypt_memory(struct vmd_vm *vm)
146 {
147 	struct vmop_create_params *vmc = &vm->vm_params;
148 	struct vm_create_params *vcp = &vmc->vmc_params;
149 	struct vm_mem_range	*vmr;
150 	size_t			 i;
151 	uint8_t			 gstate;
152 
153 	if (!vcp->vcp_sev)
154 		return (0);
155 
156 	for (i = 0; i < vm->vm_sev_nmemsegments; i++) {
157 		vmr = &vm->vm_sev_memsegments[i];
158 
159 		/* tell PSP to encrypt this range */
160 		if (psp_launch_update(vm->vm_sev_handle, vmr->vmr_va,
161 		    roundup(vmr->vmr_size, AES_XTS_BLOCKSIZE))) {
162 			log_warnx("%s: failed to launch update page "
163 			    "%zu:0x%lx", __func__, i, vmr->vmr_va);
164 			return (-1);
165 		}
166 
167 		log_debug("%s: encrypted %zu:0x%lx size 0x%lx", __func__, i,
168 		    vmr->vmr_va, vmr->vmr_size);
169 	}
170 	if (psp_launch_measure(vm->vm_sev_handle)) {
171 		log_warnx("%s: failed to launch measure", __func__);
172 		return (-1);
173 	}
174 	if (psp_launch_finish(vm->vm_sev_handle)) {
175 		log_warnx("%s: failed to launch finish", __func__);
176 		return (-1);
177 	}
178 
179 	if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
180 		log_warnx("%s: failed to get guest state", __func__);
181 		return (-1);
182 	}
183 	if (gstate != PSP_GSTATE_RUNNING) {
184 		log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
185 		return (-1);
186 	}
187 
188 	return (0);
189 }
190 
191 
192 /*
193  * Activate a guest's SEV crypto state.
194  */
195 int
sev_activate(struct vmd_vm * vm,int vcpu_id)196 sev_activate(struct vmd_vm *vm, int vcpu_id)
197 {
198 	struct vmop_create_params *vmc = &vm->vm_params;
199 	struct vm_create_params *vcp = &vmc->vmc_params;
200 	uint8_t			 gstate;
201 
202 	if (!vcp->vcp_sev)
203 		return (0);
204 
205 	if (psp_df_flush() ||
206 	    psp_activate(vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id])) {
207 		log_warnx("%s: failed to activate guest: 0x%x:0x%x", __func__,
208 		    vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id]);
209 		return (-1);
210 	}
211 
212 	if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
213 		log_warnx("%s: failed to get guest state", __func__);
214 		return (-1);
215 	}
216 	if (gstate != PSP_GSTATE_LUPDATE) {
217 		log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
218 		return (-1);
219 	}
220 
221 	return (0);
222 }
223 
224 
225 /*
226  * Deactivate and decommission a guest's SEV crypto state.
227  */
228 int
sev_shutdown(struct vmd_vm * vm)229 sev_shutdown(struct vmd_vm *vm)
230 {
231 	struct vmop_create_params *vmc = &vm->vm_params;
232 	struct vm_create_params *vcp = &vmc->vmc_params;
233 
234 	if (!vcp->vcp_sev)
235 		return (0);
236 
237 	if (psp_guest_shutdown(vm->vm_sev_handle)) {
238 		log_warnx("failed to deactivate guest");
239 		return (-1);
240 	}
241 	vm->vm_sev_handle = 0;
242 
243 	return (0);
244 }
245