]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/tpm_device.c
zfs: merge openzfs/zfs@41e55b476
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / tpm_device.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG
5  * Author: Corvin Köhne <corvink@FreeBSD.org>
6  */
7
8 #include <sys/types.h>
9
10 #include <assert.h>
11 #include <err.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <vmmapi.h>
16
17 #include "acpi.h"
18 #include "acpi_device.h"
19 #include "config.h"
20 #include "tpm_device.h"
21 #include "tpm_emul.h"
22 #include "tpm_intf.h"
23 #include "tpm_ppi.h"
24
25 #define TPM_ACPI_DEVICE_NAME "TPM"
26 #define TPM_ACPI_HARDWARE_ID "MSFT0101"
27
28 SET_DECLARE(tpm_emul_set, struct tpm_emul);
29 SET_DECLARE(tpm_intf_set, struct tpm_intf);
30 SET_DECLARE(tpm_ppi_set, struct tpm_ppi);
31
32 struct tpm_device {
33         struct vmctx *vm_ctx;
34         struct acpi_device *acpi_dev;
35         struct tpm_emul *emul;
36         void *emul_sc;
37         struct tpm_intf *intf;
38         void *intf_sc;
39         struct tpm_ppi *ppi;
40         void *ppi_sc;
41 };
42
43 static int
44 tpm_build_acpi_table(const struct acpi_device *const dev)
45 {
46         const struct tpm_device *const tpm = acpi_device_get_softc(dev);
47
48         if (tpm->intf->build_acpi_table == NULL) {
49                 return (0);
50         }
51
52         return (tpm->intf->build_acpi_table(tpm->intf_sc, tpm->vm_ctx));
53 }
54
55 static int
56 tpm_write_dsdt(const struct acpi_device *const dev)
57 {
58         int error;
59
60         const struct tpm_device *const tpm = acpi_device_get_softc(dev);
61         const struct tpm_ppi *const ppi = tpm->ppi;
62
63         /*
64          * packages for returns
65          */
66         dsdt_line("Name(TPM2, Package(2) {0, 0})");
67         dsdt_line("Name(TPM3, Package(3) {0, 0, 0})");
68
69         if (ppi->write_dsdt_regions) {
70                 error = ppi->write_dsdt_regions(tpm->ppi_sc);
71                 if (error) {
72                         warnx("%s: failed to write ppi dsdt regions\n",
73                             __func__);
74                         return (error);
75                 }
76         }
77
78         /*
79          * Device Specific Method
80          * Arg0: UUID
81          * Arg1: Revision ID
82          * Arg2: Function Index
83          * Arg3: Arguments
84          */
85         dsdt_line("Method(_DSM, 4, Serialized)");
86         dsdt_line("{");
87         dsdt_indent(1);
88         if (ppi->write_dsdt_dsm) {
89                 error = ppi->write_dsdt_dsm(tpm->ppi_sc);
90                 if (error) {
91                         warnx("%s: failed to write ppi dsdt dsm\n", __func__);
92                         return (error);
93                 }
94         }
95         dsdt_unindent(1);
96         dsdt_line("}");
97
98         return (0);
99 }
100
101 static const struct acpi_device_emul tpm_acpi_device_emul = {
102         .name = TPM_ACPI_DEVICE_NAME,
103         .hid = TPM_ACPI_HARDWARE_ID,
104         .build_table = tpm_build_acpi_table,
105         .write_dsdt = tpm_write_dsdt,
106 };
107
108 void
109 tpm_device_destroy(struct tpm_device *const dev)
110 {
111         if (dev == NULL)
112                 return;
113
114         if (dev->ppi != NULL && dev->ppi->deinit != NULL)
115                 dev->ppi->deinit(dev->ppi_sc);
116         if (dev->intf != NULL && dev->intf->deinit != NULL)
117                 dev->intf->deinit(dev->intf_sc);
118         if (dev->emul != NULL && dev->emul->deinit != NULL)
119                 dev->emul->deinit(dev->emul_sc);
120
121         acpi_device_destroy(dev->acpi_dev);
122         free(dev);
123 }
124
125 int
126 tpm_device_create(struct tpm_device **const new_dev, struct vmctx *const vm_ctx,
127     nvlist_t *const nvl)
128 {
129         struct tpm_device *dev = NULL;
130         struct tpm_emul **ppemul;
131         struct tpm_intf **ppintf;
132         struct tpm_ppi **pp_ppi;
133         const char *value;
134         int error;
135
136         if (new_dev == NULL || vm_ctx == NULL) {
137                 error = EINVAL;
138                 goto err_out;
139         }
140
141         set_config_value_node_if_unset(nvl, "intf", "crb");
142         set_config_value_node_if_unset(nvl, "ppi", "qemu");
143
144         value = get_config_value_node(nvl, "version");
145         assert(value != NULL);
146         if (strcmp(value, "2.0")) {
147                 warnx("%s: unsupported tpm version %s", __func__, value);
148                 error = EINVAL;
149                 goto err_out;
150         }
151
152         dev = calloc(1, sizeof(*dev));
153         if (dev == NULL) {
154                 error = ENOMEM;
155                 goto err_out;
156         }
157
158         dev->vm_ctx = vm_ctx;
159
160         error = acpi_device_create(&dev->acpi_dev, dev, dev->vm_ctx,
161             &tpm_acpi_device_emul);
162         if (error)
163                 goto err_out;
164
165         value = get_config_value_node(nvl, "type");
166         assert(value != NULL);
167         SET_FOREACH(ppemul, tpm_emul_set) {
168                 if (strcmp(value, (*ppemul)->name))
169                         continue;
170                 dev->emul = *ppemul;
171                 break;
172         }
173         if (dev->emul == NULL) {
174                 warnx("TPM emulation \"%s\" not found", value);
175                 error = EINVAL;
176                 goto err_out;
177         }
178
179         if (dev->emul->init) {
180                 error = dev->emul->init(&dev->emul_sc, nvl);
181                 if (error)
182                         goto err_out;
183         }
184
185         value = get_config_value_node(nvl, "intf");
186         SET_FOREACH(ppintf, tpm_intf_set) {
187                 if (strcmp(value, (*ppintf)->name)) {
188                         continue;
189                 }
190                 dev->intf = *ppintf;
191                 break;
192         }
193         if (dev->intf == NULL) {
194                 warnx("TPM interface \"%s\" not found", value);
195                 error = EINVAL;
196                 goto err_out;
197         }
198
199         if (dev->intf->init) {
200                 error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc,
201                     dev->acpi_dev);
202                 if (error)
203                         goto err_out;
204         }
205
206         value = get_config_value_node(nvl, "ppi");
207         SET_FOREACH(pp_ppi, tpm_ppi_set) {
208                 if (strcmp(value, (*pp_ppi)->name)) {
209                         continue;
210                 }
211                 dev->ppi = *pp_ppi;
212                 break;
213         }
214         if (dev->ppi == NULL) {
215                 warnx("TPM PPI \"%s\" not found\n", value);
216                 error = EINVAL;
217                 goto err_out;
218         }
219
220         if (dev->ppi->init) {
221                 error = dev->ppi->init(&dev->ppi_sc);
222                 if (error)
223                         goto err_out;
224         }
225
226         *new_dev = dev;
227
228         return (0);
229
230 err_out:
231         tpm_device_destroy(dev);
232
233         return (error);
234 }
235
236 static struct tpm_device *lpc_tpm;
237
238 int
239 init_tpm(struct vmctx *ctx)
240 {
241         nvlist_t *nvl;
242         int error;
243
244         nvl = find_config_node("tpm");
245         if (nvl == NULL)
246                 return (0);
247
248         error = tpm_device_create(&lpc_tpm, ctx, nvl);
249         if (error) {
250                 warnx("%s: unable to create a TPM device (%d)",
251                     __func__, error);
252                 return (error);
253         }
254
255         return (0);
256 }