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