xref: /freebsd/usr.sbin/bhyve/tpm_intf_crb.c (revision 4d65a7c6)
10917f925SCorvin Köhne /*-
2ccfc9600SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
30917f925SCorvin Köhne  *
40917f925SCorvin Köhne  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
50917f925SCorvin Köhne  * Author: Corvin Köhne <c.koehne@beckhoff.com>
60917f925SCorvin Köhne  */
70917f925SCorvin Köhne 
80917f925SCorvin Köhne #include <sys/types.h>
90917f925SCorvin Köhne #include <sys/param.h>
100917f925SCorvin Köhne #include <sys/linker_set.h>
110917f925SCorvin Köhne 
120917f925SCorvin Köhne #include <machine/vmm.h>
130917f925SCorvin Köhne 
140917f925SCorvin Köhne #include <assert.h>
150917f925SCorvin Köhne #include <err.h>
160917f925SCorvin Köhne #include <errno.h>
170917f925SCorvin Köhne #include <pthread.h>
180917f925SCorvin Köhne #include <pthread_np.h>
190917f925SCorvin Köhne #include <stddef.h>
200917f925SCorvin Köhne #include <stdio.h>
210917f925SCorvin Köhne #include <stdlib.h>
220917f925SCorvin Köhne #include <vmmapi.h>
230917f925SCorvin Köhne 
240917f925SCorvin Köhne #include "basl.h"
250917f925SCorvin Köhne #include "config.h"
260917f925SCorvin Köhne #include "mem.h"
270917f925SCorvin Köhne #include "qemu_fwcfg.h"
280daf5f02SCorvin Köhne #include "tpm_device.h"
290917f925SCorvin Köhne #include "tpm_intf.h"
300917f925SCorvin Köhne 
310917f925SCorvin Köhne #define TPM_CRB_ADDRESS 0xFED40000
320917f925SCorvin Köhne #define TPM_CRB_REGS_SIZE 0x1000
330917f925SCorvin Köhne 
345ea98d32SCorvin Köhne #define TPM_CRB_CONTROL_AREA_ADDRESS \
355ea98d32SCorvin Köhne 	(TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req))
365ea98d32SCorvin Köhne #define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE
375ea98d32SCorvin Köhne 
380917f925SCorvin Köhne #define TPM_CRB_DATA_BUFFER_ADDRESS \
390917f925SCorvin Köhne 	(TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer))
400917f925SCorvin Köhne #define TPM_CRB_DATA_BUFFER_SIZE 0xF80
410917f925SCorvin Köhne 
420917f925SCorvin Köhne #define TPM_CRB_LOCALITIES_MAX 5
430917f925SCorvin Köhne 
445ea98d32SCorvin Köhne #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024)
455ea98d32SCorvin Köhne 
465ea98d32SCorvin Köhne #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log"
475ea98d32SCorvin Köhne 
480daf5f02SCorvin Köhne #define TPM_CRB_INTF_NAME "crb"
490daf5f02SCorvin Köhne 
500917f925SCorvin Köhne struct tpm_crb_regs {
510917f925SCorvin Köhne 	union tpm_crb_reg_loc_state {
520917f925SCorvin Köhne 		struct {
530917f925SCorvin Köhne 			uint32_t tpm_established : 1;
540917f925SCorvin Köhne 			uint32_t loc_assigned : 1;
550917f925SCorvin Köhne 			uint32_t active_locality : 3;
560917f925SCorvin Köhne 			uint32_t _reserved : 2;
570917f925SCorvin Köhne 			uint32_t tpm_req_valid_sts : 1;
580917f925SCorvin Köhne 		};
590917f925SCorvin Köhne 		uint32_t val;
600917f925SCorvin Köhne 	} loc_state;	       /* 0h */
610917f925SCorvin Köhne 	uint8_t _reserved1[4]; /* 4h */
620917f925SCorvin Köhne 	union tpm_crb_reg_loc_ctrl {
630917f925SCorvin Köhne 		struct {
640917f925SCorvin Köhne 			uint32_t request_access : 1;
650917f925SCorvin Köhne 			uint32_t relinquish : 1;
660917f925SCorvin Köhne 			uint32_t seize : 1;
670917f925SCorvin Köhne 			uint32_t reset_establishment_bit : 1;
680917f925SCorvin Köhne 		};
690917f925SCorvin Köhne 		uint32_t val;
700917f925SCorvin Köhne 	} loc_ctrl; /* 8h */
710917f925SCorvin Köhne 	union tpm_crb_reg_loc_sts {
720917f925SCorvin Köhne 		struct {
730917f925SCorvin Köhne 			uint32_t granted : 1;
740917f925SCorvin Köhne 			uint32_t been_seized : 1;
750917f925SCorvin Köhne 		};
760917f925SCorvin Köhne 		uint32_t val;
770917f925SCorvin Köhne 	} loc_sts;		  /* Ch */
780917f925SCorvin Köhne 	uint8_t _reserved2[0x20]; /* 10h */
790917f925SCorvin Köhne 	union tpm_crb_reg_intf_id {
800917f925SCorvin Köhne 		struct {
810917f925SCorvin Köhne 			uint64_t interface_type : 4;
820917f925SCorvin Köhne 			uint64_t interface_version : 4;
830917f925SCorvin Köhne 			uint64_t cap_locality : 1;
840917f925SCorvin Köhne 			uint64_t cap_crb_idle_bypass : 1;
850917f925SCorvin Köhne 			uint64_t _reserved1 : 1;
860917f925SCorvin Köhne 			uint64_t cap_data_xfer_size_support : 2;
870917f925SCorvin Köhne 			uint64_t cap_fifo : 1;
880917f925SCorvin Köhne 			uint64_t cap_crb : 1;
890917f925SCorvin Köhne 			uint64_t _reserved2 : 2;
900917f925SCorvin Köhne 			uint64_t interface_selector : 2;
910917f925SCorvin Köhne 			uint64_t intf_sel_lock : 1;
920917f925SCorvin Köhne 			uint64_t _reserved3 : 4;
930917f925SCorvin Köhne 			uint64_t rid : 8;
940917f925SCorvin Köhne 			uint64_t vid : 16;
950917f925SCorvin Köhne 			uint64_t did : 16;
960917f925SCorvin Köhne 		};
970917f925SCorvin Köhne 		uint64_t val;
980917f925SCorvin Köhne 	} intf_id; /* 30h */
990917f925SCorvin Köhne 	union tpm_crb_reg_ctrl_ext {
1000917f925SCorvin Köhne 		struct {
1010917f925SCorvin Köhne 			uint32_t clear;
1020917f925SCorvin Köhne 			uint32_t remaining_bytes;
1030917f925SCorvin Köhne 		};
1040917f925SCorvin Köhne 		uint64_t val;
1050917f925SCorvin Köhne 	} ctrl_ext; /* 38 */
1060917f925SCorvin Köhne 	union tpm_crb_reg_ctrl_req {
1070917f925SCorvin Köhne 		struct {
1080917f925SCorvin Köhne 			uint32_t cmd_ready : 1;
1090917f925SCorvin Köhne 			uint32_t go_idle : 1;
1100917f925SCorvin Köhne 		};
1110917f925SCorvin Köhne 		uint32_t val;
1120917f925SCorvin Köhne 	} ctrl_req; /* 40h */
1130917f925SCorvin Köhne 	union tpm_crb_reg_ctrl_sts {
1140917f925SCorvin Köhne 		struct {
1150917f925SCorvin Köhne 			uint32_t tpm_sts : 1;
1160917f925SCorvin Köhne 			uint32_t tpm_idle : 1;
1170917f925SCorvin Köhne 		};
1180917f925SCorvin Köhne 		uint32_t val;
1190917f925SCorvin Köhne 	} ctrl_sts; /* 44h */
1200917f925SCorvin Köhne 	union tpm_crb_reg_ctrl_cancel {
1210917f925SCorvin Köhne 		struct {
1220917f925SCorvin Köhne 			uint32_t cancel : 1;
1230917f925SCorvin Köhne 		};
1240917f925SCorvin Köhne 		uint32_t val;
1250917f925SCorvin Köhne 	} ctrl_cancel; /* 48h */
1260917f925SCorvin Köhne 	union tpm_crb_reg_ctrl_start {
1270917f925SCorvin Köhne 		struct {
1280917f925SCorvin Köhne 			uint32_t start : 1;
1290917f925SCorvin Köhne 		};
1300917f925SCorvin Köhne 		uint32_t val;
1310917f925SCorvin Köhne 	} ctrl_start;				       /* 4Ch*/
1320917f925SCorvin Köhne 	uint32_t int_enable;			       /* 50h */
1330917f925SCorvin Köhne 	uint32_t int_sts;			       /* 54h */
1340917f925SCorvin Köhne 	uint32_t cmd_size;			       /* 58h */
1350917f925SCorvin Köhne 	uint32_t cmd_addr_lo;			       /* 5Ch */
1360917f925SCorvin Köhne 	uint32_t cmd_addr_hi;			       /* 60h */
1370917f925SCorvin Köhne 	uint32_t rsp_size;			       /* 64h */
1380917f925SCorvin Köhne 	uint64_t rsp_addr;			       /* 68h */
1390917f925SCorvin Köhne 	uint8_t _reserved3[0x10];		       /* 70h */
1400917f925SCorvin Köhne 	uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */
1410917f925SCorvin Köhne } __packed;
1420917f925SCorvin Köhne static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE,
1430917f925SCorvin Köhne     "Invalid size of tpm_crb");
1440917f925SCorvin Köhne 
1450917f925SCorvin Köhne #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size)
1460917f925SCorvin Köhne #define CRB_CMD_SIZE_WRITE(regs, val) \
1470917f925SCorvin Köhne 	do {                          \
1480917f925SCorvin Köhne 		regs.cmd_size = val;  \
1490917f925SCorvin Köhne 	} while (0)
1500917f925SCorvin Köhne #define CRB_CMD_ADDR_READ(regs) \
1510917f925SCorvin Köhne 	(((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo)
1520917f925SCorvin Köhne #define CRB_CMD_ADDR_WRITE(regs, val)                \
1530917f925SCorvin Köhne 	do {                                         \
1540917f925SCorvin Köhne 		regs.cmd_addr_lo = val & 0xFFFFFFFF; \
1550917f925SCorvin Köhne 		regs.cmd_addr_hi = val >> 32;        \
1560917f925SCorvin Köhne 	} while (0)
1570917f925SCorvin Köhne #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size)
1580917f925SCorvin Köhne #define CRB_RSP_SIZE_WRITE(regs, val) \
1590917f925SCorvin Köhne 	do {                          \
1600917f925SCorvin Köhne 		regs.rsp_size = val;  \
1610917f925SCorvin Köhne 	} while (0)
1620917f925SCorvin Köhne #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr)
1630917f925SCorvin Köhne #define CRB_RSP_ADDR_WRITE(regs, val) \
1640917f925SCorvin Köhne 	do {                          \
1650917f925SCorvin Köhne 		regs.rsp_addr = val;  \
1660917f925SCorvin Köhne 	} while (0)
1670917f925SCorvin Köhne 
1680917f925SCorvin Köhne struct tpm_crb {
1690daf5f02SCorvin Köhne 	struct tpm_emul *emul;
1700daf5f02SCorvin Köhne 	void *emul_sc;
1715ea98d32SCorvin Köhne 	uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE];
1720917f925SCorvin Köhne 	struct tpm_crb_regs regs;
1730daf5f02SCorvin Köhne 	pthread_t thread;
1740daf5f02SCorvin Köhne 	pthread_mutex_t mutex;
1750daf5f02SCorvin Köhne 	pthread_cond_t cond;
1760daf5f02SCorvin Köhne 	bool closing;
1770917f925SCorvin Köhne };
1780917f925SCorvin Köhne 
1790daf5f02SCorvin Köhne static void *
tpm_crb_thread(void * const arg)1800daf5f02SCorvin Köhne tpm_crb_thread(void *const arg)
1810daf5f02SCorvin Köhne {
1820daf5f02SCorvin Köhne 	struct tpm_crb *const crb = arg;
1830daf5f02SCorvin Köhne 
1840daf5f02SCorvin Köhne 	pthread_mutex_lock(&crb->mutex);
1850daf5f02SCorvin Köhne 	for (;;) {
186f0124ab1SCorvin Köhne 		/*
187f0124ab1SCorvin Köhne 		 * We're releasing the lock after wake up. Therefore, we have to
188f0124ab1SCorvin Köhne 		 * check the closing condition before and after going to sleep.
189f0124ab1SCorvin Köhne 		 */
190f0124ab1SCorvin Köhne 		if (crb->closing)
191f0124ab1SCorvin Köhne 			break;
192f0124ab1SCorvin Köhne 
1930daf5f02SCorvin Köhne 		pthread_cond_wait(&crb->cond, &crb->mutex);
1940daf5f02SCorvin Köhne 
1950daf5f02SCorvin Köhne 		if (crb->closing)
1960daf5f02SCorvin Köhne 			break;
1970daf5f02SCorvin Köhne 
1980daf5f02SCorvin Köhne 		const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs);
1990daf5f02SCorvin Köhne 		const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs);
2000daf5f02SCorvin Köhne 		const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs);
2010daf5f02SCorvin Köhne 		const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs);
2020daf5f02SCorvin Köhne 
2030daf5f02SCorvin Köhne 		const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
2040daf5f02SCorvin Köhne 		const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
2050daf5f02SCorvin Köhne 
2060daf5f02SCorvin Köhne 		if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE ||
2070daf5f02SCorvin Köhne 		    cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE ||
2080daf5f02SCorvin Köhne 		    rsp_off > TPM_CRB_DATA_BUFFER_SIZE ||
2090daf5f02SCorvin Köhne 		    rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) {
2100daf5f02SCorvin Köhne 			warnx(
2110daf5f02SCorvin Köhne 			    "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r",
2120daf5f02SCorvin Köhne 			    __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr,
2130daf5f02SCorvin Köhne 			    rsp_addr + rsp_size);
2140daf5f02SCorvin Köhne 			break;
2150daf5f02SCorvin Köhne 		}
2160daf5f02SCorvin Köhne 
217f0124ab1SCorvin Köhne 		uint8_t cmd[TPM_CRB_DATA_BUFFER_SIZE];
218f0124ab1SCorvin Köhne 		memcpy(cmd, crb->regs.data_buffer, TPM_CRB_DATA_BUFFER_SIZE);
219f0124ab1SCorvin Köhne 
220f0124ab1SCorvin Köhne 		/*
221f0124ab1SCorvin Köhne 		 * A TPM command can take multiple seconds to execute. As we've
222f0124ab1SCorvin Köhne 		 * copied all required values and buffers at this point, we can
223f0124ab1SCorvin Köhne 		 * release the mutex.
224f0124ab1SCorvin Köhne 		 */
225f0124ab1SCorvin Köhne 		pthread_mutex_unlock(&crb->mutex);
226f0124ab1SCorvin Köhne 
2270daf5f02SCorvin Köhne 		/*
2280daf5f02SCorvin Köhne 		 * The command response buffer interface uses a single buffer
2290daf5f02SCorvin Köhne 		 * for sending a command to and receiving a response from the
2300daf5f02SCorvin Köhne 		 * tpm. To avoid reading old data from the command buffer which
2310daf5f02SCorvin Köhne 		 * might be a security issue, we zero out the command buffer
2320daf5f02SCorvin Köhne 		 * before writing the response into it. The rsp_size parameter
2330daf5f02SCorvin Köhne 		 * is controlled by the guest and it's not guaranteed that the
2340daf5f02SCorvin Köhne 		 * response has a size of rsp_size (e.g. if the tpm returned an
2350daf5f02SCorvin Köhne 		 * error, the response would have a different size than
2360daf5f02SCorvin Köhne 		 * expected). For that reason, use a second buffer for the
2370daf5f02SCorvin Köhne 		 * response.
2380daf5f02SCorvin Köhne 		 */
2390daf5f02SCorvin Köhne 		uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 };
240f0124ab1SCorvin Köhne 		crb->emul->execute_cmd(crb->emul_sc, &cmd[cmd_off], cmd_size,
241f0124ab1SCorvin Köhne 		    &rsp[rsp_off], rsp_size);
2420daf5f02SCorvin Köhne 
243f0124ab1SCorvin Köhne 		pthread_mutex_lock(&crb->mutex);
2440daf5f02SCorvin Köhne 		memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE);
2450daf5f02SCorvin Köhne 		memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size);
2460daf5f02SCorvin Köhne 
2470daf5f02SCorvin Köhne 		crb->regs.ctrl_start.start = false;
2480daf5f02SCorvin Köhne 	}
2490daf5f02SCorvin Köhne 	pthread_mutex_unlock(&crb->mutex);
2500daf5f02SCorvin Köhne 
2510daf5f02SCorvin Köhne 	return (NULL);
2520daf5f02SCorvin Köhne }
2530daf5f02SCorvin Köhne 
2540917f925SCorvin Köhne static int
tpm_crb_mmiocpy(void * const dst,void * const src,const int size)25528dc1aa7SCorvin Köhne tpm_crb_mmiocpy(void *const dst, void *const src, const int size)
25628dc1aa7SCorvin Köhne {
25728dc1aa7SCorvin Köhne 	if (!(size == 1 || size == 2 || size == 4 || size == 8))
25828dc1aa7SCorvin Köhne 		return (EINVAL);
25928dc1aa7SCorvin Köhne 	memcpy(dst, src, size);
26028dc1aa7SCorvin Köhne 
26128dc1aa7SCorvin Köhne 	return (0);
26228dc1aa7SCorvin Köhne }
26328dc1aa7SCorvin Köhne 
26428dc1aa7SCorvin Köhne static int
tpm_crb_mem_handler(struct vcpu * vcpu __unused,const int dir,const uint64_t addr,const int size,uint64_t * const val,void * const arg1,const long arg2 __unused)26528dc1aa7SCorvin Köhne tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir,
26628dc1aa7SCorvin Köhne     const uint64_t addr, const int size, uint64_t *const val, void *const arg1,
26728dc1aa7SCorvin Köhne     const long arg2 __unused)
26828dc1aa7SCorvin Köhne {
26928dc1aa7SCorvin Köhne 	struct tpm_crb *crb;
27028dc1aa7SCorvin Köhne 	uint8_t *ptr;
27128dc1aa7SCorvin Köhne 	uint64_t off, shift;
27228dc1aa7SCorvin Köhne 	int error = 0;
27328dc1aa7SCorvin Köhne 
27428dc1aa7SCorvin Köhne 	if ((addr & (size - 1)) != 0) {
27528dc1aa7SCorvin Köhne 		warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__,
27628dc1aa7SCorvin Köhne 		    (dir == MEM_F_READ) ? "read" : "write", addr, size);
27728dc1aa7SCorvin Köhne 		return (EINVAL);
27828dc1aa7SCorvin Köhne 	}
27928dc1aa7SCorvin Köhne 
28028dc1aa7SCorvin Köhne 	crb = arg1;
28128dc1aa7SCorvin Köhne 
28228dc1aa7SCorvin Köhne 	off = addr - TPM_CRB_ADDRESS;
28328dc1aa7SCorvin Köhne 	if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) {
28428dc1aa7SCorvin Köhne 		return (EINVAL);
28528dc1aa7SCorvin Köhne 	}
28628dc1aa7SCorvin Köhne 
28728dc1aa7SCorvin Köhne 	shift = 8 * (off & 3);
28828dc1aa7SCorvin Köhne 	ptr = (uint8_t *)&crb->regs + off;
28928dc1aa7SCorvin Köhne 
29028dc1aa7SCorvin Köhne 	if (dir == MEM_F_READ) {
29128dc1aa7SCorvin Köhne 		error = tpm_crb_mmiocpy(val, ptr, size);
29228dc1aa7SCorvin Köhne 		if (error)
29328dc1aa7SCorvin Köhne 			goto err_out;
29428dc1aa7SCorvin Köhne 	} else {
29528dc1aa7SCorvin Köhne 		switch (off & ~0x3) {
29628dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, loc_ctrl): {
29728dc1aa7SCorvin Köhne 			union tpm_crb_reg_loc_ctrl loc_ctrl;
29828dc1aa7SCorvin Köhne 
29928dc1aa7SCorvin Köhne 			if ((size_t)size > sizeof(loc_ctrl))
30028dc1aa7SCorvin Köhne 				goto err_out;
30128dc1aa7SCorvin Köhne 
30228dc1aa7SCorvin Köhne 			*val = *val << shift;
30328dc1aa7SCorvin Köhne 			tpm_crb_mmiocpy(&loc_ctrl, val, size);
30428dc1aa7SCorvin Köhne 
30528dc1aa7SCorvin Köhne 			if (loc_ctrl.relinquish) {
30628dc1aa7SCorvin Köhne 				crb->regs.loc_sts.granted = false;
30728dc1aa7SCorvin Köhne 				crb->regs.loc_state.loc_assigned = false;
30828dc1aa7SCorvin Köhne 			} else if (loc_ctrl.request_access) {
30928dc1aa7SCorvin Köhne 				crb->regs.loc_sts.granted = true;
31028dc1aa7SCorvin Köhne 				crb->regs.loc_state.loc_assigned = true;
31128dc1aa7SCorvin Köhne 			}
31228dc1aa7SCorvin Köhne 
31328dc1aa7SCorvin Köhne 			break;
31428dc1aa7SCorvin Köhne 		}
31528dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, ctrl_req): {
31628dc1aa7SCorvin Köhne 			union tpm_crb_reg_ctrl_req req;
31728dc1aa7SCorvin Köhne 
31828dc1aa7SCorvin Köhne 			if ((size_t)size > sizeof(req))
31928dc1aa7SCorvin Köhne 				goto err_out;
32028dc1aa7SCorvin Köhne 
32128dc1aa7SCorvin Köhne 			*val = *val << shift;
32228dc1aa7SCorvin Köhne 			tpm_crb_mmiocpy(&req, val, size);
32328dc1aa7SCorvin Köhne 
32428dc1aa7SCorvin Köhne 			if (req.cmd_ready && !req.go_idle) {
32528dc1aa7SCorvin Köhne 				crb->regs.ctrl_sts.tpm_idle = false;
32628dc1aa7SCorvin Köhne 			} else if (!req.cmd_ready && req.go_idle) {
32728dc1aa7SCorvin Köhne 				crb->regs.ctrl_sts.tpm_idle = true;
32828dc1aa7SCorvin Köhne 			}
32928dc1aa7SCorvin Köhne 
33028dc1aa7SCorvin Köhne 			break;
33128dc1aa7SCorvin Köhne 		}
33228dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, ctrl_cancel): {
33328dc1aa7SCorvin Köhne 			/* TODO: cancel the tpm command */
33428dc1aa7SCorvin Köhne 			warnx(
33528dc1aa7SCorvin Köhne 			    "%s: cancelling a TPM command is not implemented yet",
33628dc1aa7SCorvin Köhne 			    __func__);
33728dc1aa7SCorvin Köhne 
33828dc1aa7SCorvin Köhne 			break;
33928dc1aa7SCorvin Köhne 		}
34028dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, ctrl_start): {
34128dc1aa7SCorvin Köhne 			union tpm_crb_reg_ctrl_start start;
34228dc1aa7SCorvin Köhne 
34328dc1aa7SCorvin Köhne 			if ((size_t)size > sizeof(start))
34428dc1aa7SCorvin Köhne 				goto err_out;
34528dc1aa7SCorvin Köhne 
34628dc1aa7SCorvin Köhne 			*val = *val << shift;
34728dc1aa7SCorvin Köhne 
34828dc1aa7SCorvin Köhne 			pthread_mutex_lock(&crb->mutex);
34928dc1aa7SCorvin Köhne 			tpm_crb_mmiocpy(&start, val, size);
35028dc1aa7SCorvin Köhne 
35128dc1aa7SCorvin Köhne 			if (!start.start || crb->regs.ctrl_start.start)
35228dc1aa7SCorvin Köhne 				break;
35328dc1aa7SCorvin Köhne 
35428dc1aa7SCorvin Köhne 			crb->regs.ctrl_start.start = true;
35528dc1aa7SCorvin Köhne 
35628dc1aa7SCorvin Köhne 			pthread_cond_signal(&crb->cond);
35728dc1aa7SCorvin Köhne 			pthread_mutex_unlock(&crb->mutex);
35828dc1aa7SCorvin Köhne 
35928dc1aa7SCorvin Köhne 			break;
36028dc1aa7SCorvin Köhne 		}
36128dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, cmd_size):
36228dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, cmd_addr_lo):
36328dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, cmd_addr_hi):
36428dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs, rsp_size):
36528dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs,
36628dc1aa7SCorvin Köhne 		    rsp_addr) ... offsetof(struct tpm_crb_regs, rsp_addr) +
36728dc1aa7SCorvin Köhne 		    4:
36828dc1aa7SCorvin Köhne 		case offsetof(struct tpm_crb_regs,
36928dc1aa7SCorvin Köhne 		    data_buffer) ... offsetof(struct tpm_crb_regs, data_buffer) +
37028dc1aa7SCorvin Köhne 		    TPM_CRB_DATA_BUFFER_SIZE / 4:
37128dc1aa7SCorvin Köhne 			/*
37228dc1aa7SCorvin Köhne 			 * Those fields are used to execute a TPM command. The
37328dc1aa7SCorvin Köhne 			 * crb_thread will access them. For that reason, we have
37428dc1aa7SCorvin Köhne 			 * to acquire the crb mutex in order to write them.
37528dc1aa7SCorvin Köhne 			 */
37628dc1aa7SCorvin Köhne 			pthread_mutex_lock(&crb->mutex);
37728dc1aa7SCorvin Köhne 			error = tpm_crb_mmiocpy(ptr, val, size);
37828dc1aa7SCorvin Köhne 			pthread_mutex_unlock(&crb->mutex);
37928dc1aa7SCorvin Köhne 			if (error)
38028dc1aa7SCorvin Köhne 				goto err_out;
38128dc1aa7SCorvin Köhne 			break;
38228dc1aa7SCorvin Köhne 		default:
38328dc1aa7SCorvin Köhne 			/*
38428dc1aa7SCorvin Köhne 			 * The other fields are either readonly or we do not
38528dc1aa7SCorvin Köhne 			 * support writing them.
38628dc1aa7SCorvin Köhne 			 */
38728dc1aa7SCorvin Köhne 			error = EINVAL;
38828dc1aa7SCorvin Köhne 			goto err_out;
38928dc1aa7SCorvin Köhne 		}
39028dc1aa7SCorvin Köhne 	}
39128dc1aa7SCorvin Köhne 
39228dc1aa7SCorvin Köhne 	return (0);
39328dc1aa7SCorvin Köhne 
39428dc1aa7SCorvin Köhne err_out:
39528dc1aa7SCorvin Köhne 	warnx("%s: invalid %s @ %16lx [size = %d]", __func__,
39628dc1aa7SCorvin Köhne 	    dir == MEM_F_READ ? "read" : "write", addr, size);
39728dc1aa7SCorvin Köhne 
39828dc1aa7SCorvin Köhne 	return (error);
39928dc1aa7SCorvin Köhne }
40028dc1aa7SCorvin Köhne 
40128dc1aa7SCorvin Köhne static int
tpm_crb_modify_mmio_registration(const bool registration,void * const arg1)40228dc1aa7SCorvin Köhne tpm_crb_modify_mmio_registration(const bool registration, void *const arg1)
40328dc1aa7SCorvin Köhne {
40428dc1aa7SCorvin Köhne 	struct mem_range crb_mmio = {
40528dc1aa7SCorvin Köhne 		.name = "crb-mmio",
40628dc1aa7SCorvin Köhne 		.base = TPM_CRB_ADDRESS,
40728dc1aa7SCorvin Köhne 		.size = TPM_CRB_LOCALITIES_MAX * TPM_CRB_CONTROL_AREA_SIZE,
40828dc1aa7SCorvin Köhne 		.flags = MEM_F_RW,
40928dc1aa7SCorvin Köhne 		.arg1 = arg1,
41028dc1aa7SCorvin Köhne 		.handler = tpm_crb_mem_handler,
41128dc1aa7SCorvin Köhne 	};
41228dc1aa7SCorvin Köhne 
41328dc1aa7SCorvin Köhne 	if (registration)
41428dc1aa7SCorvin Köhne 		return (register_mem(&crb_mmio));
41528dc1aa7SCorvin Köhne 	else
41628dc1aa7SCorvin Köhne 		return (unregister_mem(&crb_mmio));
41728dc1aa7SCorvin Köhne }
41828dc1aa7SCorvin Köhne 
41928dc1aa7SCorvin Köhne static int
tpm_crb_init(void ** sc,struct tpm_emul * emul,void * emul_sc,struct acpi_device * acpi_dev)42028dc1aa7SCorvin Köhne tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc,
42128dc1aa7SCorvin Köhne     struct acpi_device *acpi_dev)
4220917f925SCorvin Köhne {
4230917f925SCorvin Köhne 	struct tpm_crb *crb = NULL;
4240917f925SCorvin Köhne 	int error;
4250917f925SCorvin Köhne 
4260917f925SCorvin Köhne 	assert(sc != NULL);
4270daf5f02SCorvin Köhne 	assert(emul != NULL);
4280917f925SCorvin Köhne 
4290917f925SCorvin Köhne 	crb = calloc(1, sizeof(struct tpm_crb));
4300917f925SCorvin Köhne 	if (crb == NULL) {
4310917f925SCorvin Köhne 		warnx("%s: failed to allocate tpm crb", __func__);
4320917f925SCorvin Köhne 		error = ENOMEM;
4330917f925SCorvin Köhne 		goto err_out;
4340917f925SCorvin Köhne 	}
4350917f925SCorvin Köhne 
4360917f925SCorvin Köhne 	memset(crb, 0, sizeof(*crb));
4370917f925SCorvin Köhne 
4380daf5f02SCorvin Köhne 	crb->emul = emul;
4390daf5f02SCorvin Köhne 	crb->emul_sc = emul_sc;
4400daf5f02SCorvin Köhne 
4410917f925SCorvin Köhne 	crb->regs.loc_state.tpm_req_valid_sts = true;
4420917f925SCorvin Köhne 	crb->regs.loc_state.tpm_established = true;
4430917f925SCorvin Köhne 
4440917f925SCorvin Köhne 	crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB;
4450917f925SCorvin Köhne 	crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB;
4460917f925SCorvin Köhne 	crb->regs.intf_id.cap_locality = false;
4470917f925SCorvin Köhne 	crb->regs.intf_id.cap_crb_idle_bypass = false;
4480917f925SCorvin Köhne 	crb->regs.intf_id.cap_data_xfer_size_support =
4490917f925SCorvin Köhne 	    TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64;
4500917f925SCorvin Köhne 	crb->regs.intf_id.cap_fifo = false;
4510917f925SCorvin Köhne 	crb->regs.intf_id.cap_crb = true;
4520917f925SCorvin Köhne 	crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB;
4530917f925SCorvin Köhne 	crb->regs.intf_id.intf_sel_lock = false;
4540917f925SCorvin Köhne 	crb->regs.intf_id.rid = 0;
4550917f925SCorvin Köhne 	crb->regs.intf_id.vid = 0x1014; /* IBM */
4560917f925SCorvin Köhne 	crb->regs.intf_id.did = 0x1014; /* IBM */
4570917f925SCorvin Köhne 
4580917f925SCorvin Köhne 	crb->regs.ctrl_sts.tpm_idle = true;
4590917f925SCorvin Köhne 
4600917f925SCorvin Köhne 	CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
4610917f925SCorvin Köhne 	CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
4620917f925SCorvin Köhne 	CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
4630917f925SCorvin Köhne 	CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
4640917f925SCorvin Köhne 
4655ea98d32SCorvin Köhne 	error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME,
4665ea98d32SCorvin Köhne 	    TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area);
4675ea98d32SCorvin Köhne 	if (error) {
4685ea98d32SCorvin Köhne 		warnx("%s: failed to add fwcfg file", __func__);
4695ea98d32SCorvin Köhne 		goto err_out;
4705ea98d32SCorvin Köhne 	}
4715ea98d32SCorvin Köhne 
47228dc1aa7SCorvin Köhne 	error = acpi_device_add_res_fixed_memory32(acpi_dev, false,
47328dc1aa7SCorvin Köhne 	    TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE);
47428dc1aa7SCorvin Köhne 	if (error) {
47528dc1aa7SCorvin Köhne 		warnx("%s: failed to add acpi resources\n", __func__);
47628dc1aa7SCorvin Köhne 		goto err_out;
47728dc1aa7SCorvin Köhne 	}
47828dc1aa7SCorvin Köhne 
47928dc1aa7SCorvin Köhne 	error = tpm_crb_modify_mmio_registration(true, crb);
48028dc1aa7SCorvin Köhne 	if (error) {
48128dc1aa7SCorvin Köhne 		warnx("%s: failed to register crb mmio", __func__);
48228dc1aa7SCorvin Köhne 		goto err_out;
48328dc1aa7SCorvin Köhne 	}
48428dc1aa7SCorvin Köhne 
4850daf5f02SCorvin Köhne 	error = pthread_mutex_init(&crb->mutex, NULL);
4860daf5f02SCorvin Köhne 	if (error) {
4870daf5f02SCorvin Köhne 		warnc(error, "%s: failed to init mutex", __func__);
4880daf5f02SCorvin Köhne 		goto err_out;
4890daf5f02SCorvin Köhne 	}
4900daf5f02SCorvin Köhne 
4910daf5f02SCorvin Köhne 	error = pthread_cond_init(&crb->cond, NULL);
4920daf5f02SCorvin Köhne 	if (error) {
4930daf5f02SCorvin Köhne 		warnc(error, "%s: failed to init cond", __func__);
4940daf5f02SCorvin Köhne 		goto err_out;
4950daf5f02SCorvin Köhne 	}
4960daf5f02SCorvin Köhne 
4970daf5f02SCorvin Köhne 	error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb);
4980daf5f02SCorvin Köhne 	if (error) {
4990daf5f02SCorvin Köhne 		warnx("%s: failed to create thread\n", __func__);
5000daf5f02SCorvin Köhne 		goto err_out;
5010daf5f02SCorvin Köhne 	}
5020daf5f02SCorvin Köhne 
5030daf5f02SCorvin Köhne 	pthread_set_name_np(crb->thread, "tpm_intf_crb");
5040daf5f02SCorvin Köhne 
5050917f925SCorvin Köhne 	*sc = crb;
5060917f925SCorvin Köhne 
5070917f925SCorvin Köhne 	return (0);
5080917f925SCorvin Köhne 
5090917f925SCorvin Köhne err_out:
5100917f925SCorvin Köhne 	free(crb);
5110917f925SCorvin Köhne 
5120917f925SCorvin Köhne 	return (error);
5130917f925SCorvin Köhne }
5140917f925SCorvin Köhne 
5150917f925SCorvin Köhne static void
tpm_crb_deinit(void * sc)5160917f925SCorvin Köhne tpm_crb_deinit(void *sc)
5170917f925SCorvin Köhne {
5180917f925SCorvin Köhne 	struct tpm_crb *crb;
51928dc1aa7SCorvin Köhne 	int error;
5200917f925SCorvin Köhne 
5210917f925SCorvin Köhne 	if (sc == NULL) {
5220917f925SCorvin Köhne 		return;
5230917f925SCorvin Köhne 	}
5240917f925SCorvin Köhne 
5250917f925SCorvin Köhne 	crb = sc;
5260917f925SCorvin Köhne 
5270daf5f02SCorvin Köhne 	crb->closing = true;
5280daf5f02SCorvin Köhne 	pthread_cond_signal(&crb->cond);
5290daf5f02SCorvin Köhne 	pthread_join(crb->thread, NULL);
5300daf5f02SCorvin Köhne 
5310daf5f02SCorvin Köhne 	pthread_cond_destroy(&crb->cond);
5320daf5f02SCorvin Köhne 	pthread_mutex_destroy(&crb->mutex);
5330daf5f02SCorvin Köhne 
53428dc1aa7SCorvin Köhne 	error = tpm_crb_modify_mmio_registration(false, NULL);
53528dc1aa7SCorvin Köhne 	assert(error == 0);
53628dc1aa7SCorvin Köhne 
5370917f925SCorvin Köhne 	free(crb);
5380917f925SCorvin Köhne }
5390917f925SCorvin Köhne 
5405ea98d32SCorvin Köhne static int
tpm_crb_build_acpi_table(void * sc __unused,struct vmctx * vm_ctx)5415ea98d32SCorvin Köhne tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx)
5425ea98d32SCorvin Köhne {
5435ea98d32SCorvin Köhne 	struct basl_table *table;
5445ea98d32SCorvin Köhne 
5455ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2,
5465ea98d32SCorvin Köhne 	    BASL_TABLE_ALIGNMENT));
5475ea98d32SCorvin Köhne 
5485ea98d32SCorvin Köhne 	/* Header */
5495ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1));
5505ea98d32SCorvin Köhne 	/* Platform Class */
5515ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_append_int(table, 0, 2));
5525ea98d32SCorvin Köhne 	/* Reserved */
5535ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_append_int(table, 0, 2));
5545ea98d32SCorvin Köhne 	/* Control Address */
5555ea98d32SCorvin Köhne 	BASL_EXEC(
5565ea98d32SCorvin Köhne 	    basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8));
5575ea98d32SCorvin Köhne 	/* Start Method == (7) Command Response Buffer */
5585ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_append_int(table, 7, 4));
5595ea98d32SCorvin Köhne 	/* Start Method Specific Parameters */
5605ea98d32SCorvin Köhne 	uint8_t parameters[12] = { 0 };
5615ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_append_bytes(table, parameters, 12));
5625ea98d32SCorvin Köhne 	/* Log Area Minimum Length */
5635ea98d32SCorvin Köhne 	BASL_EXEC(
5645ea98d32SCorvin Köhne 	    basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4));
5655ea98d32SCorvin Köhne 	/* Log Area Start Address */
5665ea98d32SCorvin Köhne 	BASL_EXEC(
5675ea98d32SCorvin Köhne 	    basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
5685ea98d32SCorvin Köhne 
5695ea98d32SCorvin Köhne 	BASL_EXEC(basl_table_register_to_rsdt(table));
5705ea98d32SCorvin Köhne 
5715ea98d32SCorvin Köhne 	return (0);
5725ea98d32SCorvin Köhne }
5735ea98d32SCorvin Köhne 
5740917f925SCorvin Köhne static struct tpm_intf tpm_intf_crb = {
5750daf5f02SCorvin Köhne 	.name = TPM_CRB_INTF_NAME,
5760917f925SCorvin Köhne 	.init = tpm_crb_init,
5770917f925SCorvin Köhne 	.deinit = tpm_crb_deinit,
5785ea98d32SCorvin Köhne 	.build_acpi_table = tpm_crb_build_acpi_table,
5790917f925SCorvin Köhne };
5800917f925SCorvin Köhne TPM_INTF_SET(tpm_intf_crb);
581