2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5 * Author: Corvin Köhne <c.koehne@beckhoff.com>
10 #include <sys/param.h>
11 #include <sys/linker_set.h>
13 #include <machine/vmm.h>
19 #include <pthread_np.h>
28 #include "qemu_fwcfg.h"
29 #include "tpm_device.h"
32 #define TPM_CRB_ADDRESS 0xFED40000
33 #define TPM_CRB_REGS_SIZE 0x1000
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
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
43 #define TPM_CRB_LOCALITIES_MAX 5
45 #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024)
47 #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log"
49 #define TPM_CRB_INTF_NAME "crb"
52 union tpm_crb_reg_loc_state {
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;
62 uint8_t _reserved1[4]; /* 4h */
63 union tpm_crb_reg_loc_ctrl {
65 uint32_t request_access : 1;
66 uint32_t relinquish : 1;
68 uint32_t reset_establishment_bit : 1;
72 union tpm_crb_reg_loc_sts {
75 uint32_t been_seized : 1;
79 uint8_t _reserved2[0x20]; /* 10h */
80 union tpm_crb_reg_intf_id {
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;
90 uint64_t _reserved2 : 2;
91 uint64_t interface_selector : 2;
92 uint64_t intf_sel_lock : 1;
93 uint64_t _reserved3 : 4;
100 union tpm_crb_reg_ctrl_ext {
103 uint32_t remaining_bytes;
107 union tpm_crb_reg_ctrl_req {
109 uint32_t cmd_ready : 1;
110 uint32_t go_idle : 1;
113 } ctrl_req; /* 40h */
114 union tpm_crb_reg_ctrl_sts {
116 uint32_t tpm_sts : 1;
117 uint32_t tpm_idle : 1;
120 } ctrl_sts; /* 44h */
121 union tpm_crb_reg_ctrl_cancel {
126 } ctrl_cancel; /* 48h */
127 union tpm_crb_reg_ctrl_start {
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 */
143 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE,
144 "Invalid size of tpm_crb");
146 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size)
147 #define CRB_CMD_SIZE_WRITE(regs, val) \
149 regs.cmd_size = val; \
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) \
155 regs.cmd_addr_lo = val & 0xFFFFFFFF; \
156 regs.cmd_addr_hi = val >> 32; \
158 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size)
159 #define CRB_RSP_SIZE_WRITE(regs, val) \
161 regs.rsp_size = val; \
163 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr)
164 #define CRB_RSP_ADDR_WRITE(regs, val) \
166 regs.rsp_addr = val; \
170 struct tpm_emul *emul;
172 uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE];
173 struct tpm_crb_regs regs;
175 pthread_mutex_t mutex;
181 tpm_crb_thread(void *const arg)
183 struct tpm_crb *const crb = arg;
185 pthread_mutex_lock(&crb->mutex);
188 * We're releasing the lock after wake up. Therefore, we have to
189 * check the closing condition before and after going to sleep.
194 pthread_cond_wait(&crb->cond, &crb->mutex);
199 const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs);
200 const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs);
201 const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs);
202 const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs);
204 const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
205 const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
207 if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE ||
208 cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE ||
209 rsp_off > TPM_CRB_DATA_BUFFER_SIZE ||
210 rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) {
212 "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r",
213 __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr,
214 rsp_addr + rsp_size);
218 uint8_t cmd[TPM_CRB_DATA_BUFFER_SIZE];
219 memcpy(cmd, crb->regs.data_buffer, TPM_CRB_DATA_BUFFER_SIZE);
222 * A TPM command can take multiple seconds to execute. As we've
223 * copied all required values and buffers at this point, we can
226 pthread_mutex_unlock(&crb->mutex);
229 * The command response buffer interface uses a single buffer
230 * for sending a command to and receiving a response from the
231 * tpm. To avoid reading old data from the command buffer which
232 * might be a security issue, we zero out the command buffer
233 * before writing the response into it. The rsp_size parameter
234 * is controlled by the guest and it's not guaranteed that the
235 * response has a size of rsp_size (e.g. if the tpm returned an
236 * error, the response would have a different size than
237 * expected). For that reason, use a second buffer for the
240 uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 };
241 crb->emul->execute_cmd(crb->emul_sc, &cmd[cmd_off], cmd_size,
242 &rsp[rsp_off], rsp_size);
244 pthread_mutex_lock(&crb->mutex);
245 memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE);
246 memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size);
248 crb->regs.ctrl_start.start = false;
250 pthread_mutex_unlock(&crb->mutex);
256 tpm_crb_mmiocpy(void *const dst, void *const src, const int size)
258 if (!(size == 1 || size == 2 || size == 4 || size == 8))
260 memcpy(dst, src, size);
266 tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir,
267 const uint64_t addr, const int size, uint64_t *const val, void *const arg1,
268 const long arg2 __unused)
275 if ((addr & (size - 1)) != 0) {
276 warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__,
277 (dir == MEM_F_READ) ? "read" : "write", addr, size);
283 off = addr - TPM_CRB_ADDRESS;
284 if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) {
288 shift = 8 * (off & 3);
289 ptr = (uint8_t *)&crb->regs + off;
291 if (dir == MEM_F_READ) {
292 error = tpm_crb_mmiocpy(val, ptr, size);
296 switch (off & ~0x3) {
297 case offsetof(struct tpm_crb_regs, loc_ctrl): {
298 union tpm_crb_reg_loc_ctrl loc_ctrl;
300 if ((size_t)size > sizeof(loc_ctrl))
303 *val = *val << shift;
304 tpm_crb_mmiocpy(&loc_ctrl, val, size);
306 if (loc_ctrl.relinquish) {
307 crb->regs.loc_sts.granted = false;
308 crb->regs.loc_state.loc_assigned = false;
309 } else if (loc_ctrl.request_access) {
310 crb->regs.loc_sts.granted = true;
311 crb->regs.loc_state.loc_assigned = true;
316 case offsetof(struct tpm_crb_regs, ctrl_req): {
317 union tpm_crb_reg_ctrl_req req;
319 if ((size_t)size > sizeof(req))
322 *val = *val << shift;
323 tpm_crb_mmiocpy(&req, val, size);
325 if (req.cmd_ready && !req.go_idle) {
326 crb->regs.ctrl_sts.tpm_idle = false;
327 } else if (!req.cmd_ready && req.go_idle) {
328 crb->regs.ctrl_sts.tpm_idle = true;
333 case offsetof(struct tpm_crb_regs, ctrl_cancel): {
334 /* TODO: cancel the tpm command */
336 "%s: cancelling a TPM command is not implemented yet",
341 case offsetof(struct tpm_crb_regs, ctrl_start): {
342 union tpm_crb_reg_ctrl_start start;
344 if ((size_t)size > sizeof(start))
347 *val = *val << shift;
349 pthread_mutex_lock(&crb->mutex);
350 tpm_crb_mmiocpy(&start, val, size);
352 if (!start.start || crb->regs.ctrl_start.start)
355 crb->regs.ctrl_start.start = true;
357 pthread_cond_signal(&crb->cond);
358 pthread_mutex_unlock(&crb->mutex);
362 case offsetof(struct tpm_crb_regs, cmd_size):
363 case offsetof(struct tpm_crb_regs, cmd_addr_lo):
364 case offsetof(struct tpm_crb_regs, cmd_addr_hi):
365 case offsetof(struct tpm_crb_regs, rsp_size):
366 case offsetof(struct tpm_crb_regs,
367 rsp_addr) ... offsetof(struct tpm_crb_regs, rsp_addr) +
369 case offsetof(struct tpm_crb_regs,
370 data_buffer) ... offsetof(struct tpm_crb_regs, data_buffer) +
371 TPM_CRB_DATA_BUFFER_SIZE / 4:
373 * Those fields are used to execute a TPM command. The
374 * crb_thread will access them. For that reason, we have
375 * to acquire the crb mutex in order to write them.
377 pthread_mutex_lock(&crb->mutex);
378 error = tpm_crb_mmiocpy(ptr, val, size);
379 pthread_mutex_unlock(&crb->mutex);
385 * The other fields are either readonly or we do not
386 * support writing them.
396 warnx("%s: invalid %s @ %16lx [size = %d]", __func__,
397 dir == MEM_F_READ ? "read" : "write", addr, size);
403 tpm_crb_modify_mmio_registration(const bool registration, void *const arg1)
405 struct mem_range crb_mmio = {
407 .base = TPM_CRB_ADDRESS,
408 .size = TPM_CRB_LOCALITIES_MAX * TPM_CRB_CONTROL_AREA_SIZE,
411 .handler = tpm_crb_mem_handler,
415 return (register_mem(&crb_mmio));
417 return (unregister_mem(&crb_mmio));
421 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc,
422 struct acpi_device *acpi_dev)
424 struct tpm_crb *crb = NULL;
428 assert(emul != NULL);
430 crb = calloc(1, sizeof(struct tpm_crb));
432 warnx("%s: failed to allocate tpm crb", __func__);
437 memset(crb, 0, sizeof(*crb));
440 crb->emul_sc = emul_sc;
442 crb->regs.loc_state.tpm_req_valid_sts = true;
443 crb->regs.loc_state.tpm_established = true;
445 crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB;
446 crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB;
447 crb->regs.intf_id.cap_locality = false;
448 crb->regs.intf_id.cap_crb_idle_bypass = false;
449 crb->regs.intf_id.cap_data_xfer_size_support =
450 TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64;
451 crb->regs.intf_id.cap_fifo = false;
452 crb->regs.intf_id.cap_crb = true;
453 crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB;
454 crb->regs.intf_id.intf_sel_lock = false;
455 crb->regs.intf_id.rid = 0;
456 crb->regs.intf_id.vid = 0x1014; /* IBM */
457 crb->regs.intf_id.did = 0x1014; /* IBM */
459 crb->regs.ctrl_sts.tpm_idle = true;
461 CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
462 CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
463 CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
464 CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
466 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME,
467 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area);
469 warnx("%s: failed to add fwcfg file", __func__);
473 error = acpi_device_add_res_fixed_memory32(acpi_dev, false,
474 TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE);
476 warnx("%s: failed to add acpi resources\n", __func__);
480 error = tpm_crb_modify_mmio_registration(true, crb);
482 warnx("%s: failed to register crb mmio", __func__);
486 error = pthread_mutex_init(&crb->mutex, NULL);
488 warnc(error, "%s: failed to init mutex", __func__);
492 error = pthread_cond_init(&crb->cond, NULL);
494 warnc(error, "%s: failed to init cond", __func__);
498 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb);
500 warnx("%s: failed to create thread\n", __func__);
504 pthread_set_name_np(crb->thread, "tpm_intf_crb");
517 tpm_crb_deinit(void *sc)
529 pthread_cond_signal(&crb->cond);
530 pthread_join(crb->thread, NULL);
532 pthread_cond_destroy(&crb->cond);
533 pthread_mutex_destroy(&crb->mutex);
535 error = tpm_crb_modify_mmio_registration(false, NULL);
542 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx)
544 struct basl_table *table;
546 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2,
547 BASL_TABLE_ALIGNMENT));
550 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1));
552 BASL_EXEC(basl_table_append_int(table, 0, 2));
554 BASL_EXEC(basl_table_append_int(table, 0, 2));
555 /* Control Address */
557 basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8));
558 /* Start Method == (7) Command Response Buffer */
559 BASL_EXEC(basl_table_append_int(table, 7, 4));
560 /* Start Method Specific Parameters */
561 uint8_t parameters[12] = { 0 };
562 BASL_EXEC(basl_table_append_bytes(table, parameters, 12));
563 /* Log Area Minimum Length */
565 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4));
566 /* Log Area Start Address */
568 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
570 BASL_EXEC(basl_table_register_to_rsdt(table));
575 static struct tpm_intf tpm_intf_crb = {
576 .name = TPM_CRB_INTF_NAME,
577 .init = tpm_crb_init,
578 .deinit = tpm_crb_deinit,
579 .build_acpi_table = tpm_crb_build_acpi_table,
581 TPM_INTF_SET(tpm_intf_crb);