diff --git a/lib/include/openamp/virtio_mmio.h b/lib/include/openamp/virtio_mmio.h index db678f6..000bb5b 100644 --- a/lib/include/openamp/virtio_mmio.h +++ b/lib/include/openamp/virtio_mmio.h @@ -8,6 +8,7 @@ #ifndef OPENAMP_VIRTIO_MMIO_H #define OPENAMP_VIRTIO_MMIO_H +#include #include #include #include @@ -167,6 +168,73 @@ struct virtio_mmio_device { void *user_data; }; +/** + * @brief This is called when the driver side should be notifyed + * + * @param dev The device that need to notify the other side + */ +typedef void (*virtio_mmio_notify)(struct virtio_device *dev); + +#ifdef WITH_VIRTIO_MMIO_DEV +/** + * @brief Define an Empty MMIO register table with only the strict necessary + * for a driver to recognise the device + * + * @note The initial state NOT_READY is the current approach to block the + * device side usage from driver until it gets properly configured. + */ +#define EMPTY_MMIO_TABLE { \ + .magic = VIRTIO_MMIO_MAGIC_VALUE_STRING, \ + .version = 2, \ + .status = VIRTIO_CONFIG_STATUS_NOT_READY, \ +} + +/** @brief MMIO Device Registers: 256 bytes in practice */ +struct virtio_mmio_dev_table { + + /* 0x00 R should be 0x74726976 */ + uint32_t magic; + + /* 0x04 R */ + uint32_t version; + + /* padding */ + uint32_t padding[26]; + + /* 0x70 RW Writing non-zero values to this register sets the status flags, + * indicating the driver progress. + * Writing zero (0x0) to this register triggers a device reset. + */ + uint32_t status; +}; + +#endif + +/** @brief VirtIO mmio dev instance, should be init with mmio_dev_init */ +struct virtio_mmio_dev { + + /** VirtIO device instance */ + struct virtio_device vdev; + + /** Number of descriptors per ring */ + int vring_size; + + /** Array of virtqueues */ + struct virtqueue *vqs; + + /** Metal IO Region used to access the MMIO registers */ + struct metal_io_region *io; + + /** Called when an interrupt should be sent to the other side */ + virtio_mmio_notify notify; + + /** The features supported by this device */ + uint64_t device_features; + + /** The features supported by the driver from the other side */ + uint64_t driver_features; +}; + /** * @brief Register a VIRTIO device with the VIRTIO stack. * @@ -214,6 +282,24 @@ int virtio_mmio_device_init(struct virtio_mmio_device *vmdev, uintptr_t virt_mem */ void virtio_mmio_isr(struct virtio_device *vdev); +/** + * @brief This should be called to initialize a virtio mmio device, + * the configure function should be called next by the device driver + * + * @param dev The device to initialize + * @param io The memory region in wich the device should operate + * @param callback The callback that will be called when the other side should be notifyed + */ +void virtio_mmio_dev_init(struct virtio_mmio_dev *dev, struct metal_io_region *io, virtio_mmio_notify callback); + +/** + * @brief Should be called by the app when it receive an interrupt for the mmio device + * + * @param dev The virtio mmio device + */ +void virtio_mmio_dev_interrupt(struct virtio_mmio_dev *dev); + + #ifdef __cplusplus } #endif diff --git a/lib/include/openamp/virtio_mmio_dev.h b/lib/include/openamp/virtio_mmio_dev.h deleted file mode 100644 index b1241f7..0000000 --- a/lib/include/openamp/virtio_mmio_dev.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2023, STMicroelectronics - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef OPENAMP_VIRTIO_MMIO_DEV_H -#define OPENAMP_VIRTIO_MMIO_DEV_H - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Define an Empty MMIO register table with only the strict necessary - * for a driver to recognise the device - * - * @note The initial state NOT_READY is the current approach to block the - * device side usage from driver until it gets properly configured. - */ -#define EMPTY_MMIO_TABLE { \ - .magic = VIRTIO_MMIO_MAGIC_VALUE_STRING, \ - .version = 2, \ - .status = VIRTIO_CONFIG_STATUS_NOT_READY, \ -} - -/** - * @brief MMIO Device Registers: 256 bytes in practice, more if the configuration space is used. - * This is a trimmed down version with only the essential values needed to be detected correctly. - */ -struct mmio_table { - - /* 0x00 R should be 0x74726976 */ - uint32_t magic; - - /* 0x04 R */ - uint32_t version; - - /* padding */ - uint32_t padding[26]; - - /* 0x70 RW Writing non-zero values to this register sets the status flags, - * indicating the driver progress. - * Writing zero (0x0) to this register triggers a device reset. - */ - uint32_t status; -}; - -/** - * @brief This is called when the other side should be notifyed - * - * @param dev The device that need to notify the other side - */ -typedef void (*virtio_notify)(struct virtio_device *dev); - -/** - * @brief VirtIO mmio dev instance, should be init with mmio_dev_init, - * then the VirtIO driver should set it's data using mmio_dev_set_device_data - * - * @param vdev VirtIO device instance - * @param vring_size Number of descriptors per ring - * @param vqs Array of virtqueues - * @param io Metal IO Region used to access the MMIO registers - * @param notify Called when an interrupt should be sent to the other side - * @param device_features The features supported by this device - * @param driver_features The features supported by the driver from the other side - */ -struct virtio_mmio_dev { - struct virtio_device vdev; - int vring_size; - struct virtqueue *vqs; - struct metal_io_region *io; - virtio_notify notify; - uint64_t device_features; - uint64_t driver_features; -}; - -/** - * @brief This should be called to initialize a virtio mmio device, - * the configure function should be called next by the device driver - * - * @param dev The device to initialize - * @param io The memory region in wich the device should operate - * @param callback The callback that will be called when the other side should be notifyed - */ -void mmio_dev_init(struct virtio_mmio_dev *dev, struct metal_io_region *io, virtio_notify callback); - -/** - * @brief Should be called by the app when it receive an interrupt for the mmio device - * - * @param dev The virtio mmio device - */ -void mmio_dev_interrupt(struct virtio_mmio_dev *dev); - -#ifdef __cplusplus -} -#endif - -#endif /* OPENAMP_VIRTIO_MMIO_DEV_H */ diff --git a/lib/virtio_mmio/virtio_mmio_dev.c b/lib/virtio_mmio/virtio_mmio_dev.c index 5e3cbd8..a91f28d 100644 --- a/lib/virtio_mmio/virtio_mmio_dev.c +++ b/lib/virtio_mmio/virtio_mmio_dev.c @@ -5,19 +5,19 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include -static inline uint32_t read_reg(struct virtio_mmio_dev *dev, uint32_t address) +static inline uint32_t virtio_mmio_dev_read_reg(struct virtio_mmio_dev *dev, uint32_t address) { return metal_io_read32(dev->io, address); } -static inline void write_reg(struct virtio_mmio_dev *dev, uint32_t address, uint32_t value) +static inline void virtio_mmio_dev_write_reg(struct virtio_mmio_dev *dev, uint32_t address, uint32_t value) { metal_io_write32(dev->io, address, value); } -static void mmio_dev_notify_other_side(struct virtqueue *vq) +static void virtio_mmio_dev_notify_driver(struct virtqueue *vq) { struct virtio_mmio_dev *dev = metal_container_of(vq->vq_dev, struct virtio_mmio_dev, vdev); @@ -25,12 +25,12 @@ static void mmio_dev_notify_other_side(struct virtqueue *vq) return; /* we set the interrupt status to 1 to tell that a buffer was used */ - write_reg(dev, VIRTIO_MMIO_INTERRUPT_STATUS, 1); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_INTERRUPT_STATUS, 1); dev->notify(&dev->vdev); } -static int mmio_dev_create_virtqueues(struct virtio_device *vdev, unsigned int flags, +static int virtio_mmio_dev_create_virtqueues(struct virtio_device *vdev, unsigned int flags, unsigned int nvqs, const char **names, vq_callback *callbacks) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); @@ -50,7 +50,7 @@ static int mmio_dev_create_virtqueues(struct virtio_device *vdev, unsigned int f /* we set all infos needed by the virtqueue */ vring_info->vq->callback = callbacks[i]; vring_info->vq->vq_name = names[i]; - vring_info->vq->notify = mmio_dev_notify_other_side; + vring_info->vq->notify = virtio_mmio_dev_notify_driver; } return 0; @@ -59,7 +59,7 @@ static int mmio_dev_create_virtqueues(struct virtio_device *vdev, unsigned int f /* Since the virtqueues are not actually instantiated in the create virtqueues function, * maybe we should free the allocs from configure device ? */ -static void mmio_dev_delete_virtqueues(struct virtio_device *vdev) +static void virtio_mmio_dev_delete_virtqueues(struct virtio_device *vdev) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); @@ -67,7 +67,7 @@ static void mmio_dev_delete_virtqueues(struct virtio_device *vdev) return; /* we are deleting virtqueues so we reset the device at the same time */ - write_reg(dev, VIRTIO_MMIO_STATUS, VIRTIO_CONFIG_STATUS_NOT_READY); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_STATUS, VIRTIO_CONFIG_STATUS_NOT_READY); vdev->vrings_num = 0; dev->vring_size = 0; @@ -75,7 +75,7 @@ static void mmio_dev_delete_virtqueues(struct virtio_device *vdev) metal_free_memory(vdev->vrings_info); } -static int mmio_dev_configure_device(struct virtio_device *vdev, uint64_t device_features, +static int virtio_mmio_dev_configure_device(struct virtio_device *vdev, uint64_t device_features, uint32_t device_type, int nvqs, int vring_size) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); @@ -90,7 +90,8 @@ static int mmio_dev_configure_device(struct virtio_device *vdev, uint64_t device if (!dev->vqs) return ret; - vdev->vrings_info = metal_allocate_memory(nvqs * sizeof(struct virtio_vring_info)); + if (!vdev->vrings_info) + vdev->vrings_info = metal_allocate_memory(nvqs * sizeof(struct virtio_vring_info)); if (!vdev->vrings_info) goto free_vqs; @@ -101,15 +102,15 @@ static int mmio_dev_configure_device(struct virtio_device *vdev, uint64_t device vdev->id.device = device_type; /* tell the other side the max number of vring allowed */ - write_reg(dev, VIRTIO_MMIO_QUEUE_NUM_MAX, vring_size); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_QUEUE_NUM_MAX, vring_size); /* Set the type of driver needed by the guest */ - write_reg(dev->io, VIRTIO_MMIO_DEVICE_ID, device_type); + virtio_mmio_dev_write_reg(dev->io, VIRTIO_MMIO_DEVICE_ID, device_type); /* Allow the other side to init the mmio device */ - write_reg(dev, VIRTIO_MMIO_DEVICE_ID, device_type); - write_reg(dev, VIRTIO_MMIO_STATUS, - (uint32_t)read_reg(dev, VIRTIO_MMIO_STATUS) & ~VIRTIO_CONFIG_STATUS_NOT_READY); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_DEVICE_ID, device_type); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_STATUS, + (uint32_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_STATUS) & ~VIRTIO_CONFIG_STATUS_NOT_READY); return 0; @@ -118,108 +119,102 @@ free_vqs: return ret; } -static uint8_t mmio_dev_get_status(struct virtio_device *vdev) +static uint8_t virtio_mmio_dev_get_status(struct virtio_device *vdev) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); - return (uint8_t)read_reg(dev, VIRTIO_MMIO_STATUS); + return (uint8_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_STATUS); } -static void mmio_dev_set_status(struct virtio_device *vdev, uint8_t status) +static void virtio_mmio_dev_set_status(struct virtio_device *vdev, uint8_t status) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); - write_reg(dev, VIRTIO_MMIO_STATUS, status); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_STATUS, status); } -static uint64_t mmio_dev_get_features(struct virtio_device *vdev) +static uint64_t virtio_mmio_dev_get_features(struct virtio_device *vdev) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); return dev->device_features; } -static void mmio_dev_set_features(struct virtio_device *vdev, uint32_t feature) +static void virtio_mmio_dev_set_features(struct virtio_device *vdev, uint32_t feature) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); dev->device_features = feature; } -static void mmio_dev_read_config(struct virtio_device *vdev, uint32_t offset, void *dst, +static void virtio_mmio_dev_read_config(struct virtio_device *vdev, uint32_t offset, void *dst, int length) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); uint8_t *buf = dst; + /* TODO: replace to metal_io_block_read, it also need a sanity check regarding + * the offset and length of block read + */ for (int i = 0; i < length; i++) buf[i] = metal_io_read8(dev->io, VIRTIO_MMIO_CONFIG + i); } -static void mmio_dev_write_config(struct virtio_device *vdev, uint32_t offset, void *src, +static void virtio_mmio_dev_write_config(struct virtio_device *vdev, uint32_t offset, void *src, int length) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); uint8_t *buf = src; + /* TODO: replace to metal_io_block_write, it also need a sanity check regarding + * the offset and length of block being written + */ for (int i = 0; i < length; i++) metal_io_write8(dev->io, VIRTIO_MMIO_CONFIG + i, buf[i]); } -static void mmio_dev_reset_device(struct virtio_device *vdev) +static void virtio_mmio_dev_reset_device(struct virtio_device *vdev) { struct virtio_mmio_dev *dev = metal_container_of(vdev, struct virtio_mmio_dev, vdev); - write_reg(dev, VIRTIO_MMIO_STATUS, read_reg(dev, VIRTIO_MMIO_STATUS) | + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_STATUS, virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_STATUS) | VIRTIO_CONFIG_STATUS_NEEDS_RESET); } const static struct virtio_dispatch virtio_ops = { - .create_virtqueues = mmio_dev_create_virtqueues, - .delete_virtqueues = mmio_dev_delete_virtqueues, + .create_virtqueues = virtio_mmio_dev_create_virtqueues, + .delete_virtqueues = virtio_mmio_dev_delete_virtqueues, - .configure_device = mmio_dev_configure_device, + .configure_device = virtio_mmio_dev_configure_device, - .get_status = mmio_dev_get_status, - .set_status = mmio_dev_set_status, + .get_status = virtio_mmio_dev_get_status, + .set_status = virtio_mmio_dev_set_status, - .get_features = mmio_dev_get_features, - .set_features = mmio_dev_set_features, + .get_features = virtio_mmio_dev_get_features, + .set_features = virtio_mmio_dev_set_features, - .read_config = mmio_dev_read_config, - .write_config = mmio_dev_write_config, + .read_config = virtio_mmio_dev_read_config, + .write_config = virtio_mmio_dev_write_config, - .reset_device = mmio_dev_reset_device, + .reset_device = virtio_mmio_dev_reset_device, }; -void mmio_dev_init(struct virtio_mmio_dev *dev, struct metal_io_region *io, virtio_notify callback) -{ - dev->io = io; - dev->notify = callback; - dev->vdev.func = &virtio_ops; - - dev->driver_features = 0; - - /* Init vdev struct */ - dev->vdev.role = VIRTIO_DEV_DEVICE; -} - -static void mmio_dev_negotiate_features(struct virtio_mmio_dev *dev) +static void virtio_mmio_dev_negotiate_features(struct virtio_mmio_dev *dev) { /* we update the device features each time in case the feature sel changed */ - write_reg(dev, VIRTIO_MMIO_DEVICE_FEATURES, dev->device_features >> - (read_reg(dev, VIRTIO_MMIO_DEVICE_FEATURES_SEL) * 32)); + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_DEVICE_FEATURES, dev->device_features >> + (virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_DEVICE_FEATURES_SEL) * 32)); /* we update the driver features each time in case the feature sel changed */ - dev->driver_features |= ((uint64_t)read_reg(dev, VIRTIO_MMIO_DRIVER_FEATURES) << - (read_reg(dev, VIRTIO_MMIO_DRIVER_FEATURES_SEL) * 32)); + dev->driver_features |= ((uint64_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_DRIVER_FEATURES) << + (virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_DRIVER_FEATURES_SEL) * 32)); } -static void mmio_dev_receive_queues(struct virtio_mmio_dev *dev, struct virtio_device *vdev) +static void virtio_mmio_dev_receive_queues(struct virtio_mmio_dev *dev, struct virtio_device *vdev) { /* Now the driver should send us the virtqueues */ - uint32_t queuesel = read_reg(dev, VIRTIO_MMIO_QUEUE_SEL); + uint32_t queuesel = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_SEL); /* Update virtqueue registers according to the queuesel index */ if (queuesel >= vdev->vrings_num) @@ -229,18 +224,18 @@ static void mmio_dev_receive_queues(struct virtio_mmio_dev *dev, struct virtio_d struct vring_alloc_info *alloc_info = &vinfo->info; struct virtqueue *vq = &dev->vqs[queuesel]; - /* if this queue allready exist or the driver did not set it up */ - if (vinfo->vq || !read_reg(dev, VIRTIO_MMIO_QUEUE_READY)) { - /* tell the driver if the queue allready exist or not */ - write_reg(dev, VIRTIO_MMIO_QUEUE_READY, vinfo->vq ? 1 : 0); + /* if this queue already exists or the driver did not set it up */ + if (vinfo->vq || !virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_READY)) { + /* tell the driver if the queue already exists or not */ + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_QUEUE_READY, vinfo->vq ? 1 : 0); return; } - /* If the host set Queue Ready then we can read the virtqueue */ - alloc_info->num_descs = read_reg(dev, VIRTIO_MMIO_QUEUE_NUM); + /* If the host sets Queue Ready then we can read the virtqueue */ + alloc_info->num_descs = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_NUM); - alloc_info->vaddr = read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_LOW) | - ((uint64_t)read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_HIGH) << 32); + alloc_info->vaddr = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_LOW) | + ((uint64_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_HIGH) << 32); /* TODO: the alignment should be set in a config instead of being hardcoded */ alloc_info->align = 4096; @@ -252,21 +247,33 @@ static void mmio_dev_receive_queues(struct virtio_mmio_dev *dev, struct virtio_d vinfo->vq = vq; /* Use the vring addresses provided in case they were aligned differently */ - vq->vq_ring.desc = read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_LOW) | - ((uint64_t)read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_HIGH) << 32); + vq->vq_ring.desc = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_LOW) | + ((uint64_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_DESC_HIGH) << 32); - vq->vq_ring.avail = read_reg(dev, VIRTIO_MMIO_QUEUE_AVAIL_LOW) | - ((uint64_t)read_reg(dev, VIRTIO_MMIO_QUEUE_AVAIL_HIGH) << 32); + vq->vq_ring.avail = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_AVAIL_LOW) | + ((uint64_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_AVAIL_HIGH) << 32); - vq->vq_ring.used = read_reg(dev, VIRTIO_MMIO_QUEUE_USED_LOW) | - ((uint64_t)read_reg(dev, VIRTIO_MMIO_QUEUE_USED_HIGH) << 32); + vq->vq_ring.used = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_USED_LOW) | + ((uint64_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_USED_HIGH) << 32); } -void mmio_dev_interrupt(struct virtio_mmio_dev *dev) +void virtio_mmio_dev_init(struct virtio_mmio_dev *dev, struct metal_io_region *io, virtio_mmio_notify callback) +{ + dev->io = io; + dev->notify = callback; + dev->vdev.func = &virtio_ops; + + dev->driver_features = 0; + + /* Init vdev struct */ + dev->vdev.role = VIRTIO_DEV_DEVICE; +} + +void virtio_mmio_dev_interrupt(struct virtio_mmio_dev *dev) { struct virtio_device *vdev = &dev->vdev; - switch (read_reg(dev, VIRTIO_MMIO_STATUS)) { + switch (virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_STATUS)) { case VIRTIO_CONFIG_STATUS_RESET: dev->driver_features = 0; break; @@ -275,7 +282,7 @@ void mmio_dev_interrupt(struct virtio_mmio_dev *dev) * the features sel register are used to select the features bits we want to read */ case (VIRTIO_CONFIG_STATUS_ACK | VIRTIO_CONFIG_STATUS_DRIVER): - mmio_dev_negotiate_features(dev); + virtio_mmio_dev_negotiate_features(dev); break; /* Check if the features negotiated are the right ones @@ -285,18 +292,18 @@ void mmio_dev_interrupt(struct virtio_mmio_dev *dev) VIRTIO_CONFIG_STATUS_FEATURES_OK): /* if the features does not match then we should not allow feature ok status */ if (dev->driver_features != dev->device_features) { - write_reg(dev, VIRTIO_MMIO_STATUS, - (uint32_t)read_reg(dev, VIRTIO_MMIO_STATUS) & + virtio_mmio_dev_write_reg(dev, VIRTIO_MMIO_STATUS, + (uint32_t)virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_STATUS) & ~VIRTIO_CONFIG_STATUS_FEATURES_OK); return; } /* The features are fully negotiated ! */ vdev->features = dev->driver_features; - mmio_dev_receive_queues(dev, vdev); + virtio_mmio_dev_receive_queues(dev, vdev); break; case VIRTIO_CONFIG_STATUS_READY: - uint32_t notify_idx = read_reg(dev, VIRTIO_MMIO_QUEUE_NOTIFY); + uint32_t notify_idx = virtio_mmio_dev_read_reg(dev, VIRTIO_MMIO_QUEUE_NOTIFY); /* if the virtqueue does have an available buffer, notify it */ if (notify_idx < vdev->vrings_num && vdev->vrings_info[notify_idx].vq && virtqueue_get_desc_size(&dev->vqs[notify_idx]) > 0)