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