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