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 = ¤t_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(¤t_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