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