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