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);
187 pthread_cond_wait(&crb->cond, &crb->mutex);
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);
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;
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) {
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);
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
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],
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);
231 crb->regs.ctrl_start.start = false;
233 pthread_mutex_unlock(&crb->mutex);
239 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc)
241 struct tpm_crb *crb = NULL;
245 assert(emul != NULL);
247 crb = calloc(1, sizeof(struct tpm_crb));
249 warnx("%s: failed to allocate tpm crb", __func__);
254 memset(crb, 0, sizeof(*crb));
257 crb->emul_sc = emul_sc;
259 crb->regs.loc_state.tpm_req_valid_sts = true;
260 crb->regs.loc_state.tpm_established = true;
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 */
276 crb->regs.ctrl_sts.tpm_idle = true;
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);
283 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME,
284 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area);
286 warnx("%s: failed to add fwcfg file", __func__);
290 error = pthread_mutex_init(&crb->mutex, NULL);
292 warnc(error, "%s: failed to init mutex", __func__);
296 error = pthread_cond_init(&crb->cond, NULL);
298 warnc(error, "%s: failed to init cond", __func__);
302 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb);
304 warnx("%s: failed to create thread\n", __func__);
308 pthread_set_name_np(crb->thread, "tpm_intf_crb");
321 tpm_crb_deinit(void *sc)
332 pthread_cond_signal(&crb->cond);
333 pthread_join(crb->thread, NULL);
335 pthread_cond_destroy(&crb->cond);
336 pthread_mutex_destroy(&crb->mutex);
342 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx)
344 struct basl_table *table;
346 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2,
347 BASL_TABLE_ALIGNMENT));
350 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1));
352 BASL_EXEC(basl_table_append_int(table, 0, 2));
354 BASL_EXEC(basl_table_append_int(table, 0, 2));
355 /* Control Address */
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 */
365 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4));
366 /* Log Area Start Address */
368 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
370 BASL_EXEC(basl_table_register_to_rsdt(table));
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,
381 TPM_INTF_SET(tpm_intf_crb);