bhyve: TPM 2.0 emulation with swtpm

Implement a TPM 2.0 emulation backend to connect to a running swtpm
instance using a UNIX domain socket.

Reviewed by:		corvink
MFC after:		1 week
Differential Revision:	https://reviews.freebsd.org/D46373
This commit is contained in:
Hans Rosenfeld authored and Corvin Köhne committed 2024-10-22 14:04:24 +02:00
1 parent bc3d09e90b
commit d93fbcf022
4 files changed
+184 -20

No files matched your search

+1
View File
@@ -60,6 +60,7 @@ SRCS= \
sockstream.c \
tpm_device.c \
tpm_emul_passthru.c \
tpm_emul_swtpm.c \
tpm_intf_crb.c \
tpm_ppi_qemu.c \
uart_backend.c \
+18 -8
View File
@@ -435,7 +435,7 @@ PCI 16550 serial device.
.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 @@ access is appropriately restricted.
.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 @@ are:
.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
+33 -12
View File
@@ -160,15 +160,6 @@ Message (e.g. TightVNC).
.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 @@ Intel e82545 network interface.
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 @@ This value is required for the Intel GOP driver to work properly.
.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
+132
View File
@@ -0,0 +1,132 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 Hans Rosenfeld
* Author: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc_np.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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);