diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -60,6 +60,7 @@ sockstream.c \ tpm_device.c \ tpm_emul_passthru.c \ + tpm_emul_swtpm.c \ tpm_intf_crb.c \ tpm_ppi_qemu.c \ uart_backend.c \ diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -435,7 +435,7 @@ .It Cm lpc LPC PCI-ISA bridge with COM1, COM2, COM3, and COM4 16550 serial ports, a boot ROM, and, -optionally, a fwcfg type, and the debug/test device. +optionally, a TPM module, a fwcfg type, and the debug/test device. The LPC bridge emulation can only be configured on bus 0. .It Cm fbuf Raw framebuffer device attached to VNC server. @@ -636,16 +636,25 @@ .Sm on .El .Pp -Emulate a TPM device. The argument -.Ar path -needs to point to a valid TPM device path, i.e. -.Pa /dev/tpm0 . -.Pp +Emulate a TPM device. Supported options for .Ar type : .Bl -tag -width 10n .It Cm passthru -pass a physical TPM device through to the guest +Use a physical TPM device. +The argument +.Ar path +needs to point to a valid TPM device path, i.e. +.Pa /dev/tpm0 . +.It Cm swtpm +Connect to a running +.Cm swtpm +instance. +The argument +.Ar path +needs to point to a UNIX domain socket that a +.Cm swtpm +process is listening on. .El .Pp The @@ -655,7 +664,8 @@ .It Cm version= Ns Ar version Version of the TPM device according to the TCG specification. Defaults to -.Cm 2.0 +.Cm 2.0 , +which is the only version currently supported. .El .Ss Boot ROM device backends .Sm off diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5 --- a/usr.sbin/bhyve/bhyve_config.5 +++ b/usr.sbin/bhyve/bhyve_config.5 @@ -160,15 +160,6 @@ .It Va pci.enable_bars Ta bool Ta Ta Enable and map PCI BARs before executing any guest code. This setting is false by default when using a boot ROM and true otherwise. -.It Va tpm.path Ta string Ta Ta -Path to the host TPM device. -This is typically /dev/tpm0. -.It Va tpm.type Ta string Ta Ta -Type of the TPM device passed to the guest. -Currently, only "passthru" is supported. -.It Va tpm.version Ta string Ta 2.0 Ta -Version of the TPM device according to the TCG specification. -Currently, only version 2.0 is supported. .It Va rtc.use_localtime Ta bool Ta true Ta The real time clock uses the local time of the host. If this is set to false, the real time clock uses UTC. @@ -288,9 +279,8 @@ VGA framebuffer device attached to VNC server. .It Li lpc LPC PCI-ISA bridge with COM1-COM4 16550 serial ports, -a boot ROM, -an optional fwcfg type, -and an optional debug/test device. +a boot ROM, and, optionally, a TPM module, a fwcfg type, +and a debug/test device. This device must be configured on bus 0. .It Li hda High Definition audio controller. @@ -602,6 +592,37 @@ .It Va subdevice Ta 0 .El .El +.Ss TPM Device Settings +The TPM device stores its configuration under a top-level +.Va tpm +node rather than under the LPC TPM device's node. +Only one TPM device is supported. +The following nodes are available under +.Va tpm : +.Bl -column "tpm.version" "Format" "Default" +.It Sy Name Ta Sy Format Ta Sy Default Ta Sy Description +.It Va tpm.path Ta string Ta Ta +Path to the TPM backend. +Depending on the +.Va tpm.type , +this is either the host TPM device, typically +.Pa /dev/tpm0 , +or any UNIX domain socket on which a +.Cm swtpm +process is listening. +.It Va tpm.type Ta string Ta Ta +Type of the TPM device passed to the guest. +This can be either +.Dq passthru +to use the host TPM devices, or +.Dq swtpm +to connect to a running +.Cm swtpm +process. +.It Va tpm.version Ta string Ta 2.0 Ta +Version of the TPM device according to the TCG specification. +Currently, only version 2.0 is supported. +.El .Ss NVMe Controller Settings Each NVMe controller supports a single storage device. The device can be backed either by a memory disk described by the diff --git a/usr.sbin/bhyve/tpm_emul_swtpm.c b/usr.sbin/bhyve/tpm_emul_swtpm.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/tpm_emul_swtpm.c @@ -0,0 +1,132 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Hans Rosenfeld + * Author: Hans Rosenfeld + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "tpm_device.h" +#include "tpm_emul.h" + +struct tpm_swtpm { + int fd; +}; + +struct tpm_resp_hdr { + uint16_t tag; + uint32_t len; + uint32_t errcode; +} __packed; + +static int +tpm_swtpm_init(void **sc, nvlist_t *nvl) +{ + struct tpm_swtpm *tpm; + const char *path; + struct sockaddr_un tpm_addr; + + tpm = calloc(1, sizeof (struct tpm_swtpm)); + if (tpm == NULL) { + warnx("%s: failed to allocate tpm_swtpm", __func__); + return (ENOMEM); + } + + path = get_config_value_node(nvl, "path"); + if (path == NULL) { + warnx("%s: no socket path specified", __func__); + return (ENOENT); + } + + tpm->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (tpm->fd < 0) { + warnx("%s: unable to open tpm socket", __func__); + return (ENOENT); + } + + bzero(&tpm_addr, sizeof (tpm_addr)); + tpm_addr.sun_family = AF_UNIX; + strlcpy(tpm_addr.sun_path, path, sizeof (tpm_addr.sun_path) - 1); + + if (connect(tpm->fd, (struct sockaddr *)&tpm_addr, sizeof (tpm_addr)) == + -1) { + warnx("%s: unable to connect to tpm socket \"%s\"", __func__, + path); + return (ENOENT); + } + + *sc = tpm; + + return (0); +} + +static int +tpm_swtpm_execute_cmd(void *sc, void *cmd, uint32_t cmd_size, void *rsp, + uint32_t rsp_size) +{ + struct tpm_swtpm *tpm; + ssize_t len; + + if (rsp_size < (ssize_t)sizeof(struct tpm_resp_hdr)) { + warn("%s: rsp_size of %u is too small", __func__, rsp_size); + return (EINVAL); + } + + tpm = sc; + + len = send(tpm->fd, cmd, cmd_size, MSG_NOSIGNAL|MSG_DONTWAIT); + if (len == -1) + err(1, "%s: cmd send failed, is swtpm running?", __func__); + if (len != cmd_size) { + warn("%s: cmd write failed (bytes written: %zd / %d)", __func__, + len, cmd_size); + return (EFAULT); + } + + len = recv(tpm->fd, rsp, rsp_size, 0); + if (len == -1) + err(1, "%s: rsp recv failed, is swtpm running?", __func__); + if (len < (ssize_t)sizeof(struct tpm_resp_hdr)) { + warn("%s: rsp read failed (bytes read: %zd / %d)", __func__, + len, rsp_size); + return (EFAULT); + } + + return (0); +} + +static void +tpm_swtpm_deinit(void *sc) +{ + struct tpm_swtpm *tpm; + + tpm = sc; + if (tpm == NULL) + return; + + if (tpm->fd >= 0) + close(tpm->fd); + + free(tpm); +} + +static const struct tpm_emul tpm_emul_swtpm = { + .name = "swtpm", + .init = tpm_swtpm_init, + .deinit = tpm_swtpm_deinit, + .execute_cmd = tpm_swtpm_execute_cmd, +}; +TPM_EMUL_SET(tpm_emul_swtpm);