This patch introduces the API to create destroy the virtio ring. The
configuration API for find the number of queues and setting up
individual queues for a device.
Signed-off-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
---
plat/drivers/include/virtio/virtio.h | 138 +++++++++++++++++++---
plat/drivers/include/virtio/virtqueue.h | 145 +++++++++++++++++++++++
plat/drivers/virtio/virtio_bus.c | 6 +-
plat/drivers/virtio/virtio_pci.c | 147 ++++++++++++++++++------
plat/drivers/virtio/virtio_ring.c | 196 ++++++++++++++++++++++++++++++++
plat/kvm/Config.uk | 1 +
plat/kvm/Makefile.uk | 2 +
7 files changed, 579 insertions(+), 56 deletions(-)
create mode 100644 plat/drivers/include/virtio/virtqueue.h
create mode 100644 plat/drivers/virtio/virtio_ring.c
diff --git a/plat/drivers/include/virtio/virtio.h
b/plat/drivers/include/virtio/virtio.h
index b01faf6..1dfa495 100644
--- a/plat/drivers/include/virtio/virtio.h
+++ b/plat/drivers/include/virtio/virtio.h
@@ -42,6 +42,7 @@
#include <uk/arch/lcpu.h>
#include <uk/alloc.h>
#include <virtio/virtio_config.h>
+#include <virtio/virtqueue.h>
#ifdef __cplusplus
extern "C" {
@@ -80,20 +81,27 @@ struct virtio_dev_id {
*/
struct virtio_config_ops {
/** Resetting the device */
- void (*reset_device)(struct virtio_dev *vdev);
+ void (*device_reset)(struct virtio_dev *vdev);
/** Set configuration option */
- int (*set_config)(struct virtio_dev *vdev, __u16 offset,
- const void *buf, __u32 len);
+ int (*config_set)(struct virtio_dev *vdev, __u16 offset,
+ const void *buf, __u32 len);
/** Get configuration option */
- int (*get_config)(struct virtio_dev *vdev, __u16 offset, void *buf,
- __u32 len, __u8 type_len);
+ int (*config_get)(struct virtio_dev *vdev, __u16 offset, void *buf,
+ __u32 len, __u8 type_len);
/** Get the feature */
- __u64 (*get_features)(struct virtio_dev *vdev);
+ __u64 (*features_get)(struct virtio_dev *vdev);
/** Set the feature */
- void (*set_features)(struct virtio_dev *vdev, __u64 features);
+ void (*features_set)(struct virtio_dev *vdev, __u64 features);
/** Get and Set Status */
- __u8 (*get_status)(struct virtio_dev *vdev);
- void (*set_status)(struct virtio_dev *vdev, __u8 status);
+ __u8 (*status_get)(struct virtio_dev *vdev);
+ void (*status_set)(struct virtio_dev *vdev, __u8 status);
+ /** Find the virtqueue */
+ int (*vqs_find)(struct virtio_dev *vdev, __u16 num_vq, __u16 *vq_size);
+ /** Setup the virtqueue */
+ struct virtqueue *(*vq_setup)(struct virtio_dev *vdev, __u16 num_desc,
+ __u16 queue_id,
+ virtqueue_callback_t callback,
+ struct uk_alloc *a);
};
/**
@@ -116,6 +124,8 @@ struct virtio_driver {
struct virtio_dev {
/* Feature bit describing the virtio device */
__u64 features;
+ /* List of the virtqueue for the device */
+ UK_TAILQ_HEAD(virtqueue_head, struct virtqueue) vqs;
/* Private data of the driver */
void *priv;
/* Virtio device identifier */
@@ -142,6 +152,7 @@ void _virtio_register_driver(struct virtio_driver *vdrv);
* The status to update.
* @return
* 0 on successful updating the status.
+ * -ENOTSUP, if the operation is not supported on the virtio device.
*/
static inline int virtio_dev_status_update(struct virtio_dev *vdev, __u8
status)
{
@@ -149,32 +160,63 @@ static inline int virtio_dev_status_update(struct
virtio_dev *vdev, __u8 status)
UK_ASSERT(vdev);
- if (likely(vdev->cops->set_status)) {
- vdev->cops->set_status(vdev, status);
+ if (likely(vdev->cops->status_set)) {
+ vdev->cops->status_set(vdev, status);
rc = 0;
}
return rc;
}
+/**
+ * The function to get the feature supported by the device.
+ * @param vdev
+ * Reference to the virtio device.
+ *
+ * @return __u64
+ * A bit map of the feature supported by the device.
+ */
static inline __u64 virtio_feature_get(struct virtio_dev *vdev)
{
__u64 features = 0;
UK_ASSERT(vdev);
- if (likely(vdev->cops->get_features))
- features = vdev->cops->get_features(vdev);
+ if (likely(vdev->cops->features_get))
+ features = vdev->cops->features_get(vdev);
return features;
}
+/**
+ * The function to set the negotiated features.
+ * @param vdev
+ * Reference to the virtio device.
+ * @param feature
+ * A bit map of the feature negotiated.
+ */
static inline void virtio_feature_set(struct virtio_dev *vdev, __u32 feature)
{
UK_ASSERT(vdev);
- if (likely(vdev->cops->set_features))
- vdev->cops->set_features(vdev, feature);
+ if (likely(vdev->cops->features_set))
+ vdev->cops->features_set(vdev, feature);
}
+/**
+ * Get the configuration information from the virtio device.
+ * @param vdev
+ * Reference to the virtio device.
+ * @param offset
+ * Offset into the virtio device configuration space.
+ * @param buf
+ * A buffer to store the configuration information.
+ * @param len
+ * The length of the buffer.
+ * @param type_len
+ * The data type of the configuration data.
+ * @return int
+ * 0, on successful reading the configuration space.
+ * < 0, on error.
+ */
static inline int virtio_config_get(struct virtio_dev *vdev, __u16 offset,
void *buf, __u32 len, __u8 type_len)
{
@@ -182,12 +224,74 @@ static inline int virtio_config_get(struct virtio_dev
*vdev, __u16 offset,
UK_ASSERT(vdev);
- if (likely(vdev->cops->get_config))
- rc = vdev->cops->get_config(vdev, offset, buf, len, type_len);
+ if (likely(vdev->cops->config_get))
+ rc = vdev->cops->config_get(vdev, offset, buf, len, type_len);
+
+ return rc;
+}
+
+/**
+ * The helper function to find the number of the vqs supported on the device.
+ * @param vdev
+ * A reference to the virtio device.
+ * @param total_vqs
+ * The total number of virtqueues requested.
+ * @param vq_size
+ * An array of max descriptors on each virtqueue found on the
+ * virtio device
+ * @return int
+ * On success, the function return the number of available virtqueues
+ * On error,
+ * -ENOTSUP if the function is not supported.
+ */
+static inline int virtio_find_vqs(struct virtio_dev *vdev, __u16 total_vqs,
+ __u16 *vq_size)
+{
+ int rc = -ENOTSUP;
+
+ UK_ASSERT(vdev);
+
+ if (likely(vdev->cops->vqs_find))
+ rc = vdev->cops->vqs_find(vdev, total_vqs, vq_size);
return rc;
}
+/**
+ * A helper function to setup an individual virtqueue.
+ * @param vdev
+ * Reference to the virtio device.
+ * @param vq_id
+ * The virtqueue queue id.
+ * @param nr_desc
+ * The count of the descriptor to be configured.
+ * @param callback
+ * A reference to callback function to invoked by the virtio device on an
+ * interrupt from the virtqueue.
+ * @param a
+ * A reference to the allocator.
+ *
+ * @return struct virtqueue *
+ * On success, a reference to the virtqueue.
+ * On error,
+ * -ENOTSUP operation not supported on the device.
+ * -ENOMEM Failed allocating the virtqueue.
+ */
+static inline struct virtqueue *virtio_vqueue_setup(struct virtio_dev *vdev,
+ __u16 vq_id, __u16 nr_desc,
+ virtqueue_callback_t callback,
+ struct uk_alloc *a)
+{
+ struct virtqueue *vq = ERR2PTR(-ENOTSUP);
+
+ UK_ASSERT(vdev && a);
+
+ if (likely(vdev->cops->vq_setup))
+ vq = vdev->cops->vq_setup(vdev, vq_id, nr_desc, callback, a);
+
+ return vq;
+}
+
static inline int virtio_has_features(__u64 features, __u8 bpos)
{
__u64 tmp_feature = 0;
diff --git a/plat/drivers/include/virtio/virtqueue.h
b/plat/drivers/include/virtio/virtqueue.h
new file mode 100644
index 0000000..a8a4bc0
--- /dev/null
+++ b/plat/drivers/include/virtio/virtqueue.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
+ *
+ * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+
+#ifndef __PLAT_DRV_VIRTQUEUE_H__
+#define __PLAT_DRV_VIRTQUEUE_H__
+
+#include <uk/config.h>
+#include <uk/list.h>
+#include <uk/sglist.h>
+#include <uk/arch/types.h>
+#include <virtio/virtio_ring.h>
+#include <virtio/virtio_config.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Type declarations
+ */
+struct virtqueue;
+struct virtio_dev;
+typedef int (*virtqueue_callback_t)(struct virtqueue *, void *priv);
+typedef int (*virtqueue_notify_host_t)(struct virtio_dev *, __u16 queue_nr);
+
+/**
+ * Structure to describe the virtqueue.
+ */
+struct virtqueue {
+ /* Reference the virtio_dev it belong to */
+ struct virtio_dev *vdev;
+ /* Virtqueue identifier */
+ __u16 queue_id;
+ /* Notify to the host */
+ virtqueue_notify_host_t vq_notify_host;
+ /* Callback from the virtqueue */
+ virtqueue_callback_t vq_callback;
+ /* Next entry of the queue */
+ UK_TAILQ_ENTRY(struct virtqueue) next;
+ /* Private data structure used by the driver of the queue */
+ void *priv;
+};
+
+/**
+ * Fetch the physical address of the descriptor ring.
+ * @param vq
+ * Reference to the virtqueue.
+ *
+ * @return
+ * Return the guest physical address of the vring.
+ */
+__phys_addr virtqueue_physaddr(struct virtqueue *vq);
+
+/**
+ * Negotiate with the virtqueue features.
+ * @param feature_set
+ * The feature set the device request.
+ *
+ * @return __u64
+ * The negotiated feature set.
+ */
+__u64 virtqueue_feature_negotiate(__u64 feature_set);
+
+/**
+ * Allocate a virtqueue.
+ * @param queue_id
+ * The virtqueue hw id.
+ * @param nr_descs
+ * The number of descriptor for the queue.
+ * @param align
+ * The memory alignment for the ring memory.
+ * @param callback
+ * A reference to callback to the virtio-dev.
+ * @param notify
+ * A reference to notification function to the host.
+ * @param vdev:
+ * A reference to the virtio device.
+ * @param a:
+ * A reference to the allocator.
+ *
+ * @return struct virtqueue *
+ * On success, return a reference to the virtqueue.
+ * On failure,
+ * -ENOMEM: Failed to allocate the queue.
+ */
+struct virtqueue *virtqueue_create(__u16 queue_id, __u16 nr_descs, __u16 align,
+ virtqueue_callback_t callback,
+ virtqueue_notify_host_t notify,
+ struct virtio_dev *vdev, struct uk_alloc *a);
+
+/**
+ * Check the virtqueue if full.
+ * @param vq
+ * A reference to the virtqueue.
+ * @return int
+ * 1 on full,
+ * 0 otherwise
+ */
+int virtqueue_is_full(struct virtqueue *vq);
+
+/*
+ * Destroy a virtual queue
+ * @param vq
+ * A reference to the virtual queue
+ * @param a
+ * Reference to the memory allocator
+ */
+void virtqueue_destroy(struct virtqueue *vq, struct uk_alloc *a);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PLAT_DRV_VIRTQUEUE_H__ */
diff --git a/plat/drivers/virtio/virtio_bus.c b/plat/drivers/virtio/virtio_bus.c
index ef66ae8..b0ab9c8 100644
--- a/plat/drivers/virtio/virtio_bus.c
+++ b/plat/drivers/virtio/virtio_bus.c
@@ -92,8 +92,8 @@ static int virtio_device_reinit(struct virtio_dev *vdev)
* This may not be necessary while initializing the device for the first
* time.
*/
- if (vdev->cops->reset_device) {
- vdev->cops->reset_device(vdev);
+ if (vdev->cops->device_reset) {
+ vdev->cops->device_reset(vdev);
/* Set the device status */
vdev->status = VIRTIO_DEV_RESET;
}
@@ -140,6 +140,8 @@ int virtio_register_device(struct virtio_dev *vdev)
return rc;
}
+ /* Initialize the virtqueue list */
+ UK_TAILQ_INIT(&vdev->vqs);
/* Calling the driver add device */
rc = drv->add_dev(vdev);
diff --git a/plat/drivers/virtio/virtio_pci.c b/plat/drivers/virtio/virtio_pci.c
index ab2f2c8..d07b011 100644
--- a/plat/drivers/virtio/virtio_pci.c
+++ b/plat/drivers/virtio/virtio_pci.c
@@ -35,16 +35,13 @@
#include <uk/config.h>
#include <uk/arch/types.h>
#include <errno.h>
-#ifdef CONFIG_LIBUKALLOC
#include <uk/alloc.h>
-#else
-#error "Virtio PCI device requires UKALLOC"
-#endif /* CONFIG_LIBUKALLOC */
#include <uk/print.h>
#include <uk/plat/lcpu.h>
#include <uk/plat/irq.h>
#include <pci/pci_bus.h>
#include <virtio/virtio_config.h>
+#include <virtio/virtqueue.h>
#include <virtio/virtio.h>
#include <virtio/virtio_pci.h>
@@ -69,44 +66,118 @@ struct virtio_pci_dev {
};
/**
- * Fetch the virtio pci information from the virtiodev.
+ * Fetch the virtio pci information from the virtio device.
+ * @param vdev
+ * Reference to the virtio device.
*/
#define to_virtiopcidev(vdev) \
__containerof(vdev, struct virtio_pci_dev, vdev)
-
/**
* Static function declaration.
*/
static void vpci_legacy_pci_dev_reset(struct virtio_dev *vdev);
static int vpci_legacy_pci_config_set(struct virtio_dev *vdev, __u16 offset,
- const void *buf, __u32 len);
+ const void *buf, __u32 len);
static int vpci_legacy_pci_config_get(struct virtio_dev *vdev, __u16 offset,
- void *buf, __u32 len,
- __u8 type_len __maybe_unused);
+ void *buf, __u32 len,
+ __u8 type_len __maybe_unused);
static __u64 vpci_legacy_pci_features_get(struct virtio_dev *vdev);
static void vpci_legacy_pci_features_set(struct virtio_dev *vdev,
- __u64 features);
+ __u64 features);
+static int vpci_legacy_pci_vq_find(struct virtio_dev *vdev, __u16 num_vq,
+ __u16 *qdesc_size);
static void vpci_legacy_pci_status_set(struct virtio_dev *vdev, __u8 status);
static __u8 vpci_legacy_pci_status_get(struct virtio_dev *vdev);
+static struct virtqueue *vpci_legacy_vq_setup(struct virtio_dev *vdev,
+ __u16 queue_id,
+ __u16 num_desc,
+ virtqueue_callback_t callback,
+ struct uk_alloc *a);
static inline void virtio_device_id_add(struct virtio_dev *vdev,
- __u16 pci_dev_id, __u16 vpci_dev_id_start);
+ __u16 pci_dev_id,
+ __u16 vpci_dev_id_start);
static int virtio_pci_legacy_add_dev(struct pci_device *pci_dev,
- struct virtio_pci_dev *vpci_dev);
+ struct virtio_pci_dev *vpci_dev);
/**
* Configuration operations legacy PCI device.
*/
static struct virtio_config_ops vpci_legacy_ops = {
- .reset_device = vpci_legacy_pci_dev_reset,
- .set_config = vpci_legacy_pci_config_set,
- .get_config = vpci_legacy_pci_config_get,
- .get_features = vpci_legacy_pci_features_get,
- .set_features = vpci_legacy_pci_features_set,
- .get_status = vpci_legacy_pci_status_get,
- .set_status = vpci_legacy_pci_status_set,
+ .device_reset = vpci_legacy_pci_dev_reset,
+ .config_get = vpci_legacy_pci_config_get,
+ .config_set = vpci_legacy_pci_config_set,
+ .features_get = vpci_legacy_pci_features_get,
+ .features_set = vpci_legacy_pci_features_set,
+ .status_get = vpci_legacy_pci_status_get,
+ .status_set = vpci_legacy_pci_status_set,
+ .vqs_find = vpci_legacy_pci_vq_find,
+ .vq_setup = vpci_legacy_vq_setup,
};
+static struct virtqueue *vpci_legacy_vq_setup(struct virtio_dev *vdev,
+ __u16 queue_id,
+ __u16 num_desc,
+ virtqueue_callback_t callback,
+ struct uk_alloc *a)
+{
+ struct virtio_pci_dev *vpdev = NULL;
+ struct virtqueue *vq;
+ __phys_addr addr;
+ long flags;
+
+ UK_ASSERT(vdev != NULL);
+
+ vpdev = to_virtiopcidev(vdev);
+ vq = virtqueue_create(queue_id, num_desc, VIRTIO_PCI_VRING_ALIGN,
+ callback, NULL, vdev, a);
+ if (PTRISERR(vq)) {
+ uk_pr_err("Failed to create the virtqueue: %d\n",
+ PTR2ERR(vq));
+ goto err_exit;
+ }
+
+ /* Physical address of the queue */
+ addr = virtqueue_physaddr(vq);
+ /* Select the queue of interest */
+ virtio_cwrite16((void *)(unsigned long)vpdev->pci_base_addr,
+ VIRTIO_PCI_QUEUE_SEL, queue_id);
+ virtio_cwrite32((void *)(unsigned long)vpdev->pci_base_addr,
+ VIRTIO_PCI_QUEUE_PFN,
+ addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
+
+ flags = ukplat_lcpu_save_irqf();
+ UK_TAILQ_INSERT_TAIL(&vpdev->vdev.vqs, vq, next);
+ ukplat_lcpu_restore_irqf(flags);
+
+err_exit:
+ return vq;
+}
+
+static int vpci_legacy_pci_vq_find(struct virtio_dev *vdev, __u16 num_vqs,
+ __u16 *qdesc_size)
+{
+ struct virtio_pci_dev *vpdev = NULL;
+ int vq_cnt = 0, i = 0;
+
+ UK_ASSERT(vdev);
+ vpdev = to_virtiopcidev(vdev);
+
+ for (i = 0; i < num_vqs; i++) {
+ virtio_cwrite16((void *) (unsigned long)vpdev->pci_base_addr,
+ VIRTIO_PCI_QUEUE_SEL, i);
+ qdesc_size[i] = virtio_cread16(
+ (void *) (unsigned long)vpdev->pci_base_addr,
+ VIRTIO_PCI_QUEUE_SIZE);
+ if (unlikely(!qdesc_size[i])) {
+ uk_pr_err("Virtqueue %d not available\n", i);
+ continue;
+ }
+ vq_cnt++;
+ }
+ return vq_cnt;
+}
+
static int vpci_legacy_pci_config_set(struct virtio_dev *vdev, __u16 offset,
const void *buf, __u32 len)
{
@@ -116,14 +187,14 @@ static int vpci_legacy_pci_config_set(struct virtio_dev
*vdev, __u16 offset,
vpdev = to_virtiopcidev(vdev);
_virtio_cwrite_bytes((void *)(unsigned long)vpdev->pci_base_addr,
- VIRTIO_PCI_CONFIG_OFF + offset, buf, len, 1);
+ VIRTIO_PCI_CONFIG_OFF + offset, buf, len, 1);
return 0;
}
static int vpci_legacy_pci_config_get(struct virtio_dev *vdev, __u16 offset,
- void *buf, __u32 len,
- __u8 type_len __maybe_unused)
+ void *buf, __u32 len,
+ __u8 type_len __maybe_unused)
{
struct virtio_pci_dev *vpdev = NULL;
int rc = 0;
@@ -152,7 +223,7 @@ static __u8 vpci_legacy_pci_status_get(struct virtio_dev
*vdev)
UK_ASSERT(vdev);
vpdev = to_virtiopcidev(vdev);
return virtio_cread8((void *) (unsigned long) vpdev->pci_base_addr,
- VIRTIO_PCI_STATUS);
+ VIRTIO_PCI_STATUS);
}
static void vpci_legacy_pci_status_set(struct virtio_dev *vdev, __u8 status)
@@ -167,7 +238,7 @@ static void vpci_legacy_pci_status_set(struct virtio_dev
*vdev, __u8 status)
curr_status = vpci_legacy_pci_status_get(vdev);
status |= curr_status;
virtio_cwrite8((void *)(unsigned long) vpdev->pci_base_addr,
- VIRTIO_PCI_STATUS, status);
+ VIRTIO_PCI_STATUS, status);
}
static void vpci_legacy_pci_dev_reset(struct virtio_dev *vdev)
@@ -182,7 +253,7 @@ static void vpci_legacy_pci_dev_reset(struct virtio_dev
*vdev)
* Resetting the device.
*/
virtio_cwrite8((void *) (unsigned long)vpdev->pci_base_addr,
- VIRTIO_PCI_STATUS, VIRTIO_CONFIG_STATUS_RESET);
+ VIRTIO_PCI_STATUS, VIRTIO_CONFIG_STATUS_RESET);
/**
* Waiting for the resetting the device. Find a better way
* of doing this instead of repeating register read.
@@ -206,19 +277,21 @@ static __u64 vpci_legacy_pci_features_get(struct
virtio_dev *vdev)
vpdev = to_virtiopcidev(vdev);
features = virtio_cread32((void *) (unsigned long)vpdev->pci_base_addr,
- VIRTIO_PCI_HOST_FEATURES);
+ VIRTIO_PCI_HOST_FEATURES);
return features;
}
static void vpci_legacy_pci_features_set(struct virtio_dev *vdev,
- __u64 features)
+ __u64 features)
{
struct virtio_pci_dev *vpdev = NULL;
UK_ASSERT(vdev);
vpdev = to_virtiopcidev(vdev);
+ /* Mask out features not supported by the virtqueue driver */
+ features = virtqueue_feature_negotiate(features);
virtio_cwrite32((void *) (unsigned long)vpdev->pci_base_addr,
- VIRTIO_PCI_GUEST_FEATURES, (__u32)features);
+ VIRTIO_PCI_GUEST_FEATURES, (__u32)features);
}
static inline void virtio_device_id_add(struct virtio_dev *vdev,
@@ -232,8 +305,8 @@ static int virtio_pci_legacy_add_dev(struct pci_device
*pci_dev,
{
/* Check the valid range of the virtio legacy device */
if (pci_dev->id.device_id < 0x1000 || pci_dev->id.device_id > 0x103f) {
- uk_pr_err("Error: Invalid Virtio Devices %d\n",
- pci_dev->id.device_id);
+ uk_pr_err("Invalid Virtio Devices %04x\n",
+ pci_dev->id.device_id);
return -EINVAL;
}
@@ -242,12 +315,12 @@ static int virtio_pci_legacy_add_dev(struct pci_device *pci_dev,
/* Setting the configuration operation */
vpci_dev->vdev.cops = &vpci_legacy_ops;
- uk_pr_info("Added a virtio-pci device(%02x)\n",
- (int) pci_dev->id.device_id);
+ uk_pr_info("Added virtio-pci device %04x\n",
+ pci_dev->id.device_id);
/* Mapping the virtio device identifier */
virtio_device_id_add(&vpci_dev->vdev, pci_dev->id.device_id,
- VIRTIO_PCI_LEGACY_DEVICEID_START);
+ VIRTIO_PCI_LEGACY_DEVICEID_START);
return 0;
}
@@ -261,7 +334,7 @@ static int virtio_pci_add_dev(struct pci_device *pci_dev)
vpci_dev = uk_malloc(a, sizeof(*vpci_dev));
if (!vpci_dev) {
- uk_pr_err("Error in memory allocation of pci_device\n");
+ uk_pr_err("Failed to allocate virtio-pci device\n");
return -ENOMEM;
}
@@ -276,19 +349,19 @@ static int virtio_pci_add_dev(struct pci_device *pci_dev)
*/
rc = virtio_pci_legacy_add_dev(pci_dev, vpci_dev);
if (rc != 0) {
- uk_pr_err("Failed(%d) to probe the legacy device\n",
- rc);
+ uk_pr_err("Failed to probe (legacy) pci device: %d\n", rc);
goto free_pci_dev;
}
rc = virtio_register_device(&vpci_dev->vdev);
if (rc != 0) {
- uk_pr_err("Failed(%d) to register the virtio device\n", rc);
+ uk_pr_err("Failed to register the virtio device: %d\n", rc);
goto free_pci_dev;
}
exit:
return rc;
+
free_pci_dev:
uk_free(a, vpci_dev);
goto exit;
diff --git a/plat/drivers/virtio/virtio_ring.c
b/plat/drivers/virtio/virtio_ring.c
new file mode 100644
index 0000000..ba91594
--- /dev/null
+++ b/plat/drivers/virtio/virtio_ring.c
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
+ *
+ * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+/**
+ * Inspired from the FreeBSD.
+ * Commit-id: a89e7a10d501
+ */
+#include <uk/config.h>
+#include <string.h>
+#include <uk/print.h>
+#include <uk/errptr.h>
+#include <cpu.h>
+#include <uk/sglist.h>
+#include <uk/arch/atomic.h>
+#include <uk/plat/io.h>
+#include <virtio/virtio_ring.h>
+#include <virtio/virtqueue.h>
+
+#define VIRTQUEUE_MAX_SIZE 32768
+#define to_virtqueue_vring(vq) \
+ __containerof(vq, struct virtqueue_vring, vq)
+
+struct virtqueue_desc_info {
+ void *cookie;
+ __u16 desc_count;
+};
+
+struct virtqueue_vring {
+ struct virtqueue vq;
+ /* Descriptor Ring */
+ struct vring vring;
+ /* Reference to the vring */
+ void *vring_mem;
+ /* Keep track of available descriptors */
+ __u16 desc_avail;
+ /* Index of the next available slot */
+ __u16 head_free_desc;
+ /* Index of the last used descriptor by the host */
+ __u16 last_used_desc_idx;
+ /* Cookie to identify driver buffer */
+ struct virtqueue_desc_info vq_info[];
+};
+
+/**
+ * Static function Declaration(s).
+ */
+static void virtqueue_vring_init(struct virtqueue_vring *vrq, __u16 nr_desc,
+ __u16 align);
+
+/**
+ * Driver implementation
+ */
+__u64 virtqueue_feature_negotiate(__u64 feature_set)
+{
+ __u64 feature = (1ULL << VIRTIO_TRANSPORT_F_START) - 1;
+
+ /**
+ * Currently out vring driver does not support any ring feature. We will
+ * add support to transport feature in the future.
+ */
+ feature &= feature_set;
+ return feature;
+}
+
+__phys_addr virtqueue_physaddr(struct virtqueue *vq)
+{
+ struct virtqueue_vring *vrq = NULL;
+
+ UK_ASSERT(vq);
+
+ vrq = to_virtqueue_vring(vq);
+ return ukplat_virt_to_phys(vrq->vring_mem);
+}
+
+static void virtqueue_vring_init(struct virtqueue_vring *vrq, __u16 nr_desc,
+ __u16 align)
+{
+ int i = 0;
+
+ vring_init(&vrq->vring, nr_desc, vrq->vring_mem, align);
+
+ vrq->desc_avail = vrq->vring.num;
+ vrq->head_free_desc = 0;
+ vrq->last_used_desc_idx = 0;
+ for (i = 0; i < nr_desc - 1; i++)
+ vrq->vring.desc[i].next = i + 1;
+ /**
+ * When we reach this descriptor we have completely used all the
+ * descriptor in the vring.
+ */
+ vrq->vring.desc[nr_desc - 1].next = VIRTQUEUE_MAX_SIZE;
+}
+
+struct virtqueue *virtqueue_create(__u16 queue_id, __u16 nr_descs, __u16 align,
+ virtqueue_callback_t callback,
+ virtqueue_notify_host_t notify,
+ struct virtio_dev *vdev, struct uk_alloc *a)
+{
+ struct virtqueue_vring *vrq;
+ struct virtqueue *vq;
+ int rc;
+ size_t ring_size = 0;
+
+ UK_ASSERT(a);
+
+ vrq = uk_malloc(a, sizeof(struct virtqueue) +
+ nr_descs * sizeof(struct virtqueue_desc_info));
+ if (!vrq) {
+ uk_pr_err("Allocation of virtqueue failed\n");
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+ /**
+ * Initialize the value before referencing it in
+ * uk_posix_memalign as we don't set NULL on all failures in the
+ * allocation.
+ */
+ vrq->vring_mem = NULL;
+
+ ring_size = vring_size(nr_descs, align);
+ uk_posix_memalign(a, (void **)&vrq->vring_mem, __PAGE_SIZE, ring_size);
+ if (!vrq->vring_mem) {
+ uk_pr_err("Allocation of vring failed\n");
+ rc = -ENOMEM;
+ goto err_freevq;
+ }
+ memset(vrq->vring_mem, 0, ring_size);
+ virtqueue_vring_init(vrq, nr_descs, align);
+
+ vq = &vrq->vq;
+ vq->queue_id = queue_id;
+ vq->vdev = vdev;
+ vq->vq_callback = callback;
+ vq->vq_notify_host = notify;
+ return vq;
+
+err_freevq:
+ uk_free(a, vrq);
+err_exit:
+ return ERR2PTR(rc);
+}
+
+void virtqueue_destroy(struct virtqueue *vq, struct uk_alloc *a)
+{
+ struct virtqueue_vring *vrq;
+
+ UK_ASSERT(vq);
+
+ vrq = to_virtqueue_vring(vq);
+
+ /* Free the ring */
+ uk_free(a, vrq->vring_mem);
+
+ /* Free the virtqueue metadata */
+ uk_free(a, vrq);
+}
+
+int virtqueue_is_full(struct virtqueue *vq)
+{
+ struct virtqueue_vring *vrq;
+
+ UK_ASSERT(vq);
+
+ vrq = to_virtqueue_vring(vq);
+ return (vrq->desc_avail == 0);
+}
diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk
index afcedc0..13e5575 100644
--- a/plat/kvm/Config.uk
+++ b/plat/kvm/Config.uk
@@ -57,6 +57,7 @@ config VIRTIO_BUS
depends on (ARCH_X86_64)
depends on LIBUKBUS
select LIBUKALLOC
+ select LIBUKSGLIST
help
Virtio bus driver for probing and operating virtio device and
transport layer.
diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk
index 986bf4a..ccc8319 100644
--- a/plat/kvm/Makefile.uk
+++ b/plat/kvm/Makefile.uk
@@ -92,5 +92,7 @@ LIBKVMVIRTIO_ASINCLUDES-y +=
-I$(UK_PLAT_DRIVERS_BASE)/include
LIBKVMVIRTIO_CINCLUDES-y += -I$(UK_PLAT_DRIVERS_BASE)/include
LIBKVMVIRTIO_SRCS-$(CONFIG_VIRTIO_BUS) +=\
$(UK_PLAT_DRIVERS_BASE)/virtio/virtio_bus.c
+LIBKVMVIRTIO_SRCS-$(CONFIG_VIRTIO_BUS) +=\
+
$(UK_PLAT_DRIVERS_BASE)/virtio/virtio_ring.c
LIBKVMVIRTIO_SRCS-$(CONFIG_VIRTIO_PCI) +=\
$(UK_PLAT_DRIVERS_BASE)/virtio/virtio_pci.c