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