Commit f5ed034a authored by Timos Ampelikiotis's avatar Timos Ampelikiotis

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's avatarTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
parent bc0a4cca
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#CFLAGS := -Wall -Wextra -Werror #CFLAGS := -Wall -Wextra -Werror
#CFLAGS := -Wall -Wextra -Wno-unused-variable -Wno-unused-function #CFLAGS := -Wall -Wextra -Wno-unused-variable -Wno-unused-function
CFLAGS := -Wno-unused-variable -Wno-unused-function CFLAGS := -Wno-unused-variable -Wno-unused-function
CFLAGS += -DSERVER CFLAGS =
CC = CC =
ifeq ($(ARCH), arm64) ifeq ($(ARCH), arm64)
...@@ -33,11 +33,26 @@ endif ...@@ -33,11 +33,26 @@ endif
ifeq ($(VHOST_USER_RNG), 1) ifeq ($(VHOST_USER_RNG), 1)
CFLAGS += -DVHOST_USER_RNG_DEV 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 endif
INCL += -I . INCL += -I .
DEPS = adapter.h vhost_user_loopback.h event_notifier.h virtio_loopback.h 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) OBJS = $(SRC_C:.c=.o)
BINS = adapter BINS = adapter
......
...@@ -44,6 +44,16 @@ ...@@ -44,6 +44,16 @@
#include "vhost_user_loopback.h" #include "vhost_user_loopback.h"
#include "virtio_rng.h" #include "virtio_rng.h"
#include "vhost_user_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 */ /* Global variables */
int client_sock; int client_sock;
...@@ -54,6 +64,9 @@ struct vhost_user *vudev; ...@@ -54,6 +64,9 @@ struct vhost_user *vudev;
void vhost_user_adapter_init(void) void vhost_user_adapter_init(void)
{ {
DBG("Setup adapter data structures\n");
/* Init vhost-user device */ /* Init vhost-user device */
vudev = (struct vhost_user *)malloc(sizeof(struct vhost_user)); vudev = (struct vhost_user *)malloc(sizeof(struct vhost_user));
...@@ -85,6 +98,8 @@ void client(char *sock_path) ...@@ -85,6 +98,8 @@ void client(char *sock_path)
int rc, len; int rc, len;
struct sockaddr_un client_sockaddr; struct sockaddr_un client_sockaddr;
DBG("Create shared socket with vhost-user-device\n");
/* Initialize the struct to zero */ /* Initialize the struct to zero */
memset(&client_sockaddr, 0, sizeof(struct sockaddr_un)); memset(&client_sockaddr, 0, sizeof(struct sockaddr_un));
...@@ -93,7 +108,7 @@ void client(char *sock_path) ...@@ -93,7 +108,7 @@ void client(char *sock_path)
*/ */
client_sock = socket(AF_UNIX, SOCK_STREAM, 0); client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_sock == -1) { if (client_sock == -1) {
printf("SOCKET ERROR\n"); DBG("SOCKET ERROR\n");
exit(1); exit(1);
} }
...@@ -107,7 +122,7 @@ void client(char *sock_path) ...@@ -107,7 +122,7 @@ void client(char *sock_path)
len = sizeof(client_sockaddr); len = sizeof(client_sockaddr);
rc = connect(client_sock, (struct sockaddr *) &client_sockaddr, len); rc = connect(client_sock, (struct sockaddr *) &client_sockaddr, len);
if (rc == -1) { if (rc == -1) {
printf("CONNECT ERROR\n"); DBG("CONNECT ERROR\n");
close(client_sock); close(client_sock);
exit(1); exit(1);
} }
...@@ -121,7 +136,7 @@ static void help_args(void) ...@@ -121,7 +136,7 @@ static void help_args(void)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
#ifdef VHOST_USER_RNG_DEV #ifdef VHOST_USER
/* /*
* Check if the user has provided a socket path. * Check if the user has provided a socket path.
* If not, print the help messages. * If not, print the help messages.
...@@ -140,14 +155,37 @@ int main(int argc, char **argv) ...@@ -140,14 +155,37 @@ int main(int argc, char **argv)
/* Initialize the adapter data structures */ /* Initialize the adapter data structures */
vhost_user_adapter_init(); vhost_user_adapter_init();
/* Initialize the virtio/vhost-user device */ /* 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 #ifdef VHOST_USER_RNG_DEV
vhost_user_rng_realize(); /* <-- Enable that for vhost-user-rng */ 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 */ 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(); virtio_loopback_start();
return 0; return 0;
......
/* $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 */
...@@ -70,6 +70,8 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) ...@@ -70,6 +70,8 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
{ {
int i, r, e; int i, r, e;
DBG("vhost_dev_enable_notifiers(...)\n");
/* /*
* We will pass the notifiers to the kernel, make sure that QEMU * We will pass the notifiers to the kernel, make sure that QEMU
* doesn't interfere. * doesn't interfere.
...@@ -178,6 +180,8 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, ...@@ -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); 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); r = vhost_user_set_vring_call(&file);
if (r < 0) { if (r < 0) {
...@@ -194,6 +198,8 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, ...@@ -194,6 +198,8 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
uint64_t s, l, a; uint64_t s, l, a;
int r; int r;
DBG("vhost_virtqueue_start()\n");
int vhost_vq_index = vhost_user_get_vq_index(dev, idx); int vhost_vq_index = vhost_user_get_vq_index(dev, idx);
struct vhost_vring_file file = { struct vhost_vring_file file = {
.index = vhost_vq_index .index = vhost_vq_index
...@@ -278,6 +284,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, ...@@ -278,6 +284,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
vhost_virtqueue_mask(dev, vdev, idx, false); vhost_virtqueue_mask(dev, vdev, idx, false);
} }
DBG("vhost_virtqueue_start return successfully\n");
return 0; return 0;
} }
...@@ -289,6 +296,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) ...@@ -289,6 +296,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
hdev->started = true; hdev->started = true;
hdev->vdev = vdev; hdev->vdev = vdev;
DBG("vhost_dev_start()\n");
r = vhost_dev_set_features(hdev, hdev->log_enabled); r = vhost_dev_set_features(hdev, hdev->log_enabled);
if (r < 0) { if (r < 0) {
return r; return r;
...@@ -311,6 +320,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) ...@@ -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 */ /* This is used to exhange the loopback_fd to the vhost-user-device */
vhost_user_share_fd(); vhost_user_share_fd();
DBG("hdev->nvqs: %d\n", hdev->nvqs);
for (i = 0; i < hdev->nvqs; ++i) { for (i = 0; i < hdev->nvqs; ++i) {
r = vhost_virtqueue_start(hdev, r = vhost_virtqueue_start(hdev,
vdev, vdev,
...@@ -322,5 +333,85 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *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; 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;
}
/* /*
* Based on vhost.h of QEMU project
*
* Copyright 2022 Virtual Open Systems SAS. * Copyright 2022 Virtual Open Systems SAS.
* *
* This program is free software; you can redistribute it and/or modify * 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); ...@@ -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); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev);
void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev,
int n, bool mask); 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 */ #endif /* LOOPBACK_VHOST_H */
/*
* 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;
}
/*
* 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 */
/*
* 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");
}
/*
* 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 */
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
/* Project header files */ /* Project header files */
#include "virtio_loopback.h" #include "virtio_loopback.h"
#include "vhost_user_loopback.h" #include "vhost_user_loopback.h"
#include "vhost_loopback.h"
#include "event_notifier.h" #include "event_notifier.h"
#ifdef DEBUG #ifdef DEBUG
...@@ -402,6 +403,10 @@ int vhost_setup_slave_channel(struct vhost_dev *dev) ...@@ -402,6 +403,10 @@ int vhost_setup_slave_channel(struct vhost_dev *dev)
memcpy(msg.fds, &sv[1], sizeof(int)); memcpy(msg.fds, &sv[1], sizeof(int));
msg.fd_num = 1; msg.fd_num = 1;
/* FIXME: something missing here */
if (reply_supported) { if (reply_supported) {
msg.flags |= VHOST_USER_NEED_REPLY_MASK; msg.flags |= VHOST_USER_NEED_REPLY_MASK;
} }
...@@ -497,11 +502,13 @@ int vhost_set_vring_file(VhostUserRequest request, ...@@ -497,11 +502,13 @@ int vhost_set_vring_file(VhostUserRequest request,
int vhost_user_set_vring_kick(struct vhost_vring_file *file) 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); return vhost_set_vring_file(VHOST_USER_SET_VRING_KICK, file);
} }
int vhost_user_set_vring_call(struct vhost_vring_file *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); return vhost_set_vring_file(VHOST_USER_SET_VRING_CALL, file);
} }
...@@ -597,6 +604,214 @@ int vhost_virtqueue_init(struct vhost_dev *dev, ...@@ -597,6 +604,214 @@ int vhost_virtqueue_init(struct vhost_dev *dev,
return 0; 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 -------------------- */ /* -------------------- Vring functions -------------------- */
...@@ -651,6 +866,8 @@ int vhost_user_backend_init(struct vhost_dev *vhdev) ...@@ -651,6 +866,8 @@ int vhost_user_backend_init(struct vhost_dev *vhdev)
uint64_t features, protocol_features, ram_slots; uint64_t features, protocol_features, ram_slots;
int err; int err;
DBG("vhost_user_backend_init(...)\n");
err = vhost_user_get_features(&features); err = vhost_user_get_features(&features);
if (err < 0) { if (err < 0) {
DBG("vhost_backend_init failed\n"); DBG("vhost_backend_init failed\n");
...@@ -669,11 +886,28 @@ int vhost_user_backend_init(struct vhost_dev *vhdev) ...@@ -669,11 +886,28 @@ int vhost_user_backend_init(struct vhost_dev *vhdev)
vhdev->protocol_features = vhdev->protocol_features =
protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK; protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK;
/* /*
* TODO: Disable config bit for the rng, this might be usefull * FIXME: Disable VHOST_USER_PROTOCOL_F_SLAVE_REQ for the moment
* when new devices are added * 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); err = vhost_user_set_protocol_features(vhdev->protocol_features);
if (err < 0) { if (err < 0) {
...@@ -764,6 +998,8 @@ void vhost_dev_init(struct vhost_dev *vhdev) ...@@ -764,6 +998,8 @@ void vhost_dev_init(struct vhost_dev *vhdev)
int r, n_initialized_vqs = 0; int r, n_initialized_vqs = 0;
unsigned int i; unsigned int i;
DBG("vhost_dev_init(...)\n");
/* Vhost conf */ /* Vhost conf */
vhdev->migration_blocker = NULL; vhdev->migration_blocker = NULL;
...@@ -778,6 +1014,8 @@ void vhost_dev_init(struct vhost_dev *vhdev) ...@@ -778,6 +1014,8 @@ void vhost_dev_init(struct vhost_dev *vhdev)
if (r < 0) { if (r < 0) {
DBG("vhost_get_features failed\n"); 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) { for (i = 0; i < vhdev->nvqs; ++i, ++n_initialized_vqs) {
r = vhost_virtqueue_init(vhdev, vhdev->vqs + i, vhdev->vq_index + i); r = vhost_virtqueue_init(vhdev, vhdev->vqs + i, vhdev->vq_index + i);
...@@ -795,7 +1033,7 @@ void vhost_dev_init(struct vhost_dev *vhdev) ...@@ -795,7 +1033,7 @@ void vhost_dev_init(struct vhost_dev *vhdev)
* busyloop_timeout); * busyloop_timeout);
* if (r < 0) { * if (r < 0) {
* DBG("Failed to set busyloop timeout\n"); * DBG("Failed to set busyloop timeout\n");
* //goto fail_busyloop; * return -1;
* } * }
* } * }
* } * }
......
...@@ -59,6 +59,11 @@ struct vhost_virtqueue { ...@@ -59,6 +59,11 @@ struct vhost_virtqueue {
struct vhost_dev *dev; 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 { struct vhost_dev {
VirtIODevice *vdev; VirtIODevice *vdev;
struct vhost_virtqueue *vqs; struct vhost_virtqueue *vqs;
...@@ -81,6 +86,7 @@ struct vhost_dev { ...@@ -81,6 +86,7 @@ struct vhost_dev {
void *migration_blocker; void *migration_blocker;
/* Vhost-user struct */ /* Vhost-user struct */
uint64_t memory_slots; uint64_t memory_slots;
const VhostDevConfigOps *config_ops;
}; };
struct vhost_user { struct vhost_user {
...@@ -798,5 +804,18 @@ int vhost_user_set_vring_base(struct vhost_dev *dev, ...@@ -798,5 +804,18 @@ int vhost_user_set_vring_base(struct vhost_dev *dev,
struct vhost_vring_state *ring); struct vhost_vring_state *ring);
int vhost_user_set_vring_addr(struct vhost_dev *dev, int vhost_user_set_vring_addr(struct vhost_dev *dev,
struct vhost_vring_addr *addr); 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 */ #endif /* LIBVHOST_USER_H */
/*
* 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
/*
* 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;
}
...@@ -60,6 +60,9 @@ ...@@ -60,6 +60,9 @@
#include <pthread.h> #include <pthread.h>
#include <limits.h> #include <limits.h>
/* TODO: Deleteit, only for testing */
#include <linux/virtio_blk.h>
#ifdef DEBUG #ifdef DEBUG
#define DBG(...) printf("virtio-loopback: " __VA_ARGS__) #define DBG(...) printf("virtio-loopback: " __VA_ARGS__)
#else #else
...@@ -76,7 +79,7 @@ int fd; ...@@ -76,7 +79,7 @@ int fd;
int loopback_fd; int loopback_fd;
virtio_device_info_struct_t device_info; virtio_device_info_struct_t device_info;
virtio_neg_t *address = NULL; virtio_neg_t *address;
VirtIOMMIOProxy *proxy; VirtIOMMIOProxy *proxy;
...@@ -100,6 +103,15 @@ static int virtio_validate_features(VirtIODevice *vdev) ...@@ -100,6 +103,15 @@ static int virtio_validate_features(VirtIODevice *vdev)
return 0; 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) void virtio_set_started(VirtIODevice *vdev, bool started)
{ {
...@@ -116,6 +128,8 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) ...@@ -116,6 +128,8 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val)
{ {
VirtioDeviceClass *k = vdev->vdev_class; VirtioDeviceClass *k = vdev->vdev_class;
DBG("virtio_set_status(...)\n");
if (virtio_has_feature(vdev->guest_features, VIRTIO_F_VERSION_1)) { if (virtio_has_feature(vdev->guest_features, VIRTIO_F_VERSION_1)) {
if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) &&
val & VIRTIO_CONFIG_S_FEATURES_OK) { val & VIRTIO_CONFIG_S_FEATURES_OK) {
...@@ -133,6 +147,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) ...@@ -133,6 +147,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val)
} }
if (k->set_status) { if (k->set_status) {
DBG("k->set_status\n");
k->set_status(vdev, val); k->set_status(vdev, val);
} }
...@@ -400,6 +415,24 @@ static void virtio_irq(VirtQueue *vq) ...@@ -400,6 +415,24 @@ static void virtio_irq(VirtQueue *vq)
virtio_notify_vector(vq->vdev); 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) void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
{ {
if (!virtio_should_notify(vdev, vq)) { if (!virtio_should_notify(vdev, vq)) {
...@@ -490,6 +523,8 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, ...@@ -490,6 +523,8 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg,
{ {
unsigned num_sg = *p_num_sg; unsigned num_sg = *p_num_sg;
bool ok = false; bool ok = false;
uint64_t mmap_addr;
int ioctl_res;
if (!sz) { if (!sz) {
DBG("virtio: zero sized buffers are not allowed\n"); 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, ...@@ -505,11 +540,51 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg,
goto out; goto out;
} }
ioctl(fd, SHARE_BUF, &pa); DBG("\tpa address is: 0x%lx\n", pa);
iov[num_sg].iov_base = mmap(NULL, 8192, PROT_READ | PROT_WRITE, memcpy(&mmap_addr, &pa, sizeof(uint64_t));
MAP_SHARED, fd, 0); ioctl_res = ioctl(loopback_fd, SHARE_BUF, &mmap_addr);
/* 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; 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) { if (!iov[num_sg].iov_base) {
DBG("virtio: bogus descriptor or out of resources\n"); DBG("virtio: bogus descriptor or out of resources\n");
...@@ -916,14 +991,16 @@ void print_neg_flag(uint64_t neg_flag, bool read) ...@@ -916,14 +991,16 @@ void print_neg_flag(uint64_t neg_flag, bool read)
case VIRTIO_MMIO_CONFIG_GENERATION: /* 0x0fc */ case VIRTIO_MMIO_CONFIG_GENERATION: /* 0x0fc */
DBG("VIRTIO_MMIO_CONFIG_GENERATION\n"); DBG("VIRTIO_MMIO_CONFIG_GENERATION\n");
break; break;
case VIRTIO_MMIO_CONFIG: /* 0x100 */
DBG("VIRTIO_MMIO_CONFIG\n");
break;
default: 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; return;
} }
} }
int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val)
...@@ -944,6 +1021,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) ...@@ -944,6 +1021,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
* has finished. * has finished.
*/ */
if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) {
DBG("virtio_set_features: vdev->status "
"& VIRTIO_CONFIG_S_FEATURES_OK\n");
return -EINVAL; return -EINVAL;
} }
ret = virtio_set_features_nocheck(vdev, val); ret = virtio_set_features_nocheck(vdev, val);
...@@ -958,20 +1037,18 @@ static void virtio_queue_guest_notifier_read(EventNotifier *n) ...@@ -958,20 +1037,18 @@ static void virtio_queue_guest_notifier_read(EventNotifier *n)
int vhost_user_loopback_eventfd = 0; int vhost_user_loopback_eventfd = 0;
void *loopback_event_select(void *data) void *loopback_event_select(void *wfd)
{ {
int retval; int retval;
uint64_t eftd_ctr; uint64_t eftd_ctr;
fd_set rfds; fd_set rfds;
int s; int s;
(void) data;
DBG("\nWaiting event from vhost-user-device\n"); DBG("\nWaiting event from vhost-user-device\n");
fflush(stdout); fflush(stdout);
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(vhost_user_loopback_eventfd, &rfds); FD_SET(*(int *)wfd, &rfds);
while (1) { while (1) {
...@@ -983,11 +1060,13 @@ void *loopback_event_select(void *data) ...@@ -983,11 +1060,13 @@ void *loopback_event_select(void *data)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else if (retval > 0) { } 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)) { if (s != sizeof(uint64_t)) {
DBG("\neventfd read error. Exiting..."); DBG("\neventfd read error. Exiting...");
exit(1); exit(1);
} else { } else {
DBG("\n\nEvent has come from the vhost-user-device "
"(eventfd: %d)\n\n", *(int *)wfd);
virtio_irq(global_vdev->vq); virtio_irq(global_vdev->vq);
} }
...@@ -1007,7 +1086,8 @@ void event_notifier_set_handler(EventNotifier *e, ...@@ -1007,7 +1086,8 @@ void event_notifier_set_handler(EventNotifier *e,
vhost_user_loopback_eventfd = e->wfd; vhost_user_loopback_eventfd = e->wfd;
if (vhost_user_loopback_eventfd > 0) { 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) { if (ret != 0) {
exit(1); exit(1);
} }
...@@ -1216,6 +1296,8 @@ void virtio_queue_notify(VirtIODevice *vdev, int n) ...@@ -1216,6 +1296,8 @@ void virtio_queue_notify(VirtIODevice *vdev, int n)
{ {
VirtQueue *vq = &vdev->vq[n]; VirtQueue *vq = &vdev->vq[n];
DBG("virtio_queue_notify(...)\n");
if (!vq->vring.desc || vdev->broken) { if (!vq->vring.desc || vdev->broken) {
return; return;
} }
...@@ -1232,10 +1314,111 @@ void virtio_queue_notify(VirtIODevice *vdev, int n) ...@@ -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, static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset,
unsigned size) unsigned size)
{ {
uint64_t ret;
print_neg_flag(offset, 1); print_neg_flag(offset, 1);
if (!vdev) { if (!vdev) {
...@@ -1268,6 +1451,25 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, ...@@ -1268,6 +1451,25 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset,
offset -= VIRTIO_MMIO_CONFIG; offset -= VIRTIO_MMIO_CONFIG;
/* TODO: To be implemented */ /* 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; return 4;
} }
...@@ -1293,8 +1495,12 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, ...@@ -1293,8 +1495,12 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset,
case VIRTIO_MMIO_DEVICE_FEATURES: case VIRTIO_MMIO_DEVICE_FEATURES:
if (proxy->legacy) { if (proxy->legacy) {
if (proxy->host_features_sel) { 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; return 0;
} else { } else {
DBG("vdev->host_features: 0x%lx\n", vdev->host_features);
return vdev->host_features; return vdev->host_features;
} }
} else { } else {
...@@ -1381,7 +1587,29 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, ...@@ -1381,7 +1587,29 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset,
if (offset >= VIRTIO_MMIO_CONFIG) { if (offset >= VIRTIO_MMIO_CONFIG) {
offset -= VIRTIO_MMIO_CONFIG; offset -= VIRTIO_MMIO_CONFIG;
/* TODO: To be implemented */ /* 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; return;
} }
if (size != 4) { if (size != 4) {
...@@ -1401,7 +1629,9 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, ...@@ -1401,7 +1629,9 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset,
if (proxy->guest_features_sel) { if (proxy->guest_features_sel) {
DBG("attempt to write guest features with " DBG("attempt to write guest features with "
"guest_features_sel > 0 in legacy mode\n"); "guest_features_sel > 0 in legacy mode\n");
DBG("Set driver features: 0x%lx\n", value);
} else { } else {
DBG("Set driver features: 0x%lx\n", value);
virtio_set_features(vdev, value); virtio_set_features(vdev, value);
} }
} else { } else {
...@@ -1460,7 +1690,7 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, ...@@ -1460,7 +1690,7 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset,
} else { } else {
(void)value; (void)value;
uint64_t desc_addr; 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, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0); MAP_SHARED, fd, 0);
...@@ -1596,6 +1826,8 @@ void adapter_read_write_cb(void) ...@@ -1596,6 +1826,8 @@ void adapter_read_write_cb(void)
address->data, address->size); address->data, address->size);
} }
DBG("Return to the driver\n");
/* /*
* Note the driver that we have done * Note the driver that we have done
* All the required actions. * All the required actions.
...@@ -1736,6 +1968,7 @@ bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev) ...@@ -1736,6 +1968,7 @@ bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev)
void virtio_loopback_bus_init(VirtioBus *k) void virtio_loopback_bus_init(VirtioBus *k)
{ {
DBG("virtio_loopback_bus_init(...)\n");
k->set_guest_notifiers = virtio_loopback_set_guest_notifiers; k->set_guest_notifiers = virtio_loopback_set_guest_notifiers;
k->ioeventfd_enabled = virtio_loopback_ioeventfd_enabled; k->ioeventfd_enabled = virtio_loopback_ioeventfd_enabled;
k->ioeventfd_assign = virtio_loopback_ioeventfd_assign; k->ioeventfd_assign = virtio_loopback_ioeventfd_assign;
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include "event_notifier.h" #include "event_notifier.h"
#define sizeof_field(type, field) sizeof(((type *)0)->field)
/* Magic value ("virt" string) - Read Only */ /* Magic value ("virt" string) - Read Only */
#define VIRTIO_MMIO_MAGIC_VALUE 0x000 #define VIRTIO_MMIO_MAGIC_VALUE 0x000
...@@ -163,6 +165,7 @@ ...@@ -163,6 +165,7 @@
#define SHARE_BUF _IOC(_IOC_WRITE, 'k', 6, sizeof(uint64_t)) #define SHARE_BUF _IOC(_IOC_WRITE, 'k', 6, sizeof(uint64_t))
#define USED_INFO _IOC(_IOC_WRITE, 'k', 7, 0) #define USED_INFO _IOC(_IOC_WRITE, 'k', 7, 0)
#define DATA_INFO _IOC(_IOC_WRITE, 'k', 8, 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 #define VIRTIO_PCI_VRING_ALIGN 4096
...@@ -342,8 +345,11 @@ typedef struct VirtQueue { ...@@ -342,8 +345,11 @@ typedef struct VirtQueue {
} VirtQueue; } VirtQueue;
typedef struct VirtIORNG VirtIORNG; typedef struct VirtIORNG VirtIORNG;
typedef struct VirtIOInput VirtIOInput;
typedef struct VHostUserRNG VHostUserRNG; typedef struct VHostUserRNG VHostUserRNG;
typedef struct VirtioDeviceClass VirtioDeviceClass; typedef struct VirtioDeviceClass VirtioDeviceClass;
typedef struct VHostUserBlk VHostUserBlk;
typedef struct VhostUserInput VhostUserInput;
typedef struct VirtioBus VirtioBus; typedef struct VirtioBus VirtioBus;
typedef struct VirtIODevice { typedef struct VirtIODevice {
...@@ -362,6 +368,7 @@ typedef struct VirtIODevice { ...@@ -362,6 +368,7 @@ typedef struct VirtIODevice {
uint32_t generation; uint32_t generation;
int nvectors; int nvectors;
VirtQueue *vq; VirtQueue *vq;
VirtQueue **vqs;
uint16_t device_id; uint16_t device_id;
bool vm_running; bool vm_running;
bool broken; /* device in invalid state, needs reset */ bool broken; /* device in invalid state, needs reset */
...@@ -375,7 +382,10 @@ typedef struct VirtIODevice { ...@@ -375,7 +382,10 @@ typedef struct VirtIODevice {
uint8_t device_endian; uint8_t device_endian;
bool use_guest_notifier_mask; bool use_guest_notifier_mask;
VirtIORNG *vrng; VirtIORNG *vrng;
VirtIOInput *vinput;
VHostUserRNG *vhrng; VHostUserRNG *vhrng;
VHostUserBlk *vhublk;
VhostUserInput *vhuinput;
} VirtIODevice; } VirtIODevice;
typedef struct efd_data { typedef struct efd_data {
...@@ -503,6 +513,8 @@ typedef struct VirtioDeviceClass { ...@@ -503,6 +513,8 @@ typedef struct VirtioDeviceClass {
void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
void (*reset)(VirtIODevice *vdev); void (*reset)(VirtIODevice *vdev);
void (*set_status)(VirtIODevice *vdev, uint8_t val); void (*set_status)(VirtIODevice *vdev, uint8_t val);
void (*realize)(void);
void (*unrealize)(VirtIODevice *vdev);
/* /*
* For transitional devices, this is a bitmap of features * For transitional devices, this is a bitmap of features
* that are only exposed on the legacy interface but not * that are only exposed on the legacy interface but not
...@@ -537,6 +549,7 @@ typedef struct VirtioDeviceClass { ...@@ -537,6 +549,7 @@ typedef struct VirtioDeviceClass {
*/ */
int (*post_load)(VirtIODevice *vdev); int (*post_load)(VirtIODevice *vdev);
bool (*primary_unplug_pending)(void *opaque); bool (*primary_unplug_pending)(void *opaque);
struct vhost_dev *(*get_vhost)(VirtIODevice *vdev);
} VirtioDeviceClass; } VirtioDeviceClass;
/* Global variables */ /* Global variables */
...@@ -546,7 +559,7 @@ extern int loopback_fd; ...@@ -546,7 +559,7 @@ extern int loopback_fd;
void handle_input(VirtIODevice *vdev, VirtQueue *vq); void handle_input(VirtIODevice *vdev, VirtQueue *vq);
void *my_select(void *data); void *my_select(void *data);
void *wait_read_write(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 create_rng_struct(void);
void print_neg_flag(uint64_t neg_flag, bool read); void print_neg_flag(uint64_t neg_flag, bool read);
void adapter_read_write_cb(void); void adapter_read_write_cb(void);
...@@ -558,6 +571,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, ...@@ -558,6 +571,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
unsigned max_in_bytes, unsigned max_out_bytes); unsigned max_in_bytes, unsigned max_out_bytes);
void virtio_add_feature(uint64_t *features, unsigned int fbit); void virtio_add_feature(uint64_t *features, unsigned int fbit);
bool virtio_has_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); int virtio_queue_empty(VirtQueue *vq);
void *virtqueue_pop(VirtQueue *vq, size_t sz); void *virtqueue_pop(VirtQueue *vq, size_t sz);
...@@ -604,6 +618,7 @@ void event_notifier_set_handler(EventNotifier *e, ...@@ -604,6 +618,7 @@ void event_notifier_set_handler(EventNotifier *e,
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
unsigned int max, unsigned int *next); 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 * 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, ...@@ -618,7 +633,7 @@ int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
* Legacy name for VIRTIO_F_ACCESS_PLATFORM * Legacy name for VIRTIO_F_ACCESS_PLATFORM
* (for compatibility with old userspace) * (for compatibility with old userspace)
*/ */
#define VIRTIO_F_IOMMU_PLATFORM VIRTIO_F_ACCESS_PLATFORM #define VIRTIO_F_IOMMU_PLATFORM 33
/* QEMU Aligned functions */ /* QEMU Aligned functions */
/* /*
......
...@@ -16,17 +16,14 @@ ...@@ -16,17 +16,14 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifndef VIRTIO_RNG #ifndef VIRTIO_RNG_DEV
#define VIRTIO_RNG #define VIRTIO_RNG_DEV
#include "virtio_loopback.h" #include "virtio_loopback.h"
extern const char test_str[64]; extern const char test_str[64];
typedef void RngBackend;
typedef struct VirtIORNGConf { typedef struct VirtIORNGConf {
RngBackend *rng;
uint64_t max_bytes; uint64_t max_bytes;
uint32_t period_ms; uint32_t period_ms;
} VirtIORNGConf; } VirtIORNGConf;
...@@ -37,7 +34,6 @@ typedef struct VirtIORNG { ...@@ -37,7 +34,6 @@ typedef struct VirtIORNG {
/* Only one vq - guest puts buffer(s) on it when it needs entropy */ /* Only one vq - guest puts buffer(s) on it when it needs entropy */
VirtQueue *vq; VirtQueue *vq;
VirtIORNGConf conf; VirtIORNGConf conf;
RngBackend *rng;
/* /*
* We purposefully don't migrate this state. The quota will reset on the * We purposefully don't migrate this state. The quota will reset on the
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment