xref: /freebsd/usr.sbin/bhyve/tpm_intf_crb.c (revision c9fdd4f3cc18c03683de85318ba8d318f96b58c4)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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_intf.h"
30 
31 #define TPM_CRB_ADDRESS 0xFED40000
32 #define TPM_CRB_REGS_SIZE 0x1000
33 
34 #define TPM_CRB_DATA_BUFFER_ADDRESS \
35 	(TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer))
36 #define TPM_CRB_DATA_BUFFER_SIZE 0xF80
37 
38 #define TPM_CRB_LOCALITIES_MAX 5
39 
40 struct tpm_crb_regs {
41 	union tpm_crb_reg_loc_state {
42 		struct {
43 			uint32_t tpm_established : 1;
44 			uint32_t loc_assigned : 1;
45 			uint32_t active_locality : 3;
46 			uint32_t _reserved : 2;
47 			uint32_t tpm_req_valid_sts : 1;
48 		};
49 		uint32_t val;
50 	} loc_state;	       /* 0h */
51 	uint8_t _reserved1[4]; /* 4h */
52 	union tpm_crb_reg_loc_ctrl {
53 		struct {
54 			uint32_t request_access : 1;
55 			uint32_t relinquish : 1;
56 			uint32_t seize : 1;
57 			uint32_t reset_establishment_bit : 1;
58 		};
59 		uint32_t val;
60 	} loc_ctrl; /* 8h */
61 	union tpm_crb_reg_loc_sts {
62 		struct {
63 			uint32_t granted : 1;
64 			uint32_t been_seized : 1;
65 		};
66 		uint32_t val;
67 	} loc_sts;		  /* Ch */
68 	uint8_t _reserved2[0x20]; /* 10h */
69 	union tpm_crb_reg_intf_id {
70 		struct {
71 			uint64_t interface_type : 4;
72 			uint64_t interface_version : 4;
73 			uint64_t cap_locality : 1;
74 			uint64_t cap_crb_idle_bypass : 1;
75 			uint64_t _reserved1 : 1;
76 			uint64_t cap_data_xfer_size_support : 2;
77 			uint64_t cap_fifo : 1;
78 			uint64_t cap_crb : 1;
79 			uint64_t _reserved2 : 2;
80 			uint64_t interface_selector : 2;
81 			uint64_t intf_sel_lock : 1;
82 			uint64_t _reserved3 : 4;
83 			uint64_t rid : 8;
84 			uint64_t vid : 16;
85 			uint64_t did : 16;
86 		};
87 		uint64_t val;
88 	} intf_id; /* 30h */
89 	union tpm_crb_reg_ctrl_ext {
90 		struct {
91 			uint32_t clear;
92 			uint32_t remaining_bytes;
93 		};
94 		uint64_t val;
95 	} ctrl_ext; /* 38 */
96 	union tpm_crb_reg_ctrl_req {
97 		struct {
98 			uint32_t cmd_ready : 1;
99 			uint32_t go_idle : 1;
100 		};
101 		uint32_t val;
102 	} ctrl_req; /* 40h */
103 	union tpm_crb_reg_ctrl_sts {
104 		struct {
105 			uint32_t tpm_sts : 1;
106 			uint32_t tpm_idle : 1;
107 		};
108 		uint32_t val;
109 	} ctrl_sts; /* 44h */
110 	union tpm_crb_reg_ctrl_cancel {
111 		struct {
112 			uint32_t cancel : 1;
113 		};
114 		uint32_t val;
115 	} ctrl_cancel; /* 48h */
116 	union tpm_crb_reg_ctrl_start {
117 		struct {
118 			uint32_t start : 1;
119 		};
120 		uint32_t val;
121 	} ctrl_start;				       /* 4Ch*/
122 	uint32_t int_enable;			       /* 50h */
123 	uint32_t int_sts;			       /* 54h */
124 	uint32_t cmd_size;			       /* 58h */
125 	uint32_t cmd_addr_lo;			       /* 5Ch */
126 	uint32_t cmd_addr_hi;			       /* 60h */
127 	uint32_t rsp_size;			       /* 64h */
128 	uint64_t rsp_addr;			       /* 68h */
129 	uint8_t _reserved3[0x10];		       /* 70h */
130 	uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */
131 } __packed;
132 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE,
133     "Invalid size of tpm_crb");
134 
135 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size)
136 #define CRB_CMD_SIZE_WRITE(regs, val) \
137 	do {                          \
138 		regs.cmd_size = val;  \
139 	} while (0)
140 #define CRB_CMD_ADDR_READ(regs) \
141 	(((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo)
142 #define CRB_CMD_ADDR_WRITE(regs, val)                \
143 	do {                                         \
144 		regs.cmd_addr_lo = val & 0xFFFFFFFF; \
145 		regs.cmd_addr_hi = val >> 32;        \
146 	} while (0)
147 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size)
148 #define CRB_RSP_SIZE_WRITE(regs, val) \
149 	do {                          \
150 		regs.rsp_size = val;  \
151 	} while (0)
152 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr)
153 #define CRB_RSP_ADDR_WRITE(regs, val) \
154 	do {                          \
155 		regs.rsp_addr = val;  \
156 	} while (0)
157 
158 struct tpm_crb {
159 	struct tpm_crb_regs regs;
160 };
161 
162 static int
163 tpm_crb_init(void **sc)
164 {
165 	struct tpm_crb *crb = NULL;
166 	int error;
167 
168 	assert(sc != NULL);
169 
170 	crb = calloc(1, sizeof(struct tpm_crb));
171 	if (crb == NULL) {
172 		warnx("%s: failed to allocate tpm crb", __func__);
173 		error = ENOMEM;
174 		goto err_out;
175 	}
176 
177 	memset(crb, 0, sizeof(*crb));
178 
179 	crb->regs.loc_state.tpm_req_valid_sts = true;
180 	crb->regs.loc_state.tpm_established = true;
181 
182 	crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB;
183 	crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB;
184 	crb->regs.intf_id.cap_locality = false;
185 	crb->regs.intf_id.cap_crb_idle_bypass = false;
186 	crb->regs.intf_id.cap_data_xfer_size_support =
187 	    TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64;
188 	crb->regs.intf_id.cap_fifo = false;
189 	crb->regs.intf_id.cap_crb = true;
190 	crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB;
191 	crb->regs.intf_id.intf_sel_lock = false;
192 	crb->regs.intf_id.rid = 0;
193 	crb->regs.intf_id.vid = 0x1014; /* IBM */
194 	crb->regs.intf_id.did = 0x1014; /* IBM */
195 
196 	crb->regs.ctrl_sts.tpm_idle = true;
197 
198 	CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
199 	CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
200 	CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE);
201 	CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS);
202 
203 	*sc = crb;
204 
205 	return (0);
206 
207 err_out:
208 	free(crb);
209 
210 	return (error);
211 }
212 
213 static void
214 tpm_crb_deinit(void *sc)
215 {
216 	struct tpm_crb *crb;
217 
218 	if (sc == NULL) {
219 		return;
220 	}
221 
222 	crb = sc;
223 
224 	free(crb);
225 }
226 
227 static struct tpm_intf tpm_intf_crb = {
228 	.name = "crb",
229 	.init = tpm_crb_init,
230 	.deinit = tpm_crb_deinit,
231 };
232 TPM_INTF_SET(tpm_intf_crb);
233