diff options
author | Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> | 2022-11-04 15:32:11 +0100 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-03 15:18:54 +0300 |
commit | a3fcee5911bf760f9f4522e94cb9e6ab22a7eb95 (patch) | |
tree | 9bc175084b6bc1da48651fdc3767567e2a54c22e | |
parent | 2b09c69cc896841c7828408083f6cb443fef8612 (diff) |
Virtio-loopback-adapter Beta version for review:
- Beta version of the code to be discussed during the review planned on the 09/11/2022
- Before being merged into master, this code might be changed with fixes and optimization
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | adapter.c | 50 | ||||
-rw-r--r-- | queue.h | 142 | ||||
-rw-r--r-- | vhost_loopback.c | 91 | ||||
-rw-r--r-- | vhost_loopback.h | 35 | ||||
-rw-r--r-- | vhost_user_blk.c | 514 | ||||
-rw-r--r-- | vhost_user_blk.h | 59 | ||||
-rw-r--r-- | vhost_user_input.c | 224 | ||||
-rw-r--r-- | vhost_user_input.h | 187 | ||||
-rw-r--r-- | vhost_user_loopback.c | 246 | ||||
-rw-r--r-- | vhost_user_loopback.h | 19 | ||||
-rw-r--r-- | virtio_blk.h | 95 | ||||
-rw-r--r-- | virtio_input.c | 284 | ||||
-rw-r--r-- | virtio_loopback.c | 263 | ||||
-rw-r--r-- | virtio_loopback.h | 19 | ||||
-rw-r--r-- | virtio_rng.h | 8 |
16 files changed, 2220 insertions, 35 deletions
@@ -21,7 +21,7 @@ #CFLAGS := -Wall -Wextra -Werror #CFLAGS := -Wall -Wextra -Wno-unused-variable -Wno-unused-function CFLAGS := -Wno-unused-variable -Wno-unused-function -CFLAGS += -DSERVER +CFLAGS = CC = ifeq ($(ARCH), arm64) @@ -33,11 +33,26 @@ endif ifeq ($(VHOST_USER_RNG), 1) CFLAGS += -DVHOST_USER_RNG_DEV + CFLAGS += -DVHOST_USER +endif + +ifeq ($(VHOST_USER_BLK), 1) + CFLAGS += -DVHOST_USER_BLK_DEV + CFLAGS += -DVHOST_USER +endif + +ifeq ($(VHOST_USER_INPUT), 1) + CFLAGS += -DVHOST_USER_INPUT_DEV + CFLAGS += -DVHOST_USER +endif + +ifeq ($(VIRTIO_RNG), 1) + CFLAGS += -DVIRTIO_RNG endif INCL += -I . DEPS = adapter.h vhost_user_loopback.h event_notifier.h virtio_loopback.h -SRC_C = event_notifier.c vhost_user_loopback.c virtio_loopback.c virtio_rng.c vhost_user_rng.c vhost_loopback.c adapter.c +SRC_C = event_notifier.c vhost_user_loopback.c virtio_loopback.c virtio_rng.c virtio_input.c vhost_user_input.c vhost_user_blk.c vhost_user_rng.c vhost_loopback.c adapter.c OBJS = $(SRC_C:.c=.o) BINS = adapter @@ -44,6 +44,16 @@ #include "vhost_user_loopback.h" #include "virtio_rng.h" #include "vhost_user_rng.h" +#include "vhost_user_blk.h" +#include "vhost_user_input.h" + + +#ifdef DEBUG +#define DBG(...) printf("adapter: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + /* Global variables */ int client_sock; @@ -54,6 +64,9 @@ struct vhost_user *vudev; void vhost_user_adapter_init(void) { + + DBG("Setup adapter data structures\n"); + /* Init vhost-user device */ vudev = (struct vhost_user *)malloc(sizeof(struct vhost_user)); @@ -85,6 +98,8 @@ void client(char *sock_path) int rc, len; struct sockaddr_un client_sockaddr; + DBG("Create shared socket with vhost-user-device\n"); + /* Initialize the struct to zero */ memset(&client_sockaddr, 0, sizeof(struct sockaddr_un)); @@ -93,7 +108,7 @@ void client(char *sock_path) */ client_sock = socket(AF_UNIX, SOCK_STREAM, 0); if (client_sock == -1) { - printf("SOCKET ERROR\n"); + DBG("SOCKET ERROR\n"); exit(1); } @@ -107,7 +122,7 @@ void client(char *sock_path) len = sizeof(client_sockaddr); rc = connect(client_sock, (struct sockaddr *) &client_sockaddr, len); if (rc == -1) { - printf("CONNECT ERROR\n"); + DBG("CONNECT ERROR\n"); close(client_sock); exit(1); } @@ -121,7 +136,7 @@ static void help_args(void) int main(int argc, char **argv) { -#ifdef VHOST_USER_RNG_DEV +#ifdef VHOST_USER /* * Check if the user has provided a socket path. * If not, print the help messages. @@ -140,14 +155,37 @@ int main(int argc, char **argv) /* Initialize the adapter data structures */ vhost_user_adapter_init(); + /* Initialize the virtio/vhost-user device */ +#ifdef VHOST_USER + +#ifdef VHOST_USER_INPUT_DEV + vhost_user_input_init(global_vdev); /* <-- Enable that for vhost-user-rng */ + virtio_input_device_realize(); +#endif /* VHOST_USER_INPUT_DEV */ + +#ifdef VHOST_USER_BLK_DEV + vhost_user_blk_realize(); /* <-- Enable that for vhost-user-blk */ +#endif /* VHOST_USER_BLK_DEV */ + #ifdef VHOST_USER_RNG_DEV vhost_user_rng_realize(); /* <-- Enable that for vhost-user-rng */ -#else +#endif /* VHOST_USER_RNG_DEV */ + +#else /* VHOST_USER */ + +#ifdef VIRTIO_RNG virtio_rng_realize(); /* <-- Enable that for simple rng */ -#endif +#else /* VIRTIO_RNG */ + DBG("You have not defined any device\n"); + exit(1); +#endif /* VIRTIO_RNG */ + +#endif /* VHOST_USER */ - /* Start loopback trasnport layer and communiation with the loopback driver */ + /* + * Start loopback trasnport layer and communiation with the loopback driver + */ virtio_loopback_start(); return 0; @@ -0,0 +1,142 @@ +/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ + +/* + * QEMU version: Copy from netbsd, removed debug code, removed some of + * the implementations. Left in singly-linked lists, lists, simple + * queues, and tail queues. + */ + +/* + * Based on queue.h of QEMU project + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef QEMU_SYS_QUEUE_H +#define QEMU_SYS_QUEUE_H + +/* + * This file defines four types of data structures: singly-linked lists, + * lists, simple queues, and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + + +typedef struct QTailQLink { + void *tql_next; + struct QTailQLink *tql_prev; +} QTailQLink; + +/* + * Tail queue definitions. The union acts as a poor man template, as if + * it were QTailQLink<type>. + */ +#define QTAILQ_HEAD(name, type) \ +union name { \ + struct type *tqh_first; \ + QTailQLink tqh_circ; \ +} + +#define QTAILQ_HEAD_INITIALIZER(head) \ + { .tqh_circ = { NULL, &(head).tqh_circ } } + +#define QTAILQ_ENTRY(type) \ +union { \ + struct type *tqe_next; \ + QTailQLink tqe_circ; \ +} + +/* + * Tail queue functions. + */ +#define QTAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_circ.tql_prev = &(head)->tqh_circ; \ +} while (0) + +#define QTAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ + (head)->tqh_circ.tql_prev->tql_next = (elm); \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ +} while (0) + + +#define QTAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +/* + * Tail queue access methods. + */ +#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define QTAILQ_FIRST(head) ((head)->tqh_first) +#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define field_at_offset(base, offset, type) \ + ((type *) (((char *) (base)) + (offset))) + + +#endif /* QEMU_SYS_QUEUE_H */ diff --git a/vhost_loopback.c b/vhost_loopback.c index 95ac21e..aed1cc5 100644 --- a/vhost_loopback.c +++ b/vhost_loopback.c @@ -70,6 +70,8 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r, e; + DBG("vhost_dev_enable_notifiers(...)\n"); + /* * We will pass the notifiers to the kernel, make sure that QEMU * doesn't interfere. @@ -178,6 +180,8 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, } file.index = vhost_user_get_vq_index(hdev, n); + DBG("vhost_virtqueue_mask -> index: %d, n: %d, file.fd: %d\n", + index, n, file.fd); r = vhost_user_set_vring_call(&file); if (r < 0) { @@ -194,6 +198,8 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, uint64_t s, l, a; int r; + DBG("vhost_virtqueue_start()\n"); + int vhost_vq_index = vhost_user_get_vq_index(dev, idx); struct vhost_vring_file file = { .index = vhost_vq_index @@ -278,6 +284,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, vhost_virtqueue_mask(dev, vdev, idx, false); } + DBG("vhost_virtqueue_start return successfully\n"); return 0; } @@ -289,6 +296,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->started = true; hdev->vdev = vdev; + DBG("vhost_dev_start()\n"); + r = vhost_dev_set_features(hdev, hdev->log_enabled); if (r < 0) { return r; @@ -311,6 +320,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) /* This is used to exhange the loopback_fd to the vhost-user-device */ vhost_user_share_fd(); + DBG("hdev->nvqs: %d\n", hdev->nvqs); + for (i = 0; i < hdev->nvqs; ++i) { r = vhost_virtqueue_start(hdev, vdev, @@ -322,5 +333,85 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } } + DBG("vhost_dev_start return successfully\n"); + return 0; } + + +int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config, + uint32_t config_len) +{ + DBG("vhost_dev_get_config(...)\n"); + + return vhost_user_get_config(hdev, config, config_len); +} + +int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + DBG("vhost_dev_set_config(...)\n"); + return vhost_user_set_config(hdev, data, offset, size, flags); + +} + +void vhost_dev_set_config_notifier(struct vhost_dev *hdev, + const VhostDevConfigOps *ops) +{ + DBG("vhost_dev_set_config_notifier(...)\n"); + hdev->config_ops = ops; +} + +int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + int r; + + /* + * TODO: Check if we need that + * if (hdev->vhost_ops->vhost_get_inflight_fd == NULL || + * hdev->vhost_ops->vhost_set_inflight_fd == NULL) { + * return 0; + * } + */ + + hdev->vdev = vdev; + + r = vhost_dev_set_features(hdev, hdev->log_enabled); + if (r < 0) { + DBG("vhost_dev_prepare_inflight failed\n"); + return r; + } + + return 0; +} + +int vhost_dev_set_inflight(struct vhost_dev *dev, + struct vhost_inflight *inflight) +{ + int r; + + if (inflight->addr) { + r = vhost_user_set_inflight_fd(dev, inflight); + if (r) { + DBG("vhost_set_inflight_fd failed\n"); + return -1; + } + } + + return 0; +} + +int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, + struct vhost_inflight *inflight) +{ + int r; + + r = vhost_user_get_inflight_fd(dev, queue_size, inflight); + if (r) { + DBG("vhost_get_inflight_fd failed\n"); + return -1; + } + + return 0; +} + diff --git a/vhost_loopback.h b/vhost_loopback.h index f63a819..69a3f0d 100644 --- a/vhost_loopback.h +++ b/vhost_loopback.h @@ -1,4 +1,6 @@ /* + * Based on vhost.h of QEMU project + * * Copyright 2022 Virtual Open Systems SAS. * * This program is free software; you can redistribute it and/or modify @@ -31,5 +33,38 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask); +int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config, + uint32_t config_len); +int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags); +/** + * vhost_dev_set_config_notifier() - register VhostDevConfigOps + * @hdev: common vhost_dev_structure + * @ops: notifier ops + * + * If the device is expected to change configuration a notifier can be + * setup to handle the case. + */ + +typedef struct VhostDevConfigOps VhostDevConfigOps; + +void vhost_dev_set_config_notifier(struct vhost_dev *dev, + const VhostDevConfigOps *ops); +int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev); + +int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, + struct vhost_inflight *inflight); + +int vhost_dev_set_inflight(struct vhost_dev *dev, + struct vhost_inflight *inflight); + + +struct vhost_inflight { + int fd; + void *addr; + uint64_t size; + uint64_t offset; + uint16_t queue_size; +}; #endif /* LOOPBACK_VHOST_H */ diff --git a/vhost_user_blk.c b/vhost_user_blk.c new file mode 100644 index 0000000..eebd599 --- /dev/null +++ b/vhost_user_blk.c @@ -0,0 +1,514 @@ +/* + * Based on vhost-user-blk.c of QEMU project + * + * Copyright(C) 2017 Intel Corporation. + * + * Authors: + * Changpeng Liu <changpeng.liu@intel.com> + * + * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by: + * Felipe Franciosi <felipe@nutanix.com> + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> + * Nicholas Bellinger <nab@risingtidesystems.com> + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <sys/param.h> + +/* Project header files */ +#include "vhost_user_blk.h" + +#ifdef DEBUG +#define DBG(...) printf("vhost-user-blk: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + + +#define REALIZE_CONNECTION_RETRIES 3 + +static int vhost_user_blk_start(VirtIODevice *vdev) +{ + VHostUserBlk *s = vdev->vhublk; + VirtioBus *k = vdev->vbus; + int i, ret; + + DBG("vhost_user_blk_start\n"); + + if (!k->set_guest_notifiers) { + DBG("binding does not support guest notifiers\n"); + return -1; + } + + ret = vhost_dev_enable_notifiers(s->vhost_dev, vdev); + if (ret < 0) { + DBG("Error enabling host notifiers\n"); + return ret; + } + + ret = k->set_guest_notifiers(k->vdev, s->vhost_dev->nvqs, true); + if (ret < 0) { + DBG("Error enabling host notifier\n"); + return ret; + } + + s->vhost_dev->acked_features = vdev->guest_features; + + /* FIXME: We might do not need that */ + ret = vhost_dev_prepare_inflight(s->vhost_dev, vdev); + if (ret < 0) { + DBG("Error setting inflight format\n"); + return ret; + } + + if (!s->inflight->addr) { + ret = vhost_dev_get_inflight(s->vhost_dev, s->queue_size, s->inflight); + if (ret < 0) { + DBG("Error getting inflight\n"); + return ret; + } + } + + ret = vhost_dev_set_inflight(s->vhost_dev, s->inflight); + if (ret < 0) { + DBG("Error setting inflight\n"); + return ret; + } + + DBG("After vhost_dev_set_inflight\n"); + + ret = vhost_dev_start(s->vhost_dev, vdev); + if (ret < 0) { + DBG("Error starting vhost\n"); + return ret; + } + s->started_vu = true; + + DBG("vhost_virtqueue_mask\n"); + /* + * 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 < s->vhost_dev->nvqs; i++) { + vhost_virtqueue_mask(s->vhost_dev, vdev, i, false); + } + + DBG("vhost_user_blk_start return successfully: %d\n", ret); + return ret; + +} + +static void vhost_user_blk_stop(VirtIODevice *vdev) +{ + DBG("Not yet implemented\n"); +} + +static int vhost_user_blk_handle_config_change(struct vhost_dev *dev) +{ + int ret; + struct virtio_blk_config blkcfg; + VHostUserBlk *s = dev->vdev->vhublk; + + DBG("vhost_user_blk_handle_config_change(...)\n"); + + ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg, + sizeof(struct virtio_blk_config)); + if (ret < 0) { + DBG("vhost_dev_get_config\n"); + return ret; + } + + /* valid for resize only */ + if (blkcfg.capacity != s->blkcfg.capacity) { + DBG("blkcfg.capacity != s->blkcfg.capacity\n"); + s->blkcfg.capacity = blkcfg.capacity; + memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config)); + DBG("To virtio_notify_config\n"); + virtio_notify_config(dev->vdev); + } + + return 0; +} + + +const VhostDevConfigOps blk_ops = { + .vhost_dev_config_notifier = vhost_user_blk_handle_config_change, +}; + + +static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, + uint64_t features) +{ + VHostUserBlk *s = vdev->vhublk; + + DBG("vhost_user_blk_get_features()\n"); + + /* Turn on pre-defined features */ + virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); + virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); + virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); + virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); + virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); + virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); + /* + * TODO: Delete if not needed + * virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); + */ + + /* + * The next line makes the blk read only + * + * virtio_add_feature(&features, VIRTIO_BLK_F_RO); + * + */ + + if (s->num_queues > 1) { + virtio_add_feature(&features, VIRTIO_BLK_F_MQ); + } + + return features; +} + +static int vhost_user_blk_connect(VirtIODevice *vdev) +{ + VHostUserBlk *s = vdev->vhublk; + int ret = 0; + + DBG("vhost_user_blk_connect(...)\n"); + + if (s->connected) { + DBG("s->connected\n"); + return 0; + } + s->connected = true; + s->vhost_dev->num_queues = s->num_queues; + s->vhost_dev->nvqs = s->num_queues; + s->vhost_dev->vqs = s->vhost_vqs; + s->vhost_dev->vq_index = 0; + s->vhost_dev->backend_features = 0; + + vhost_dev_set_config_notifier(s->vhost_dev, &blk_ops); + + vhost_dev_init(s->vhost_dev); + + /* Pass the new obtained features */ + global_vdev->host_features = global_vdev->vhublk->vhost_dev->features; + + /* Disable VIRTIO_RING_F_INDIRECT_DESC, to be supported in future release */ + global_vdev->host_features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC); + + DBG("After init global_vdev->host_features: 0x%lx\n", + global_vdev->host_features); + + /* Restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + ret = vhost_user_blk_start(vdev); + if (ret < 0) { + DBG("vhost_user_blk_start failed\n"); + return ret; + } + } + + DBG("vhost_user_blk_connect return successfully!\n"); + + return 0; +} + +static void vhost_user_blk_disconnect(VirtIODevice *dev) +{ + DBG("vhost_user_blk_disconnect not yet implemented\n"); +} + +static void vhost_user_blk_chr_closed_bh(void *opaque) +{ + DBG("vhost_user_blk_chr_closed_bh not yet implemented\n"); +} + +static void vhost_user_blk_event(void *opaque) +{ + DBG("vhost_user_blk_event not yet implemented"); +} + +static int vhost_user_blk_realize_connect(VHostUserBlk *s) +{ + int ret; + + DBG("vhost_user_blk_realize_connect(...)\n"); + s->connected = false; + + DBG("s->vdev: 0x%lx\n", (uint64_t)s->parent); + DBG("global_vdev: 0x%lx\n", (uint64_t)global_vdev); + ret = vhost_user_blk_connect(s->parent); + if (ret < 0) { + DBG("vhost_user_blk_connect failed\n"); + return ret; + } + DBG("s->connected: %d\n", s->connected); + + ret = vhost_dev_get_config(s->vhost_dev, (uint8_t *)&s->blkcfg, + sizeof(struct virtio_blk_config)); + if (ret < 0) { + DBG("vhost_dev_get_config failed\n"); + return ret; + } + + return 0; +} + + +static void vhost_user_blk_device_unrealize(VirtIODevice *vdev) +{ + DBG("vhost_user_blk_device_unrealize not yet implemented\n"); +} + +static void vhost_user_blk_reset(VirtIODevice *vdev) +{ + DBG("vhost_user_blk_reset not yet implemented\n"); +} + +static void vhost_user_blk_set_config(VirtIODevice *vdev, + const uint8_t *config); + + +static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config) +{ + VHostUserBlk *s = vdev->vhublk; + + DBG("vhost_user_blk_update_config(...)\n"); + + /* Our num_queues overrides the device backend */ + memcpy(&s->blkcfg.num_queues, &s->num_queues, sizeof(uint64_t)); + + memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config)); +} + +static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VHostUserBlk *s = vdev->vhublk; + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; + int ret; + + DBG("vhost_user_blk_set_config(...)\n"); + + + /* + * TODO: Disabled for the current release + * if (blkcfg->wce == s->blkcfg.wce) { + * DBG("blkcfg->wce == s->blkcfg.wce\n"); + * return; + * } + */ + + ret = vhost_dev_set_config(s->vhost_dev, &blkcfg->wce, + offsetof(struct virtio_blk_config, wce), + sizeof(blkcfg->wce), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + DBG("set device config space failed\n"); + return; + } + + s->blkcfg.wce = blkcfg->wce; +} + + +static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserBlk *s = vdev->vhublk; + /* Just for testing: bool should_start = true; */ + bool should_start = virtio_device_started(vdev, status); + int ret; + + DBG("vhost_user_blk_set_status (...)\n"); + + /* TODO: Remove if not needed */ + if (!s->connected) { + DBG("Not connected!\n"); + return; + } + + DBG("should_start == %d\n", should_start); + if (s->vhost_dev->started == should_start) { + DBG("s->dev->started == should_start\n"); + return; + } + + if (should_start) { + ret = vhost_user_blk_start(vdev); + if (ret < 0) { + DBG("vhost_user_blk_start returned error\n"); + } + } else { + DBG("Call vhost_user_blk_stop (not yet in place)\n"); + /* TODO: vhost_user_blk_stop(vdev); */ + } + + DBG("vhost_user_blk_set_status return successfully\n"); +} + + +static void virtio_dev_class_init(VirtIODevice *vdev) +{ + DBG("virtio_dev_class_init\n"); + + vdev->vdev_class = (VirtioDeviceClass *)malloc(sizeof(VirtioDeviceClass)); + vdev->vdev_class->parent = vdev; + vdev->vdev_class->realize = vhost_user_blk_realize; + vdev->vdev_class->unrealize = vhost_user_blk_device_unrealize; + vdev->vdev_class->get_config = vhost_user_blk_update_config; + vdev->vdev_class->set_config = vhost_user_blk_set_config; + vdev->vdev_class->get_features = vhost_user_blk_get_features; + vdev->vdev_class->set_status = vhost_user_blk_set_status; + vdev->vdev_class->reset = vhost_user_blk_reset; +} + + +void vhost_user_blk_init(VirtIODevice *vdev) +{ + + DBG("vhost_user_blk_init\n"); + + VHostUserBlk *vhublk = (VHostUserBlk *)malloc(sizeof(VHostUserBlk)); + vdev->vhublk = vhublk; + vhublk->parent = vdev; + vhublk->virtqs = vdev->vqs; + vhublk->vhost_dev = dev; + + virtio_dev_class_init(vdev); + virtio_loopback_bus_init(vdev->vbus); +} + + +static void vhost_user_blk_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. + */ + DBG("vhost_user_blk_handle_output not yet implemented\n"); +} + + +void print_config(uint8_t *config) +{ + struct virtio_blk_config *config_strct = (struct virtio_blk_config *)config; + + DBG("uint64_t capacity: %llu\n", config_strct->capacity); + DBG("uint32_t size_max: %u\n", config_strct->size_max); + DBG("uint32_t seg_max: %u\n", config_strct->seg_max); + + DBG("virtio_blk_geometry:\n"); + DBG(" uint16_t cylinders: %u\n", + config_strct->geometry.cylinders); + DBG(" uint8_t heads: %u\n", + config_strct->geometry.heads); + DBG(" uint8_t sectors: %u\n", + config_strct->geometry.sectors); + + DBG("uint32_t blk_size: %u\n", config_strct->blk_size); + DBG("uint8_t physical_block_exp: %u\n", + config_strct->physical_block_exp); + DBG("uint8_t alignment_offset: %u\n", + config_strct->alignment_offset); + DBG("uint16_t min_io_size: %u\n", config_strct->min_io_size); + DBG("uint32_t opt_io_size: %u\n", config_strct->opt_io_size); + DBG("uint8_t wce: %u\n", config_strct->wce); + DBG("uint8_t unused: %u\n", config_strct->unused); + DBG("uint16_t num_queues: %u\n", config_strct->num_queues); + DBG("uint32_t max_discard_sectors: %u\n", + config_strct->max_discard_sectors); + DBG("uint32_t max_discard_seg: %u\n", config_strct->max_discard_seg); + DBG("uint32_t discard_sector_alignment: %u\n", + config_strct->discard_sector_alignment); + DBG("uint32_t max_write_zeroes_sectors: %u\n", + config_strct->max_write_zeroes_sectors); + DBG("uint32_t max_write_zeroes_seg: %u\n", + config_strct->max_write_zeroes_seg); + DBG("uint8_t write_zeroes_may_unmap: %u\n", + config_strct->write_zeroes_may_unmap); + DBG("uint8_t unused1[3]: %u\n", config_strct->unused1[0]); + DBG("uint8_t unused1[3]: %u\n", config_strct->unused1[1]); + DBG("uint8_t unused1[3]: %u\n", config_strct->unused1[2]); +} + +void vhost_user_blk_realize(void) +{ + int retries; + int i, ret; + + DBG("vhost_user_blk_realize\n"); + + /* This needs to be added */ + proxy = (VirtIOMMIOProxy *)malloc(sizeof(VirtIOMMIOProxy)); + *proxy = (VirtIOMMIOProxy) { + .legacy = 1, + }; + + /* VIRTIO_ID_BLOCK is 2, check virtio_ids.h in linux */ + virtio_dev_init(global_vdev, "virtio-blk", 2, + sizeof(struct virtio_blk_config)); + + vhost_user_blk_init(global_vdev); + + /* FIXME: We temporarily hardcoded the vrtqueues number */ + global_vdev->vhublk->num_queues = 1; + + /* FIXME: We temporarily hardcoded the vrtqueues size */ + global_vdev->vhublk->queue_size = 128; + + /* NOTE: global_vdev->vqs == vhublk->virtqs */ + global_vdev->vqs = (VirtQueue **)malloc(sizeof(VirtQueue *) + * global_vdev->vhublk->num_queues); + for (i = 0; i < global_vdev->vhublk->num_queues; i++) { + global_vdev->vqs[i] = virtio_add_queue(global_vdev, + global_vdev->vhublk->queue_size, + vhost_user_blk_handle_output); + } + + global_vdev->vhublk->inflight = (struct vhost_inflight *)malloc( + sizeof(struct vhost_inflight)); + global_vdev->vhublk->vhost_vqs = (struct vhost_virtqueue *)malloc( + sizeof(struct vhost_virtqueue) * + global_vdev->vhublk->num_queues); + + retries = REALIZE_CONNECTION_RETRIES; + + do { + ret = vhost_user_blk_realize_connect(global_vdev->vhublk); + } while (ret < 0 && retries--); + + if (ret < 0) { + DBG("vhost_user_blk_realize_connect: -EPROTO\n"); + } + + DBG("final global_vdev->host_features: 0x%lx\n", + global_vdev->host_features); + + print_config((uint8_t *)(&global_vdev->vhublk->blkcfg)); + + return; + +} + diff --git a/vhost_user_blk.h b/vhost_user_blk.h new file mode 100644 index 0000000..ddb21fe --- /dev/null +++ b/vhost_user_blk.h @@ -0,0 +1,59 @@ +/* + * Based on vhost-user-blk.h of QEMU project + * + * Copyright(C) 2017 Intel Corporation. + * + * Authors: + * Changpeng Liu <changpeng.liu@intel.com> + * + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef VHOST_USER_BLK +#define VHOST_USER_BLK + +#include "vhost_loopback.h" +#include "vhost_user_loopback.h" +#include "virtio_loopback.h" +#include <linux/virtio_blk.h> + +#define TYPE_VHOST_USER_BLK "vhost-user-blk" + +#define VHOST_USER_BLK_AUTO_NUM_QUEUES UINT16_MAX + +struct VHostUserBlk { + VirtIODevice *parent; + struct vhost_virtqueue *vhost_vq; + struct vhost_dev *vhost_dev; + VirtQueue *req_vq; + VirtQueue **virtqs; + uint16_t num_queues; + uint32_t queue_size; + /* uint32_t config_wce; //We will need it for the next release */ + struct vhost_inflight *inflight; + struct vhost_virtqueue *vhost_vqs; + struct virtio_blk_config blkcfg; + bool connected; + bool started_vu; +}; + + +void vhost_user_blk_realize(void); + +#endif /* VHOST_USER_BLK */ diff --git a/vhost_user_input.c b/vhost_user_input.c new file mode 100644 index 0000000..cf3fb2e --- /dev/null +++ b/vhost_user_input.c @@ -0,0 +1,224 @@ +/* + * Based on vhost-user-input.c of QEMU project + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <sys/param.h> + +/* Project header files */ +#include "vhost_user_input.h" + +#ifdef DEBUG +#define DBG(...) printf("vhost-user-input: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + + +static int vhost_input_config_change(struct vhost_dev *dev) +{ + DBG("vhost-user-input: unhandled backend config change\n"); + return -1; +} + +const VhostDevConfigOps config_ops = { + .vhost_dev_config_notifier = vhost_input_config_change, +}; + +static void vhost_input_change_active(VirtIOInput *vinput) +{ + DBG("vhost_input_change_active(...)\n"); + + VhostUserInput *vhuinput = global_vdev->vhuinput; + + if (vinput->active) { + vhost_user_backend_start(global_vdev); + } else { + vhost_user_backend_stop(global_vdev); + } +} + +static void vhost_input_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + DBG("vhost_input_get_config(...)\n"); + + VirtIOInput *vinput = vdev->vinput; + VhostUserInput *vhi = vdev->vhuinput; + int ret; + + memset(config_data, 0, vinput->cfg_size); + + ret = vhost_dev_get_config(vhi->vhost_dev, config_data, vinput->cfg_size); + if (ret) { + DBG("vhost_input_get_config failed\n"); + return; + } +} + +static void vhost_input_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + DBG("vhost_input_set_config(...)\n"); + + VhostUserInput *vhi = vdev->vhuinput; + int ret; + + ret = vhost_dev_set_config(vhi->vhost_dev, config_data, + 0, sizeof(virtio_input_config), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + DBG("vhost_input_set_config failed\n"); + return; + } + + virtio_notify_config(vdev); +} + +static struct vhost_dev *vhost_input_get_vhost(VirtIODevice *vdev) +{ + DBG("vhost_input_get_vhost(...)\n"); + + return vdev->vhuinput->vhost_dev; +} + +static void vhost_input_class_init(VirtIODevice *vdev) +{ + DBG("vhost_input_class_init(...)\n"); + + + /* Comment out the following lines to get the local config */ + vdev->vdev_class->get_config = vhost_input_get_config; + vdev->vdev_class->set_config = vhost_input_set_config; + + vdev->vdev_class->get_vhost = vhost_input_get_vhost; + + vdev->vhuinput->vdev_input->input_class->realize = vhost_user_input_realize; + vdev->vhuinput->vdev_input->input_class->change_active = + vhost_input_change_active; +} + + +void vhost_user_input_init(VirtIODevice *vdev) +{ + + DBG("vhost_user_input_init(...)\n"); + + struct VirtIOInputClass *input_class = (struct VirtIOInputClass *)malloc( + sizeof(struct VirtIOInputClass)); + VirtIOInput *vinput = (VirtIOInput *)malloc(sizeof(VirtIOInput)); + VhostUserInput *vhuinput = (VhostUserInput *)malloc(sizeof(VhostUserInput)); + + vdev->vinput = vinput; + vdev->vinput->input_class = input_class; + + vdev->vhuinput = vhuinput; + vhuinput->vdev = vdev; + vhuinput->vhost_dev = dev; + vhuinput->vdev_input = vinput; + + /* + * Call first the virtio_input class init to set up + * the basic functionality. + */ + virtio_input_class_init(vdev); + + /* Then call the vhost_user class init */ + vhost_input_class_init(vdev); + + /* finally initialize the bus */ + virtio_loopback_bus_init(vdev->vbus); +} + + +void vhost_user_input_realize() +{ + int nvqs = 2; /* qemu choice: 2 */ + + DBG("vhost_user_input_realize()\n"); + + vhost_dev_set_config_notifier(global_vdev->vhuinput->vhost_dev, + &config_ops); + + global_vdev->vhuinput->vdev_input->cfg_size = + sizeof_field(virtio_input_config, u); + + global_vdev->vhuinput->vhost_dev->vq_index = 0; + global_vdev->vhuinput->vhost_dev->backend_features = 0; + global_vdev->vhuinput->vhost_dev->num_queues = nvqs; + + + global_vdev->vhuinput->vhost_dev->nvqs = nvqs; + global_vdev->vhuinput->vhost_dev->vqs = (struct vhost_virtqueue *)malloc( + sizeof(struct vhost_virtqueue) * nvqs); + vhost_dev_init(global_vdev->vhuinput->vhost_dev); + + /* Pass the new obtained features */ + global_vdev->host_features = global_vdev->vhuinput->vhost_dev->features; +} + +void vhost_user_backend_start(VirtIODevice *vdev) +{ + VirtioBus *k = vdev->vbus; + int ret, i; + + DBG("vhost_user_backend_start(...)\n"); + + if (vdev->started) { + DBG("Device has already been started!\n"); + return; + } + + if (!k->set_guest_notifiers) { + DBG("binding does not support guest notifiers\n"); + return; + } + + ret = vhost_dev_enable_notifiers(vdev->vhuinput->vhost_dev, vdev); + if (ret < 0) { + DBG("vhost_dev_enable_notifiers failed!\n"); + return; + } + + DBG("k->set_guest_notifiers, nvqs: %d\n", vdev->vhuinput->vhost_dev->nvqs); + ret = k->set_guest_notifiers(vdev, vdev->vhuinput->vhost_dev->nvqs, true); + if (ret < 0) { + DBG("Error binding guest notifier\n"); + } + + vdev->vhuinput->vhost_dev->acked_features = vdev->guest_features; + ret = vhost_dev_start(vdev->vhuinput->vhost_dev, vdev); + if (ret < 0) { + DBG("Error start vhost dev\n"); + return; + } + + /* + * 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 < vdev->vhuinput->vhost_dev->nvqs; i++) { + vhost_virtqueue_mask(vdev->vhuinput->vhost_dev, vdev, + vdev->vhuinput->vhost_dev->vq_index + i, false); + } + + vdev->started = true; + return; + +} + +void vhost_user_backend_stop(VirtIODevice *vdev) +{ + DBG("vhost_user_backend_stop() not yet implemented\n"); +} diff --git a/vhost_user_input.h b/vhost_user_input.h new file mode 100644 index 0000000..db5ed33 --- /dev/null +++ b/vhost_user_input.h @@ -0,0 +1,187 @@ +/* + * Based on virtio-input.h of QEMU project + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef VHOST_USER_INPUT +#define VHOST_USER_INPUT + +#include "vhost_loopback.h" +#include "vhost_user_loopback.h" +#include "virtio_loopback.h" +#include <linux/virtio_input.h> +#include "queue.h" +#include <sys/mman.h> + +/* ----------------------------------------------------------------- */ +/* virtio input protocol */ + +typedef struct virtio_input_absinfo virtio_input_absinfo; +typedef struct virtio_input_config virtio_input_config; +typedef struct virtio_input_event virtio_input_event; + +/* ----------------------------------------------------------------- */ +/* qemu internals */ + +#define TYPE_VIRTIO_INPUT "virtio-input-device" +#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid-device" +#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard-device" +#define TYPE_VIRTIO_MOUSE "virtio-mouse-device" +#define TYPE_VIRTIO_TABLET "virtio-tablet-device" + +#define TYPE_VIRTIO_INPUT_HOST "virtio-input-host-device" + +#define TYPE_VHOST_USER_INPUT "vhost-user-input" + +typedef struct VirtIOInputConfig { + virtio_input_config config; + QTAILQ_ENTRY(VirtIOInputConfig) node; +} VirtIOInputConfig; + +struct VirtIOInputClass; + +typedef struct VirtIOInput { + VirtIODevice *parent_dev; + struct VirtIOInputClass *input_class; + uint8_t cfg_select; + uint8_t cfg_subsel; + uint32_t cfg_size; + QTAILQ_HEAD(, VirtIOInputConfig) cfg_list; + VirtQueue *evt, *sts; + char *serial; + + struct { + virtio_input_event event; + VirtQueueElement *elem; + } *queue; + uint32_t qindex, qsize; + + bool active; + +} VirtIOInput; + +typedef struct VirtIOInputClass { + VirtioDeviceClass *parent_class; + void (*realize)(); + void (*unrealize)(VirtIODevice *dev); + void (*change_active)(VirtIOInput *vinput); + void (*handle_status)(VirtIOInput *vinput, virtio_input_event *event); +} VirtIOInputClass; + +struct VirtIOInputHID { + VirtIOInput parent_obj; + char *display; + uint32_t head; + int ledstate; + bool wheel_axis; +}; + +struct VirtIOInputHost { + VirtIOInput parent_obj; + char *evdev; + int fd; +}; + +typedef struct VhostUserInput { + VirtIOInput *vdev_input; + struct vhost_dev *vhost_dev; + VirtIODevice *vdev; + bool started; + bool completed; +} VhostUserInput; + +#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard" +#define BUS_VIRTUAL 0x06 + + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f +#define EV_CNT (EV_MAX + 1) + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f +#define LED_CNT (LED_MAX + 1) + +/* + * Keys and buttons + * + * Most of the keys/buttons are modeled after USB HUT 1.12 + * (see http://www.usb.org/developers/hidpage). + * Abbreviations in the comments: + * AC - Application Control + * AL - Application Launch Button + * SC - System Control + */ +#define KEY_G 34 + +static struct virtio_input_config virtio_keyboard_config[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_KEYBOARD), + .u.string = VIRTIO_ID_NAME_KEYBOARD, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = (BUS_VIRTUAL), + .vendor = (0x0627), /* same we use for usb hid devices */ + .product = (0x0001), + .version = (0x0001), + }, + },{ + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_KEY, + .size = 1, + .u.bitmap = { + KEY_G, + }, + }, + {}, /* End of list */ +}; + +void vhost_user_backend_start(VirtIODevice *vdev); +void vhost_user_backend_stop(VirtIODevice *vdev); + +void virtio_input_init_config(VirtIOInput *vinput, + virtio_input_config *config); +void virtio_input_class_init(VirtIODevice *vdev); +void virtio_input_device_realize(); + +void vhost_user_input_init(VirtIODevice *vdev); +void vhost_user_input_realize(); + +#endif /* VHOST_USER_INPU */ diff --git a/vhost_user_loopback.c b/vhost_user_loopback.c index 3df7bc3..368e699 100644 --- a/vhost_user_loopback.c +++ b/vhost_user_loopback.c @@ -49,6 +49,7 @@ /* Project header files */ #include "virtio_loopback.h" #include "vhost_user_loopback.h" +#include "vhost_loopback.h" #include "event_notifier.h" #ifdef DEBUG @@ -402,6 +403,10 @@ int vhost_setup_slave_channel(struct vhost_dev *dev) memcpy(msg.fds, &sv[1], sizeof(int)); msg.fd_num = 1; + + /* FIXME: something missing here */ + + if (reply_supported) { msg.flags |= VHOST_USER_NEED_REPLY_MASK; } @@ -497,11 +502,13 @@ int vhost_set_vring_file(VhostUserRequest request, int vhost_user_set_vring_kick(struct vhost_vring_file *file) { + DBG("Call vhost_user_set_vring_kick()\n"); return vhost_set_vring_file(VHOST_USER_SET_VRING_KICK, file); } int vhost_user_set_vring_call(struct vhost_vring_file *file) { + DBG("Call vhost_user_set_vring_call()\n"); return vhost_set_vring_file(VHOST_USER_SET_VRING_CALL, file); } @@ -597,6 +604,214 @@ int vhost_virtqueue_init(struct vhost_dev *dev, return 0; } +int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config, + uint32_t config_len) +{ + int ret; + VhostUserMsg msg = { + .request = VHOST_USER_GET_CONFIG, + .flags = VHOST_USER_VERSION, + .size = VHOST_USER_CONFIG_HDR_SIZE + config_len, + }; + + DBG("dev->protocol_features: 0x%lx\n", dev->protocol_features); + DBG("VHOST_USER_PROTOCOL_F_CONFIG: 0x%x\n", VHOST_USER_PROTOCOL_F_CONFIG); + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_CONFIG)) { + DBG("VHOST_USER_PROTOCOL_F_CONFIG not supported\n"); + return -1; + } + + msg.payload.config.offset = 0; + msg.payload.config.size = config_len; + ret = vu_message_write(client_sock, &msg); + DBG("vu_message_write return: %d\n", ret); + if (ret < 0) { + DBG("vhost_get_config failed\n"); + return -1; + } + + ret = vu_message_read(client_sock, &msg); + if (ret < 0) { + DBG("vhost_get_config failed\n"); + return -1; + } + + if (msg.request != VHOST_USER_GET_CONFIG) { + DBG("Received unexpected msg type. Expected %d received %d", + VHOST_USER_GET_CONFIG, msg.request); + return -1; + } + + if (msg.size != VHOST_USER_CONFIG_HDR_SIZE + config_len) { + DBG("Received bad msg size.\n"); + return -1; + } + + memcpy(config, msg.payload.config.region, config_len); + + DBG("Received config: %u, config_len: %u\n", *config, config_len); + + DBG("vhost_user_get_config return successfully\n"); + + return 0; +} + +int vhost_user_set_config(struct vhost_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + int ret; + uint8_t *p; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + VhostUserMsg msg = { + .request = VHOST_USER_SET_CONFIG, + .flags = VHOST_USER_VERSION, + .size = VHOST_USER_CONFIG_HDR_SIZE + size, + }; + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_CONFIG)) { + DBG("VHOST_USER_PROTOCOL_F_CONFIG not supported\n"); + return -ENOTSUP; + } + + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + if (size > VHOST_USER_MAX_CONFIG_SIZE) { + return -EINVAL; + } + + msg.payload.config.offset = offset, + msg.payload.config.size = size, + msg.payload.config.flags = flags, + p = msg.payload.config.region; + memcpy(p, data, size); + + ret = vu_message_write(client_sock, &msg); + DBG("vu_message_write return: %d\n", ret); + if (ret < 0) { + return ret; + } + + if (reply_supported) { + return process_message_reply(&msg); + DBG("Reply is done!\n"); + } + + return 0; +} + + +int vhost_user_get_inflight_fd(struct vhost_dev *dev, + uint16_t queue_size, + struct vhost_inflight *inflight) +{ + void *addr; + int fd; + int ret; + VhostUserMsg msg = { + .request = VHOST_USER_GET_INFLIGHT_FD, + .flags = VHOST_USER_VERSION, + .payload.inflight.num_queues = dev->nvqs, + .payload.inflight.queue_size = queue_size, + .size = sizeof(msg.payload.inflight), + }; + + DBG("vhost_user_get_inflight_fd\n"); + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + /* NOTE: This stays here as a reference */ + ret = vu_message_write(client_sock, &msg); + if (ret < 0) { + DBG("vhost_user_get_inflight_fd\n\t->write error\n"); + return ret; + } + + /* NOTE: This stays here as a reference */ + ret = vu_message_read(client_sock, &msg); + if (ret < 0) { + DBG("vhost_user_get_inflight_fd\n\t->read error\n"); + return ret; + } + + if (msg.request != VHOST_USER_GET_INFLIGHT_FD) { + DBG("Received unexpected msg type. " + "Expected %d received %d\n", + VHOST_USER_GET_INFLIGHT_FD, msg.request); + return -1; + } + + if (msg.size != sizeof(msg.payload.inflight)) { + DBG("Received bad msg size.\n"); + return -1; + } + + if (!msg.payload.inflight.mmap_size) { + DBG("!msg.payload.inflight.mmap_size\n"); + return 0; + } + + /* FIXME: This needs to be checked */ + memcpy(&fd, msg.fds, sizeof(int)); + if (fd < 0) { + DBG("Failed to get mem fd\n"); + return -1; + } + + addr = mmap(0, msg.payload.inflight.mmap_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, msg.payload.inflight.mmap_offset); + + if (addr == MAP_FAILED) { + DBG("Failed to mmap mem fd\n"); + close(fd); + return -1; + } + + inflight->addr = addr; + inflight->fd = fd; + inflight->size = msg.payload.inflight.mmap_size; + inflight->offset = msg.payload.inflight.mmap_offset; + inflight->queue_size = queue_size; + + return 0; +} + + +int vhost_user_set_inflight_fd(struct vhost_dev *dev, + struct vhost_inflight *inflight) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_INFLIGHT_FD, + .flags = VHOST_USER_VERSION, + .payload.inflight.mmap_size = inflight->size, + .payload.inflight.mmap_offset = inflight->offset, + .payload.inflight.num_queues = dev->nvqs, + .payload.inflight.queue_size = inflight->queue_size, + .size = sizeof(msg.payload.inflight), + }; + + DBG("vhost_user_set_inflight_fd\n"); + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + msg.fd_num = 1; + memcpy(msg.fds, &inflight->fd, msg.fd_num * sizeof(int)); + + return !vu_message_write(client_sock, &msg); /* Returns true or false*/ +} + /* -------------------- Vring functions -------------------- */ @@ -651,6 +866,8 @@ int vhost_user_backend_init(struct vhost_dev *vhdev) uint64_t features, protocol_features, ram_slots; int err; + DBG("vhost_user_backend_init(...)\n"); + err = vhost_user_get_features(&features); if (err < 0) { DBG("vhost_backend_init failed\n"); @@ -669,11 +886,28 @@ int vhost_user_backend_init(struct vhost_dev *vhdev) vhdev->protocol_features = protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK; + /* - * TODO: Disable config bit for the rng, this might be usefull - * when new devices are added + * FIXME: Disable VHOST_USER_PROTOCOL_F_SLAVE_REQ for the moment + * vhdev->protocol_features &= + * ~(1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ); */ - vhdev->protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_CONFIG); + + /* FIXME: Disable VHOST_USER_GET_INFLIGHT_FD for the moment */ + vhdev->protocol_features &= + ~(1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD); + + if (!vhdev->config_ops || + !vhdev->config_ops->vhost_dev_config_notifier) { + /* Don't acknowledge CONFIG feature if device doesn't support it */ + dev->protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_CONFIG); + } else if (!(protocol_features & + (1ULL << VHOST_USER_PROTOCOL_F_CONFIG))) { + DBG("Device expects VHOST_USER_PROTOCOL_F_CONFIG " + "but backend does not support it.\n"); + return -EINVAL; + } + err = vhost_user_set_protocol_features(vhdev->protocol_features); if (err < 0) { @@ -764,6 +998,8 @@ void vhost_dev_init(struct vhost_dev *vhdev) int r, n_initialized_vqs = 0; unsigned int i; + DBG("vhost_dev_init(...)\n"); + /* Vhost conf */ vhdev->migration_blocker = NULL; @@ -778,6 +1014,8 @@ void vhost_dev_init(struct vhost_dev *vhdev) if (r < 0) { DBG("vhost_get_features failed\n"); } + DBG("Print vhost_dev_init->features: 0x%lx\n", features); + for (i = 0; i < vhdev->nvqs; ++i, ++n_initialized_vqs) { r = vhost_virtqueue_init(vhdev, vhdev->vqs + i, vhdev->vq_index + i); @@ -795,7 +1033,7 @@ void vhost_dev_init(struct vhost_dev *vhdev) * busyloop_timeout); * if (r < 0) { * DBG("Failed to set busyloop timeout\n"); - * //goto fail_busyloop; + * return -1; * } * } * } diff --git a/vhost_user_loopback.h b/vhost_user_loopback.h index 4a98516..a56a1d2 100644 --- a/vhost_user_loopback.h +++ b/vhost_user_loopback.h @@ -59,6 +59,11 @@ struct vhost_virtqueue { struct vhost_dev *dev; }; +typedef struct VhostDevConfigOps { + /* Vhost device config space changed callback */ + int (*vhost_dev_config_notifier)(struct vhost_dev *dev); +} VhostDevConfigOps; + struct vhost_dev { VirtIODevice *vdev; struct vhost_virtqueue *vqs; @@ -81,6 +86,7 @@ struct vhost_dev { void *migration_blocker; /* Vhost-user struct */ uint64_t memory_slots; + const VhostDevConfigOps *config_ops; }; struct vhost_user { @@ -798,5 +804,18 @@ int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring); int vhost_user_set_vring_addr(struct vhost_dev *dev, struct vhost_vring_addr *addr); +int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config, + uint32_t config_len); +int vhost_user_set_config(struct vhost_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags); + +/* FIXME: This need to move in a better place */ +struct vhost_inflight; +int vhost_user_get_inflight_fd(struct vhost_dev *dev, + uint16_t queue_size, + struct vhost_inflight *inflight); +int vhost_user_set_inflight_fd(struct vhost_dev *dev, + struct vhost_inflight *inflight); + #endif /* LIBVHOST_USER_H */ diff --git a/virtio_blk.h b/virtio_blk.h new file mode 100644 index 0000000..75534ed --- /dev/null +++ b/virtio_blk.h @@ -0,0 +1,95 @@ +/* + * Virtio Block Device + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_VIRTIO_BLK_H +#define QEMU_VIRTIO_BLK_H + +#include "standard-headers/linux/virtio_blk.h" +#include "hw/virtio/virtio.h" +#include "hw/block/block.h" +#include "sysemu/iothread.h" +#include "sysemu/block-backend.h" +#include "qom/object.h" + +#define TYPE_VIRTIO_BLK "virtio-blk-device" +#define VIRTIO_BLK_AUTO_NUM_QUEUES UINT16_MAX +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK) + +/* This is the last element of the write scatter-gather list */ +struct virtio_blk_inhdr { + unsigned char status; +}; + + +struct VirtIOBlkConf { + BlockConf conf; + IOThread *iothread; + char *serial; + uint32_t request_merging; + uint16_t num_queues; + uint16_t queue_size; + bool seg_max_adjust; + bool report_discard_granularity; + uint32_t max_discard_sectors; + uint32_t max_write_zeroes_sectors; + bool x_enable_wce_if_config_wce; +}; + + +struct VirtIOBlockDataPlane; + +struct VirtIOBlockReq; +struct VirtIOBlock { + VirtIODevice parent_obj; + BlockBackend *blk; + void *rq; + QEMUBH *bh; + VirtIOBlkConf conf; + unsigned short sector_mask; + bool original_wce; + VMChangeStateEntry *change; + bool dataplane_disabled; + bool dataplane_started; + struct VirtIOBlockDataPlane *dataplane; + uint64_t host_features; + size_t config_size; +}; + +typedef struct VirtIOBlockReq { + VirtQueueElement elem; + int64_t sector_num; + VirtIOBlock *dev; + VirtQueue *vq; + IOVDiscardUndo inhdr_undo; + IOVDiscardUndo outhdr_undo; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr out; + QEMUIOVector qiov; + size_t in_len; + struct VirtIOBlockReq *next; + struct VirtIOBlockReq *mr_next; + BlockAcctCookie acct; +} VirtIOBlockReq; + +#define VIRTIO_BLK_MAX_MERGE_REQS 32 + +typedef struct MultiReqBuffer { + VirtIOBlockReq *reqs[VIRTIO_BLK_MAX_MERGE_REQS]; + unsigned int num_reqs; + bool is_write; +} MultiReqBuffer; + +bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); +void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh); + +#endif diff --git a/virtio_input.c b/virtio_input.c new file mode 100644 index 0000000..793806c --- /dev/null +++ b/virtio_input.c @@ -0,0 +1,284 @@ +/* + * Based on virtio-input.h of QEMU project + * + * Copyright (c) 2022 Virtual Open Systems SAS. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <sys/param.h> + +/* Project header files */ +#include "vhost_user_input.h" + +#ifdef DEBUG +#define DBG(...) printf("virtio-input: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + +#define VIRTIO_INPUT_VM_VERSION 1 + +/* ----------------------------------------------------------------- */ + +void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) +{ + DBG("virtio_input_send() not yet implemeted\n"); +} + +static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq) +{ + DBG("virtio_input_handle_evt(...)\n"); + /* nothing */ +} + +static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOInputClass *vic = vdev->vinput->input_class; + VirtIOInput *vinput = vdev->vinput; + virtio_input_event event; + VirtQueueElement *elem; + int len; + + DBG("virtio_input_handle_sts(...)\n"); + + for (;;) { + elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + + memset(&event, 0, sizeof(event)); + /* FIXME: add iov_to_buf func */ + len = 1; + /* + * TODO: Will be added in a next release + * len = iov_to_buf(elem->out_sg, elem->out_num, + * 0, &event, sizeof(event)); + */ + if (vic->handle_status) { + vic->handle_status(vinput, &event); + } + virtqueue_push(vinput->sts, elem, len); + munmap(elem, sizeof(VirtQueueElement)); + } + virtio_notify(vdev, vinput->sts); +} + +virtio_input_config *virtio_input_find_config(VirtIOInput *vinput, + uint8_t select, + uint8_t subsel) +{ + DBG("virtio_input_find_config(...)\n"); + VirtIOInputConfig *cfg; + + QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) { + if (select == cfg->config.select && + subsel == cfg->config.subsel) { + return &cfg->config; + } + } + return NULL; +} + +void virtio_input_add_config(VirtIOInput *vinput, + virtio_input_config *config) +{ + DBG("virtio_input_add_config(...)\n"); + VirtIOInputConfig *cfg; + + if (virtio_input_find_config(vinput, config->select, config->subsel)) { + /* should not happen */ + DBG("Error duplicate config: %d/%d\n", config->select, config->subsel); + exit(1); + } + + cfg = (VirtIOInputConfig *)malloc(sizeof(VirtIOInputConfig)); + cfg->config = *config; + + QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node); +} + +void virtio_input_init_config(VirtIOInput *vinput, + virtio_input_config *config) +{ + DBG("virtio_input_init_config(...)\n"); + int i = 0; + + QTAILQ_INIT(&vinput->cfg_list); + while (config[i].select) { + virtio_input_add_config(vinput, config + i); + i++; + } +} + +void virtio_input_idstr_config(VirtIOInput *vinput, + uint8_t select, const char *string) +{ + DBG("virtio_input_idstr_config(...)\n"); + virtio_input_config id; + + if (!string) { + return; + } + memset(&id, 0, sizeof(id)); + id.select = select; + id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string); + virtio_input_add_config(vinput, &id); +} + +static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + DBG("virtio_input_get_config(...)\n"); + VirtIOInput *vinput = vdev->vinput; + virtio_input_config *config; + + config = virtio_input_find_config(vinput, vinput->cfg_select, + vinput->cfg_subsel); + if (config) { + memcpy(config_data, config, vinput->cfg_size); + } else { + memset(config_data, 0, vinput->cfg_size); + } +} + +static void virtio_input_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VirtIOInput *vinput = vdev->vinput; + virtio_input_config *config = (virtio_input_config *)config_data; + + DBG("virtio_input_set_config(...)\n"); + + vinput->cfg_select = config->select; + vinput->cfg_subsel = config->subsel; + virtio_notify_config(vdev); +} + +static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f) +{ + DBG("virtio_input_get_features(...)\n"); + return f; +} + +static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) +{ + VirtIOInputClass *vic = vdev->vinput->input_class; + VirtIOInput *vinput = vdev->vinput; + bool should_start = virtio_device_started(vdev, val); + + if (should_start) { + if (!vinput->active) { + vinput->active = true; + if (vic->change_active) { + vic->change_active(vinput); + } + } + } +} + +static void virtio_input_reset(VirtIODevice *vdev) +{ + VirtIOInputClass *vic = vdev->vinput->input_class; + VirtIOInput *vinput = vdev->vinput; + + DBG("virtio_input_reset(...)\n"); + + if (vinput->active) { + vinput->active = false; + if (vic->change_active) { + vic->change_active(vinput); + } + } +} + +static int virtio_input_post_load(void *opaque, int version_id) +{ + VirtIOInput *vinput = global_vdev->vinput; + VirtIOInputClass *vic = global_vdev->vinput->input_class; + VirtIODevice *vdev = global_vdev; + + DBG("virtio_input_post_load(...)\n"); + + vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK; + if (vic->change_active) { + vic->change_active(vinput); + } + return 0; +} + +void virtio_input_device_realize() +{ + VirtIODevice *vdev = global_vdev; + struct VirtIOInputClass *vic = vdev->vinput->input_class; + VirtIOInput *vinput = vdev->vinput; + VirtIOInputConfig *cfg; + + DBG("virtio_input_device_realize(...)\n"); + + /* This needs to be added */ + proxy = (VirtIOMMIOProxy *)malloc(sizeof(VirtIOMMIOProxy)); + *proxy = (VirtIOMMIOProxy) { + .legacy = 1, + }; + + if (vic->realize) { + vic->realize(vdev); + } + + virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL, + vinput->serial); + + QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) { + if (vinput->cfg_size < cfg->config.size) { + vinput->cfg_size = cfg->config.size; + } + } + vinput->cfg_size += 8; + + virtio_input_init_config(vinput, virtio_keyboard_config); + + virtio_dev_init(vdev, "virtio-input", 18, vinput->cfg_size); + vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt); + vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts); + + /* FIXME: do we need that? */ + memcpy(global_vdev->vq, vinput->evt, sizeof(VirtQueue)); + memcpy(global_vdev->vq, vinput->sts, sizeof(VirtQueue)); + + DBG("global_vdev->guest_features: 0x%lx\n", global_vdev->guest_features); +} + +static void virtio_input_finalize(VirtIODevice *vdev) +{ + DBG("virtio_input_finalize not yet implemented"); +} + +static void virtio_input_device_unrealize(VirtIODevice *vdev) +{ + DBG("virtio_input_device_unrealize not yet implemented"); +} + + +void virtio_input_class_init(VirtIODevice *vdev) +{ + vdev->vdev_class = (VirtioDeviceClass *)malloc(sizeof(VirtioDeviceClass)); + vdev->vdev_class->parent = vdev; + + DBG("virtio_input_class_init(...)\n"); + + vdev->vdev_class->realize = virtio_input_device_realize; + vdev->vdev_class->get_config = virtio_input_get_config; + vdev->vdev_class->set_config = virtio_input_set_config; + vdev->vdev_class->get_features = virtio_input_get_features; + vdev->vdev_class->set_status = virtio_input_set_status; + vdev->vdev_class->reset = virtio_input_reset; +} diff --git a/virtio_loopback.c b/virtio_loopback.c index 5a831ce..90458cc 100644 --- a/virtio_loopback.c +++ b/virtio_loopback.c @@ -60,6 +60,9 @@ #include <pthread.h> #include <limits.h> +/* TODO: Deleteit, only for testing */ +#include <linux/virtio_blk.h> + #ifdef DEBUG #define DBG(...) printf("virtio-loopback: " __VA_ARGS__) #else @@ -76,7 +79,7 @@ int fd; int loopback_fd; virtio_device_info_struct_t device_info; -virtio_neg_t *address = NULL; +virtio_neg_t *address; VirtIOMMIOProxy *proxy; @@ -100,6 +103,15 @@ static int virtio_validate_features(VirtIODevice *vdev) return 0; } +bool virtio_device_started(VirtIODevice *vdev, uint8_t status) +{ + + DBG("virtio_device_started: %d\n", status & VIRTIO_CONFIG_S_DRIVER_OK); + DBG("status: %d\n", status); + + return status & VIRTIO_CONFIG_S_DRIVER_OK; +} + void virtio_set_started(VirtIODevice *vdev, bool started) { @@ -116,6 +128,8 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) { VirtioDeviceClass *k = vdev->vdev_class; + DBG("virtio_set_status(...)\n"); + if (virtio_has_feature(vdev->guest_features, VIRTIO_F_VERSION_1)) { if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && val & VIRTIO_CONFIG_S_FEATURES_OK) { @@ -133,6 +147,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) } if (k->set_status) { + DBG("k->set_status\n"); k->set_status(vdev, val); } @@ -400,6 +415,24 @@ static void virtio_irq(VirtQueue *vq) virtio_notify_vector(vq->vdev); } +void virtio_notify_config(VirtIODevice *vdev) +{ + + DBG("virtio_notify_config\n"); + + if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return; + } + + virtio_set_isr(vdev, 0x3); + vdev->generation++; + /* + * MMIO does not use vector parameter: + * virtio_notify_vector(vdev, vdev->config_vector); + */ + virtio_notify_vector(vdev); +} + void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) { if (!virtio_should_notify(vdev, vq)) { @@ -490,6 +523,8 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, { unsigned num_sg = *p_num_sg; bool ok = false; + uint64_t mmap_addr; + int ioctl_res; if (!sz) { DBG("virtio: zero sized buffers are not allowed\n"); @@ -505,11 +540,51 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, goto out; } - ioctl(fd, SHARE_BUF, &pa); + DBG("\tpa address is: 0x%lx\n", pa); + + memcpy(&mmap_addr, &pa, sizeof(uint64_t)); + ioctl_res = ioctl(loopback_fd, SHARE_BUF, &mmap_addr); - iov[num_sg].iov_base = mmap(NULL, 8192, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); + /* Notify the loopback driver what you want to' mmap' */ + if (ioctl_res < 0) { + DBG("SHARE_BUF failed\n"); + exit(1); + } else { + if (mmap_addr == 0) { + + if ((pa & 0xff) == 0) { + ioctl(loopback_fd, MAP_BLK); + } + + DBG("Try to mmap pa: 0x%lx, size: %lx\n", pa, len); + iov[num_sg].iov_base = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_SHARED, loopback_fd, 0); + int retries = 5; + while ((retries > 0) && ((int64_t)iov[num_sg].iov_base < 0)) { + iov[num_sg].iov_base = mmap(NULL, len, + PROT_READ | PROT_WRITE, + MAP_SHARED, loopback_fd, 0); + retries--; + } + + if ((int64_t)iov[num_sg].iov_base < 0) { + DBG("Bad mapping\n"); + exit(1); + } + } else { + iov[num_sg].iov_base = (void *)mmap_addr; + } + } + + /* Fix the offset */ iov[num_sg].iov_base += pa & 0xfff; + DBG("\tMMap address (iov_base): 0x%lx\n", + (uint64_t)iov[num_sg].iov_base); + + /* Update len: Remaining size in the current page */ + if (sz > PAGE_SIZE - (pa & 0xfff)) { + len = PAGE_SIZE - (pa & 0xfff); + } if (!iov[num_sg].iov_base) { DBG("virtio: bogus descriptor or out of resources\n"); @@ -916,14 +991,16 @@ void print_neg_flag(uint64_t neg_flag, bool read) case VIRTIO_MMIO_CONFIG_GENERATION: /* 0x0fc */ DBG("VIRTIO_MMIO_CONFIG_GENERATION\n"); break; - case VIRTIO_MMIO_CONFIG: /* 0x100 */ - DBG("VIRTIO_MMIO_CONFIG\n"); - break; default: - DBG("Negotiation flag Unknown: %ld\n", neg_flag); + if (neg_flag >= VIRTIO_MMIO_CONFIG) { + DBG("\tVIRTIO_MMIO_CONFIG\n"); + } else { + DBG("\tNegotiation flag Unknown: %ld\n", neg_flag); + } return; } + } int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) @@ -944,6 +1021,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) * has finished. */ if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { + DBG("virtio_set_features: vdev->status " + "& VIRTIO_CONFIG_S_FEATURES_OK\n"); return -EINVAL; } ret = virtio_set_features_nocheck(vdev, val); @@ -958,20 +1037,18 @@ static void virtio_queue_guest_notifier_read(EventNotifier *n) int vhost_user_loopback_eventfd = 0; -void *loopback_event_select(void *data) +void *loopback_event_select(void *wfd) { int retval; uint64_t eftd_ctr; fd_set rfds; int s; - (void) data; - DBG("\nWaiting event from vhost-user-device\n"); fflush(stdout); FD_ZERO(&rfds); - FD_SET(vhost_user_loopback_eventfd, &rfds); + FD_SET(*(int *)wfd, &rfds); while (1) { @@ -983,11 +1060,13 @@ void *loopback_event_select(void *data) exit(EXIT_FAILURE); } else if (retval > 0) { - s = read(vhost_user_loopback_eventfd, &eftd_ctr, sizeof(uint64_t)); + s = read(*(int *)wfd, &eftd_ctr, sizeof(uint64_t)); if (s != sizeof(uint64_t)) { DBG("\neventfd read error. Exiting..."); exit(1); } else { + DBG("\n\nEvent has come from the vhost-user-device " + "(eventfd: %d)\n\n", *(int *)wfd); virtio_irq(global_vdev->vq); } @@ -1007,7 +1086,8 @@ void event_notifier_set_handler(EventNotifier *e, vhost_user_loopback_eventfd = e->wfd; if (vhost_user_loopback_eventfd > 0) { - ret = pthread_create(&thread_id, NULL, loopback_event_select, NULL); + ret = pthread_create(&thread_id, NULL, loopback_event_select, + (void *)(&(e->wfd))); if (ret != 0) { exit(1); } @@ -1216,6 +1296,8 @@ void virtio_queue_notify(VirtIODevice *vdev, int n) { VirtQueue *vq = &vdev->vq[n]; + DBG("virtio_queue_notify(...)\n"); + if (!vq->vring.desc || vdev->broken) { return; } @@ -1232,10 +1314,111 @@ void virtio_queue_notify(VirtIODevice *vdev, int n) } +uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint8_t val; + + if (addr + sizeof(val) > vdev->config_len) { + DBG("virtio_config_readb failed\n"); + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + memcpy(&val, (uint8_t *)(vdev->config + addr), sizeof(uint8_t)); + + return val; +} + +uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint16_t val; + + if (addr + sizeof(val) > vdev->config_len) { + DBG("virtio_config_readw failed\n"); + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + memcpy(&val, (uint16_t *)(vdev->config + addr), sizeof(uint64_t)); + return val; +} + +uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint32_t val; + + if (addr + sizeof(val) > vdev->config_len) { + DBG("virtio_config_readl failed\n"); + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + memcpy(&val, (uint32_t *)(vdev->config + addr), sizeof(uint32_t)); + return val; +} + +void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint8_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + memcpy((uint8_t *)(vdev->config + addr), &val, sizeof(uint8_t)); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint16_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + memcpy((uint16_t *)(vdev->config + addr), &val, sizeof(uint16_t)); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = vdev->vdev_class; + uint32_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + memcpy((uint32_t *)(vdev->config + addr), &val, sizeof(uint32_t)); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + + + static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, unsigned size) { + uint64_t ret; + print_neg_flag(offset, 1); if (!vdev) { @@ -1268,6 +1451,25 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, offset -= VIRTIO_MMIO_CONFIG; /* TODO: To be implemented */ + DBG("VIRTIO_MMIO_CONFIG: size: %u, offset: %lu\n", size, offset); + + if (proxy->legacy) { + switch (size) { + case 1: + ret = virtio_config_readb(vdev, offset); + break; + case 2: + ret = virtio_config_readw(vdev, offset); + break; + case 4: + ret = virtio_config_readl(vdev, offset); + break; + default: + abort(); + } + DBG("VIRTIO_MMIO_CONFIG: ret: %lu\n", ret); + return ret; + } return 4; } @@ -1293,8 +1495,12 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, case VIRTIO_MMIO_DEVICE_FEATURES: if (proxy->legacy) { if (proxy->host_features_sel) { + DBG("attempt to read host features with " + "host_features_sel > 0 in legacy mode\n"); + DBG("vdev->host_features: 0x%lx\n", vdev->host_features); return 0; } else { + DBG("vdev->host_features: 0x%lx\n", vdev->host_features); return vdev->host_features; } } else { @@ -1381,7 +1587,29 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, if (offset >= VIRTIO_MMIO_CONFIG) { offset -= VIRTIO_MMIO_CONFIG; + /* TODO: To be implemented */ + DBG("VIRTIO_MMIO_CONFIG flag write\n"); + + if (proxy->legacy) { + switch (size) { + case 1: + virtio_config_writeb(vdev, offset, value); + break; + case 2: + virtio_config_writew(vdev, offset, value); + break; + case 4: + virtio_config_writel(vdev, offset, value); + break; + default: + DBG("VIRTIO_MMIO_CONFIG abort\n"); + abort(); + } + return; + } + DBG("write: VIRTIO_MMIO_CONFIG\n"); + return; } if (size != 4) { @@ -1401,7 +1629,9 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, if (proxy->guest_features_sel) { DBG("attempt to write guest features with " "guest_features_sel > 0 in legacy mode\n"); + DBG("Set driver features: 0x%lx\n", value); } else { + DBG("Set driver features: 0x%lx\n", value); virtio_set_features(vdev, value); } } else { @@ -1460,7 +1690,7 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, } else { (void)value; uint64_t desc_addr; - desc_addr = (uint64_t)mmap(NULL, 16 * PAGE_SIZE, + desc_addr = (uint64_t)mmap(NULL, 10 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); @@ -1596,6 +1826,8 @@ void adapter_read_write_cb(void) address->data, address->size); } + DBG("Return to the driver\n"); + /* * Note the driver that we have done * All the required actions. @@ -1736,6 +1968,7 @@ bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev) void virtio_loopback_bus_init(VirtioBus *k) { + DBG("virtio_loopback_bus_init(...)\n"); k->set_guest_notifiers = virtio_loopback_set_guest_notifiers; k->ioeventfd_enabled = virtio_loopback_ioeventfd_enabled; k->ioeventfd_assign = virtio_loopback_ioeventfd_assign; diff --git a/virtio_loopback.h b/virtio_loopback.h index 49ae219..779da70 100644 --- a/virtio_loopback.h +++ b/virtio_loopback.h @@ -31,6 +31,8 @@ #include "event_notifier.h" +#define sizeof_field(type, field) sizeof(((type *)0)->field) + /* Magic value ("virt" string) - Read Only */ #define VIRTIO_MMIO_MAGIC_VALUE 0x000 @@ -163,6 +165,7 @@ #define SHARE_BUF _IOC(_IOC_WRITE, 'k', 6, sizeof(uint64_t)) #define USED_INFO _IOC(_IOC_WRITE, 'k', 7, 0) #define DATA_INFO _IOC(_IOC_WRITE, 'k', 8, 0) +#define MAP_BLK _IOC(_IOC_WRITE, 'k', 9, 0) #define VIRTIO_PCI_VRING_ALIGN 4096 @@ -342,8 +345,11 @@ typedef struct VirtQueue { } VirtQueue; typedef struct VirtIORNG VirtIORNG; +typedef struct VirtIOInput VirtIOInput; typedef struct VHostUserRNG VHostUserRNG; typedef struct VirtioDeviceClass VirtioDeviceClass; +typedef struct VHostUserBlk VHostUserBlk; +typedef struct VhostUserInput VhostUserInput; typedef struct VirtioBus VirtioBus; typedef struct VirtIODevice { @@ -362,6 +368,7 @@ typedef struct VirtIODevice { uint32_t generation; int nvectors; VirtQueue *vq; + VirtQueue **vqs; uint16_t device_id; bool vm_running; bool broken; /* device in invalid state, needs reset */ @@ -375,7 +382,10 @@ typedef struct VirtIODevice { uint8_t device_endian; bool use_guest_notifier_mask; VirtIORNG *vrng; + VirtIOInput *vinput; VHostUserRNG *vhrng; + VHostUserBlk *vhublk; + VhostUserInput *vhuinput; } VirtIODevice; typedef struct efd_data { @@ -503,6 +513,8 @@ typedef struct VirtioDeviceClass { void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); void (*set_status)(VirtIODevice *vdev, uint8_t val); + void (*realize)(void); + void (*unrealize)(VirtIODevice *vdev); /* * For transitional devices, this is a bitmap of features * that are only exposed on the legacy interface but not @@ -537,6 +549,7 @@ typedef struct VirtioDeviceClass { */ int (*post_load)(VirtIODevice *vdev); bool (*primary_unplug_pending)(void *opaque); + struct vhost_dev *(*get_vhost)(VirtIODevice *vdev); } VirtioDeviceClass; /* Global variables */ @@ -546,7 +559,7 @@ extern int loopback_fd; void handle_input(VirtIODevice *vdev, VirtQueue *vq); void *my_select(void *data); void *wait_read_write(void *data); -void *my_notify(void *data); +void virtio_notify_config(VirtIODevice *vdev); void create_rng_struct(void); void print_neg_flag(uint64_t neg_flag, bool read); void adapter_read_write_cb(void); @@ -558,6 +571,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, unsigned max_in_bytes, unsigned max_out_bytes); void virtio_add_feature(uint64_t *features, unsigned int fbit); bool virtio_has_feature(uint64_t features, unsigned int fbit); +bool virtio_device_started(VirtIODevice *vdev, uint8_t status); int virtio_queue_empty(VirtQueue *vq); void *virtqueue_pop(VirtQueue *vq, size_t sz); @@ -604,6 +618,7 @@ void event_notifier_set_handler(EventNotifier *e, void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, unsigned int max, unsigned int *next); +void print_config(uint8_t *config); /* * Do we get callbacks when the ring is completely used, even if we've @@ -618,7 +633,7 @@ int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, * Legacy name for VIRTIO_F_ACCESS_PLATFORM * (for compatibility with old userspace) */ -#define VIRTIO_F_IOMMU_PLATFORM VIRTIO_F_ACCESS_PLATFORM +#define VIRTIO_F_IOMMU_PLATFORM 33 /* QEMU Aligned functions */ /* diff --git a/virtio_rng.h b/virtio_rng.h index 812ddb4..8147864 100644 --- a/virtio_rng.h +++ b/virtio_rng.h @@ -16,17 +16,14 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef VIRTIO_RNG -#define VIRTIO_RNG +#ifndef VIRTIO_RNG_DEV +#define VIRTIO_RNG_DEV #include "virtio_loopback.h" extern const char test_str[64]; -typedef void RngBackend; - typedef struct VirtIORNGConf { - RngBackend *rng; uint64_t max_bytes; uint32_t period_ms; } VirtIORNGConf; @@ -37,7 +34,6 @@ typedef struct VirtIORNG { /* Only one vq - guest puts buffer(s) on it when it needs entropy */ VirtQueue *vq; VirtIORNGConf conf; - RngBackend *rng; /* * We purposefully don't migrate this state. The quota will reset on the |