]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/tpm_intf_crb.c
Upgrade to OpenPAM Ximenia.
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / tpm_intf_crb.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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_device.h"
30 #include "tpm_intf.h"
31
32 #define TPM_CRB_ADDRESS 0xFED40000
33 #define TPM_CRB_REGS_SIZE 0x1000
34
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
38
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
42
43 #define TPM_CRB_LOCALITIES_MAX 5
44
45 #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024)
46
47 #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log"
48
49 #define TPM_CRB_INTF_NAME "crb"
50
51 struct tpm_crb_regs {
52         union tpm_crb_reg_loc_state {
53                 struct {
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;
59                 };
60                 uint32_t val;
61         } loc_state;           /* 0h */
62         uint8_t _reserved1[4]; /* 4h */
63         union tpm_crb_reg_loc_ctrl {
64                 struct {
65                         uint32_t request_access : 1;
66                         uint32_t relinquish : 1;
67                         uint32_t seize : 1;
68                         uint32_t reset_establishment_bit : 1;
69                 };
70                 uint32_t val;
71         } loc_ctrl; /* 8h */
72         union tpm_crb_reg_loc_sts {
73                 struct {
74                         uint32_t granted : 1;
75                         uint32_t been_seized : 1;
76                 };
77                 uint32_t val;
78         } loc_sts;                /* Ch */
79         uint8_t _reserved2[0x20]; /* 10h */
80         union tpm_crb_reg_intf_id {
81                 struct {
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;
89                         uint64_t cap_crb : 1;
90                         uint64_t _reserved2 : 2;
91                         uint64_t interface_selector : 2;
92                         uint64_t intf_sel_lock : 1;
93                         uint64_t _reserved3 : 4;
94                         uint64_t rid : 8;
95                         uint64_t vid : 16;
96                         uint64_t did : 16;
97                 };
98                 uint64_t val;
99         } intf_id; /* 30h */
100         union tpm_crb_reg_ctrl_ext {
101                 struct {
102                         uint32_t clear;
103                         uint32_t remaining_bytes;
104                 };
105                 uint64_t val;
106         } ctrl_ext; /* 38 */
107         union tpm_crb_reg_ctrl_req {
108                 struct {
109                         uint32_t cmd_ready : 1;
110                         uint32_t go_idle : 1;
111                 };
112                 uint32_t val;
113         } ctrl_req; /* 40h */
114         union tpm_crb_reg_ctrl_sts {
115                 struct {
116                         uint32_t tpm_sts : 1;
117                         uint32_t tpm_idle : 1;
118                 };
119                 uint32_t val;
120         } ctrl_sts; /* 44h */
121         union tpm_crb_reg_ctrl_cancel {
122                 struct {
123                         uint32_t cancel : 1;
124                 };
125                 uint32_t val;
126         } ctrl_cancel; /* 48h */
127         union tpm_crb_reg_ctrl_start {
128                 struct {
129                         uint32_t start : 1;
130                 };
131                 uint32_t val;
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 */
142 } __packed;
143 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE,
144     "Invalid size of tpm_crb");
145
146 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size)
147 #define CRB_CMD_SIZE_WRITE(regs, val) \
148         do {                          \
149                 regs.cmd_size = val;  \
150         } while (0)
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)                \
154         do {                                         \
155                 regs.cmd_addr_lo = val & 0xFFFFFFFF; \
156                 regs.cmd_addr_hi = val >> 32;        \
157         } while (0)
158 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size)
159 #define CRB_RSP_SIZE_WRITE(regs, val) \
160         do {                          \
161                 regs.rsp_size = val;  \
162         } while (0)
163 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr)
164 #define CRB_RSP_ADDR_WRITE(regs, val) \
165         do {                          \
166                 regs.rsp_addr = val;  \
167         } while (0)
168
169 struct tpm_crb {
170         struct tpm_emul *emul;
171         void *emul_sc;
172         uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE];
173         struct tpm_crb_regs regs;
174         pthread_t thread;
175         pthread_mutex_t mutex;
176         pthread_cond_t cond;
177         bool closing;
178 };
179
180 static void *
181 tpm_crb_thread(void *const arg)
182 {
183         struct tpm_crb *const crb = arg;
184
185         pthread_mutex_lock(&crb->mutex);
186         for (;;) {
187                 pthread_cond_wait(&crb->cond, &crb->mutex);
188
189                 if (crb->closing)
190                         break;
191
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);
196
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;
199
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) {
204                         warnx(
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);
208                         break;
209                 }
210
211                 /*
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
221                  * response.
222                  */
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],
226                     rsp_size);
227
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);
230
231                 crb->regs.ctrl_start.start = false;
232         }
233         pthread_mutex_unlock(&crb->mutex);
234
235         return (NULL);
236 }
237
238 static int
239 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc)
240 {
241         struct tpm_crb *crb = NULL;
242         int error;
243
244         assert(sc != NULL);
245         assert(emul != NULL);
246
247         crb = calloc(1, sizeof(struct tpm_crb));
248         if (crb == NULL) {
249                 warnx("%s: failed to allocate tpm crb", __func__);
250                 error = ENOMEM;
251                 goto err_out;
252         }
253
254         memset(crb, 0, sizeof(*crb));
255
256         crb->emul = emul;
257         crb->emul_sc = emul_sc;
258
259         crb->regs.loc_state.tpm_req_valid_sts = true;
260         crb->regs.loc_state.tpm_established = true;
261
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 */
275
276         crb->regs.ctrl_sts.tpm_idle = true;
277
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);
282
283         error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME,
284             TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area);
285         if (error) {
286                 warnx("%s: failed to add fwcfg file", __func__);
287                 goto err_out;
288         }
289
290         error = pthread_mutex_init(&crb->mutex, NULL);
291         if (error) {
292                 warnc(error, "%s: failed to init mutex", __func__);
293                 goto err_out;
294         }
295
296         error = pthread_cond_init(&crb->cond, NULL);
297         if (error) {
298                 warnc(error, "%s: failed to init cond", __func__);
299                 goto err_out;
300         }
301
302         error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb);
303         if (error) {
304                 warnx("%s: failed to create thread\n", __func__);
305                 goto err_out;
306         }
307
308         pthread_set_name_np(crb->thread, "tpm_intf_crb");
309
310         *sc = crb;
311
312         return (0);
313
314 err_out:
315         free(crb);
316
317         return (error);
318 }
319
320 static void
321 tpm_crb_deinit(void *sc)
322 {
323         struct tpm_crb *crb;
324
325         if (sc == NULL) {
326                 return;
327         }
328
329         crb = sc;
330
331         crb->closing = true;
332         pthread_cond_signal(&crb->cond);
333         pthread_join(crb->thread, NULL);
334
335         pthread_cond_destroy(&crb->cond);
336         pthread_mutex_destroy(&crb->mutex);
337
338         free(crb);
339 }
340
341 static int
342 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx)
343 {
344         struct basl_table *table;
345
346         BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2,
347             BASL_TABLE_ALIGNMENT));
348
349         /* Header */
350         BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1));
351         /* Platform Class */
352         BASL_EXEC(basl_table_append_int(table, 0, 2));
353         /* Reserved */
354         BASL_EXEC(basl_table_append_int(table, 0, 2));
355         /* Control Address */
356         BASL_EXEC(
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 */
364         BASL_EXEC(
365             basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4));
366         /* Log Area Start Address */
367         BASL_EXEC(
368             basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8));
369
370         BASL_EXEC(basl_table_register_to_rsdt(table));
371
372         return (0);
373 }
374
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,
380 };
381 TPM_INTF_SET(tpm_intf_crb);