[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>
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |