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