xref: /illumos-gate/usr/src/cmd/bhyve/tpm_intf_crb.c (revision 32640292)
1*32640292SAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*32640292SAndy Fiddaman  *
4*32640292SAndy Fiddaman  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5*32640292SAndy Fiddaman  * Author: Corvin Köhne <c.koehne@beckhoff.com>
6*32640292SAndy Fiddaman  */
7*32640292SAndy Fiddaman 
8*32640292SAndy Fiddaman #include <sys/cdefs.h>
9*32640292SAndy Fiddaman #include <sys/types.h>
10*32640292SAndy Fiddaman #include <sys/param.h>
11*32640292SAndy Fiddaman #include <sys/linker_set.h>
12*32640292SAndy Fiddaman 
13*32640292SAndy Fiddaman #include <machine/vmm.h>
14*32640292SAndy Fiddaman 
15*32640292SAndy Fiddaman #include <assert.h>
16*32640292SAndy Fiddaman #include <err.h>
17*32640292SAndy Fiddaman #include <errno.h>
18*32640292SAndy Fiddaman #include <pthread.h>
19*32640292SAndy Fiddaman #include <pthread_np.h>
20*32640292SAndy Fiddaman #include <stddef.h>
21*32640292SAndy Fiddaman #include <stdio.h>
22*32640292SAndy Fiddaman #include <stdlib.h>
23*32640292SAndy Fiddaman #include <vmmapi.h>
24*32640292SAndy Fiddaman 
25*32640292SAndy Fiddaman #include "basl.h"
26*32640292SAndy Fiddaman #include "config.h"
27*32640292SAndy Fiddaman #include "mem.h"
28*32640292SAndy Fiddaman #include "qemu_fwcfg.h"
29*32640292SAndy Fiddaman #include "tpm_device.h"
30*32640292SAndy Fiddaman #include "tpm_intf.h"
31*32640292SAndy Fiddaman 
32*32640292SAndy Fiddaman #define TPM_CRB_ADDRESS 0xFED40000
33*32640292SAndy Fiddaman #define TPM_CRB_REGS_SIZE 0x1000
34*32640292SAndy Fiddaman 
35*32640292SAndy Fiddaman #define TPM_CRB_CONTROL_AREA_ADDRESS \
36*32640292SAndy Fiddaman 	(TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req))
37*32640292SAndy Fiddaman #define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE
38*32640292SAndy Fiddaman 
39*32640292SAndy Fiddaman #define TPM_CRB_DATA_BUFFER_ADDRESS \
40*32640292SAndy Fiddaman 	(TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer))
41*32640292SAndy Fiddaman #define TPM_CRB_DATA_BUFFER_SIZE 0xF80
42*32640292SAndy Fiddaman 
43*32640292SAndy Fiddaman #define TPM_CRB_LOCALITIES_MAX 5
44*32640292SAndy Fiddaman 
45*32640292SAndy Fiddaman #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024)
46*32640292SAndy Fiddaman 
47*32640292SAndy Fiddaman #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log"
48*32640292SAndy Fiddaman 
49*32640292SAndy Fiddaman #define TPM_CRB_INTF_NAME "crb"
50*32640292SAndy Fiddaman 
51*32640292SAndy Fiddaman struct tpm_crb_regs {
52*32640292SAndy Fiddaman 	union tpm_crb_reg_loc_state {
53*32640292SAndy Fiddaman 		struct {
54*32640292SAndy Fiddaman 			uint32_t tpm_established : 1;
55*32640292SAndy Fiddaman 			uint32_t loc_assigned : 1;
56*32640292SAndy Fiddaman 			uint32_t active_locality : 3;
57*32640292SAndy Fiddaman 			uint32_t _reserved : 2;
58*32640292SAndy Fiddaman 			uint32_t tpm_req_valid_sts : 1;
59*32640292SAndy Fiddaman 		};
60*32640292SAndy Fiddaman 		uint32_t val;
61*32640292SAndy Fiddaman 	} loc_state;	       /* 0h */
62*32640292SAndy Fiddaman 	uint8_t _reserved1[4]; /* 4h */
63*32640292SAndy Fiddaman 	union tpm_crb_reg_loc_ctrl {
64*32640292SAndy Fiddaman 		struct {
65*32640292SAndy Fiddaman 			uint32_t request_access : 1;
66*32640292SAndy Fiddaman 			uint32_t relinquish : 1;
67*32640292SAndy Fiddaman 			uint32_t seize : 1;
68*32640292SAndy Fiddaman 			uint32_t reset_establishment_bit : 1;
69*32640292SAndy Fiddaman 		};
70*32640292SAndy Fiddaman 		uint32_t val;
71*32640292SAndy Fiddaman 	} loc_ctrl; /* 8h */
72*32640292SAndy Fiddaman 	union tpm_crb_reg_loc_sts {
73*32640292SAndy Fiddaman 		struct {
74*32640292SAndy Fiddaman 			uint32_t granted : 1;
75*32640292SAndy Fiddaman 			uint32_t been_seized : 1;
76*32640292SAndy Fiddaman 		};
77*32640292SAndy Fiddaman 		uint32_t val;
78*32640292SAndy Fiddaman 	} loc_sts;		  /* Ch */
79*32640292SAndy Fiddaman 	uint8_t _reserved2[0x20]; /* 10h */
80*32640292SAndy Fiddaman 	union tpm_crb_reg_intf_id {
81*32640292SAndy Fiddaman 		struct {
82*32640292SAndy Fiddaman 			uint64_t interface_type : 4;
83*32640292SAndy Fiddaman 			uint64_t interface_version : 4;
84*32640292SAndy Fiddaman 			uint64_t cap_locality : 1;
85*32640292SAndy Fiddaman 			uint64_t cap_crb_idle_bypass : 1;
86*32640292SAndy Fiddaman 			uint64_t _reserved1 : 1;
87*32640292SAndy Fiddaman 			uint64_t cap_data_xfer_size_support : 2;
88*32640292SAndy Fiddaman 			uint64_t cap_fifo : 1;
89*32640292SAndy Fiddaman 			uint64_t cap_crb : 1;
90*32640292SAndy Fiddaman 			uint64_t _reserved2 : 2;
91*32640292SAndy Fiddaman 			uint64_t interface_selector : 2;
92*32640292SAndy Fiddaman 			uint64_t intf_sel_lock : 1;
93*32640292SAndy Fiddaman 			uint64_t _reserved3 : 4;
94*32640292SAndy Fiddaman 			uint64_t rid : 8;
95*32640292SAndy Fiddaman 			uint64_t vid : 16;
96*32640292SAndy Fiddaman 			uint64_t did : 16;
97*32640292SAndy Fiddaman 		};
98*32640292SAndy Fiddaman 		uint64_t val;
99*32640292SAndy Fiddaman 	} intf_id; /* 30h */
100*32640292SAndy Fiddaman 	union tpm_crb_reg_ctrl_ext {
101*32640292SAndy Fiddaman 		struct {
102*32640292SAndy Fiddaman 			uint32_t clear;
103*32640292SAndy Fiddaman 			uint32_t remaining_bytes;
104*32640292SAndy Fiddaman 		};
105*32640292SAndy Fiddaman 		uint64_t val;
106*32640292SAndy Fiddaman 	} ctrl_ext; /* 38 */
107*32640292SAndy Fiddaman 	union tpm_crb_reg_ctrl_req {
108*32640292SAndy Fiddaman 		struct {
109*32640292SAndy Fiddaman 			uint32_t cmd_ready : 1;
110*32640292SAndy Fiddaman 			uint32_t go_idle : 1;
111*32640292SAndy Fiddaman 		};
112*32640292SAndy Fiddaman 		uint32_t val;
113*32640292SAndy Fiddaman 	} ctrl_req; /* 40h */
114*32640292SAndy Fiddaman 	union tpm_crb_reg_ctrl_sts {
115*32640292SAndy Fiddaman 		struct {
116*32640292SAndy Fiddaman 			uint32_t tpm_sts : 1;
117*32640292SAndy Fiddaman 			uint32_t tpm_idle : 1;
118*32640292SAndy Fiddaman 		};
119*32640292SAndy Fiddaman 		uint32_t val;
120*32640292SAndy Fiddaman 	} ctrl_sts; /* 44h */
121*32640292SAndy Fiddaman 	union tpm_crb_reg_ctrl_cancel {
122*32640292SAndy Fiddaman 		struct {
123*32640292SAndy Fiddaman 			uint32_t cancel : 1;
124*32640292SAndy Fiddaman 		};
125*32640292SAndy Fiddaman 		uint32_t val;
126*32640292SAndy Fiddaman 	} ctrl_cancel; /* 48h */
127*32640292SAndy Fiddaman 	union tpm_crb_reg_ctrl_start {
128*32640292SAndy Fiddaman 		struct {
129*32640292SAndy Fiddaman 			uint32_t start : 1;
130*32640292SAndy Fiddaman 		};
131*32640292SAndy Fiddaman 		uint32_t val;
132*32640292SAndy Fiddaman 	} ctrl_start;				       /* 4Ch*/
133*32640292SAndy Fiddaman 	uint32_t int_enable;			       /* 50h */
134*32640292SAndy Fiddaman 	uint32_t int_sts;			       /* 54h */
135*32640292SAndy Fiddaman 	uint32_t cmd_size;			       /* 58h */
136*32640292SAndy Fiddaman 	uint32_t cmd_addr_lo;			       /* 5Ch */
137*32640292SAndy Fiddaman 	uint32_t cmd_addr_hi;			       /* 60h */
138*32640292SAndy Fiddaman 	uint32_t rsp_size;			       /* 64h */
139*32640292SAndy Fiddaman 	uint64_t rsp_addr;			       /* 68h */
140*32640292SAndy Fiddaman 	uint8_t _reserved3[0x10];		       /* 70h */
141*32640292SAndy Fiddaman 	uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */
142*32640292SAndy Fiddaman } __packed;
143*32640292SAndy Fiddaman static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE,
144*32640292SAndy Fiddaman     "Invalid size of tpm_crb");
145*32640292SAndy Fiddaman 
146*32640292SAndy Fiddaman #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size)
147*32640292SAndy Fiddaman #define CRB_CMD_SIZE_WRITE(regs, val) \
148*32640292SAndy Fiddaman 	do {                          \
149*32640292SAndy Fiddaman 		regs.cmd_size = val;  \
150*32640292SAndy Fiddaman 	} while (0)
151*32640292SAndy Fiddaman #define CRB_CMD_ADDR_READ(regs) \
152*32640292SAndy Fiddaman 	(((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo)
153*32640292SAndy Fiddaman #define CRB_CMD_ADDR_WRITE(regs, val)                \
154*32640292SAndy Fiddaman 	do {                                         \
155*32640292SAndy Fiddaman 		regs.cmd_addr_lo = val & 0xFFFFFFFF; \
156*32640292SAndy Fiddaman 		regs.cmd_addr_hi = val >> 32;        \
157*32640292SAndy Fiddaman 	} while (0)
158*32640292SAndy Fiddaman #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size)
159*32640292SAndy Fiddaman #define CRB_RSP_SIZE_WRITE(regs, val) \
160*32640292SAndy Fiddaman 	do {                          \
161*32640292SAndy Fiddaman 		regs.rsp_size = val;  \
162*32640292SAndy Fiddaman 	} while (0)
163*32640292SAndy Fiddaman #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr)
164*32640292SAndy Fiddaman #define CRB_RSP_ADDR_WRITE(regs, val) \
165*32640292SAndy Fiddaman 	do {                          \
166*32640292SAndy Fiddaman 		regs.rsp_addr = val;  \
167*32640292SAndy Fiddaman 	} while (0)
168*32640292SAndy Fiddaman 
169*32640292SAndy Fiddaman struct tpm_crb {
170*32640292SAndy Fiddaman 	struct tpm_emul *emul;
171*32640292SAndy Fiddaman 	void *emul_sc;
172*32640292SAndy Fiddaman 	uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE];
173*32640292SAndy Fiddaman 	struct tpm_crb_regs regs;
174*32640292SAndy Fiddaman 	pthread_t thread;
175*32640292SAndy Fiddaman 	pthread_mutex_t mutex;
176*32640292SAndy Fiddaman 	pthread_cond_t cond;
177*32640292SAndy Fiddaman 	bool closing;
178*32640292SAndy Fiddaman };
179*32640292SAndy Fiddaman 
180*32640292SAndy Fiddaman static void *
tpm_crb_thread(void * const arg)181*32640292SAndy Fiddaman tpm_crb_thread(void *const arg)
182*32640292SAndy Fiddaman {
183*32640292SAndy Fiddaman 	struct tpm_crb *const crb = arg;
184*32640292SAndy Fiddaman 
185*32640292SAndy Fiddaman 	pthread_mutex_lock(&crb->mutex);
186*32640292SAndy Fiddaman 	for (;;) {
187*32640292SAndy Fiddaman 		/*
188*32640292SAndy Fiddaman 		 * We're releasing the lock after wake up. Therefore, we have to
189*32640292SAndy Fiddaman 		 * check the closing condition before and after going to sleep.
190*32640292SAndy Fiddaman 		 */
191*32640292SAndy Fiddaman 		if (crb->closing)
192*32640292SAndy Fiddaman 			break;
193*32640292SAndy Fiddaman 
194*32640292SAndy Fiddaman 		pthread_cond_wait(&crb->cond, &crb->mutex);
195*32640292SAndy Fiddaman 
196*32640292SAndy Fiddaman 		if (crb->closing)
197*32640292SAndy Fiddaman 			break;
198*32640292SAndy Fiddaman 
199*32640292SAndy Fiddaman 		const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs);
200*32640292SAndy Fiddaman 		const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs);
201*32640292SAndy Fiddaman 		const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs);
202*32640292SAndy Fiddaman 		const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs);
203*32640292SAndy Fiddaman 
204*32640292SAndy Fiddaman 		const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
205*32640292SAndy Fiddaman 		const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS;
206*32640292SAndy Fiddaman 
207*32640292SAndy Fiddaman 		if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE ||
208*32640292SAndy Fiddaman 		    cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE ||
209*32640292SAndy Fiddaman 		    rsp_off > TPM_CRB_DATA_BUFFER_SIZE ||
210*32640292SAndy Fiddaman 		    rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) {
211*32640292SAndy Fiddaman 			warnx(
212*32640292SAndy Fiddaman 			    "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r",
213*32640292SAndy Fiddaman 			    __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr,
214*32640292SAndy Fiddaman 			    rsp_addr + rsp_size);
215*32640292SAndy Fiddaman 			break;
216*32640292SAndy Fiddaman 		}
217*32640292SAndy Fiddaman 
218*32640292SAndy Fiddaman 		uint8_t cmd[TPM_CRB_DATA_BUFFER_SIZE];
219*32640292SAndy Fiddaman 		memcpy(cmd, crb->regs.data_buffer, TPM_CRB_DATA_BUFFER_SIZE);
220*32640292SAndy Fiddaman 
221*32640292SAndy Fiddaman 		/*
222*32640292SAndy Fiddaman 		 * A TPM command can take multiple seconds to execute. As we've
223*32640292SAndy Fiddaman 		 * copied all required values and buffers at this point, we can
224*32640292SAndy Fiddaman 		 * release the mutex.
225*32640292SAndy Fiddaman 		 */
226*32640292SAndy Fiddaman 		pthread_mutex_unlock(&crb->mutex);
227*32640292SAndy Fiddaman 
228*32640292SAndy Fiddaman 		/*
229*32640292SAndy Fiddaman 		 * The command response buffer interface uses a single buffer
230*32640292SAndy Fiddaman 		 * for sending a command to and receiving a response from the
231*32640292SAndy Fiddaman 		 * tpm. To avoid reading old data from the command buffer which
232*32640292SAndy Fiddaman 		 * might be a security issue, we zero out the command buffer
233*32640292SAndy Fiddaman 		 * before writing the response into it. The rsp_size parameter
234*32640292SAndy Fiddaman 		 * is controlled by the guest and it's not guaranteed that the
235*32640292SAndy Fiddaman 		 * response has a size of rsp_size (e.g. if the tpm returned an
236*32640292SAndy Fiddaman 		 * error, the response would have a different size than
237*32640292SAndy Fiddaman 		 * expected). For that reason, use a second buffer for the
238*32640292SAndy Fiddaman 		 * response.
239*32640292SAndy Fiddaman 		 */
240*32640292SAndy Fiddaman 		uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 };
241*32640292SAndy Fiddaman 		crb->emul->execute_cmd(crb->emul_sc, &cmd[cmd_off], cmd_size,
242*32640292SAndy Fiddaman 		    &rsp[rsp_off], rsp_size);
243*32640292SAndy Fiddaman 
244*32640292SAndy Fiddaman 		pthread_mutex_lock(&crb->mutex);
245*32640292SAndy Fiddaman 		memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE);
246*32640292SAndy Fiddaman 		memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size);
247*32640292SAndy Fiddaman 
248*32640292SAndy Fiddaman 		crb->regs.ctrl_start.start = false;
249*32640292SAndy Fiddaman 	}
250*32640292SAndy Fiddaman 	pthread_mutex_unlock(&crb->mutex);
251*32640292SAndy Fiddaman 
252*32640292SAndy Fiddaman 	return (NULL);
253*32640292SAndy Fiddaman }
254*32640292SAndy Fiddaman 
255*32640292SAndy Fiddaman static int
tpm_crb_mmiocpy(void * const dst,void * const src,const int size)256*32640292SAndy Fiddaman tpm_crb_mmiocpy(void *const dst, void *const src, const int size)
257*32640292SAndy Fiddaman {
258*32640292SAndy Fiddaman 	if (!(size == 1 || size == 2 || size == 4 || size == 8))
259*32640292SAndy Fiddaman 		return (EINVAL);
260*32640292SAndy Fiddaman 	memcpy(dst, src, size);
261*32640292SAndy Fiddaman 
262*32640292SAndy Fiddaman 	return (0);
263*32640292SAndy Fiddaman }
264*32640292SAndy Fiddaman 
265*32640292SAndy Fiddaman 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)266*32640292SAndy Fiddaman tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir,
267*32640292SAndy Fiddaman     const uint64_t addr, const int size, uint64_t *const val, void *const arg1,
268*32640292SAndy Fiddaman     const long arg2 __unused)
269*32640292SAndy Fiddaman {
270*32640292SAndy Fiddaman 	struct tpm_crb *crb;
271*32640292SAndy Fiddaman 	uint8_t *ptr;
272*32640292SAndy Fiddaman 	uint64_t off, shift;
273*32640292SAndy Fiddaman 	int error = 0;
274*32640292SAndy Fiddaman 
275*32640292SAndy Fiddaman 	if ((addr & (size - 1)) != 0) {
276*32640292SAndy Fiddaman 		warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__,
277*32640292SAndy Fiddaman 		    (dir == MEM_F_READ) ? "read" : "write", addr, size);
278*32640292SAndy Fiddaman 		return (EINVAL);
279*32640292SAndy Fiddaman 	}
280*32640292SAndy Fiddaman 
281*32640292SAndy Fiddaman 	crb = arg1;
282*32640292SAndy Fiddaman 
283*32640292SAndy Fiddaman 	off = addr - TPM_CRB_ADDRESS;
284*32640292SAndy Fiddaman 	if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) {
285*32640292SAndy Fiddaman 		return (EINVAL);
286*32640292SAndy Fiddaman 	}
287*32640292SAndy Fiddaman 
288*32640292SAndy Fiddaman 	shift = 8 * (off & 3);
289*32640292SAndy Fiddaman 	ptr = (uint8_t *)&crb->regs + off;
290*32640292SAndy Fiddaman 
291*32640292SAndy Fiddaman 	if (dir == MEM_F_READ) {
292*32640292SAndy Fiddaman 		error = tpm_crb_mmiocpy(val, ptr, size);
293*32640292SAndy Fiddaman 		if (error)
294*32640292SAndy Fiddaman 			goto err_out;
295*32640292SAndy Fiddaman 	} else {
296*32640292SAndy Fiddaman 		switch (off & ~0x3) {
297*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, loc_ctrl): {
298*32640292SAndy Fiddaman 			union tpm_crb_reg_loc_ctrl loc_ctrl;
299*32640292SAndy Fiddaman 
300*32640292SAndy Fiddaman 			if ((size_t)size > sizeof(loc_ctrl))
301*32640292SAndy Fiddaman 				goto err_out;
302*32640292SAndy Fiddaman 
303*32640292SAndy Fiddaman 			*val = *val << shift;
304*32640292SAndy Fiddaman 			tpm_crb_mmiocpy(&loc_ctrl, val, size);
305*32640292SAndy Fiddaman 
306*32640292SAndy Fiddaman 			if (loc_ctrl.relinquish) {
307*32640292SAndy Fiddaman 				crb->regs.loc_sts.granted = false;
308*32640292SAndy Fiddaman 				crb->regs.loc_state.loc_assigned = false;
309*32640292SAndy Fiddaman 			} else if (loc_ctrl.request_access) {
310*32640292SAndy Fiddaman 				crb->regs.loc_sts.granted = true;
311*32640292SAndy Fiddaman 				crb->regs.loc_state.loc_assigned = true;
312*32640292SAndy Fiddaman 			}
313*32640292SAndy Fiddaman 
314*32640292SAndy Fiddaman 			break;
315*32640292SAndy Fiddaman 		}
316*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, ctrl_req): {
317*32640292SAndy Fiddaman 			union tpm_crb_reg_ctrl_req req;
318*32640292SAndy Fiddaman 
319*32640292SAndy Fiddaman 			if ((size_t)size > sizeof(req))
320*32640292SAndy Fiddaman 				goto err_out;
321*32640292SAndy Fiddaman 
322*32640292SAndy Fiddaman 			*val = *val << shift;
323*32640292SAndy Fiddaman 			tpm_crb_mmiocpy(&req, val, size);
324*32640292SAndy Fiddaman 
325*32640292SAndy Fiddaman 			if (req.cmd_ready && !req.go_idle) {
326*32640292SAndy Fiddaman 				crb->regs.ctrl_sts.tpm_idle = false;
327*32640292SAndy Fiddaman 			} else if (!req.cmd_ready && req.go_idle) {
328*32640292SAndy Fiddaman 				crb->regs.ctrl_sts.tpm_idle = true;
329*32640292SAndy Fiddaman 			}
330*32640292SAndy Fiddaman 
331*32640292SAndy Fiddaman 			break;
332*32640292SAndy Fiddaman 		}
333*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, ctrl_cancel): {
334*32640292SAndy Fiddaman 			/* TODO: cancel the tpm command */
335*32640292SAndy Fiddaman 			warnx(
336*32640292SAndy Fiddaman 			    "%s: cancelling a TPM command is not implemented yet",
337*32640292SAndy Fiddaman 			    __func__);
338*32640292SAndy Fiddaman 
339*32640292SAndy Fiddaman 			break;
340*32640292SAndy Fiddaman 		}
341*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, ctrl_start): {
342*32640292SAndy Fiddaman 			union tpm_crb_reg_ctrl_start start;
343*32640292SAndy Fiddaman 
344*32640292SAndy Fiddaman 			if ((size_t)size > sizeof(start))
345*32640292SAndy Fiddaman 				goto err_out;
346*32640292SAndy Fiddaman 
347*32640292SAndy Fiddaman 			*val = *val << shift;
348*32640292SAndy Fiddaman 
349*32640292SAndy Fiddaman 			pthread_mutex_lock(&crb->mutex);
350*32640292SAndy Fiddaman 			tpm_crb_mmiocpy(&start, val, size);
351*32640292SAndy Fiddaman 
352*32640292SAndy Fiddaman 			if (!start.start || crb->regs.ctrl_start.start)
353*32640292SAndy Fiddaman 				break;
354*32640292SAndy Fiddaman 
355*32640292SAndy Fiddaman 			crb->regs.ctrl_start.start = true;
356*32640292SAndy Fiddaman 
357*32640292SAndy Fiddaman 			pthread_cond_signal(&crb->cond);
358*32640292SAndy Fiddaman 			pthread_mutex_unlock(&crb->mutex);
359*32640292SAndy Fiddaman 
360*32640292SAndy Fiddaman 			break;
361*32640292SAndy Fiddaman 		}
362*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, cmd_size):
363*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, cmd_addr_lo):
364*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, cmd_addr_hi):
365*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs, rsp_size):
366*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs,
367*32640292SAndy Fiddaman 		    rsp_addr) ... offsetof(struct tpm_crb_regs, rsp_addr) +
368*32640292SAndy Fiddaman 		    4:
369*32640292SAndy Fiddaman 		case offsetof(struct tpm_crb_regs,
370*32640292SAndy Fiddaman 		    data_buffer) ... offsetof(struct tpm_crb_regs, data_buffer) +
371*32640292SAndy Fiddaman 		    TPM_CRB_DATA_BUFFER_SIZE / 4:
372*32640292SAndy Fiddaman 			/*
373*32640292SAndy Fiddaman 			 * Those fields are used to execute a TPM command. The
374*32640292SAndy Fiddaman 			 * crb_thread will access them. For that reason, we have
375*32640292SAndy Fiddaman 			 * to acquire the crb mutex in order to write them.
376*32640292SAndy Fiddaman 			 */
377*32640292SAndy Fiddaman 			pthread_mutex_lock(&crb->mutex);
378*32640292SAndy Fiddaman 			error = tpm_crb_mmiocpy(ptr, val, size);
379*32640292SAndy Fiddaman 			pthread_mutex_unlock(&crb->mutex);
380*32640292SAndy Fiddaman 			if (error)
381*32640292SAndy Fiddaman 				goto err_out;
382*32640292SAndy Fiddaman 			break;
383*32640292SAndy Fiddaman 		default:
384*32640292SAndy Fiddaman 			/*
385*32640292SAndy Fiddaman 			 * The other fields are either readonly or we do not
386*32640292SAndy Fiddaman 			 * support writing them.
387*32640292SAndy Fiddaman 			 */
388*32640292SAndy Fiddaman 			error = EINVAL;
389*32640292SAndy Fiddaman 			goto err_out;
390*32640292SAndy Fiddaman 		}
391*32640292SAndy Fiddaman 	}
392*32640292SAndy Fiddaman 
393*32640292SAndy Fiddaman 	return (0);
394*32640292SAndy Fiddaman 
395*32640292SAndy Fiddaman err_out:
396*32640292SAndy Fiddaman 	warnx("%s: invalid %s @ %16lx [size = %d]", __func__,
397*32640292SAndy Fiddaman 	    dir == MEM_F_READ ? "read" : "write", addr, size);
398*32640292SAndy Fiddaman 
399*32640292SAndy Fiddaman 	return (error);
400*32640292SAndy Fiddaman }
401*32640292SAndy Fiddaman 
402*32640292SAndy Fiddaman static int
tpm_crb_modify_mmio_registration(const bool registration,void * const arg1)403*32640292SAndy Fiddaman tpm_crb_modify_mmio_registration(const bool registration, void *const arg1)
404*32640292SAndy Fiddaman {
405*32640292SAndy Fiddaman 	struct mem_range crb_mmio = {
406*32640292SAndy Fiddaman 		.name = "crb-mmio",
407*32640292SAndy Fiddaman 		.base = TPM_CRB_ADDRESS,
408*32640292SAndy Fiddaman 		.size = TPM_CRB_LOCALITIES_MAX * TPM_CRB_CONTROL_AREA_SIZE,
409*32640292SAndy Fiddaman 		.flags = MEM_F_RW,
410*32640292SAndy Fiddaman 		.arg1 = arg1,
411*32640292SAndy Fiddaman 		.handler = tpm_crb_mem_handler,
412*32640292SAndy Fiddaman 	};
413*32640292SAndy Fiddaman 
414*32640292SAndy Fiddaman 	if (registration)
415*32640292SAndy Fiddaman 		return (register_mem(&crb_mmio));
416*32640292SAndy Fiddaman 	else
417*32640292SAndy Fiddaman 		return (unregister_mem(&crb_mmio));
418*32640292SAndy Fiddaman }
419*32640292SAndy Fiddaman 
420*32640292SAndy Fiddaman static int
tpm_crb_init(void ** sc,struct tpm_emul * emul,void * emul_sc,struct acpi_device * acpi_dev)421*32640292SAndy Fiddaman tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc,
422*32640292SAndy Fiddaman     struct acpi_device *acpi_dev)
423*32640292SAndy Fiddaman {
424*32640292SAndy Fiddaman 	struct tpm_crb *crb = NULL;
425*32640292SAndy Fiddaman 	int error;
426*32640292SAndy Fiddaman 
427*32640292SAndy Fiddaman 	assert(sc != NULL);
428*32640292SAndy Fiddaman 	assert(emul != NULL);
429*32640292SAndy Fiddaman 
430*32640292SAndy Fiddaman 	crb = calloc(1, sizeof(struct tpm_crb));
431*32640292SAndy Fiddaman 	if (crb == NULL) {
432*32640292SAndy Fiddaman 		warnx("%s: failed to allocate tpm crb", __func__);
433*32640292SAndy Fiddaman 		error = ENOMEM;
434*32640292SAndy Fiddaman 		goto err_out;
435*32640292SAndy Fiddaman 	}
436*32640292SAndy Fiddaman 
437*32640292SAndy Fiddaman 	memset(crb, 0, sizeof(*crb));
438*32640292SAndy Fiddaman 
439*32640292SAndy Fiddaman 	crb->emul = emul;
440*32640292SAndy Fiddaman 	crb->emul_sc = emul_sc;
441*32640292SAndy Fiddaman 
442*32640292SAndy Fiddaman 	crb->regs.loc_state.tpm_req_valid_sts = true;
443*32640292SAndy Fiddaman 	crb->regs.loc_state.tpm_established = true;
444*32640292SAndy Fiddaman 
445*32640292SAndy Fiddaman 	crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB;
446*32640292SAndy Fiddaman 	crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB;
447*32640292SAndy Fiddaman 	crb->regs.intf_id.cap_locality = false;
448*32640292SAndy Fiddaman 	crb->regs.intf_id.cap_crb_idle_bypass = false;
449*32640292SAndy Fiddaman 	crb->regs.intf_id.cap_data_xfer_size_support =
450*32640292SAndy Fiddaman 	    TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64;
451*32640292SAndy Fiddaman 	crb->regs.intf_id.cap_fifo = false;
452*32640292SAndy Fiddaman 	crb->regs.intf_id.cap_crb = true;
453*32640292SAndy Fiddaman 	crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB;
454*32640292SAndy Fiddaman 	crb->regs.intf_id.intf_sel_lock = false;
455*32640292SAndy Fiddaman 	crb->regs.intf_id.rid = 0;
456*32640292SAndy Fiddaman 	crb->regs.intf_id.vid = 0x1014; /* IBM */
457*32640292SAndy Fiddaman 	crb->regs.intf_id.did = 0x1014; /* IBM */
458*32640292SAndy Fiddaman 
459*32640292SAndy Fiddaman 	crb->regs.ctrl_sts.tpm_idle = true;
460*32640292SAndy Fiddaman 
461*32640292SAndy Fiddaman 	CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
462*32640292SAndy Fiddaman 	CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
463*32640292SAndy Fiddaman 	CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
464*32640292SAndy Fiddaman 	CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
465*32640292SAndy Fiddaman 
466*32640292SAndy Fiddaman 	error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME,
467*32640292SAndy Fiddaman 	    TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area);
468*32640292SAndy Fiddaman 	if (error) {
469*32640292SAndy Fiddaman 		warnx("%s: failed to add fwcfg file", __func__);
470*32640292SAndy Fiddaman 		goto err_out;
471*32640292SAndy Fiddaman 	}
472*32640292SAndy Fiddaman 
473*32640292SAndy Fiddaman 	error = acpi_device_add_res_fixed_memory32(acpi_dev, false,
474*32640292SAndy Fiddaman 	    TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE);
475*32640292SAndy Fiddaman 	if (error) {
476*32640292SAndy Fiddaman 		warnx("%s: failed to add acpi resources\n", __func__);
477*32640292SAndy Fiddaman 		goto err_out;
478*32640292SAndy Fiddaman 	}
479*32640292SAndy Fiddaman 
480*32640292SAndy Fiddaman 	error = tpm_crb_modify_mmio_registration(true, crb);
481*32640292SAndy Fiddaman 	if (error) {
482*32640292SAndy Fiddaman 		warnx("%s: failed to register crb mmio", __func__);
483*32640292SAndy Fiddaman 		goto err_out;
484*32640292SAndy Fiddaman 	}
485*32640292SAndy Fiddaman 
486*32640292SAndy Fiddaman 	error = pthread_mutex_init(&crb->mutex, NULL);
487*32640292SAndy Fiddaman 	if (error) {
488*32640292SAndy Fiddaman 		warnc(error, "%s: failed to init mutex", __func__);
489*32640292SAndy Fiddaman 		goto err_out;
490*32640292SAndy Fiddaman 	}
491*32640292SAndy Fiddaman 
492*32640292SAndy Fiddaman 	error = pthread_cond_init(&crb->cond, NULL);
493*32640292SAndy Fiddaman 	if (error) {
494*32640292SAndy Fiddaman 		warnc(error, "%s: failed to init cond", __func__);
495*32640292SAndy Fiddaman 		goto err_out;
496*32640292SAndy Fiddaman 	}
497*32640292SAndy Fiddaman 
498*32640292SAndy Fiddaman 	error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb);
499*32640292SAndy Fiddaman 	if (error) {
500*32640292SAndy Fiddaman 		warnx("%s: failed to create thread\n", __func__);
501*32640292SAndy Fiddaman 		goto err_out;
502*32640292SAndy Fiddaman 	}
503*32640292SAndy Fiddaman 
504*32640292SAndy Fiddaman 	pthread_set_name_np(crb->thread, "tpm_intf_crb");
505*32640292SAndy Fiddaman 
506*32640292SAndy Fiddaman 	*sc = crb;
507*32640292SAndy Fiddaman 
508*32640292SAndy Fiddaman 	return (0);
509*32640292SAndy Fiddaman 
510*32640292SAndy Fiddaman err_out:
511*32640292SAndy Fiddaman 	free(crb);
512*32640292SAndy Fiddaman 
513*32640292SAndy Fiddaman 	return (error);
514*32640292SAndy Fiddaman }
515*32640292SAndy Fiddaman 
516*32640292SAndy Fiddaman static void
tpm_crb_deinit(void * sc)517*32640292SAndy Fiddaman tpm_crb_deinit(void *sc)
518*32640292SAndy Fiddaman {
519*32640292SAndy Fiddaman 	struct tpm_crb *crb;
520*32640292SAndy Fiddaman 	int error;
521*32640292SAndy Fiddaman 
522*32640292SAndy Fiddaman 	if (sc == NULL) {
523*32640292SAndy Fiddaman 		return;
524*32640292SAndy Fiddaman 	}
525*32640292SAndy Fiddaman 
526*32640292SAndy Fiddaman 	crb = sc;
527*32640292SAndy Fiddaman 
528*32640292SAndy Fiddaman 	crb->closing = true;
529*32640292SAndy Fiddaman 	pthread_cond_signal(&crb->cond);
530*32640292SAndy Fiddaman 	pthread_join(crb->thread, NULL);
531*32640292SAndy Fiddaman 
532*32640292SAndy Fiddaman 	pthread_cond_destroy(&crb->cond);
533*32640292SAndy Fiddaman 	pthread_mutex_destroy(&crb->mutex);
534*32640292SAndy Fiddaman 
535*32640292SAndy Fiddaman 	error = tpm_crb_modify_mmio_registration(false, NULL);
536*32640292SAndy Fiddaman 	assert(error == 0);
537*32640292SAndy Fiddaman 
538*32640292SAndy Fiddaman 	free(crb);
539*32640292SAndy Fiddaman }
540*32640292SAndy Fiddaman 
541*32640292SAndy Fiddaman static int
tpm_crb_build_acpi_table(void * sc __unused,struct vmctx * vm_ctx)542*32640292SAndy Fiddaman tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx)
543*32640292SAndy Fiddaman {
544*32640292SAndy Fiddaman 	struct basl_table *table;
545*32640292SAndy Fiddaman 
546*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2,
547*32640292SAndy Fiddaman 	    BASL_TABLE_ALIGNMENT));
548*32640292SAndy Fiddaman 
549*32640292SAndy Fiddaman 	/* Header */
550*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1));
551*32640292SAndy Fiddaman 	/* Platform Class */
552*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, 2));
553*32640292SAndy Fiddaman 	/* Reserved */
554*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, 2));
555*32640292SAndy Fiddaman 	/* Control Address */
556*32640292SAndy Fiddaman 	BASL_EXEC(
557*32640292SAndy Fiddaman 	    basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8));
558*32640292SAndy Fiddaman 	/* Start Method == (7) Command Response Buffer */
559*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 7, 4));
560*32640292SAndy Fiddaman 	/* Start Method Specific Parameters */
561*32640292SAndy Fiddaman 	uint8_t parameters[12] = { 0 };
562*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_append_bytes(table, parameters, 12));
563*32640292SAndy Fiddaman 	/* Log Area Minimum Length */
564*32640292SAndy Fiddaman 	BASL_EXEC(
565*32640292SAndy Fiddaman 	    basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4));
566*32640292SAndy Fiddaman 	/* Log Area Start Address */
567*32640292SAndy Fiddaman #ifdef	__FreeBSD__
568*32640292SAndy Fiddaman 	BASL_EXEC(
569*32640292SAndy Fiddaman 	    basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
570*32640292SAndy Fiddaman #else
571*32640292SAndy Fiddaman 	BASL_EXEC(
572*32640292SAndy Fiddaman 	    basl_table_append_fwcfg(table,
573*32640292SAndy Fiddaman 	    (const uint8_t *)TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
574*32640292SAndy Fiddaman #endif
575*32640292SAndy Fiddaman 
576*32640292SAndy Fiddaman 	BASL_EXEC(basl_table_register_to_rsdt(table));
577*32640292SAndy Fiddaman 
578*32640292SAndy Fiddaman 	return (0);
579*32640292SAndy Fiddaman }
580*32640292SAndy Fiddaman 
581*32640292SAndy Fiddaman static struct tpm_intf tpm_intf_crb = {
582*32640292SAndy Fiddaman 	.name = TPM_CRB_INTF_NAME,
583*32640292SAndy Fiddaman 	.init = tpm_crb_init,
584*32640292SAndy Fiddaman 	.deinit = tpm_crb_deinit,
585*32640292SAndy Fiddaman 	.build_acpi_table = tpm_crb_build_acpi_table,
586*32640292SAndy Fiddaman };
587*32640292SAndy Fiddaman TPM_INTF_SET(tpm_intf_crb);
588