xref: /freebsd/usr.sbin/bhyve/tpm_device.c (revision 43e29d03f416d7dda52112a29600a7c82ee1a91e)
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_device.h"
18 #include "config.h"
19 #include "tpm_device.h"
20 #include "tpm_emul.h"
21 #include "tpm_intf.h"
22 
23 #define TPM_ACPI_DEVICE_NAME "TPM"
24 #define TPM_ACPI_HARDWARE_ID "MSFT0101"
25 
26 SET_DECLARE(tpm_emul_set, struct tpm_emul);
27 SET_DECLARE(tpm_intf_set, struct tpm_intf);
28 
29 struct tpm_device {
30 	struct vmctx *vm_ctx;
31 	struct acpi_device *acpi_dev;
32 	struct tpm_emul *emul;
33 	void *emul_sc;
34 	struct tpm_intf *intf;
35 	void *intf_sc;
36 };
37 
38 static int
39 tpm_build_acpi_table(const struct acpi_device *const dev)
40 {
41 	const struct tpm_device *const tpm = acpi_device_get_softc(dev);
42 
43 	if (tpm->intf->build_acpi_table == NULL) {
44 		return (0);
45 	}
46 
47 	return (tpm->intf->build_acpi_table(tpm->intf_sc, tpm->vm_ctx));
48 }
49 
50 static const struct acpi_device_emul tpm_acpi_device_emul = {
51 	.name = TPM_ACPI_DEVICE_NAME,
52 	.hid = TPM_ACPI_HARDWARE_ID,
53 	.build_table = tpm_build_acpi_table,
54 };
55 
56 void
57 tpm_device_destroy(struct tpm_device *const dev)
58 {
59 	if (dev == NULL)
60 		return;
61 
62 	if (dev->intf != NULL && dev->intf->deinit != NULL)
63 		dev->intf->deinit(dev->intf_sc);
64 	if (dev->emul != NULL && dev->emul->deinit != NULL)
65 		dev->emul->deinit(dev->emul_sc);
66 
67 	acpi_device_destroy(dev->acpi_dev);
68 	free(dev);
69 }
70 
71 int
72 tpm_device_create(struct tpm_device **const new_dev, struct vmctx *const vm_ctx,
73     nvlist_t *const nvl)
74 {
75 	struct tpm_device *dev = NULL;
76 	struct tpm_emul **ppemul;
77 	struct tpm_intf **ppintf;
78 	const char *value;
79 	int error;
80 
81 	if (new_dev == NULL || vm_ctx == NULL) {
82 		error = EINVAL;
83 		goto err_out;
84 	}
85 
86 	set_config_value_node_if_unset(nvl, "intf", "crb");
87 
88 	value = get_config_value_node(nvl, "version");
89 	assert(value != NULL);
90 	if (strcmp(value, "2.0")) {
91 		warnx("%s: unsupported tpm version %s", __func__, value);
92 		error = EINVAL;
93 		goto err_out;
94 	}
95 
96 	dev = calloc(1, sizeof(*dev));
97 	if (dev == NULL) {
98 		error = ENOMEM;
99 		goto err_out;
100 	}
101 
102 	dev->vm_ctx = vm_ctx;
103 
104 	error = acpi_device_create(&dev->acpi_dev, dev, dev->vm_ctx,
105 	    &tpm_acpi_device_emul);
106 	if (error)
107 		goto err_out;
108 
109 	value = get_config_value_node(nvl, "type");
110 	assert(value != NULL);
111 	SET_FOREACH(ppemul, tpm_emul_set) {
112 		if (strcmp(value, (*ppemul)->name))
113 			continue;
114 		dev->emul = *ppemul;
115 		break;
116 	}
117 	if (dev->emul == NULL) {
118 		warnx("TPM emulation \"%s\" not found", value);
119 		error = EINVAL;
120 		goto err_out;
121 	}
122 
123 	if (dev->emul->init) {
124 		error = dev->emul->init(&dev->emul_sc, nvl);
125 		if (error)
126 			goto err_out;
127 	}
128 
129 	value = get_config_value_node(nvl, "intf");
130 	SET_FOREACH(ppintf, tpm_intf_set) {
131 		if (strcmp(value, (*ppintf)->name)) {
132 			continue;
133 		}
134 		dev->intf = *ppintf;
135 		break;
136 	}
137 	if (dev->intf == NULL) {
138 		warnx("TPM interface \"%s\" not found", value);
139 		error = EINVAL;
140 		goto err_out;
141 	}
142 
143 	if (dev->intf->init) {
144 		error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc);
145 		if (error)
146 			goto err_out;
147 	}
148 
149 	*new_dev = dev;
150 
151 	return (0);
152 
153 err_out:
154 	tpm_device_destroy(dev);
155 
156 	return (error);
157 }
158