summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2022-11-04 15:32:11 +0100
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-03 15:18:54 +0300
commita3fcee5911bf760f9f4522e94cb9e6ab22a7eb95 (patch)
tree9bc175084b6bc1da48651fdc3767567e2a54c22e
parent2b09c69cc896841c7828408083f6cb443fef8612 (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--Makefile19
-rw-r--r--adapter.c50
-rw-r--r--queue.h142
-rw-r--r--vhost_loopback.c91
-rw-r--r--vhost_loopback.h35
-rw-r--r--vhost_user_blk.c514
-rw-r--r--vhost_user_blk.h59
-rw-r--r--vhost_user_input.c224
-rw-r--r--vhost_user_input.h187
-rw-r--r--vhost_user_loopback.c246
-rw-r--r--vhost_user_loopback.h19
-rw-r--r--virtio_blk.h95
-rw-r--r--virtio_input.c284
-rw-r--r--virtio_loopback.c263
-rw-r--r--virtio_loopback.h19
-rw-r--r--virtio_rng.h8
16 files changed, 2220 insertions, 35 deletions
diff --git a/Makefile b/Makefile
index af51327..218d7a6 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/adapter.c b/adapter.c
index 9474076..c907acd 100644
--- a/adapter.c
+++ b/adapter.c
@@ -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;
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000..a2505e2
--- /dev/null
+++ b/queue.h
@@ -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