Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 00/19] vhost-user-rpmb (Replay Protected Memory Block)
Hi, This is an initial implementation of a vhost-user backend for the VirtIO RPMB device. The device is currently in the draft of the next VirtIO specification and describes block device which uses combination of a key, nonce, hashing and a persistent write counter to prevent replay attacks (hence Replay Protected Memory Block). It is implemented as a vhost-user device because we want to experiment in making portable backends that can be used with multiple hypervisors. We also want to support backends isolated in their own separate service VMs with limited memory cross-sections with the principle guest. This is part of a wider initiative called project Stratos for which you can find information here: https://collaborate.linaro.org/display/STR/Stratos I mention this to explain the decision to duplicate some of the utility functions (specifically iov and hmac handling) and write the daemon as a fairly pure glib application that just depends on libvhost-user. As it happens I ended up having to include libqemuutil as libvhost-user requires qemu_memfd_alloc. Whether this is an oversight for libvhost-user or it means we should split these daemons into a separate repository is a discussion I would like to have with the community. Now I have a working reference implementation I also want to explore how easy it is to write a Rust version of the backend which raises similar questions about where such a project should live. The current Linux kernel doesn't support RPMB devices in the vanilla tree so if you want to test you will need to look at my testing tree which is based on Thomas Winkler's original patches although somewhat cut down and pared back to just support the JDEC style frames of the upstream spec and the simple chardev based userspace interface. You can find my kernel testing tree here: https://git.linaro.org/people/alex.bennee/linux.git/log/?h=testing/virtio-rpmb The above branch includes a simple test script with the rpmb userspace tool which I've used to exercise the various features. I'm unsure if there will ever be a push to upstream support for RPMB to the kernel as access to these sorts of devices are usually the preserve of firmware living in the secure world. There is currently work underway to support this device in uboot and I suspect eventually there will be support for OPTEE as well. Any review comments gratefully received as well as discussion about if we should consider creating some new projects for housing these sort of vhost-user backends. Alex Benn?e (19): tools/virtiofsd: add support for --socket-group hw/block: add boilerplate for vhost-user-rpmb device hw/virtio: move virtio-pci.h into shared include space hw/block: add vhost-user-rpmb-pci boilerplate virtio-pci: add notification trace points tools/vhost-user-rpmb: add boilerplate and initial main tools/vhost-user-rpmb: implement --print-capabilities tools/vhost-user-rpmb: connect to fd and instantiate basic run loop tools/vhost-user-rpmb: add a --verbose/debug flags for logging tools/vhost-user-rpmb: handle shutdown and SIGINT/SIGHUP cleanly tools/vhost-user-rpmb: add --flash-path for backing store tools/vhost-user-rpmb: import hmac_sha256 functions tools/vhost-user-rpmb: implement the PROGRAM_KEY handshake tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_GET_WRITE_COUNTER tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_DATA_WRITE tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_DATA_READ tools/vhost-user-rpmb: add key persistence tools/vhost-user-rpmb: allow setting of the write_count docs: add a man page for vhost-user-rpmb docs/tools/index.rst | 1 + docs/tools/vhost-user-rpmb.rst | 102 +++ docs/tools/virtiofsd.rst | 4 + include/hw/virtio/vhost-user-rpmb.h | 46 ++ {hw => include/hw}/virtio/virtio-pci.h | 0 tools/vhost-user-rpmb/hmac_sha256.h | 87 ++ tools/virtiofsd/fuse_i.h | 1 + hw/block/vhost-user-rpmb-pci.c | 82 ++ hw/block/vhost-user-rpmb.c | 333 ++++++++ hw/virtio/vhost-scsi-pci.c | 2 +- hw/virtio/vhost-user-blk-pci.c | 2 +- hw/virtio/vhost-user-fs-pci.c | 2 +- hw/virtio/vhost-user-input-pci.c | 2 +- hw/virtio/vhost-user-scsi-pci.c | 2 +- hw/virtio/vhost-user-vsock-pci.c | 2 +- hw/virtio/vhost-vsock-pci.c | 2 +- hw/virtio/virtio-9p-pci.c | 2 +- hw/virtio/virtio-balloon-pci.c | 2 +- hw/virtio/virtio-blk-pci.c | 2 +- hw/virtio/virtio-input-host-pci.c | 2 +- hw/virtio/virtio-input-pci.c | 2 +- hw/virtio/virtio-iommu-pci.c | 2 +- hw/virtio/virtio-net-pci.c | 2 +- hw/virtio/virtio-pci.c | 5 +- hw/virtio/virtio-rng-pci.c | 2 +- hw/virtio/virtio-scsi-pci.c | 2 +- hw/virtio/virtio-serial-pci.c | 2 +- tools/vhost-user-rpmb/hmac_sha256.c | 331 ++++++++ tools/vhost-user-rpmb/main.c | 880 +++++++++++++++++++++ tools/virtiofsd/fuse_lowlevel.c | 6 + tools/virtiofsd/fuse_virtio.c | 20 +- MAINTAINERS | 5 + hw/block/Kconfig | 5 + hw/block/meson.build | 3 + hw/virtio/trace-events | 7 +- tools/meson.build | 8 + tools/vhost-user-rpmb/50-qemu-rpmb.json.in | 5 + tools/vhost-user-rpmb/meson.build | 12 + 38 files changed, 1956 insertions(+), 21 deletions(-) create mode 100644 docs/tools/vhost-user-rpmb.rst create mode 100644 include/hw/virtio/vhost-user-rpmb.h rename {hw => include/hw}/virtio/virtio-pci.h (100%) create mode 100644 tools/vhost-user-rpmb/hmac_sha256.h create mode 100644 hw/block/vhost-user-rpmb-pci.c create mode 100644 hw/block/vhost-user-rpmb.c create mode 100644 tools/vhost-user-rpmb/hmac_sha256.c create mode 100644 tools/vhost-user-rpmb/main.c create mode 100644 tools/vhost-user-rpmb/50-qemu-rpmb.json.in create mode 100644 tools/vhost-user-rpmb/meson.build -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 01/19] tools/virtiofsd: add support for --socket-group
If you like running QEMU as a normal user (very common for TCG runs) but you have to run virtiofsd as a root user you run into connection problems. Adding support for an optional --socket-group allows the users to keep using the command line. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> Reviewed-by: Stefan Hajnoczi <stefanha at redhat.com> --- v1 - tweak documentation and commentary --- docs/tools/virtiofsd.rst | 4 ++++ tools/virtiofsd/fuse_i.h | 1 + tools/virtiofsd/fuse_lowlevel.c | 6 ++++++ tools/virtiofsd/fuse_virtio.c | 20 ++++++++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index e33c81ed41f1..085f9b12a6a3 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -87,6 +87,10 @@ Options Listen on vhost-user UNIX domain socket at PATH. +.. option:: --socket-group=GROUP + + Set the vhost-user UNIX domain socket gid to GROUP. + .. option:: --fd=FDNUM Accept connections from vhost-user UNIX domain socket file descriptor FDNUM. diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h index 1240828208ab..492e002181e2 100644 --- a/tools/virtiofsd/fuse_i.h +++ b/tools/virtiofsd/fuse_i.h @@ -68,6 +68,7 @@ struct fuse_session { size_t bufsize; int error; char *vu_socket_path; + char *vu_socket_group; int vu_listen_fd; int vu_socketfd; struct fv_VuDev *virtio_dev; diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c index 2dd36ec03b6e..4d1ba2925d1b 100644 --- a/tools/virtiofsd/fuse_lowlevel.c +++ b/tools/virtiofsd/fuse_lowlevel.c @@ -2523,6 +2523,7 @@ static const struct fuse_opt fuse_ll_opts[] = { LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), LL_OPTION("--socket-path=%s", vu_socket_path, 0), + LL_OPTION("--socket-group=%s", vu_socket_group, 0), LL_OPTION("--fd=%d", vu_listen_fd, 0), LL_OPTION("--thread-pool-size=%d", thread_pool_size, 0), FUSE_OPT_END @@ -2630,6 +2631,11 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, "fuse: --socket-path and --fd cannot be given together\n"); goto out4; } + if (se->vu_socket_group && !se->vu_socket_path) { + fuse_log(FUSE_LOG_ERR, + "fuse: --socket-group can only be used with --socket-path\n"); + goto out4; + } se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 9e5537506c16..7942d3d11a87 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -31,6 +31,8 @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> +#include <sys/types.h> +#include <grp.h> #include <unistd.h> #include "contrib/libvhost-user/libvhost-user.h" @@ -924,15 +926,29 @@ static int fv_create_listen_socket(struct fuse_session *se) /* * Unfortunately bind doesn't let you set the mask on the socket, - * so set umask to 077 and restore it later. + * so set umask appropriately and restore it later. */ - old_umask = umask(0077); + if (se->vu_socket_group) { + old_umask = umask(S_IROTH | S_IWOTH | S_IXOTH); + } else { + old_umask = umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH); + } if (bind(listen_sock, (struct sockaddr *)&un, addr_len) == -1) { fuse_log(FUSE_LOG_ERR, "vhost socket bind: %m\n"); close(listen_sock); umask(old_umask); return -1; } + if (se->vu_socket_group) { + struct group *g = getgrnam(se->vu_socket_group); + if (g) { + if (!chown(se->vu_socket_path, -1, g->gr_gid)) { + fuse_log(FUSE_LOG_WARNING, + "vhost socket failed to set group to %s (%d)\n", + se->vu_socket_group, g->gr_gid); + } + } + } umask(old_umask); if (listen(listen_sock, 1) == -1) { -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 02/19] hw/block: add boilerplate for vhost-user-rpmb device
This creates the QEMU side of the vhost-user-rpmb device which connects to the remote daemon. It is based of the reasonably modern vhost-user-fs code with bits from vhost-user-blk as we want the virtio-config itself to be sourced from the daemon. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- include/hw/virtio/vhost-user-rpmb.h | 46 ++++ hw/block/vhost-user-rpmb.c | 333 ++++++++++++++++++++++++++++ hw/block/Kconfig | 5 + hw/block/meson.build | 1 + 4 files changed, 385 insertions(+) create mode 100644 include/hw/virtio/vhost-user-rpmb.h create mode 100644 hw/block/vhost-user-rpmb.c diff --git a/include/hw/virtio/vhost-user-rpmb.h b/include/hw/virtio/vhost-user-rpmb.h new file mode 100644 index 000000000000..7e5988127dc2 --- /dev/null +++ b/include/hw/virtio/vhost-user-rpmb.h @@ -0,0 +1,46 @@ +/* + * vhost-user-rpmb virtio device + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _VHOST_USER_RPMB_H_ +#define _VHOST_USER_RPMB_H_ + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" + +#define TYPE_VHOST_USER_RPMB "vhost-user-rpmb-device" +#define VHOST_USER_RPMB(obj) \ + OBJECT_CHECK(VHostUserRPMB, (obj), TYPE_VHOST_USER_RPMB) + +/* This is defined in the VIRTIO spec */ +struct virtio_rpmb_config { + uint8_t capacity; + uint8_t max_wr_cnt; + uint8_t max_rd_cnt; +}; + +typedef struct { + CharBackend chardev; + struct virtio_rpmb_config config; +} VHostUserRPMBConf; + +typedef struct { + /*< private >*/ + VirtIODevice parent; + VHostUserRPMBConf conf; + struct vhost_virtqueue *vhost_vq; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + VirtQueue *req_vq; + bool connected; + /*< public >*/ +} VHostUserRPMB; + + +#endif /* _VHOST_USER_RPMB_H_ */ diff --git a/hw/block/vhost-user-rpmb.c b/hw/block/vhost-user-rpmb.c new file mode 100644 index 000000000000..de243e7a53a0 --- /dev/null +++ b/hw/block/vhost-user-rpmb.c @@ -0,0 +1,333 @@ +/* + * Vhost-user RPMB virtio device + * + * This is the boilerplate for instantiating a vhost-user device + * implementing a Replay Protected Memory Block (RPMB) device. This is + * a type of flash chip that is protected from replay attacks and used + * for tamper resistant storage. The actual back-end for this driver + * is the vhost-user-rpmb daemon. The code here just connects up the + * device in QEMU and allows it to be instantiated. + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-rpmb.h" +#include "qemu/error-report.h" + +/* currently there is no RPMB driver in Linux */ +#define VIRTIO_ID_RPMB 28 /* virtio RPMB */ + +static void vurpmb_get_config(VirtIODevice *vdev, uint8_t *config) +{ + /* this somehow needs to come from the vhost-user daemon */ +} + +static void vurpmb_start(VirtIODevice *vdev) +{ + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + int i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&rpmb->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, rpmb->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + rpmb->vhost_dev.acked_features = vdev->guest_features; + ret = vhost_dev_start(&rpmb->vhost_dev, vdev); + if (ret < 0) { + error_report("Error starting vhost-user-rpmb: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < rpmb->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&rpmb->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, rpmb->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&rpmb->vhost_dev, vdev); +} + +static void vurpmb_stop(VirtIODevice *vdev) +{ + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&rpmb->vhost_dev, vdev); + + ret = k->set_guest_notifiers(qbus->parent, rpmb->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&rpmb->vhost_dev, vdev); +} + +static void vurpmb_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + + if (!vdev->vm_running) { + should_start = false; + } + + if (rpmb->vhost_dev.started == should_start) { + return; + } + + if (should_start) { + vurpmb_start(vdev); + } else { + vurpmb_stop(vdev); + } +} + +static uint64_t vurpmb_get_features(VirtIODevice *vdev, + uint64_t requested_features, + Error **errp) +{ + /* No feature bits used yet */ + return requested_features; +} + +static void vurpmb_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vurpmb_guest_notifier_mask(VirtIODevice *vdev, int idx, + bool mask) +{ + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + vhost_virtqueue_mask(&rpmb->vhost_dev, vdev, idx, mask); +} + +static bool vurpmb_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + return vhost_virtqueue_pending(&rpmb->vhost_dev, idx); +} + +/* + * Chardev connect/disconnect events + */ + +static int vurpmb_handle_config_change(struct vhost_dev *dev) +{ + int ret; + VHostUserRPMB *rpmb = VHOST_USER_RPMB(dev->vdev); + + ret = vhost_dev_get_config(dev, (uint8_t *)&rpmb->conf.config, + sizeof(struct virtio_rpmb_config)); + if (ret < 0) { + error_report("get config space failed"); + return -1; + } + + return 0; +} + +const VhostDevConfigOps rpmb_ops = { + .vhost_dev_config_notifier = vurpmb_handle_config_change, +}; + +static int vurpmb_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + + if (rpmb->connected) { + return 0; + } + rpmb->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vurpmb_start(vdev); + } + + return 0; +} + +static void vurpmb_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + + if (!rpmb->connected) { + return; + } + rpmb->connected = false; + + if (rpmb->vhost_dev.started) { + vurpmb_stop(vdev); + } + + vhost_dev_cleanup(&rpmb->vhost_dev); +} + +static void vurpmb_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRPMB *rpmb = VHOST_USER_RPMB(vdev); + + switch (event) { + case CHR_EVENT_OPENED: + if (vurpmb_connect(dev) < 0) { + qemu_chr_fe_disconnect(&rpmb->conf.chardev); + return; + } + break; + case CHR_EVENT_CLOSED: + vurpmb_disconnect(dev); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserRPMB *rpmb) +{ + vhost_user_cleanup(&rpmb->vhost_user); + virtio_delete_queue(rpmb->req_vq); + virtio_cleanup(vdev); + g_free(rpmb->vhost_dev.vqs); + rpmb->vhost_dev.vqs = NULL; +} + + +static void vurpmb_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRPMB *rpmb = VHOST_USER_RPMB(dev); + int ret; + + if (!rpmb->conf.chardev.chr) { + error_setg(errp, "missing chardev"); + return; + } + + if (!vhost_user_init(&rpmb->vhost_user, &rpmb->conf.chardev, errp)) { + return; + } + + virtio_init(vdev, "vhost-user-rpmb", VIRTIO_ID_RPMB, + sizeof(struct virtio_rpmb_config)); + + /* One request queue, 4 elements in case we don't do indirect */ + rpmb->req_vq = virtio_add_queue(vdev, 4, vurpmb_handle_output); + rpmb->vhost_dev.nvqs = 1; + rpmb->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rpmb->vhost_dev.nvqs); + ret = vhost_dev_init(&rpmb->vhost_dev, &rpmb->vhost_user, + VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost_dev_init failed"); + do_vhost_user_cleanup(vdev, rpmb); + } + + /* + * At this point the next event we will get is a connection from + * the daemon on the control socket. + */ + + qemu_chr_fe_set_handlers(&rpmb->conf.chardev, NULL, NULL, vurpmb_event, + NULL, (void *)dev, NULL, true); + + return; +} + +static void vurpmb_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRPMB *rpmb = VHOST_USER_RPMB(dev); + + /* This will stop vhost backend if appropriate. */ + vurpmb_set_status(vdev, 0); + + do_vhost_user_cleanup(vdev, rpmb); +} + +static const VMStateDescription vurpmb_vmstate = { + .name = "vhost-user-rpmb", + .unmigratable = 1, +}; + +static Property vurpmb_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserRPMB, conf.chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vurpmb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vurpmb_properties); + dc->vmsd = &vurpmb_vmstate; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + vdc->realize = vurpmb_device_realize; + vdc->unrealize = vurpmb_device_unrealize; + vdc->get_features = vurpmb_get_features; + vdc->get_config = vurpmb_get_config; + vdc->set_status = vurpmb_set_status; + vdc->guest_notifier_mask = vurpmb_guest_notifier_mask; + vdc->guest_notifier_pending = vurpmb_guest_notifier_pending; +} + +static const TypeInfo vurpmb_info = { + .name = TYPE_VHOST_USER_RPMB, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserRPMB), + .class_init = vurpmb_class_init, +}; + +static void vurpmb_register_types(void) +{ + type_register_static(&vurpmb_info); +} + +type_init(vurpmb_register_types) diff --git a/hw/block/Kconfig b/hw/block/Kconfig index 2d17f481adc6..12e21870847a 100644 --- a/hw/block/Kconfig +++ b/hw/block/Kconfig @@ -38,5 +38,10 @@ config VHOST_USER_BLK default y if VIRTIO_PCI depends on VIRTIO && VHOST_USER && LINUX +config VHOST_USER_RPMB + bool + default y + depends on VIRTIO && VHOST_USER + config SWIM bool diff --git a/hw/block/meson.build b/hw/block/meson.build index 78cad8f7cba1..114222f18424 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -17,5 +17,6 @@ softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('nvme.c')) specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) +specific_ss.add(when: 'CONFIG_VHOST_USER_RPMB', if_true: files('vhost-user-rpmb.c')) subdir('dataplane') -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 03/19] hw/virtio: move virtio-pci.h into shared include space
This allows other device classes that will be exposed via PCI to be able to do so in the appropriate hw/ directory. I resisted the temptation to re-order headers to be more aesthetically pleasing. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- {hw => include/hw}/virtio/virtio-pci.h | 0 hw/virtio/vhost-scsi-pci.c | 2 +- hw/virtio/vhost-user-blk-pci.c | 2 +- hw/virtio/vhost-user-fs-pci.c | 2 +- hw/virtio/vhost-user-input-pci.c | 2 +- hw/virtio/vhost-user-scsi-pci.c | 2 +- hw/virtio/vhost-user-vsock-pci.c | 2 +- hw/virtio/vhost-vsock-pci.c | 2 +- hw/virtio/virtio-9p-pci.c | 2 +- hw/virtio/virtio-balloon-pci.c | 2 +- hw/virtio/virtio-blk-pci.c | 2 +- hw/virtio/virtio-input-host-pci.c | 2 +- hw/virtio/virtio-input-pci.c | 2 +- hw/virtio/virtio-iommu-pci.c | 2 +- hw/virtio/virtio-net-pci.c | 2 +- hw/virtio/virtio-pci.c | 2 +- hw/virtio/virtio-rng-pci.c | 2 +- hw/virtio/virtio-scsi-pci.c | 2 +- hw/virtio/virtio-serial-pci.c | 2 +- 19 files changed, 18 insertions(+), 18 deletions(-) rename {hw => include/hw}/virtio/virtio-pci.h (100%) diff --git a/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h similarity index 100% rename from hw/virtio/virtio-pci.h rename to include/hw/virtio/virtio-pci.h diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index cb71a294faaf..08980bc23bdb 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -21,7 +21,7 @@ #include "hw/virtio/vhost-scsi.h" #include "qapi/error.h" #include "qemu/module.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VHostSCSIPCI VHostSCSIPCI; diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index 33b404d8a225..eef8641a9891 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VHostUserBlkPCI VHostUserBlkPCI; diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c index 8bb389bd282a..777249e8bc4d 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "hw/qdev-properties.h" #include "hw/virtio/vhost-user-fs.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" struct VHostUserFSPCI { diff --git a/hw/virtio/vhost-user-input-pci.c b/hw/virtio/vhost-user-input-pci.c index c9d3e9113a5e..b858898a3630 100644 --- a/hw/virtio/vhost-user-input-pci.c +++ b/hw/virtio/vhost-user-input-pci.c @@ -9,7 +9,7 @@ #include "hw/virtio/virtio-input.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VHostUserInputPCI VHostUserInputPCI; diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index d5343412a11c..75882e3cf943 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -30,7 +30,7 @@ #include "hw/pci/msix.h" #include "hw/loader.h" #include "sysemu/kvm.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VHostUserSCSIPCI VHostUserSCSIPCI; diff --git a/hw/virtio/vhost-user-vsock-pci.c b/hw/virtio/vhost-user-vsock-pci.c index 763f89984e91..a50845ea87a3 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/qdev-properties.h" #include "hw/virtio/vhost-user-vsock.h" #include "qom/object.h" diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index e56067b42781..35773fbcb3b0 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/qdev-properties.h" #include "hw/virtio/vhost-vsock.h" #include "qemu/module.h" diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c index e07adcd9ea78..94c14f0b98c9 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/9pfs/virtio-9p.h" #include "hw/qdev-properties.h" #include "qemu/module.h" diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c index a2c5cc7207a9..8d5a212b94cd 100644 --- a/hw/virtio/virtio-balloon-pci.c +++ b/hw/virtio/virtio-balloon-pci.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-balloon.h" #include "qapi/error.h" diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index 9d5795810c36..9743bee965af 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -19,7 +19,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-blk.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/virtio/virtio-input-host-pci.c b/hw/virtio/virtio-input-host-pci.c index 0ac360de4f34..cf8a9cf9e8db 100644 --- a/hw/virtio/virtio-input-host-pci.c +++ b/hw/virtio/virtio-input-host-pci.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-input.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index 85acd3d2ebb4..d208df68af5f 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-input.h" #include "qemu/module.h" diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 76540e57b104..022447b5a469 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-iommu.h" #include "hw/qdev-properties.h" #include "qapi/error.h" diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index 292d13d27815..954b6fbf2d58 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -19,7 +19,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-net.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5bc769f685ce..507cb57c410f 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -32,7 +32,7 @@ #include "hw/pci/msix.h" #include "hw/loader.h" #include "sysemu/kvm.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index c1f916268be7..151ece6f946a 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-rng.h" #include "qapi/error.h" #include "qemu/module.h" diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index 97fab742368a..e8e3442f3828 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -18,7 +18,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/module.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index 35bcd961c988..cea31adcc4c6 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -20,7 +20,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-serial.h" #include "qemu/module.h" -#include "virtio-pci.h" +#include "hw/virtio/virtio-pci.h" #include "qom/object.h" typedef struct VirtIOSerialPCI VirtIOSerialPCI; -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 04/19] hw/block: add vhost-user-rpmb-pci boilerplate
This allows is to instantiate a vhost-user-rpmb device as part of a PCI bus. It is mostly boilerplate which looks pretty similar to the vhost-user-fs-pci device if you squint. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- - enable use IOEVENTFD flag - swap obj set bool args --- hw/block/vhost-user-rpmb-pci.c | 82 ++++++++++++++++++++++++++++++++++ hw/block/meson.build | 2 + 2 files changed, 84 insertions(+) create mode 100644 hw/block/vhost-user-rpmb-pci.c diff --git a/hw/block/vhost-user-rpmb-pci.c b/hw/block/vhost-user-rpmb-pci.c new file mode 100644 index 000000000000..f0518305a1d9 --- /dev/null +++ b/hw/block/vhost-user-rpmb-pci.c @@ -0,0 +1,82 @@ +/* + * Vhost-user RPMB virtio device PCI glue + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-rpmb.h" +#include "hw/virtio/virtio-pci.h" + +struct VHostUserRPMBPCI { + VirtIOPCIProxy parent_obj; + VHostUserRPMB vdev; +}; + +typedef struct VHostUserRPMBPCI VHostUserRPMBPCI; + +#define TYPE_VHOST_USER_RPMB_PCI "vhost-user-rpmb-pci-base" + +#define VHOST_USER_RPMB_PCI(obj) \ + OBJECT_CHECK(VHostUserRPMBPCI, (obj), TYPE_VHOST_USER_RPMB_PCI) + +static Property vurpmb_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vurpmb_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserRPMBPCI *dev = VHOST_USER_RPMB_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 1; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), "realized", true, errp); +} + +static void vurpmb_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vurpmb_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + device_class_set_props(dc, vurpmb_pci_properties); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_OTHER; +} + +static void vurpmb_pci_instance_init(Object *obj) +{ + VHostUserRPMBPCI *dev = VHOST_USER_RPMB_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_RPMB); +} + +static const VirtioPCIDeviceTypeInfo vurpmb_pci_info = { + .base_name = TYPE_VHOST_USER_RPMB_PCI, + .non_transitional_name = "vhost-user-rpmb-pci", + .instance_size = sizeof(VHostUserRPMBPCI), + .instance_init = vurpmb_pci_instance_init, + .class_init = vurpmb_pci_class_init, +}; + +static void vurpmb_pci_register(void) +{ + virtio_pci_types_register(&vurpmb_pci_info); +} + +type_init(vurpmb_pci_register); diff --git a/hw/block/meson.build b/hw/block/meson.build index 114222f18424..0b2d10201e28 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -18,5 +18,7 @@ softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('nvme.c')) specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) specific_ss.add(when: 'CONFIG_VHOST_USER_RPMB', if_true: files('vhost-user-rpmb.c')) +specific_ss.add(when: ['CONFIG_VHOST_USER_RPMB', 'CONFIG_VIRTIO_PCI' ], + if_true: files('vhost-user-rpmb-pci.c')) subdir('dataplane') -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 05/19] virtio-pci: add notification trace points
Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- hw/virtio/virtio-pci.c | 3 +++ hw/virtio/trace-events | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 507cb57c410f..33a40e31d955 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -36,6 +36,7 @@ #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" +#include "trace.h" #define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) @@ -1340,6 +1341,7 @@ static void virtio_pci_notify_write(void *opaque, hwaddr addr, unsigned queue = addr / virtio_pci_queue_mem_mult(proxy); if (vdev != NULL && queue < VIRTIO_QUEUE_MAX) { + trace_virtio_pci_notify_write(addr, val, size); virtio_queue_notify(vdev, queue); } } @@ -1353,6 +1355,7 @@ static void virtio_pci_notify_write_pio(void *opaque, hwaddr addr, unsigned queue = val; if (vdev != NULL && queue < VIRTIO_QUEUE_MAX) { + trace_virtio_pci_notify_write_pio(addr, val, size); virtio_queue_notify(vdev, queue); } } diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 845200bf109d..189972b9213a 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -55,7 +55,12 @@ virtio_mmio_guest_page(uint64_t size, int shift) "guest page size 0x%" PRIx64 " virtio_mmio_queue_write(uint64_t value, int max_size) "mmio_queue write 0x%" PRIx64 " max %d" virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d" -# virtio-iommu.c +# virtio-pci.c +virtio_pci_notify(uint16_t vector) "virtio_pci_notify vec 0x%x" +virtio_pci_notify_write(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)" +virtio_pci_notify_write_pio(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)" + +# hw/virtio/virtio-iommu.c virtio_iommu_device_reset(void) "reset!" virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64 virtio_iommu_device_status(uint8_t status) "driver status = %d" -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 06/19] tools/vhost-user-rpmb: add boilerplate and initial main
This adds the boilerplate files for a new vhost-user helper called vhost-user-rpmb which will support virtio based RPMB (Replay Protected Memory Block) devices. This commit just adds the initial boilerplate for building the binary with the common vhost-user options. As of this commit the only useful output you get is when running: vhost-user-rpmb --help Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 37 ++++++++++++++++++++++ MAINTAINERS | 5 +++ tools/meson.build | 8 +++++ tools/vhost-user-rpmb/50-qemu-rpmb.json.in | 5 +++ tools/vhost-user-rpmb/meson.build | 11 +++++++ 5 files changed, 66 insertions(+) create mode 100644 tools/vhost-user-rpmb/main.c create mode 100644 tools/vhost-user-rpmb/50-qemu-rpmb.json.in create mode 100644 tools/vhost-user-rpmb/meson.build diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c new file mode 100644 index 000000000000..a40a680a74ca --- /dev/null +++ b/tools/vhost-user-rpmb/main.c @@ -0,0 +1,37 @@ +/* + * VIRTIO RPMB Emulation via vhost-user + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <glib.h> + +static gchar *socket_path; +static gint socket_fd; +static gboolean print_cap; + +static GOptionEntry options[] +{ + { "socket-path", 0, 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" }, + { "fd", 0, 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" }, + { "print-capabilities", 0, 0, G_OPTION_ARG_NONE, &print_cap, "Output to stdout the backend capabilities in JSON format and exit", NULL}, + { NULL } +}; + +int main (int argc, char *argv[]) +{ + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new ("vhost-user-rpmb - vhost-user emulation of RPBM device"); + g_option_context_add_main_entries (context, options, "vhost-user-rpmb"); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_print ("option parsing failed: %s\n", error->message); + exit (1); + } + + +} diff --git a/MAINTAINERS b/MAINTAINERS index 3d17cad19aa0..e325c1024a33 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1873,6 +1873,11 @@ F: hw/virtio/virtio-mem-pci.h F: hw/virtio/virtio-mem-pci.c F: include/hw/virtio/virtio-mem.h +virtio-rpmb +M: Alex Benn?e <alex.bennee at linaro.org> +S: Supported +F: tools/vhost-user-rpmb/* + nvme M: Keith Busch <kbusch at kernel.org> M: Klaus Jensen <its at irrelevant.dk> diff --git a/tools/meson.build b/tools/meson.build index 513bd2ff4fd2..408048c6357c 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -8,3 +8,11 @@ have_virtiofsd = (have_system and if have_virtiofsd subdir('virtiofsd') endif + +have_virtiorpmb = (have_system and + have_tools and + 'CONFIG_LINUX' in config_host) + +if have_virtiorpmb + subdir('vhost-user-rpmb') +endif diff --git a/tools/vhost-user-rpmb/50-qemu-rpmb.json.in b/tools/vhost-user-rpmb/50-qemu-rpmb.json.in new file mode 100644 index 000000000000..2b033cda567c --- /dev/null +++ b/tools/vhost-user-rpmb/50-qemu-rpmb.json.in @@ -0,0 +1,5 @@ +{ + "description": "QEMU vhost-user-rpmb", + "type": "block", + "binary": "@libexecdir@/vhost-user-rpmb" +} diff --git a/tools/vhost-user-rpmb/meson.build b/tools/vhost-user-rpmb/meson.build new file mode 100644 index 000000000000..e0df1b69a3fb --- /dev/null +++ b/tools/vhost-user-rpmb/meson.build @@ -0,0 +1,11 @@ +executable('vhost-user-rpmb', files( + 'main.c'), + dependencies: [glib], + link_with: [libvhost_user], + install: true, + install_dir: get_option('libexecdir')) + +configure_file(input: '50-qemu-rpmb.json.in', + output: '50-qemu-rpmb.json', + configuration: config_host, + install_dir: qemu_datadir / 'vhost-user') -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 07/19] tools/vhost-user-rpmb: implement --print-capabilities
A very simple capabilities dump. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index a40a680a74ca..6b1989125bd6 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -7,6 +7,7 @@ */ #include <glib.h> +#include <stdio.h> static gchar *socket_path; static gint socket_fd; @@ -20,6 +21,14 @@ static GOptionEntry options[] { NULL } }; +/* Print vhost-user.json backend program capabilities */ +static void print_capabilities(void) +{ + printf("{\n"); + printf(" \"type\": \"block\"\n"); + printf("}\n"); +} + int main (int argc, char *argv[]) { GError *error = NULL; @@ -33,5 +42,9 @@ int main (int argc, char *argv[]) exit (1); } + if (print_cap) { + print_capabilities(); + exit(0); + } } -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 08/19] tools/vhost-user-rpmb: connect to fd and instantiate basic run loop
Again doesn't do much on it's own but will create a socket and wait on messages coming in from the vhost-user message socket. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 211 +++++++++++++++++++++++++++++- tools/vhost-user-rpmb/meson.build | 2 +- 2 files changed, 210 insertions(+), 3 deletions(-) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 6b1989125bd6..269c86cbb633 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -7,10 +7,23 @@ */ #include <glib.h> +#include <gio/gio.h> +#include <gio/gunixsocketaddress.h> #include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "contrib/libvhost-user/libvhost-user.h" + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof(((type *) 0)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member));}) +#endif static gchar *socket_path; -static gint socket_fd; +static gint socket_fd = -1; static gboolean print_cap; static GOptionEntry options[] @@ -21,6 +34,147 @@ static GOptionEntry options[] { NULL } }; +enum { + VHOST_USER_RPMB_MAX_QUEUES = 1, +}; + +/* These structures are defined in the specification */ + +struct virtio_rpmb_config { + uint8_t capacity; + uint8_t max_wr_cnt; + uint8_t max_rd_cnt; +}; + +struct virtio_rpmb_frame { + uint8_t stuff[196]; + uint8_t key_mac[32]; + uint8_t data[256]; + uint8_t nonce[16]; + /* remaining fields are big-endian */ + uint32_t write_counter; + uint16_t address; + uint16_t block_count; + uint16_t result; + uint16_t req_resp; +}; + +/* + * Structure to track internal state of RPMB Device + */ + +typedef struct VuRpmb { + VugDev dev; + struct virtio_rpmb_config virtio_config; +} VuRpmb; + +struct virtio_rpmb_ctrl_command { + VuVirtqElement elem; + VuVirtq *vq; + struct virtio_rpmb_frame frame; + uint32_t error; + bool finished; +}; + +static void vrpmb_panic(VuDev *dev, const char *msg) +{ + g_critical("%s\n", msg); + exit(EXIT_FAILURE); +} + +static uint64_t vrpmb_get_features(VuDev *dev) +{ + return 0; +} + +static void vrpmb_set_features(VuDev *dev, uint64_t features) +{ + if (features) { + g_autoptr(GString) s = g_string_new("Requested un-handled feature"); + g_string_append_printf(s, " 0x%" PRIx64 "", features); + g_info("%s: %s", __func__, s->str); + } +} + +/* + * The configuration of the device is static and set when we start the + * daemon. + */ +static int +vrpmb_get_config(VuDev *dev, uint8_t *config, uint32_t len) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + g_return_val_if_fail(len <= sizeof(struct virtio_rpmb_config), -1); + memcpy(config, &r->virtio_config, len); + return 0; +} + +static int +vrpmb_set_config(VuDev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + /* ignore */ + return 0; +} + +static void +vrpmb_handle_ctrl(VuDev *dev, int qidx) +{ + VuVirtq *vq = vu_get_queue(dev, qidx); + struct virtio_rpmb_ctrl_command *cmd = NULL; + + for (;;) { + cmd = vu_queue_pop(dev, vq, sizeof(struct virtio_rpmb_ctrl_command)); + if (!cmd) { + break; + } + + g_debug("un-handled cmd: %p", cmd); + } +} + +static void +vrpmb_queue_set_started(VuDev *dev, int qidx, bool started) +{ + VuVirtq *vq = vu_get_queue(dev, qidx); + + g_debug("queue started %d:%d\n", qidx, started); + + switch (qidx) { + case 0: + vu_set_queue_handler(dev, vq, started ? vrpmb_handle_ctrl : NULL); + break; + default: + break; + } +} + +static int +vrpmb_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) +{ + switch (msg->request) { + default: + return 0; + } + + return 0; +} + +static const VuDevIface vuiface = { + .set_features = vrpmb_set_features, + .get_features = vrpmb_get_features, + .queue_set_started = vrpmb_queue_set_started, + .process_msg = vrpmb_process_msg, + .get_config = vrpmb_get_config, + .set_config = vrpmb_set_config, +}; + +static void vrpmb_destroy(VuRpmb *r) +{ + vug_deinit(&r->dev); +} + /* Print vhost-user.json backend program capabilities */ static void print_capabilities(void) { @@ -33,8 +187,11 @@ int main (int argc, char *argv[]) { GError *error = NULL; GOptionContext *context; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GSocket) socket = NULL; + VuRpmb rpmb = { }; - context = g_option_context_new ("vhost-user-rpmb - vhost-user emulation of RPBM device"); + context = g_option_context_new ("vhost-user emulation of RPBM device"); g_option_context_add_main_entries (context, options, "vhost-user-rpmb"); if (!g_option_context_parse (context, &argc, &argv, &error)) { @@ -47,4 +204,54 @@ int main (int argc, char *argv[]) exit(0); } + if (!socket_path && socket_fd < 0) { + g_printerr("Please specify either --fd or --socket-path\n"); + exit(EXIT_FAILURE); + } + + /* + * Now create a vhost-user socket that we will receive messages + * on. Once we have our handler set up we can enter the glib main + * loop. + */ + if (socket_path) { + g_autoptr(GSocketAddress) addr = g_unix_socket_address_new(socket_path); + g_autoptr(GSocket) bind_socket = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, &error); + + if (!g_socket_bind(bind_socket, addr, false, &error)) { + g_printerr("Failed to bind to socket at %s (%s).\n", + socket_path, error->message); + exit(EXIT_FAILURE); + } + if (!g_socket_listen(bind_socket, &error)) { + g_printerr("Failed to listen on socket %s (%s).\n", + socket_path, error->message); + } + g_message("awaiting connection to %s", socket_path); + socket = g_socket_accept(bind_socket, NULL, &error); + if (!socket) { + g_printerr("Failed to accept on socket %s (%s).\n", + socket_path, error->message); + } + } else { + socket = g_socket_new_from_fd(socket_fd, &error); + if (!socket) { + g_printerr("Failed to connect to FD %d (%s).\n", + socket_fd, error->message); + exit(EXIT_FAILURE); + } + } + + if (!vug_init(&rpmb.dev, VHOST_USER_RPMB_MAX_QUEUES, g_socket_get_fd(socket), + vrpmb_panic, &vuiface)) { + g_printerr("Failed to initialize libvhost-user-glib.\n"); + exit(EXIT_FAILURE); + } + + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + vrpmb_destroy(&rpmb); } diff --git a/tools/vhost-user-rpmb/meson.build b/tools/vhost-user-rpmb/meson.build index e0df1b69a3fb..cf80bedd99ac 100644 --- a/tools/vhost-user-rpmb/meson.build +++ b/tools/vhost-user-rpmb/meson.build @@ -1,6 +1,6 @@ executable('vhost-user-rpmb', files( 'main.c'), - dependencies: [glib], + dependencies: [qemuutil, glib, gio], link_with: [libvhost_user], install: true, install_dir: get_option('libexecdir')) -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 09/19] tools/vhost-user-rpmb: add a --verbose/debug flags for logging
This gives us two levels of informational output when tracing what the daemon is doing. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 269c86cbb633..1be0d4b8a567 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -6,6 +6,9 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#define G_LOG_DOMAIN "vhost-user-rpmb" +#define G_LOG_USE_STRUCTURED 1 + #include <glib.h> #include <gio/gio.h> #include <gio/gunixsocketaddress.h> @@ -25,12 +28,16 @@ static gchar *socket_path; static gint socket_fd = -1; static gboolean print_cap; +static gboolean verbose; +static gboolean debug; static GOptionEntry options[] { { "socket-path", 0, 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" }, { "fd", 0, 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" }, { "print-capabilities", 0, 0, G_OPTION_ARG_NONE, &print_cap, "Output to stdout the backend capabilities in JSON format and exit", NULL}, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be more verbose in output", NULL}, + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Include debug output", NULL}, { NULL } }; @@ -84,6 +91,7 @@ static void vrpmb_panic(VuDev *dev, const char *msg) static uint64_t vrpmb_get_features(VuDev *dev) { + g_info("%s: replying", __func__); return 0; } @@ -209,6 +217,17 @@ int main (int argc, char *argv[]) exit(EXIT_FAILURE); } + if (verbose || debug) { + g_log_set_handler(NULL, G_LOG_LEVEL_MASK, g_log_default_handler, NULL); + if (debug) { + g_setenv("G_MESSAGES_DEBUG", "all", true); + } + } else { + g_log_set_handler(NULL, + G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR, + g_log_default_handler, NULL); + } + /* * Now create a vhost-user socket that we will receive messages * on. Once we have our handler set up we can enter the glib main -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 10/19] tools/vhost-user-rpmb: handle shutdown and SIGINT/SIGHUP cleanly
The libvhost-user library will just exit if it handles the VHOST_USER_NONE message and we want to ensure we have tidied up after ourselves. As we need to signal the shutdown of the main loop we need to move the information into the VuRmb state structure. We also want to do the same if we catch a SIGINT/SIGHUP termination signal. While we are at it add some instrumentation so we can follow the program flow. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 49 +++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 1be0d4b8a567..7b3b29ccfc5b 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -12,6 +12,7 @@ #include <glib.h> #include <gio/gio.h> #include <gio/gunixsocketaddress.h> +#include <glib-unix.h> #include <stdio.h> #include <string.h> #include <inttypes.h> @@ -73,6 +74,7 @@ struct virtio_rpmb_frame { typedef struct VuRpmb { VugDev dev; struct virtio_rpmb_config virtio_config; + GMainLoop *loop; } VuRpmb; struct virtio_rpmb_ctrl_command { @@ -158,10 +160,22 @@ vrpmb_queue_set_started(VuDev *dev, int qidx, bool started) } } -static int -vrpmb_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) +/* + * vrpmb_process_msg: process messages of vhost-user interface + * + * Any that are not handled here are processed by the libvhost library + * itself. + */ +static int vrpmb_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) { + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + + g_info("%s: msg %d", __func__, msg->request); + switch (msg->request) { + case VHOST_USER_NONE: + g_main_loop_quit(r->loop); + return 1; default: return 0; } @@ -181,6 +195,9 @@ static const VuDevIface vuiface = { static void vrpmb_destroy(VuRpmb *r) { vug_deinit(&r->dev); + if (socket_path) { + unlink(socket_path); + } } /* Print vhost-user.json backend program capabilities */ @@ -191,11 +208,18 @@ static void print_capabilities(void) printf("}\n"); } +static gboolean hangup(gpointer user_data) +{ + GMainLoop *loop = (GMainLoop *) user_data; + g_info("%s: caught hangup/quit signal, quitting main loop", __func__); + g_main_loop_quit(loop); + return true; +} + int main (int argc, char *argv[]) { GError *error = NULL; GOptionContext *context; - g_autoptr(GMainLoop) loop = NULL; g_autoptr(GSocket) socket = NULL; VuRpmb rpmb = { }; @@ -262,15 +286,28 @@ int main (int argc, char *argv[]) } } + /* + * Create the main loop first so all the various sources can be + * added. As well as catching signals we need to ensure vug_init + * can add it's GSource watches. + */ + + rpmb.loop = g_main_loop_new(NULL, FALSE); + /* catch exit signals */ + g_unix_signal_add(SIGHUP, hangup, rpmb.loop); + g_unix_signal_add(SIGINT, hangup, rpmb.loop); + if (!vug_init(&rpmb.dev, VHOST_USER_RPMB_MAX_QUEUES, g_socket_get_fd(socket), vrpmb_panic, &vuiface)) { g_printerr("Failed to initialize libvhost-user-glib.\n"); exit(EXIT_FAILURE); } - loop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(loop); - g_main_loop_unref(loop); + g_message("entering main loop, awaiting messages"); + g_main_loop_run(rpmb.loop); + g_message("finished main loop, cleaning up"); + + g_main_loop_unref(rpmb.loop); vrpmb_destroy(&rpmb); } -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 11/19] tools/vhost-user-rpmb: add --flash-path for backing store
We will need to store the data somewhere so add the option to point to the file where we will keep the data. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 58 +++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 7b3b29ccfc5b..64bd7e79f573 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -13,9 +13,15 @@ #include <gio/gio.h> #include <gio/gunixsocketaddress.h> #include <glib-unix.h> +#include <glib/gstdio.h> #include <stdio.h> #include <string.h> #include <inttypes.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> #include "contrib/libvhost-user/libvhost-user-glib.h" #include "contrib/libvhost-user/libvhost-user.h" @@ -27,6 +33,7 @@ #endif static gchar *socket_path; +static char *flash_path; static gint socket_fd = -1; static gboolean print_cap; static gboolean verbose; @@ -35,6 +42,7 @@ static gboolean debug; static GOptionEntry options[] { { "socket-path", 0, 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" }, + { "flash-path", 0, 0, G_OPTION_ARG_FILENAME, &flash_path, "Location of raw flash image file", "PATH" }, { "fd", 0, 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" }, { "print-capabilities", 0, 0, G_OPTION_ARG_NONE, &print_cap, "Output to stdout the backend capabilities in JSON format and exit", NULL}, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be more verbose in output", NULL}, @@ -47,6 +55,8 @@ enum { }; /* These structures are defined in the specification */ +#define KiB (1UL << 10) +#define MAX_RPMB_SIZE (KiB * 128 * 256) struct virtio_rpmb_config { uint8_t capacity; @@ -75,6 +85,8 @@ typedef struct VuRpmb { VugDev dev; struct virtio_rpmb_config virtio_config; GMainLoop *loop; + int flash_fd; + void *flash_map; } VuRpmb; struct virtio_rpmb_ctrl_command { @@ -116,6 +128,8 @@ vrpmb_get_config(VuDev *dev, uint8_t *config, uint32_t len) VuRpmb *r = container_of(dev, VuRpmb, dev.parent); g_return_val_if_fail(len <= sizeof(struct virtio_rpmb_config), -1); memcpy(config, &r->virtio_config, len); + + g_info("%s: done", __func__); return 0; } @@ -192,6 +206,41 @@ static const VuDevIface vuiface = { .set_config = vrpmb_set_config, }; +static bool vrpmb_load_flash_image(VuRpmb *r, char *img_path) +{ + GStatBuf statbuf; + size_t map_size; + + if (g_stat(img_path, &statbuf) < 0) { + g_error("couldn't stat %s", img_path); + return false; + } + + r->flash_fd = g_open(img_path, O_RDWR, 0); + if (r->flash_fd < 0) { + g_error("couldn't open %s (%s)", img_path, strerror(errno)); + return false; + } + + if (statbuf.st_size > MAX_RPMB_SIZE) { + g_warning("%s larger than maximum size supported", img_path); + map_size = MAX_RPMB_SIZE; + } else { + map_size = statbuf.st_size; + } + r->virtio_config.capacity = map_size / (128 *KiB); + r->virtio_config.max_wr_cnt = 1; + r->virtio_config.max_rd_cnt = 1; + + r->flash_map = mmap(NULL, map_size, PROT_READ, MAP_SHARED, r->flash_fd, 0); + if (r->flash_map == MAP_FAILED) { + g_error("failed to mmap file"); + return false; + } + + return true; +} + static void vrpmb_destroy(VuRpmb *r) { vug_deinit(&r->dev); @@ -216,7 +265,7 @@ static gboolean hangup(gpointer user_data) return true; } -int main (int argc, char *argv[]) +int main(int argc, char *argv[]) { GError *error = NULL; GOptionContext *context; @@ -236,6 +285,13 @@ int main (int argc, char *argv[]) exit(0); } + if (!flash_path || !g_file_test(flash_path, G_FILE_TEST_EXISTS)) { + g_printerr("Please specify a valid --flash-path for the flash image\n"); + exit(EXIT_FAILURE); + } else { + vrpmb_load_flash_image(&rpmb, flash_path); + } + if (!socket_path && socket_fd < 0) { g_printerr("Please specify either --fd or --socket-path\n"); exit(EXIT_FAILURE); -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 12/19] tools/vhost-user-rpmb: import hmac_sha256 functions
We need to calculate HMAC-256SHA as part of the protocol. To avoid making the daemon dependent on QEMU's internal crypto library we import the functions here. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/hmac_sha256.h | 87 ++++++++ tools/vhost-user-rpmb/hmac_sha256.c | 331 ++++++++++++++++++++++++++++ tools/vhost-user-rpmb/meson.build | 3 +- 3 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 tools/vhost-user-rpmb/hmac_sha256.h create mode 100644 tools/vhost-user-rpmb/hmac_sha256.c diff --git a/tools/vhost-user-rpmb/hmac_sha256.h b/tools/vhost-user-rpmb/hmac_sha256.h new file mode 100644 index 000000000000..e67a5baedecd --- /dev/null +++ b/tools/vhost-user-rpmb/hmac_sha256.h @@ -0,0 +1,87 @@ +/* + * HMAC-SHA-256 implementation + * Last update: 06/15/2005 + * Issue date: 06/15/2005 + * + * Copyright (C) 2005 Olivier Gay <olivier.gay at a3.epfl.ch> + * All rights reserved. + * + * Copyright (c) 2016, 2020, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef HMAC_SHA256_H +#define HMAC_SHA256_H + +#define SHA256_DIGEST_SIZE ( 256 / 8) +#define SHA256_BLOCK_SIZE ( 512 / 8) + +#ifndef SHA2_TYPES +#define SHA2_TYPES +typedef unsigned char uint8; +typedef unsigned int uint32; +typedef unsigned long long uint64; +#endif + +typedef struct { + unsigned int tot_len; + unsigned int len; + unsigned char block[2 * SHA256_BLOCK_SIZE]; + uint32 h[8]; +} sha256_ctx; + +void sha256_init(sha256_ctx * ctx); +void sha256_update(sha256_ctx *ctx, const unsigned char *message, + unsigned int len); +void sha256_final(sha256_ctx *ctx, unsigned char *digest); +void sha256(const unsigned char *message, unsigned int len, + unsigned char *digest); + +typedef struct { + sha256_ctx ctx_inside; + sha256_ctx ctx_outside; + + /* for hmac_reinit */ + sha256_ctx ctx_inside_reinit; + sha256_ctx ctx_outside_reinit; + + unsigned char block_ipad[SHA256_BLOCK_SIZE]; + unsigned char block_opad[SHA256_BLOCK_SIZE]; +} hmac_sha256_ctx; + +void hmac_sha256_init(hmac_sha256_ctx *ctx, const unsigned char *key, + unsigned int key_size); +void hmac_sha256_reinit(hmac_sha256_ctx *ctx); +void hmac_sha256_update(hmac_sha256_ctx *ctx, const unsigned char *message, + unsigned int message_len); +void hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *mac, + unsigned int mac_size); +void hmac_sha256(const unsigned char *key, unsigned int key_size, + const unsigned char *message, unsigned int message_len, + unsigned char *mac, unsigned mac_size); + +#endif /* !HMAC_SHA256_H */ diff --git a/tools/vhost-user-rpmb/hmac_sha256.c b/tools/vhost-user-rpmb/hmac_sha256.c new file mode 100644 index 000000000000..f6640a46c616 --- /dev/null +++ b/tools/vhost-user-rpmb/hmac_sha256.c @@ -0,0 +1,331 @@ +/* + * HMAC-SHA-256 implementation + * Last update: 06/15/2005 + * Issue date: 06/15/2005 + * + * Copyright (C) 2005 Olivier Gay <olivier.gay at a3.epfl.ch> + * All rights reserved. + * + * Copyright (c) 2016, 2020 Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +#include "hmac_sha256.h" + +/* SHA256 functions */ + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8) ((x) ); \ + *((str) + 2) = (uint8) ((x) >> 8); \ + *((str) + 1) = (uint8) ((x) >> 16); \ + *((str) + 0) = (uint8) ((x) >> 24); \ +} + +#define PACK32(str, x) \ +{ \ + *(x) = ((uint32) *((str) + 3) ) \ + | ((uint32) *((str) + 2) << 8) \ + | ((uint32) *((str) + 1) << 16) \ + | ((uint32) *((str) + 0) << 24); \ +} + +#define UNPACK64(x, str) \ +{ \ + *((str) + 7) = (uint8) ((x) ); \ + *((str) + 6) = (uint8) ((x) >> 8); \ + *((str) + 5) = (uint8) ((x) >> 16); \ + *((str) + 4) = (uint8) ((x) >> 24); \ + *((str) + 3) = (uint8) ((x) >> 32); \ + *((str) + 2) = (uint8) ((x) >> 40); \ + *((str) + 1) = (uint8) ((x) >> 48); \ + *((str) + 0) = (uint8) ((x) >> 56); \ +} + +#define PACK64(str, x) \ +{ \ + *(x) = ((uint64) *((str) + 7) ) \ + | ((uint64) *((str) + 6) << 8) \ + | ((uint64) *((str) + 5) << 16) \ + | ((uint64) *((str) + 4) << 24) \ + | ((uint64) *((str) + 3) << 32) \ + | ((uint64) *((str) + 2) << 40) \ + | ((uint64) *((str) + 1) << 48) \ + | ((uint64) *((str) + 0) << 56); \ +} + +#define SHA256_SCR(i) \ +{ \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ +} + +uint32 sha256_h0[8] + {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +uint32 sha256_k[64] + {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +/* SHA-256 functions */ + +static void sha256_transf(sha256_ctx *ctx, const unsigned char *message, + unsigned int block_nb) +{ + uint32 w[64]; + uint32 wv[8]; + uint32 t1, t2; + const unsigned char *sub_block; + int i; + int j; + + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 6); + + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha256_k[j] + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } + } +} + +void sha256(const unsigned char *message, unsigned int len, unsigned char *digest) +{ + sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, message, len); + sha256_final(&ctx, digest); +} + +void sha256_init(sha256_ctx *ctx) +{ + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha256_h0[i]; + } + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha256_update(sha256_ctx *ctx, const unsigned char *message, + unsigned int len) +{ + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const unsigned char *shifted_message; + + tmp_len = SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA256_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha256_transf(ctx, ctx->block, 1); + sha256_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA256_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 6], + rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +void sha256_final(sha256_ctx *ctx, unsigned char *digest) +{ + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + int i; + + block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) + < (ctx->len % SHA256_BLOCK_SIZE))); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + sha256_transf(ctx, ctx->block, block_nb); + + for (i = 0 ; i < 8; i++) { + UNPACK32(ctx->h[i], &digest[i << 2]); + } +} + +/* HMAC-SHA-256 functions */ + +void hmac_sha256_init(hmac_sha256_ctx *ctx, const unsigned char *key, + unsigned int key_size) +{ + unsigned int fill; + unsigned int num; + + const unsigned char *key_used; + unsigned char key_temp[SHA256_DIGEST_SIZE]; + int i; + + if (key_size == SHA256_BLOCK_SIZE) { + key_used = key; + num = SHA256_BLOCK_SIZE; + } else { + if (key_size > SHA256_BLOCK_SIZE){ + num = SHA256_DIGEST_SIZE; + sha256(key, key_size, key_temp); + key_used = key_temp; + } else { /* key_size > SHA256_BLOCK_SIZE */ + key_used = key; + num = key_size; + } + fill = SHA256_BLOCK_SIZE - num; + + memset(ctx->block_ipad + num, 0x36, fill); + memset(ctx->block_opad + num, 0x5c, fill); + } + + for (i = 0; i < (int) num; i++) { + ctx->block_ipad[i] = key_used[i] ^ 0x36; + ctx->block_opad[i] = key_used[i] ^ 0x5c; + } + + sha256_init(&ctx->ctx_inside); + sha256_update(&ctx->ctx_inside, ctx->block_ipad, SHA256_BLOCK_SIZE); + + sha256_init(&ctx->ctx_outside); + sha256_update(&ctx->ctx_outside, ctx->block_opad, + SHA256_BLOCK_SIZE); + + /* for hmac_reinit */ + memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside, + sizeof(sha256_ctx)); + memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside, + sizeof(sha256_ctx)); +} + +void hmac_sha256_reinit(hmac_sha256_ctx *ctx) +{ + memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit, + sizeof(sha256_ctx)); + memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit, + sizeof(sha256_ctx)); +} + +void hmac_sha256_update(hmac_sha256_ctx *ctx, const unsigned char *message, + unsigned int message_len) +{ + sha256_update(&ctx->ctx_inside, message, message_len); +} + +void hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *mac, + unsigned int mac_size) +{ + unsigned char digest_inside[SHA256_DIGEST_SIZE]; + unsigned char mac_temp[SHA256_DIGEST_SIZE]; + + sha256_final(&ctx->ctx_inside, digest_inside); + sha256_update(&ctx->ctx_outside, digest_inside, SHA256_DIGEST_SIZE); + sha256_final(&ctx->ctx_outside, mac_temp); + memcpy(mac, mac_temp, mac_size); +} + +void hmac_sha256(const unsigned char *key, unsigned int key_size, + const unsigned char *message, unsigned int message_len, + unsigned char *mac, unsigned mac_size) +{ + hmac_sha256_ctx ctx; + + hmac_sha256_init(&ctx, key, key_size); + hmac_sha256_update(&ctx, message, message_len); + hmac_sha256_final(&ctx, mac, mac_size); +} diff --git a/tools/vhost-user-rpmb/meson.build b/tools/vhost-user-rpmb/meson.build index cf80bedd99ac..f964837d151d 100644 --- a/tools/vhost-user-rpmb/meson.build +++ b/tools/vhost-user-rpmb/meson.build @@ -1,5 +1,6 @@ executable('vhost-user-rpmb', files( - 'main.c'), + 'main.c', + 'hmac_sha256.c'), dependencies: [qemuutil, glib, gio], link_with: [libvhost_user], install: true, -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 13/19] tools/vhost-user-rpmb: implement the PROGRAM_KEY handshake
This implements the first handshake of the device initialisation which is the programming of the device key. This can only be done once per-device. Currently there is no persistence for the device key and other metadata such as the write count. This will be added later. [TODO: clarify the spec if we should respond immediately or on request] Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 299 +++++++++++++++++++++++++++++++++-- 1 file changed, 286 insertions(+), 13 deletions(-) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 64bd7e79f573..9c98f6916f6f 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -22,10 +22,14 @@ #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> +#include <endian.h> +#include <assert.h> #include "contrib/libvhost-user/libvhost-user-glib.h" #include "contrib/libvhost-user/libvhost-user.h" +#include "hmac_sha256.h" + #ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof(((type *) 0)->member) *__mptr = (ptr); \ @@ -57,6 +61,31 @@ enum { /* These structures are defined in the specification */ #define KiB (1UL << 10) #define MAX_RPMB_SIZE (KiB * 128 * 256) +#define RPMB_KEY_MAC_SIZE 32 + +/* RPMB Request Types */ +#define VIRTIO_RPMB_REQ_PROGRAM_KEY 0x0001 +#define VIRTIO_RPMB_REQ_GET_WRITE_COUNTER 0x0002 +#define VIRTIO_RPMB_REQ_DATA_WRITE 0x0003 +#define VIRTIO_RPMB_REQ_DATA_READ 0x0004 +#define VIRTIO_RPMB_REQ_RESULT_READ 0x0005 + +/* RPMB Response Types */ +#define VIRTIO_RPMB_RESP_PROGRAM_KEY 0x0100 +#define VIRTIO_RPMB_RESP_GET_COUNTER 0x0200 +#define VIRTIO_RPMB_RESP_DATA_WRITE 0x0300 +#define VIRTIO_RPMB_RESP_DATA_READ 0x0400 + +/* RPMB Operation Results */ +#define VIRTIO_RPMB_RES_OK 0x0000 +#define VIRTIO_RPMB_RES_GENERAL_FAILURE 0x0001 +#define VIRTIO_RPMB_RES_AUTH_FAILURE 0x0002 +#define VIRTIO_RPMB_RES_COUNT_FAILURE 0x0003 +#define VIRTIO_RPMB_RES_ADDR_FAILURE 0x0004 +#define VIRTIO_RPMB_RES_WRITE_FAILURE 0x0005 +#define VIRTIO_RPMB_RES_READ_FAILURE 0x0006 +#define VIRTIO_RPMB_RES_NO_AUTH_KEY 0x0007 +#define VIRTIO_RPMB_RES_WRITE_COUNTER_EXPIRED 0x0080 struct virtio_rpmb_config { uint8_t capacity; @@ -64,9 +93,13 @@ struct virtio_rpmb_config { uint8_t max_rd_cnt; }; +/* + * This is based on the JDEC standard and not the currently not + * up-streamed NVME standard. + */ struct virtio_rpmb_frame { uint8_t stuff[196]; - uint8_t key_mac[32]; + uint8_t key_mac[RPMB_KEY_MAC_SIZE]; uint8_t data[256]; uint8_t nonce[16]; /* remaining fields are big-endian */ @@ -75,7 +108,7 @@ struct virtio_rpmb_frame { uint16_t block_count; uint16_t result; uint16_t req_resp; -}; +} __attribute__((packed)); /* * Structure to track internal state of RPMB Device @@ -87,15 +120,63 @@ typedef struct VuRpmb { GMainLoop *loop; int flash_fd; void *flash_map; + uint8_t *key; + uint16_t last_result; + uint16_t last_reqresp; } VuRpmb; -struct virtio_rpmb_ctrl_command { - VuVirtqElement elem; - VuVirtq *vq; - struct virtio_rpmb_frame frame; - uint32_t error; - bool finished; -}; +/* refer to util/iov.c */ +static size_t vrpmb_iov_size(const struct iovec *iov, + const unsigned int iov_cnt) +{ + size_t len; + unsigned int i; + + len = 0; + for (i = 0; i < iov_cnt; i++) { + len += iov[i].iov_len; + } + return len; +} + + +static size_t vrpmb_iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes) +{ + size_t done; + unsigned int i; + for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { + if (offset < iov[i].iov_len) { + size_t len = MIN(iov[i].iov_len - offset, bytes - done); + memcpy(buf + done, iov[i].iov_base + offset, len); + done += len; + offset = 0; + } else { + offset -= iov[i].iov_len; + } + } + assert(offset == 0); + return done; +} + +static size_t vrpmb_iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes) +{ + size_t done; + unsigned int i; + for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { + if (offset < iov[i].iov_len) { + size_t len = MIN(iov[i].iov_len - offset, bytes - done); + memcpy(iov[i].iov_base + offset, buf + done, len); + done += len; + offset = 0; + } else { + offset -= iov[i].iov_len; + } + } + assert(offset == 0); + return done; +} static void vrpmb_panic(VuDev *dev, const char *msg) { @@ -142,19 +223,211 @@ vrpmb_set_config(VuDev *dev, const uint8_t *data, return 0; } +/* + * vrpmb_update_mac_in_frame: + * + * From the spec: + * The MAC is calculated using HMAC SHA-256. It takes + * as input a key and a message. The key used for the MAC calculation + * is always the 256-bit RPMB authentication key. The message used as + * input to the MAC calculation is the concatenation of the fields in + * the RPMB frames excluding stuff bytes and the MAC itself. + * + * The code to do this has been lifted from the optee supplicant code + * which itself uses a 3 clause BSD chunk of code. + */ + +static void vrpmb_update_mac_in_frame(VuRpmb *r, struct virtio_rpmb_frame *frm) +{ + hmac_sha256_ctx ctx; + static const int dlen = (sizeof(struct virtio_rpmb_frame) - + offsetof(struct virtio_rpmb_frame, data)); + + hmac_sha256_init(&ctx, r->key, RPMB_KEY_MAC_SIZE); + hmac_sha256_update(&ctx, frm->data, dlen); + hmac_sha256_final(&ctx, &frm->key_mac[0], 32); +} + +/* + * Handlers for individual control messages + */ + +/* + * vrpmb_handle_program_key: + * + * Program the device with our key. The spec is a little hazzy on if + * we respond straight away or we wait for the user to send a + * VIRTIO_RPMB_REQ_RESULT_READ request. + */ +static void vrpmb_handle_program_key(VuDev *dev, struct virtio_rpmb_frame *frame) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + + /* + * Run the checks from: + * 5.12.6.1.1 Device Requirements: Device Operation: Program Key + */ + r->last_reqresp = VIRTIO_RPMB_RESP_PROGRAM_KEY; + + /* Fail if already programmed */ + if (r->key) { + g_debug("key already programmed"); + r->last_result = VIRTIO_RPMB_RES_WRITE_FAILURE; + } else if (be16toh(frame->block_count) != 1) { + g_debug("weird block counts (%d)", frame->block_count); + r->last_result = VIRTIO_RPMB_RES_GENERAL_FAILURE; + } else { + r->key = g_memdup(&frame->key_mac[0], RPMB_KEY_MAC_SIZE); + r->last_result = VIRTIO_RPMB_RES_OK; + } + + g_info("%s: req_resp = %x, result = %x", __func__, + r->last_reqresp, r->last_result); + return; +} + +/* + * Return the result of the last message. This is only valid if the + * previous message was VIRTIO_RPMB_REQ_PROGRAM_KEY or + * VIRTIO_RPMB_REQ_DATA_WRITE. + * + * The frame should be freed once sent. + */ +static struct virtio_rpmb_frame * vrpmb_handle_result_read(VuDev *dev) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + struct virtio_rpmb_frame *resp = g_new0(struct virtio_rpmb_frame, 1); + + if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY || + r->last_reqresp == VIRTIO_RPMB_REQ_DATA_WRITE) { + resp->result = htobe16(r->last_result); + resp->req_resp = htobe16(r->last_reqresp); + } else { + resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); + } + + /* calculate HMAC */ + if (!r->key) { + resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); + } else { + vrpmb_update_mac_in_frame(r, resp); + } + + g_info("%s: result = %x req_resp = %x", __func__, + be16toh(resp->result), + be16toh(resp->req_resp)); + return resp; +} + +static void fmt_bytes(GString *s, uint8_t *bytes, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i % 16 == 0) { + g_string_append_c(s, '\n'); + } + g_string_append_printf(s, "%x ", bytes[i]); + } +} + +static void vrpmb_dump_frame(struct virtio_rpmb_frame *frame) +{ + g_autoptr(GString) s = g_string_new("frame: "); + + g_string_append_printf(s, " %p\n", frame); + g_string_append_printf(s, "key_mac:"); + fmt_bytes(s, (uint8_t *) &frame->key_mac[0], 32); + g_string_append_printf(s, "\ndata:"); + fmt_bytes(s, (uint8_t *) &frame->data, 256); + g_string_append_printf(s, "\nnonce:"); + fmt_bytes(s, (uint8_t *) &frame->nonce, 16); + g_string_append_printf(s, "\nwrite_counter: %d\n", + be32toh(frame->write_counter)); + g_string_append_printf(s, "address: %#04x\n", be16toh(frame->address)); + g_string_append_printf(s, "block_count: %d\n", be16toh(frame->block_count)); + g_string_append_printf(s, "result: %d\n", be16toh(frame->result)); + g_string_append_printf(s, "req_resp: %d\n", be16toh(frame->req_resp)); + + g_debug("%s: %s\n", __func__, s->str); +} + static void vrpmb_handle_ctrl(VuDev *dev, int qidx) { VuVirtq *vq = vu_get_queue(dev, qidx); - struct virtio_rpmb_ctrl_command *cmd = NULL; + struct virtio_rpmb_frame *frames = NULL; for (;;) { - cmd = vu_queue_pop(dev, vq, sizeof(struct virtio_rpmb_ctrl_command)); - if (!cmd) { + VuVirtqElement *elem; + size_t len, frame_sz = sizeof(struct virtio_rpmb_frame); + int n; + + elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { break; } + g_debug("%s: got queue (in %d, out %d)", __func__, + elem->in_num, elem->out_num); - g_debug("un-handled cmd: %p", cmd); + len = vrpmb_iov_size(elem->out_sg, elem->out_num); + frames = g_realloc(frames, len); + vrpmb_iov_to_buf(elem->out_sg, elem->out_num, 0, frames, len); + + if (len % frame_sz != 0) { + g_warning("%s: incomplete frames %zu/%zu != 0\n", + __func__, len, frame_sz); + } + + for (n = 0; n < len / frame_sz; n++) { + struct virtio_rpmb_frame *f = &frames[n]; + struct virtio_rpmb_frame *resp = NULL; + uint16_t req_resp = be16toh(f->req_resp); + bool responded = false; + + if (debug) { + g_info("req_resp=%x", req_resp); + vrpmb_dump_frame(f); + } + + switch (req_resp) { + case VIRTIO_RPMB_REQ_PROGRAM_KEY: + vrpmb_handle_program_key(dev, f); + break; + case VIRTIO_RPMB_REQ_RESULT_READ: + if (!responded) { + resp = vrpmb_handle_result_read(dev); + } else { + g_warning("%s: already sent a response in this set of frames", + __func__); + } + break; + default: + g_debug("un-handled request: %x", f->req_resp); + break; + } + + /* + * Do we have a frame to send back? + */ + if (resp) { + g_debug("sending response frame: %p", resp); + if (debug) { + vrpmb_dump_frame(resp); + } + len = vrpmb_iov_from_buf(elem->in_sg, + elem->in_num, 0, resp, sizeof(*resp)); + if (len != sizeof(*resp)) { + g_critical("%s: response size incorrect %zu vs %zu", + __func__, len, sizeof(*resp)); + } else { + vu_queue_push(dev, vq, elem, len); + vu_queue_notify(dev, vq); + responded = true; + } + + g_free(resp); + } + } } } -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 14/19] tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_GET_WRITE_COUNTER
This is the first function with an implied response that doesn't need a VIRTIO_RPMB_REQ_RESULT_READ. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 9c98f6916f6f..88747c50fa44 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -121,8 +121,10 @@ typedef struct VuRpmb { int flash_fd; void *flash_map; uint8_t *key; + uint8_t last_nonce[16]; uint16_t last_result; uint16_t last_reqresp; + uint32_t write_count; } VuRpmb; /* refer to util/iov.c */ @@ -286,6 +288,42 @@ static void vrpmb_handle_program_key(VuDev *dev, struct virtio_rpmb_frame *frame return; } +/* + * vrpmb_handle_get_write_counter: + * + * We respond straight away with re-using the frame as sent. + */ +static struct virtio_rpmb_frame * +vrpmb_handle_get_write_counter(VuDev *dev, struct virtio_rpmb_frame *frame) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + struct virtio_rpmb_frame *resp = g_new0(struct virtio_rpmb_frame, 1); + + /* + * Run the checks from: + * 5.12.6.1.2 Device Requirements: Device Operation: Get Write Counter + */ + + resp->req_resp = htobe16(VIRTIO_RPMB_RESP_GET_COUNTER); + if (!r->key) { + g_debug("no key programmed"); + resp->result = htobe16(VIRTIO_RPMB_RES_NO_AUTH_KEY); + return resp; + } else if (be16toh(frame->block_count) > 1) { /* allow 0 (NONCONF) */ + g_debug("invalid block count (%d)", be16toh(frame->block_count)); + resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); + } else { + resp->write_counter = htobe32(r->write_count); + } + /* copy nonce */ + memcpy(&resp->nonce, &frame->nonce, sizeof(frame->nonce)); + + /* calculate MAC */ + vrpmb_update_mac_in_frame(r, resp); + + return resp; +} + /* * Return the result of the last message. This is only valid if the * previous message was VIRTIO_RPMB_REQ_PROGRAM_KEY or @@ -298,6 +336,9 @@ static struct virtio_rpmb_frame * vrpmb_handle_result_read(VuDev *dev) VuRpmb *r = container_of(dev, VuRpmb, dev.parent); struct virtio_rpmb_frame *resp = g_new0(struct virtio_rpmb_frame, 1); + g_info("%s: for request:%x result:%x", __func__, + r->last_reqresp, r->last_result); + if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY || r->last_reqresp == VIRTIO_RPMB_REQ_DATA_WRITE) { resp->result = htobe16(r->last_result); @@ -393,6 +434,9 @@ vrpmb_handle_ctrl(VuDev *dev, int qidx) case VIRTIO_RPMB_REQ_PROGRAM_KEY: vrpmb_handle_program_key(dev, f); break; + case VIRTIO_RPMB_REQ_GET_WRITE_COUNTER: + resp = vrpmb_handle_get_write_counter(dev, f); + break; case VIRTIO_RPMB_REQ_RESULT_READ: if (!responded) { resp = vrpmb_handle_result_read(dev); -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 15/19] tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_DATA_WRITE
With this command we are finally updating data to the backing store and cycling the write_count and each successful write. We also include the write count in all response frames as the spec is a little unclear but the example test code expected it. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 111 +++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 6 deletions(-) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 88747c50fa44..a17c3b4bcc4e 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -62,6 +62,7 @@ enum { #define KiB (1UL << 10) #define MAX_RPMB_SIZE (KiB * 128 * 256) #define RPMB_KEY_MAC_SIZE 32 +#define RPMB_BLOCK_SIZE 256 /* RPMB Request Types */ #define VIRTIO_RPMB_REQ_PROGRAM_KEY 0x0001 @@ -100,7 +101,7 @@ struct virtio_rpmb_config { struct virtio_rpmb_frame { uint8_t stuff[196]; uint8_t key_mac[RPMB_KEY_MAC_SIZE]; - uint8_t data[256]; + uint8_t data[RPMB_BLOCK_SIZE]; uint8_t nonce[16]; /* remaining fields are big-endian */ uint32_t write_counter; @@ -124,6 +125,7 @@ typedef struct VuRpmb { uint8_t last_nonce[16]; uint16_t last_result; uint16_t last_reqresp; + uint16_t last_address; uint32_t write_count; } VuRpmb; @@ -239,17 +241,30 @@ vrpmb_set_config(VuDev *dev, const uint8_t *data, * which itself uses a 3 clause BSD chunk of code. */ +static const int rpmb_frame_dlen = (sizeof(struct virtio_rpmb_frame) - + offsetof(struct virtio_rpmb_frame, data)); + static void vrpmb_update_mac_in_frame(VuRpmb *r, struct virtio_rpmb_frame *frm) { hmac_sha256_ctx ctx; - static const int dlen = (sizeof(struct virtio_rpmb_frame) - - offsetof(struct virtio_rpmb_frame, data)); hmac_sha256_init(&ctx, r->key, RPMB_KEY_MAC_SIZE); - hmac_sha256_update(&ctx, frm->data, dlen); + hmac_sha256_update(&ctx, frm->data, rpmb_frame_dlen); hmac_sha256_final(&ctx, &frm->key_mac[0], 32); } +static bool vrpmb_verify_mac_in_frame(VuRpmb *r, struct virtio_rpmb_frame *frm) +{ + hmac_sha256_ctx ctx; + uint8_t calculated_mac[RPMB_KEY_MAC_SIZE]; + + hmac_sha256_init(&ctx, r->key, RPMB_KEY_MAC_SIZE); + hmac_sha256_update(&ctx, frm->data, rpmb_frame_dlen); + hmac_sha256_final(&ctx, calculated_mac, RPMB_KEY_MAC_SIZE); + + return memcmp(calculated_mac, frm->key_mac, RPMB_KEY_MAC_SIZE) == 0; +} + /* * Handlers for individual control messages */ @@ -324,6 +339,82 @@ vrpmb_handle_get_write_counter(VuDev *dev, struct virtio_rpmb_frame *frame) return resp; } +/* + * vrpmb_handle_write: + * + * We will report the success/fail on receipt of + * VIRTIO_RPMB_REQ_RESULT_READ. Returns the number of extra frames + * processed in the request. + */ +static int vrpmb_handle_write(VuDev *dev, struct virtio_rpmb_frame *frame) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + int extra_frames = 0; + uint16_t block_count = be16toh(frame->block_count); + uint32_t write_counter = be32toh(frame->write_counter); + size_t offset; + + r->last_reqresp = VIRTIO_RPMB_RESP_DATA_WRITE; + r->last_address = be16toh(frame->address); + offset = r->last_address * RPMB_BLOCK_SIZE; + + /* + * Run the checks from: + * 5.12.6.1.3 Device Requirements: Device Operation: Data Write + */ + if (!r->key) { + g_warning("no key programmed"); + r->last_result = VIRTIO_RPMB_RES_NO_AUTH_KEY; + } else if (block_count == 0 || + block_count > r->virtio_config.max_wr_cnt) { + r->last_result = VIRTIO_RPMB_RES_GENERAL_FAILURE; + } else if (false /* what does an expired write counter mean? */) { + r->last_result = VIRTIO_RPMB_RES_WRITE_COUNTER_EXPIRED; + } else if (offset > (r->virtio_config.capacity * (128 * KiB))) { + r->last_result = VIRTIO_RPMB_RES_ADDR_FAILURE; + } else if (!vrpmb_verify_mac_in_frame(r, frame)) { + r->last_result = VIRTIO_RPMB_RES_AUTH_FAILURE; + } else if (write_counter != r->write_count) { + r->last_result = VIRTIO_RPMB_RES_COUNT_FAILURE; + } else { + int i; + /* At this point we have a valid authenticated write request + * so the counter can incremented and we can attempt to + * update the backing device. + */ + r->write_count++; + for (i = 0; i < block_count; i++) { + void *blk = r->flash_map + offset; + g_debug("%s: writing block %d", __func__, i); + if (mprotect(blk, RPMB_BLOCK_SIZE, PROT_WRITE) != 0) { + r->last_result = VIRTIO_RPMB_RES_WRITE_FAILURE; + break; + } + memcpy(blk, frame[i].data, RPMB_BLOCK_SIZE); + if (msync(blk, RPMB_BLOCK_SIZE, MS_SYNC) != 0) { + g_warning("%s: failed to sync update", __func__); + r->last_result = VIRTIO_RPMB_RES_WRITE_FAILURE; + break; + } + if (mprotect(blk, RPMB_BLOCK_SIZE, PROT_READ) != 0) { + g_warning("%s: failed to re-apply read protection", __func__); + r->last_result = VIRTIO_RPMB_RES_GENERAL_FAILURE; + break; + } + offset += RPMB_BLOCK_SIZE; + } + r->last_result = VIRTIO_RPMB_RES_OK; + extra_frames = i - 1; + } + + g_info("%s: %s (%x, %d extra frames processed), write_count=%d", __func__, + r->last_result == VIRTIO_RPMB_RES_OK ? "successful":"failed", + r->last_result, extra_frames, r->write_count); + + return extra_frames; +} + + /* * Return the result of the last message. This is only valid if the * previous message was VIRTIO_RPMB_REQ_PROGRAM_KEY or @@ -339,10 +430,14 @@ static struct virtio_rpmb_frame * vrpmb_handle_result_read(VuDev *dev) g_info("%s: for request:%x result:%x", __func__, r->last_reqresp, r->last_result); - if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY || - r->last_reqresp == VIRTIO_RPMB_REQ_DATA_WRITE) { + if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY) { resp->result = htobe16(r->last_result); resp->req_resp = htobe16(r->last_reqresp); + } else if (r->last_reqresp == VIRTIO_RPMB_RESP_DATA_WRITE) { + resp->result = htobe16(r->last_result); + resp->req_resp = htobe16(r->last_reqresp); + resp->write_counter = htobe32(r->write_count); + resp->address = htobe16(r->last_address); } else { resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); } @@ -445,6 +540,10 @@ vrpmb_handle_ctrl(VuDev *dev, int qidx) __func__); } break; + case VIRTIO_RPMB_REQ_DATA_WRITE: + /* we can have multiple blocks handled */ + n += vrpmb_handle_write(dev, f); + break; default: g_debug("un-handled request: %x", f->req_resp); break; -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 16/19] tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_DATA_READ
The read command is a lot simpler to implement. However the spec does specify you can only read a single block at a time so we limit it to that. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index a17c3b4bcc4e..49d4e00b24a9 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -414,6 +414,55 @@ static int vrpmb_handle_write(VuDev *dev, struct virtio_rpmb_frame *frame) return extra_frames; } +/* + * vrpmb_handle_read: + * + * Unlike the write operation we return a frame with the result of the + * read here. While the config specifies a maximum read count the spec + * is limited to a single read at a time. + */ +static struct virtio_rpmb_frame * +vrpmb_handle_read(VuDev *dev, struct virtio_rpmb_frame *frame) +{ + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); + size_t offset = be16toh(frame->address) * RPMB_BLOCK_SIZE; + uint16_t block_count = be16toh(frame->block_count); + struct virtio_rpmb_frame *resp = g_new0(struct virtio_rpmb_frame, 1); + + resp->req_resp = htobe16(VIRTIO_RPMB_RESP_DATA_READ); + resp->address = frame->address; + resp->block_count = htobe16(1); + + /* + * Run the checks from: + * 5.12.6.1.4 Device Requirements: Device Operation: Data Read + */ + if (!r->key) { + g_warning("no key programmed"); + resp->result = htobe16(VIRTIO_RPMB_RES_NO_AUTH_KEY); + } else if (block_count != 1) { + /* + * Despite the config the spec only allows for reading one + * block at a time: "If block count has not been set to 1 then + * VIRTIO_RPMB_RES_GENERAL_FAILURE SHOULD be responded as + * result." + */ + resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); + } else if (offset > (r->virtio_config.capacity * (128 * KiB))) { + resp->result = htobe16(VIRTIO_RPMB_RES_ADDR_FAILURE); + } else { + void *blk = r->flash_map + offset; + g_debug("%s: reading block from %p (%zu)", __func__, blk, offset); + memcpy(resp->data, blk, RPMB_BLOCK_SIZE); + resp->result = htobe16(VIRTIO_RPMB_RES_OK); + } + + /* Final housekeeping, copy nonce and calculate MAC */ + memcpy(&resp->nonce, &frame->nonce, sizeof(frame->nonce)); + vrpmb_update_mac_in_frame(r, resp); + + return resp; +} /* * Return the result of the last message. This is only valid if the @@ -544,6 +593,9 @@ vrpmb_handle_ctrl(VuDev *dev, int qidx) /* we can have multiple blocks handled */ n += vrpmb_handle_write(dev, f); break; + case VIRTIO_RPMB_REQ_DATA_READ: + resp = vrpmb_handle_read(dev, f); + break; default: g_debug("un-handled request: %x", f->req_resp); break; -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 17/19] tools/vhost-user-rpmb: add key persistence
Add support for persisting the key in --key-path. By default it will accept the program-key command and store the key in the key file. If you pass --key-set then the key is deemed to be programmed and can't be re-programmed. Obviously you will need some other mechanism to let the guest know what the key is so it can do other operations. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 49d4e00b24a9..34607ad19429 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -38,15 +38,19 @@ static gchar *socket_path; static char *flash_path; +static char *key_path; static gint socket_fd = -1; static gboolean print_cap; static gboolean verbose; static gboolean debug; +static gboolean key_set; static GOptionEntry options[] { { "socket-path", 0, 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" }, { "flash-path", 0, 0, G_OPTION_ARG_FILENAME, &flash_path, "Location of raw flash image file", "PATH" }, + { "key-path", 0, 0, G_OPTION_ARG_FILENAME, &key_path, "Location of persistent keyfile", "KEY"}, + { "key-set", 0, 0, G_OPTION_ARG_NONE, &key_set, "Is the key already programmed", NULL}, { "fd", 0, 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" }, { "print-capabilities", 0, 0, G_OPTION_ARG_NONE, &print_cap, "Output to stdout the backend capabilities in JSON format and exit", NULL}, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be more verbose in output", NULL}, @@ -296,8 +300,18 @@ static void vrpmb_handle_program_key(VuDev *dev, struct virtio_rpmb_frame *frame } else { r->key = g_memdup(&frame->key_mac[0], RPMB_KEY_MAC_SIZE); r->last_result = VIRTIO_RPMB_RES_OK; + if (key_path) { + GError *err = NULL; + if (!g_file_set_contents(key_path, (char *) r->key, + RPMB_KEY_MAC_SIZE, &err)) { + g_warning("%s: unable to persist key data to %s: %s", + __func__, key_path, err->message); + g_error_free(err); + } + } } + g_info("%s: req_resp = %x, result = %x", __func__, r->last_reqresp, r->last_result); return; @@ -709,6 +723,25 @@ static bool vrpmb_load_flash_image(VuRpmb *r, char *img_path) return true; } +static void vrpmb_set_key(VuRpmb *r, char *key_path) +{ + GError *err = NULL; + gsize length; + + if (!g_file_get_contents(key_path, (char **) &r->key, &length, &err)) { + g_print("Unable to read %s: %s", key_path, err->message); + exit(1); + } + if (length < RPMB_KEY_MAC_SIZE) { + g_print("key file to small %ld < %d", length, RPMB_KEY_MAC_SIZE); + exit(1); + } else if (length > RPMB_KEY_MAC_SIZE) { + /* being too big isn't fatal, we just ignore the excess */ + g_warning("%ld bytes of %s ignore (file too big)", + length - RPMB_KEY_MAC_SIZE, key_path); + } +} + static void vrpmb_destroy(VuRpmb *r) { vug_deinit(&r->dev); @@ -760,6 +793,10 @@ int main(int argc, char *argv[]) vrpmb_load_flash_image(&rpmb, flash_path); } + if (key_path && key_set) { + vrpmb_set_key(&rpmb, key_path); + } + if (!socket_path && socket_fd < 0) { g_printerr("Please specify either --fd or --socket-path\n"); exit(EXIT_FAILURE); -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 18/19] tools/vhost-user-rpmb: allow setting of the write_count
This is mostly useful for testing. Practically all guest operations will probe the write count before any write transaction. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- tools/vhost-user-rpmb/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c index 34607ad19429..c798a6f66cd2 100644 --- a/tools/vhost-user-rpmb/main.c +++ b/tools/vhost-user-rpmb/main.c @@ -39,6 +39,7 @@ static gchar *socket_path; static char *flash_path; static char *key_path; +static gint initial_counter; static gint socket_fd = -1; static gboolean print_cap; static gboolean verbose; @@ -49,6 +50,7 @@ static GOptionEntry options[] { { "socket-path", 0, 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" }, { "flash-path", 0, 0, G_OPTION_ARG_FILENAME, &flash_path, "Location of raw flash image file", "PATH" }, + { "initial-counter", 0, 0, G_OPTION_ARG_INT, &initial_counter, "Set initial value of write counter", NULL}, { "key-path", 0, 0, G_OPTION_ARG_FILENAME, &key_path, "Location of persistent keyfile", "KEY"}, { "key-set", 0, 0, G_OPTION_ARG_NONE, &key_set, "Is the key already programmed", NULL}, { "fd", 0, 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" }, @@ -797,6 +799,10 @@ int main(int argc, char *argv[]) vrpmb_set_key(&rpmb, key_path); } + if (initial_counter) { + rpmb.write_count = initial_counter; + } + if (!socket_path && socket_fd < 0) { g_printerr("Please specify either --fd or --socket-path\n"); exit(EXIT_FAILURE); -- 2.20.1
Alex Bennée
2020-Sep-25 12:51 UTC
[RFC PATCH 19/19] docs: add a man page for vhost-user-rpmb
Basic usage and example invocation. Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> --- docs/tools/index.rst | 1 + docs/tools/vhost-user-rpmb.rst | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 docs/tools/vhost-user-rpmb.rst diff --git a/docs/tools/index.rst b/docs/tools/index.rst index 232ce9f3e463..488784b3bf1f 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -15,3 +15,4 @@ Contents: qemu-trace-stap virtfs-proxy-helper virtiofsd + vhost-user-rpmb diff --git a/docs/tools/vhost-user-rpmb.rst b/docs/tools/vhost-user-rpmb.rst new file mode 100644 index 000000000000..40d5d3bceb7c --- /dev/null +++ b/docs/tools/vhost-user-rpmb.rst @@ -0,0 +1,102 @@ +QEMU vhost-user-rpmb - rpmb emulation backend +============================================+ +Synopsis +-------- + +**vhost-user-rpmb** [*OPTIONS*] + +Description +----------- + +This program is a vhost-user backed that emulates a VirtIO Replay +Protected Memory Block device. These are usually special partitions +that are part of a flash device that offer protection against reply +attacks. They are used to store secure information in a way that is +hard to tamper with. + +This program is designed to work with QEMU's ``--device +vhost-user-rpmb-pci`` but should work with any virtual machine +monitor (VMM) that supports vhost-user. See the Examples section +below. + +This program requires a backing store to persist any data programmed +into the device. The spec supports devices up 32Mb in size. For the +daemon this is simply a raw file of the appropriate size. To program +the device it needs to have a key. This can either be programmed by +the guest at the start or come from a key file supplied to the daemon. + +Options +------- + +.. program:: vhost-user-rpmb + +.. option:: -h, --help + + Print help. + +.. option:: -V, --version + + Print version. + +.. option:: -v, --verbose + + Increase verbosity of output + +.. option:: --debug + + Enable debug output. + +.. option:: --socket-path=PATH + + Listen on vhost-user UNIX domain socket at PATH. Incompatible with --fd. + +.. option:: --fd=FDNUM + + Accept connections from vhost-user UNIX domain socket file descriptor FDNUM. + The file descriptor must already be listening for connections. + Incompatible with --socket-path. + +.. option:: --flash-path=PATH + + Path to the backing store for the flash image, can be up to 32Mb in size. + +.. option:: --key-path=PATH + + Path to the backing store for the key of 32 bytes. + +.. option:: --key-set + + Treat the value of key-path as set meaning the key cannot be + reprogrammed by the guest. + +.. option:: --initial-counter=N + + Set the initial value of the devices write count. It is + incremented by each write operation. + +Examples +-------- + +The daemon should be started first: + +:: + + host# vhost-user-rpmb --socket-path=vrpmb.sock \ + --flash-path=flash.img \ + --key-path=key --key-set \ + --initial-counter=1234 + +The QEMU invocation needs to create a chardev socket the device can +use to communicate as well as share the guests memory over a memfd. + +:: + + host# qemu-system \ + -chardev socket,path=vrpmb.sock,id=vrpmb \ + -device vhost-user-rpmb-pci,chardev=vrpmb,id=rpmb \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + -numa node,memdev=mem \ + ... + -- 2.20.1
Alex Bennée
2020-Sep-28 14:56 UTC
[RFC PATCH 15/19] tools/vhost-user-rpmb: implement VIRTIO_RPMB_REQ_DATA_WRITE
Joakim Bech <joakim.bech at linaro.org> writes:> On Fri, Sep 25, 2020 at 01:51:43PM +0100, Alex Benn?e wrote: >> With this command we are finally updating data to the backing store >> and cycling the write_count and each successful write. We also include >> the write count in all response frames as the spec is a little unclear >> but the example test code expected it. >> >> Signed-off-by: Alex Benn?e <alex.bennee at linaro.org> >> --- >> tools/vhost-user-rpmb/main.c | 111 +++++++++++++++++++++++++++++++++-- >> 1 file changed, 105 insertions(+), 6 deletions(-) >> >> diff --git a/tools/vhost-user-rpmb/main.c b/tools/vhost-user-rpmb/main.c >> index 88747c50fa44..a17c3b4bcc4e 100644 >> --- a/tools/vhost-user-rpmb/main.c >> +++ b/tools/vhost-user-rpmb/main.c >> @@ -62,6 +62,7 @@ enum { >> #define KiB (1UL << 10) >> #define MAX_RPMB_SIZE (KiB * 128 * 256) >> #define RPMB_KEY_MAC_SIZE 32 >> +#define RPMB_BLOCK_SIZE 256 >> >> /* RPMB Request Types */ >> #define VIRTIO_RPMB_REQ_PROGRAM_KEY 0x0001 >> @@ -100,7 +101,7 @@ struct virtio_rpmb_config { >> struct virtio_rpmb_frame { >> uint8_t stuff[196]; >> uint8_t key_mac[RPMB_KEY_MAC_SIZE]; >> - uint8_t data[256]; >> + uint8_t data[RPMB_BLOCK_SIZE]; >> uint8_t nonce[16]; >> /* remaining fields are big-endian */ >> uint32_t write_counter; >> @@ -124,6 +125,7 @@ typedef struct VuRpmb { >> uint8_t last_nonce[16]; >> uint16_t last_result; >> uint16_t last_reqresp; >> + uint16_t last_address; >> uint32_t write_count; >> } VuRpmb; >> >> @@ -239,17 +241,30 @@ vrpmb_set_config(VuDev *dev, const uint8_t *data, >> * which itself uses a 3 clause BSD chunk of code. >> */ >> >> +static const int rpmb_frame_dlen = (sizeof(struct virtio_rpmb_frame) - >> + offsetof(struct virtio_rpmb_frame, data)); >> + >> static void vrpmb_update_mac_in_frame(VuRpmb *r, struct virtio_rpmb_frame *frm) >> { >> hmac_sha256_ctx ctx; >> - static const int dlen = (sizeof(struct virtio_rpmb_frame) - >> - offsetof(struct virtio_rpmb_frame, data)); >> >> hmac_sha256_init(&ctx, r->key, RPMB_KEY_MAC_SIZE); >> - hmac_sha256_update(&ctx, frm->data, dlen); >> + hmac_sha256_update(&ctx, frm->data, rpmb_frame_dlen); >> hmac_sha256_final(&ctx, &frm->key_mac[0], 32); >> } >> >> +static bool vrpmb_verify_mac_in_frame(VuRpmb *r, struct virtio_rpmb_frame *frm) >> +{ >> + hmac_sha256_ctx ctx; >> + uint8_t calculated_mac[RPMB_KEY_MAC_SIZE]; >> + >> + hmac_sha256_init(&ctx, r->key, RPMB_KEY_MAC_SIZE); >> + hmac_sha256_update(&ctx, frm->data, rpmb_frame_dlen); >> + hmac_sha256_final(&ctx, calculated_mac, RPMB_KEY_MAC_SIZE); >> + >> + return memcmp(calculated_mac, frm->key_mac, RPMB_KEY_MAC_SIZE) == 0; >> > I'd prefer using a constant time comparison function for this one > instead of memcmp (regardless if it's used for simulation or not) to > prevent eventual timing attacks.Could you recommend such a function for this?> >> +} >> + >> /* >> * Handlers for individual control messages >> */ >> @@ -324,6 +339,82 @@ vrpmb_handle_get_write_counter(VuDev *dev, struct virtio_rpmb_frame *frame) >> return resp; >> } >> >> +/* >> + * vrpmb_handle_write: >> + * >> + * We will report the success/fail on receipt of >> + * VIRTIO_RPMB_REQ_RESULT_READ. Returns the number of extra frames >> + * processed in the request. >> + */ >> +static int vrpmb_handle_write(VuDev *dev, struct virtio_rpmb_frame *frame) >> +{ >> + VuRpmb *r = container_of(dev, VuRpmb, dev.parent); >> + int extra_frames = 0; >> + uint16_t block_count = be16toh(frame->block_count); >> + uint32_t write_counter = be32toh(frame->write_counter); >> + size_t offset; >> + >> + r->last_reqresp = VIRTIO_RPMB_RESP_DATA_WRITE; >> + r->last_address = be16toh(frame->address); >> + offset = r->last_address * RPMB_BLOCK_SIZE; >> + >> + /* >> + * Run the checks from: >> + * 5.12.6.1.3 Device Requirements: Device Operation: Data Write >> + */ >> + if (!r->key) { >> + g_warning("no key programmed"); >> + r->last_result = VIRTIO_RPMB_RES_NO_AUTH_KEY; >> + } else if (block_count == 0 || >> + block_count > r->virtio_config.max_wr_cnt) { >> + r->last_result = VIRTIO_RPMB_RES_GENERAL_FAILURE; >> + } else if (false /* what does an expired write counter mean? */) { >> > IIRC, the counter has room for a 32-bit value and the counter will never > wrap around. So once the counter have reached max for uint32_t, then > there is an additional bit set (permanently) in the operation result. > I.e., writes are no longer possible once that has happened. That is > probably what you're referring to here I suppose.That would make sense. I'll see if I can make the spec language a bit clearer as well.> >> + r->last_result = VIRTIO_RPMB_RES_WRITE_COUNTER_EXPIRED; >> + } else if (offset > (r->virtio_config.capacity * (128 * KiB))) { >> + r->last_result = VIRTIO_RPMB_RES_ADDR_FAILURE; >> + } else if (!vrpmb_verify_mac_in_frame(r, frame)) { >> + r->last_result = VIRTIO_RPMB_RES_AUTH_FAILURE; >> + } else if (write_counter != r->write_count) { >> + r->last_result = VIRTIO_RPMB_RES_COUNT_FAILURE; >> + } else { >> + int i; >> + /* At this point we have a valid authenticated write request >> + * so the counter can incremented and we can attempt to >> + * update the backing device. >> + */ >> + r->write_count++; >> + for (i = 0; i < block_count; i++) { >> + void *blk = r->flash_map + offset; >> + g_debug("%s: writing block %d", __func__, i); >> + if (mprotect(blk, RPMB_BLOCK_SIZE, PROT_WRITE) != 0) { >> + r->last_result = VIRTIO_RPMB_RES_WRITE_FAILURE; >> + break; >> + } >> + memcpy(blk, frame[i].data, RPMB_BLOCK_SIZE); >> + if (msync(blk, RPMB_BLOCK_SIZE, MS_SYNC) != 0) { >> + g_warning("%s: failed to sync update", __func__); >> + r->last_result = VIRTIO_RPMB_RES_WRITE_FAILURE; >> + break; >> + } >> + if (mprotect(blk, RPMB_BLOCK_SIZE, PROT_READ) != 0) { >> + g_warning("%s: failed to re-apply read protection", __func__); >> + r->last_result = VIRTIO_RPMB_RES_GENERAL_FAILURE; >> + break; >> + } >> + offset += RPMB_BLOCK_SIZE; >> + } >> + r->last_result = VIRTIO_RPMB_RES_OK; >> + extra_frames = i - 1; >> + } >> + >> + g_info("%s: %s (%x, %d extra frames processed), write_count=%d", __func__, >> + r->last_result == VIRTIO_RPMB_RES_OK ? "successful":"failed", >> + r->last_result, extra_frames, r->write_count); >> + >> + return extra_frames; >> +} >> + >> + >> /* >> * Return the result of the last message. This is only valid if the >> * previous message was VIRTIO_RPMB_REQ_PROGRAM_KEY or >> @@ -339,10 +430,14 @@ static struct virtio_rpmb_frame * vrpmb_handle_result_read(VuDev *dev) >> g_info("%s: for request:%x result:%x", __func__, >> r->last_reqresp, r->last_result); >> >> - if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY || >> - r->last_reqresp == VIRTIO_RPMB_REQ_DATA_WRITE) { >> + if (r->last_reqresp == VIRTIO_RPMB_RESP_PROGRAM_KEY) { >> resp->result = htobe16(r->last_result); >> resp->req_resp = htobe16(r->last_reqresp); >> + } else if (r->last_reqresp == VIRTIO_RPMB_RESP_DATA_WRITE) { >> + resp->result = htobe16(r->last_result); >> + resp->req_resp = htobe16(r->last_reqresp); >> + resp->write_counter = htobe32(r->write_count); >> + resp->address = htobe16(r->last_address); >> } else { >> resp->result = htobe16(VIRTIO_RPMB_RES_GENERAL_FAILURE); >> } >> @@ -445,6 +540,10 @@ vrpmb_handle_ctrl(VuDev *dev, int qidx) >> __func__); >> } >> break; >> + case VIRTIO_RPMB_REQ_DATA_WRITE: >> + /* we can have multiple blocks handled */ >> + n += vrpmb_handle_write(dev, f); >> + break; >> default: >> g_debug("un-handled request: %x", f->req_resp); >> break; >> -- >> 2.20.1 >>Thanks! -- Alex Benn?e