xref: /illumos-gate/usr/src/cmd/bhyve/tpm_device.c (revision 32640292)
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 #ifndef	__FreeBSD__
16 #include <sys/vmm.h>
17 #include <machine/vmm.h>
18 #endif
19 #include <vmmapi.h>
20 
21 #include "acpi.h"
22 #include "acpi_device.h"
23 #include "config.h"
24 #include "tpm_device.h"
25 #include "tpm_emul.h"
26 #include "tpm_intf.h"
27 #include "tpm_ppi.h"
28 
29 #define TPM_ACPI_DEVICE_NAME "TPM"
30 #define TPM_ACPI_HARDWARE_ID "MSFT0101"
31 
32 SET_DECLARE(tpm_emul_set, struct tpm_emul);
33 SET_DECLARE(tpm_intf_set, struct tpm_intf);
34 SET_DECLARE(tpm_ppi_set, struct tpm_ppi);
35 
36 struct tpm_device {
37 	struct vmctx *vm_ctx;
38 	struct acpi_device *acpi_dev;
39 	struct tpm_emul *emul;
40 	void *emul_sc;
41 	struct tpm_intf *intf;
42 	void *intf_sc;
43 	struct tpm_ppi *ppi;
44 	void *ppi_sc;
45 };
46 
47 static int
tpm_build_acpi_table(const struct acpi_device * const dev)48 tpm_build_acpi_table(const struct acpi_device *const dev)
49 {
50 	const struct tpm_device *const tpm = acpi_device_get_softc(dev);
51 
52 	if (tpm->intf->build_acpi_table == NULL) {
53 		return (0);
54 	}
55 
56 	return (tpm->intf->build_acpi_table(tpm->intf_sc, tpm->vm_ctx));
57 }
58 
59 static int
tpm_write_dsdt(const struct acpi_device * const dev)60 tpm_write_dsdt(const struct acpi_device *const dev)
61 {
62 	int error;
63 
64 	const struct tpm_device *const tpm = acpi_device_get_softc(dev);
65 	const struct tpm_ppi *const ppi = tpm->ppi;
66 
67 	/*
68 	 * packages for returns
69 	 */
70 	dsdt_line("Name(TPM2, Package(2) {0, 0})");
71 	dsdt_line("Name(TPM3, Package(3) {0, 0, 0})");
72 
73 	if (ppi->write_dsdt_regions) {
74 		error = ppi->write_dsdt_regions(tpm->ppi_sc);
75 		if (error) {
76 			warnx("%s: failed to write ppi dsdt regions\n",
77 			    __func__);
78 			return (error);
79 		}
80 	}
81 
82 	/*
83 	 * Device Specific Method
84 	 * Arg0: UUID
85 	 * Arg1: Revision ID
86 	 * Arg2: Function Index
87 	 * Arg3: Arguments
88 	 */
89 	dsdt_line("Method(_DSM, 4, Serialized)");
90 	dsdt_line("{");
91 	dsdt_indent(1);
92 	if (ppi->write_dsdt_dsm) {
93 		error = ppi->write_dsdt_dsm(tpm->ppi_sc);
94 		if (error) {
95 			warnx("%s: failed to write ppi dsdt dsm\n", __func__);
96 			return (error);
97 		}
98 	}
99 	dsdt_unindent(1);
100 	dsdt_line("}");
101 
102 	return (0);
103 }
104 
105 static const struct acpi_device_emul tpm_acpi_device_emul = {
106 	.name = TPM_ACPI_DEVICE_NAME,
107 	.hid = TPM_ACPI_HARDWARE_ID,
108 	.build_table = tpm_build_acpi_table,
109 	.write_dsdt = tpm_write_dsdt,
110 };
111 
112 void
tpm_device_destroy(struct tpm_device * const dev)113 tpm_device_destroy(struct tpm_device *const dev)
114 {
115 	if (dev == NULL)
116 		return;
117 
118 	if (dev->ppi != NULL && dev->ppi->deinit != NULL)
119 		dev->ppi->deinit(dev->ppi_sc);
120 	if (dev->intf != NULL && dev->intf->deinit != NULL)
121 		dev->intf->deinit(dev->intf_sc);
122 	if (dev->emul != NULL && dev->emul->deinit != NULL)
123 		dev->emul->deinit(dev->emul_sc);
124 
125 	acpi_device_destroy(dev->acpi_dev);
126 	free(dev);
127 }
128 
129 int
tpm_device_create(struct tpm_device ** const new_dev,struct vmctx * const vm_ctx,nvlist_t * const nvl)130 tpm_device_create(struct tpm_device **const new_dev, struct vmctx *const vm_ctx,
131     nvlist_t *const nvl)
132 {
133 	struct tpm_device *dev = NULL;
134 	struct tpm_emul **ppemul;
135 	struct tpm_intf **ppintf;
136 	struct tpm_ppi **pp_ppi;
137 	const char *value;
138 	int error;
139 
140 	if (new_dev == NULL || vm_ctx == NULL) {
141 		error = EINVAL;
142 		goto err_out;
143 	}
144 
145 	set_config_value_node_if_unset(nvl, "intf", "crb");
146 	set_config_value_node_if_unset(nvl, "ppi", "qemu");
147 
148 	value = get_config_value_node(nvl, "version");
149 	assert(value != NULL);
150 	if (strcmp(value, "2.0")) {
151 		warnx("%s: unsupported tpm version %s", __func__, value);
152 		error = EINVAL;
153 		goto err_out;
154 	}
155 
156 	dev = calloc(1, sizeof(*dev));
157 	if (dev == NULL) {
158 		error = ENOMEM;
159 		goto err_out;
160 	}
161 
162 	dev->vm_ctx = vm_ctx;
163 
164 	error = acpi_device_create(&dev->acpi_dev, dev, dev->vm_ctx,
165 	    &tpm_acpi_device_emul);
166 	if (error)
167 		goto err_out;
168 
169 	value = get_config_value_node(nvl, "type");
170 	assert(value != NULL);
171 	SET_FOREACH(ppemul, tpm_emul_set) {
172 		if (strcmp(value, (*ppemul)->name))
173 			continue;
174 		dev->emul = *ppemul;
175 		break;
176 	}
177 	if (dev->emul == NULL) {
178 		warnx("TPM emulation \"%s\" not found", value);
179 		error = EINVAL;
180 		goto err_out;
181 	}
182 
183 	if (dev->emul->init) {
184 		error = dev->emul->init(&dev->emul_sc, nvl);
185 		if (error)
186 			goto err_out;
187 	}
188 
189 	value = get_config_value_node(nvl, "intf");
190 	SET_FOREACH(ppintf, tpm_intf_set) {
191 		if (strcmp(value, (*ppintf)->name)) {
192 			continue;
193 		}
194 		dev->intf = *ppintf;
195 		break;
196 	}
197 	if (dev->intf == NULL) {
198 		warnx("TPM interface \"%s\" not found", value);
199 		error = EINVAL;
200 		goto err_out;
201 	}
202 
203 	if (dev->intf->init) {
204 		error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc,
205 		    dev->acpi_dev);
206 		if (error)
207 			goto err_out;
208 	}
209 
210 	value = get_config_value_node(nvl, "ppi");
211 	SET_FOREACH(pp_ppi, tpm_ppi_set) {
212 		if (strcmp(value, (*pp_ppi)->name)) {
213 			continue;
214 		}
215 		dev->ppi = *pp_ppi;
216 		break;
217 	}
218 	if (dev->ppi == NULL) {
219 		warnx("TPM PPI \"%s\" not found\n", value);
220 		error = EINVAL;
221 		goto err_out;
222 	}
223 
224 	if (dev->ppi->init) {
225 		error = dev->ppi->init(&dev->ppi_sc);
226 		if (error)
227 			goto err_out;
228 	}
229 
230 	*new_dev = dev;
231 
232 	return (0);
233 
234 err_out:
235 	tpm_device_destroy(dev);
236 
237 	return (error);
238 }
239 
240 #ifdef	__FreeBSD__
241 static struct tpm_device *lpc_tpm;
242 
243 int
init_tpm(struct vmctx * ctx)244 init_tpm(struct vmctx *ctx)
245 {
246 	nvlist_t *nvl;
247 	int error;
248 
249 	nvl = find_config_node("tpm");
250 	if (nvl == NULL)
251 		return (0);
252 
253 	error = tpm_device_create(&lpc_tpm, ctx, nvl);
254 	if (error) {
255 		warnx("%s: unable to create a TPM device (%d)",
256 		    __func__, error);
257 		return (error);
258 	}
259 
260 	return (0);
261 }
262 #else
263 int
init_tpm(struct vmctx * ctx __unused)264 init_tpm(struct vmctx *ctx __unused)
265 {
266 	/*
267 	 * Until illumos has a TPM 2.0 driver, we can't finish plumbing the TPM
268 	 * pass-through.
269 	 */
270 	if (find_config_node("tpm") != NULL)
271 		errx(4, "TPM devices are not yet supported on illumos");
272 
273 	return (0);
274 }
275 #endif
276