xref: /freebsd/usr.sbin/bhyve/tpm_device.c (revision 19fae0f66023a97a9b464b3beeeabb2081f575b3)
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