[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFCv4,04/35] plat/virtio: Add new virtio_mmio device/driver on arm64



Hi, Justin.
This patch looks good, I've only found some minor typos. I'll let them here, as
inline comments.

diff --git a/plat/drivers/virtio/virtio_mmio.c 
b/plat/drivers/virtio/virtio_mmio.c
new file mode 100644
index 0000000..6716153
--- /dev/null
+++ b/plat/drivers/virtio/virtio_mmio.c
@@ -0,0 +1,480 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Jia He <justin.he@xxxxxxx>
+ *
+ * Copyright (c) 2020, Arm Ltd. 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.
+ */
+
+#include <uk/config.h>
+#include <uk/arch/types.h>
+#include <errno.h>
+#include <string.h>
+#include <uk/alloc.h>
+#include <uk/print.h>
+#include <uk/plat/lcpu.h>
+#include <uk/plat/irq.h>
+#include <uk/bus.h>
+#include <uk/bitops.h>
+
+#include <platform_bus.h>
+#include <virtio/virtio_config.h>
+#include <virtio/virtio_bus.h>
+#include <virtio/virtqueue.h>
+#include <virtio/virtio_mmio.h>
+
+/* The alignment to use between consumer and producer parts of vring.
+ * Currently hardcoded to the page size. */
+#define VIRTIO_MMIO_VRING_ALIGN                __PAGE_SIZE
+
+static struct uk_alloc *a;
+struct virtio_mmio_device_id {
+       uint16_t device_id;
+       uint32_t vendor;
+};
+
+struct virtio_mmio_device {
+       struct virtio_dev vdev;
+       char *name;
+       struct virtio_mmio_device_id  id;
+       struct virtio_mmio_driver     *drv;
+       unsigned long version;
+       unsigned long irq;
+       void *base;
+       struct pf_device *pfdev;
+};
+
+#define to_virtio_mmio_device(_dev) \
+       __containerof(_dev, struct virtio_mmio_device, vdev)
+
+struct virtio_mmio_vq_info {
+       /* the actual virtqueue */
+       struct virtqueue *vq;
+};
+typedef void vq_callback_t(struct virtqueue *);
+
+/* Configuration interface */
+
+static __u64 vm_get_features(struct virtio_dev *vdev)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       __u64 features = 0;
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 1);
+       features = virtio_cread32(vm_dev->base, VIRTIO_MMIO_DEVICE_FEATURES);
+       features <<= 32;
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
+       features |= virtio_cread32(vm_dev->base, VIRTIO_MMIO_DEVICE_FEATURES);
+
+       return features;
+}
+
+static void vm_set_features(struct virtio_dev *vdev,
+                                        __u64 features)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+       /* Give virtio_ring a chance to accept features. */
+       virtqueue_feature_negotiate(features);
+

  "Make sure there are no mixed devices"

+       /* Make sure there is are no mixed devices */
+       if (vm_dev->version == 2 &&
+               !uk_test_bit(VIRTIO_F_VERSION_1, &vdev->features)) {
+               uk_pr_err("New virtio-mmio devices (version 2) must provide 
VIRTIO_F_VERSION_1 feature!\n");
+               return;
+       }
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 1);
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DRIVER_FEATURES,
+                                               (__u32)(vdev->features >> 32));
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_DRIVER_FEATURES,
+                                               (__u32)vdev->features);
+
+       return;
+}
+
+static int vm_get(struct virtio_dev *vdev, __u16 offset,
+                  void *buf, __u32 len, __u8 type_len __unused)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       void *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
+       __u8 b;
+       __u16 w;
+       __u32 l;
+
+       if (vm_dev->version == 1) {
+               __u8 *ptr = buf;
+               unsigned i;
+
+               for (i = 0; i < len; i++)
+                       ptr[i] = virtio_cread8(base, offset + i);
+               return len;
+       }
+
+       switch (len) {
+       case 1:
+               b = virtio_cread8(base, offset);
+               memcpy(buf, &b, sizeof b);
+               break;
+       case 2:
+               w = (virtio_cread16(base, offset));
+               memcpy(buf, &w, sizeof w);
+               break;
+       case 4:
+               l = (virtio_cread32(base, offset));
+               memcpy(buf, &l, sizeof l);
+               break;
+       case 8:
+               l = (virtio_cread32(base, offset));
+               memcpy(buf, &l, sizeof l);
+               l = (virtio_cread32(base, offset + sizeof l));
+               memcpy(buf + sizeof l, &l, sizeof l);
+               break;
+       default:
+               uk_pr_err("Not supported length(%d) for io read\n", len);
+               UK_BUG();
+       }
+
+       return len;
+}
+
+static int vm_set(struct virtio_dev *vdev, __u16 offset,
+                  const void *buf, __u32 len)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       void *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
+       __u8 b;
+       __u16 w;
+       __u32 l;
+
+       if (vm_dev->version == 1) {
+               const __u8 *ptr = buf;
+               __u32 i;
+
+               for (i = 0; i < len; i++)
+                       virtio_cwrite8(base, offset + i, ptr[i]);
+               return 0;
+       }
+
+       switch (len) {
+       case 1:
+               memcpy(&b, buf, sizeof b);
+               virtio_cwrite8(base, offset, b);
+               break;
+       case 2:
+               memcpy(&w, buf, sizeof w);
+               virtio_cwrite16(base, offset, w);
+               break;
+       case 4:
+               memcpy(&l, buf, sizeof l);
+               virtio_cwrite32(base, offset, l);
+               break;
+       case 8:
+               memcpy(&l, buf, sizeof l);
+               virtio_cwrite32(base, offset, l);
+               memcpy(&l, buf + sizeof l, sizeof(l));
+               virtio_cwrite32(base, offset + sizeof l, l);
+               break;
+       default:
+               uk_pr_err("Not supported length(%d) for io write\n", len);
+               UK_BUG();
+       }
+
+       return 0;
+}
+
+static __u8 vm_get_status(struct virtio_dev *vdev)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+       return virtio_cread32(vm_dev->base, VIRTIO_MMIO_STATUS) & 0xff;
+}
+
+static void vm_set_status(struct virtio_dev *vdev, __u8 status)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+       /* We should never be setting status to 0. */
+       UK_BUGON(status == 0);
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_STATUS, status);
+}
+
+static void vm_reset(struct virtio_dev *vdev)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+       /* 0 status means a reset. */
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_STATUS, 0);
+}
+
+/* Transport interface */
+
+/* the notify function used when creating a virt queue */
+static int vm_notify(struct virtio_dev *vdev, __u16 queue_id)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+       /* We write the queue's selector into the notification register to
+        * signal the other end */
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_NOTIFY, queue_id);
+       return 1;
+}
+
+/* Notify all virtqueues on an interrupt. */
+static int vm_interrupt(void *opaque)
+{
+       struct virtio_mmio_device *vm_dev = opaque;
+       unsigned long status;
+       unsigned long flags;
+       int rc = 0;
+       struct virtqueue *vq;
+
+       /* Read and acknowledge interrupts */
+       status = virtio_cread32(vm_dev->base, VIRTIO_MMIO_INTERRUPT_STATUS);
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_INTERRUPT_ACK, status);
+
+       if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) {
+               uk_pr_warn("Unsupported config change interrupt received on 
virtio-mmio device %p\n",
+                          vm_dev);
+       }
+
+       if (likely(status & VIRTIO_MMIO_INT_VRING)) {
+               flags = ukplat_lcpu_save_irqf();
+               UK_TAILQ_FOREACH(vq, &vm_dev->vdev.vqs, next) {
+                       rc |= virtqueue_ring_interrupt(vq);
+               }
+               ukplat_lcpu_restore_irqf(flags);
+       }
+
+       return rc;
+}
+
+
+static struct virtqueue *vm_setup_vq(struct virtio_dev *vdev,
+                                       __u16 queue_id,
+                                       __u16 num_desc,
+                                       virtqueue_callback_t callback,
+                                       struct uk_alloc *a)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       struct virtqueue *vq;
+       unsigned long flags;
+
+       /* Create the vring */
+       vq = virtqueue_create(queue_id, num_desc, VIRTIO_MMIO_VRING_ALIGN,
+                             callback, vm_notify, vdev, a);
+
+       if (PTRISERR(vq)) {
+               uk_pr_err("Failed to create the virtqueue: %d\n",
+                         PTR2ERR(vq));
+
+               goto err_exit;
+       }
+
+       /* Select the queue we're interested in */
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_SEL, queue_id);
+
+       /* Activate the queue */
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_NUM, 
(__u32)virtqueue_vring_get_num(vq));
+       if (vm_dev->version == 1) {
+
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_ALIGN, 
__PAGE_SIZE);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_PFN,
+                                       virtqueue_physaddr(vq) >> __PAGE_SHIFT);
+       } else {
+               __u64 addr;
+               addr = virtqueue_physaddr(vq);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_DESC_LOW, 
(__u32)addr);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_DESC_HIGH,
+                                                               (__u32)(addr >> 
32));
+
+               addr = virtqueue_get_avail_addr(vq);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_AVAIL_LOW, 
(__u32)addr);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_AVAIL_HIGH,
+                                                               (__u32)(addr >> 
32));
+
+               addr =  virtqueue_get_used_addr(vq);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_USED_LOW, 
(__u32)addr);
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_USED_HIGH,
+                                                               (__u32)(addr >> 
32));
+
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_READY, 1);
+       }
+
+       flags = ukplat_lcpu_save_irqf();
+       UK_TAILQ_INSERT_TAIL(&vm_dev->vdev.vqs, vq, next);
+       ukplat_lcpu_restore_irqf(flags);
+
+err_exit:
+       return vq;
+}
+
+static int vm_find_vqs(struct virtio_dev *vdev, __u16 num_vqs, __u16 
*qdesc_size)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       unsigned int irq = vm_dev->pfdev->irq;
+       int i, err;
+       int vq_cnt = 0;
+
+       err = ukplat_irq_register(irq, vm_interrupt, vm_dev);
+       if (err)
+               return err;
+
+       for (i = 0; i < num_vqs; ++i) {
+               /* Select the queue we're interested in */
+               virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_QUEUE_SEL, i);
+
+               /* Queue shouldn't already be set up. */
+               if (virtio_cread32(vm_dev->base, (vm_dev->version == 1 ?
+                               VIRTIO_MMIO_QUEUE_PFN : 
VIRTIO_MMIO_QUEUE_READY))) {
+                       uk_pr_err("vm_find_vqs error mmio queue not ready\n");
+                       err = -ENOENT;
+                       goto error_exit;
+               }
+
+               qdesc_size[i] = virtio_cread32(vm_dev->base, 
VIRTIO_MMIO_QUEUE_NUM_MAX);
+               if (qdesc_size[i] == 0) {
+                       err = -ENOENT;
+                       goto error_exit;
+               }
+
+               vq_cnt++;
+       }
+
+       return vq_cnt;
+error_exit:
+       uk_pr_err("err in vm_find_vqs :%d\n", err);
+       return err;
+}
+
+static struct virtio_config_ops virtio_mmio_config_ops = {
+       .config_get     = vm_get,
+       .config_set     = vm_set,
+       .status_get     = vm_get_status,
+       .status_set     = vm_set_status,
+       .device_reset   = vm_reset,
+       .features_get   = vm_get_features,
+       .features_set   = vm_set_features,
+       .vqs_find       = vm_find_vqs,
+       .vq_setup       = vm_setup_vq,
+};
+
+static int virtio_mmio_add_dev(struct pf_device *pfdev)
+{
+       struct virtio_mmio_device *vm_dev;
+       unsigned int magic;
+       int rc;
+
+       UK_ASSERT(pfdev != NULL);
+       uk_pr_info("virtio_mmio_add dev\n");
+
+       vm_dev = uk_malloc(a, sizeof(*vm_dev));
+       if (!vm_dev) {

  "Failed to allocate virtio-mmio...." (instead of virtio-pci)

+               uk_pr_err("Failed to allocate virtio-pci device\n");
+               return -ENOMEM;
+       }
+
+       /* Fetch Pf Device information */
+       vm_dev->pfdev = pfdev;
+       vm_dev->base = (void *)pfdev->base;
+       vm_dev->vdev.cops = &virtio_mmio_config_ops;
+       vm_dev->name = "virtio_mmio";
+
+       if (vm_dev->base == NULL)
+               return -EFAULT;
+
+       magic = virtio_cread32(vm_dev->base, VIRTIO_MMIO_MAGIC_VALUE);
+       if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
+               uk_pr_err("Wrong magic value 0x%x!\n", magic);
+               return -ENODEV;
+       }
+
+       /* Check device version */
+       vm_dev->version = virtio_cread32(vm_dev->base, VIRTIO_MMIO_VERSION);
+       if (vm_dev->version < 1 || vm_dev->version > 2) {
+               uk_pr_err("Version %ld not supported!\n", vm_dev->version);
+               return -ENXIO;
+       }
+
+       vm_dev->vdev.id.virtio_device_id = virtio_cread32(vm_dev->base, 
VIRTIO_MMIO_DEVICE_ID);
+       if (vm_dev->vdev.id.virtio_device_id == 0) {
+               /*
+                * virtio-mmio device with an ID 0 is a (dummy) placeholder
+                * with no function. End probing now with no error reported.
+                */
+               uk_pr_err("virtio_device_id is 0\n");
+
+               return -ENODEV;
+       }
+       vm_dev->id.vendor = virtio_cread32(vm_dev->base, VIRTIO_MMIO_VENDOR_ID);
+
+       virtio_cwrite32(vm_dev->base, VIRTIO_MMIO_GUEST_PAGE_SIZE, __PAGE_SIZE);
+
+       rc = virtio_bus_register_device(&vm_dev->vdev);
+       if (rc != 0) {
+               uk_pr_err("Failed to register the virtio device: %d\n", rc);
+               goto free_pf_dev;
+       }
+
+       uk_pr_info("finish add a virtio mmio dev\n");
+
+       return rc;
+
+free_pf_dev:
+       uk_free(a, vm_dev);
+
+       return 0;
+}
+
+static int virtio_mmio_drv_init(struct uk_alloc *drv_allocator)
+{
+       /* driver initialization */
+       if (!drv_allocator)
+               return -EINVAL;
+
+       a = drv_allocator;
+
+       return 0;
+}
+
+static const struct pf_device_id virtio_mmio_ids = {
+               .device_id = VIRTIO_MMIO_ID
+};
+
+static struct pf_driver virtio_mmio_drv = {
+       .device_ids = &virtio_mmio_ids,
+       .init = virtio_mmio_drv_init,
+       .add_dev = virtio_mmio_add_dev
+};
+
+PF_REGISTER_DRIVER(&virtio_mmio_drv);


Reviewed-by: Razvan Virtan <virtanrazvan@xxxxxxxxx>



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.