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
tpm_build_acpi_table(const struct acpi_device * const dev)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
tpm_write_dsdt(const struct acpi_device * const dev)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
tpm_device_destroy(struct tpm_device * const dev)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
tpm_device_create(struct tpm_device ** const new_dev,struct vmctx * const vm_ctx,nvlist_t * const nvl)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
init_tpm(struct vmctx * ctx)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 }
257