1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG 5 * Author: Corvin Köhne <c.koehne@beckhoff.com> 6 */ 7 8 #include <sys/cdefs.h> 9 #include <sys/types.h> 10 #include <sys/param.h> 11 #include <sys/linker_set.h> 12 13 #include <machine/vmm.h> 14 15 #include <assert.h> 16 #include <err.h> 17 #include <errno.h> 18 #include <pthread.h> 19 #include <pthread_np.h> 20 #include <stddef.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <vmmapi.h> 24 25 #include "basl.h" 26 #include "config.h" 27 #include "mem.h" 28 #include "qemu_fwcfg.h" 29 #include "tpm_device.h" 30 #include "tpm_intf.h" 31 32 #define TPM_CRB_ADDRESS 0xFED40000 33 #define TPM_CRB_REGS_SIZE 0x1000 34 35 #define TPM_CRB_CONTROL_AREA_ADDRESS \ 36 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req)) 37 #define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE 38 39 #define TPM_CRB_DATA_BUFFER_ADDRESS \ 40 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer)) 41 #define TPM_CRB_DATA_BUFFER_SIZE 0xF80 42 43 #define TPM_CRB_LOCALITIES_MAX 5 44 45 #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024) 46 47 #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log" 48 49 #define TPM_CRB_INTF_NAME "crb" 50 51 struct tpm_crb_regs { 52 union tpm_crb_reg_loc_state { 53 struct { 54 uint32_t tpm_established : 1; 55 uint32_t loc_assigned : 1; 56 uint32_t active_locality : 3; 57 uint32_t _reserved : 2; 58 uint32_t tpm_req_valid_sts : 1; 59 }; 60 uint32_t val; 61 } loc_state; /* 0h */ 62 uint8_t _reserved1[4]; /* 4h */ 63 union tpm_crb_reg_loc_ctrl { 64 struct { 65 uint32_t request_access : 1; 66 uint32_t relinquish : 1; 67 uint32_t seize : 1; 68 uint32_t reset_establishment_bit : 1; 69 }; 70 uint32_t val; 71 } loc_ctrl; /* 8h */ 72 union tpm_crb_reg_loc_sts { 73 struct { 74 uint32_t granted : 1; 75 uint32_t been_seized : 1; 76 }; 77 uint32_t val; 78 } loc_sts; /* Ch */ 79 uint8_t _reserved2[0x20]; /* 10h */ 80 union tpm_crb_reg_intf_id { 81 struct { 82 uint64_t interface_type : 4; 83 uint64_t interface_version : 4; 84 uint64_t cap_locality : 1; 85 uint64_t cap_crb_idle_bypass : 1; 86 uint64_t _reserved1 : 1; 87 uint64_t cap_data_xfer_size_support : 2; 88 uint64_t cap_fifo : 1; 89 uint64_t cap_crb : 1; 90 uint64_t _reserved2 : 2; 91 uint64_t interface_selector : 2; 92 uint64_t intf_sel_lock : 1; 93 uint64_t _reserved3 : 4; 94 uint64_t rid : 8; 95 uint64_t vid : 16; 96 uint64_t did : 16; 97 }; 98 uint64_t val; 99 } intf_id; /* 30h */ 100 union tpm_crb_reg_ctrl_ext { 101 struct { 102 uint32_t clear; 103 uint32_t remaining_bytes; 104 }; 105 uint64_t val; 106 } ctrl_ext; /* 38 */ 107 union tpm_crb_reg_ctrl_req { 108 struct { 109 uint32_t cmd_ready : 1; 110 uint32_t go_idle : 1; 111 }; 112 uint32_t val; 113 } ctrl_req; /* 40h */ 114 union tpm_crb_reg_ctrl_sts { 115 struct { 116 uint32_t tpm_sts : 1; 117 uint32_t tpm_idle : 1; 118 }; 119 uint32_t val; 120 } ctrl_sts; /* 44h */ 121 union tpm_crb_reg_ctrl_cancel { 122 struct { 123 uint32_t cancel : 1; 124 }; 125 uint32_t val; 126 } ctrl_cancel; /* 48h */ 127 union tpm_crb_reg_ctrl_start { 128 struct { 129 uint32_t start : 1; 130 }; 131 uint32_t val; 132 } ctrl_start; /* 4Ch*/ 133 uint32_t int_enable; /* 50h */ 134 uint32_t int_sts; /* 54h */ 135 uint32_t cmd_size; /* 58h */ 136 uint32_t cmd_addr_lo; /* 5Ch */ 137 uint32_t cmd_addr_hi; /* 60h */ 138 uint32_t rsp_size; /* 64h */ 139 uint64_t rsp_addr; /* 68h */ 140 uint8_t _reserved3[0x10]; /* 70h */ 141 uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */ 142 } __packed; 143 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE, 144 "Invalid size of tpm_crb"); 145 146 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size) 147 #define CRB_CMD_SIZE_WRITE(regs, val) \ 148 do { \ 149 regs.cmd_size = val; \ 150 } while (0) 151 #define CRB_CMD_ADDR_READ(regs) \ 152 (((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo) 153 #define CRB_CMD_ADDR_WRITE(regs, val) \ 154 do { \ 155 regs.cmd_addr_lo = val & 0xFFFFFFFF; \ 156 regs.cmd_addr_hi = val >> 32; \ 157 } while (0) 158 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size) 159 #define CRB_RSP_SIZE_WRITE(regs, val) \ 160 do { \ 161 regs.rsp_size = val; \ 162 } while (0) 163 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr) 164 #define CRB_RSP_ADDR_WRITE(regs, val) \ 165 do { \ 166 regs.rsp_addr = val; \ 167 } while (0) 168 169 struct tpm_crb { 170 struct tpm_emul *emul; 171 void *emul_sc; 172 uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE]; 173 struct tpm_crb_regs regs; 174 pthread_t thread; 175 pthread_mutex_t mutex; 176 pthread_cond_t cond; 177 bool closing; 178 }; 179 180 static void * 181 tpm_crb_thread(void *const arg) 182 { 183 struct tpm_crb *const crb = arg; 184 185 pthread_mutex_lock(&crb->mutex); 186 for (;;) { 187 pthread_cond_wait(&crb->cond, &crb->mutex); 188 189 if (crb->closing) 190 break; 191 192 const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs); 193 const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs); 194 const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs); 195 const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs); 196 197 const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 198 const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 199 200 if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE || 201 cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE || 202 rsp_off > TPM_CRB_DATA_BUFFER_SIZE || 203 rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) { 204 warnx( 205 "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r", 206 __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr, 207 rsp_addr + rsp_size); 208 break; 209 } 210 211 /* 212 * The command response buffer interface uses a single buffer 213 * for sending a command to and receiving a response from the 214 * tpm. To avoid reading old data from the command buffer which 215 * might be a security issue, we zero out the command buffer 216 * before writing the response into it. The rsp_size parameter 217 * is controlled by the guest and it's not guaranteed that the 218 * response has a size of rsp_size (e.g. if the tpm returned an 219 * error, the response would have a different size than 220 * expected). For that reason, use a second buffer for the 221 * response. 222 */ 223 uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 }; 224 crb->emul->execute_cmd(crb->emul_sc, 225 &crb->regs.data_buffer[cmd_off], cmd_size, &rsp[rsp_off], 226 rsp_size); 227 228 memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE); 229 memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size); 230 231 crb->regs.ctrl_start.start = false; 232 } 233 pthread_mutex_unlock(&crb->mutex); 234 235 return (NULL); 236 } 237 238 static int 239 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc) 240 { 241 struct tpm_crb *crb = NULL; 242 int error; 243 244 assert(sc != NULL); 245 assert(emul != NULL); 246 247 crb = calloc(1, sizeof(struct tpm_crb)); 248 if (crb == NULL) { 249 warnx("%s: failed to allocate tpm crb", __func__); 250 error = ENOMEM; 251 goto err_out; 252 } 253 254 memset(crb, 0, sizeof(*crb)); 255 256 crb->emul = emul; 257 crb->emul_sc = emul_sc; 258 259 crb->regs.loc_state.tpm_req_valid_sts = true; 260 crb->regs.loc_state.tpm_established = true; 261 262 crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB; 263 crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB; 264 crb->regs.intf_id.cap_locality = false; 265 crb->regs.intf_id.cap_crb_idle_bypass = false; 266 crb->regs.intf_id.cap_data_xfer_size_support = 267 TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64; 268 crb->regs.intf_id.cap_fifo = false; 269 crb->regs.intf_id.cap_crb = true; 270 crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB; 271 crb->regs.intf_id.intf_sel_lock = false; 272 crb->regs.intf_id.rid = 0; 273 crb->regs.intf_id.vid = 0x1014; /* IBM */ 274 crb->regs.intf_id.did = 0x1014; /* IBM */ 275 276 crb->regs.ctrl_sts.tpm_idle = true; 277 278 CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 279 CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 280 CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 281 CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 282 283 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME, 284 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area); 285 if (error) { 286 warnx("%s: failed to add fwcfg file", __func__); 287 goto err_out; 288 } 289 290 error = pthread_mutex_init(&crb->mutex, NULL); 291 if (error) { 292 warnc(error, "%s: failed to init mutex", __func__); 293 goto err_out; 294 } 295 296 error = pthread_cond_init(&crb->cond, NULL); 297 if (error) { 298 warnc(error, "%s: failed to init cond", __func__); 299 goto err_out; 300 } 301 302 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb); 303 if (error) { 304 warnx("%s: failed to create thread\n", __func__); 305 goto err_out; 306 } 307 308 pthread_set_name_np(crb->thread, "tpm_intf_crb"); 309 310 *sc = crb; 311 312 return (0); 313 314 err_out: 315 free(crb); 316 317 return (error); 318 } 319 320 static void 321 tpm_crb_deinit(void *sc) 322 { 323 struct tpm_crb *crb; 324 325 if (sc == NULL) { 326 return; 327 } 328 329 crb = sc; 330 331 crb->closing = true; 332 pthread_cond_signal(&crb->cond); 333 pthread_join(crb->thread, NULL); 334 335 pthread_cond_destroy(&crb->cond); 336 pthread_mutex_destroy(&crb->mutex); 337 338 free(crb); 339 } 340 341 static int 342 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx) 343 { 344 struct basl_table *table; 345 346 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2, 347 BASL_TABLE_ALIGNMENT)); 348 349 /* Header */ 350 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1)); 351 /* Platform Class */ 352 BASL_EXEC(basl_table_append_int(table, 0, 2)); 353 /* Reserved */ 354 BASL_EXEC(basl_table_append_int(table, 0, 2)); 355 /* Control Address */ 356 BASL_EXEC( 357 basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8)); 358 /* Start Method == (7) Command Response Buffer */ 359 BASL_EXEC(basl_table_append_int(table, 7, 4)); 360 /* Start Method Specific Parameters */ 361 uint8_t parameters[12] = { 0 }; 362 BASL_EXEC(basl_table_append_bytes(table, parameters, 12)); 363 /* Log Area Minimum Length */ 364 BASL_EXEC( 365 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4)); 366 /* Log Area Start Address */ 367 BASL_EXEC( 368 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); 369 370 BASL_EXEC(basl_table_register_to_rsdt(table)); 371 372 return (0); 373 } 374 375 static struct tpm_intf tpm_intf_crb = { 376 .name = TPM_CRB_INTF_NAME, 377 .init = tpm_crb_init, 378 .deinit = tpm_crb_deinit, 379 .build_acpi_table = tpm_crb_build_acpi_table, 380 }; 381 TPM_INTF_SET(tpm_intf_crb); 382