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