1 /* 2 * virtqueue support adapted from the Linux kernel. 3 * 4 * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2. 7 */ 8 #include "libcflat.h" 9 #include "devicetree.h" 10 #include "alloc.h" 11 #include "asm/page.h" 12 #include "asm/io.h" 13 #include "virtio.h" 14 #include "virtio-mmio.h" 15 16 static void vm_get(struct virtio_device *vdev, unsigned offset, 17 void *buf, unsigned len) 18 { 19 struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 20 u8 *p = buf; 21 unsigned i; 22 23 for (i = 0; i < len; ++i) 24 p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); 25 } 26 27 static void vm_set(struct virtio_device *vdev, unsigned offset, 28 const void *buf, unsigned len) 29 { 30 struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 31 const u8 *p = buf; 32 unsigned i; 33 34 for (i = 0; i < len; ++i) 35 writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); 36 } 37 38 static bool vm_notify(struct virtqueue *vq) 39 { 40 struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev); 41 writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); 42 return true; 43 } 44 45 static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, 46 unsigned index, 47 void (*callback)(struct virtqueue *vq), 48 const char *name) 49 { 50 struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 51 struct vring_virtqueue *vq; 52 void *queue; 53 unsigned num = VIRTIO_MMIO_QUEUE_NUM_MIN; 54 55 vq = calloc(1, sizeof(*vq)); 56 queue = memalign(PAGE_SIZE, VIRTIO_MMIO_QUEUE_SIZE_MIN); 57 assert(vq && queue); 58 59 writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); 60 61 assert(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX) >= num); 62 63 if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN) != 0) { 64 printf("%s: virtqueue %d already setup! base=%p\n", 65 __func__, index, vm_dev->base); 66 return NULL; 67 } 68 69 writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); 70 writel(VIRTIO_MMIO_VRING_ALIGN, 71 vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); 72 writel(virt_to_pfn(queue), vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); 73 74 vring_init_virtqueue(vq, index, num, VIRTIO_MMIO_VRING_ALIGN, 75 vdev, queue, vm_notify, callback, name); 76 77 return &vq->vq; 78 } 79 80 static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs, 81 struct virtqueue *vqs[], vq_callback_t *callbacks[], 82 const char *names[]) 83 { 84 unsigned i; 85 86 for (i = 0; i < nvqs; ++i) { 87 vqs[i] = vm_setup_vq(vdev, i, 88 callbacks ? callbacks[i] : NULL, 89 names ? names[i] : ""); 90 if (vqs[i] == NULL) 91 return -1; 92 } 93 94 return 0; 95 } 96 97 static const struct virtio_config_ops vm_config_ops = { 98 .get = vm_get, 99 .set = vm_set, 100 .find_vqs = vm_find_vqs, 101 }; 102 103 static void vm_device_init(struct virtio_mmio_device *vm_dev) 104 { 105 vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); 106 vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); 107 vm_dev->vdev.config = &vm_config_ops; 108 109 writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); 110 } 111 112 /****************************************************** 113 * virtio-mmio device tree support 114 ******************************************************/ 115 116 struct vm_dt_info { 117 u32 devid; 118 void *base; 119 }; 120 121 static int vm_dt_match(const struct dt_device *dev, int fdtnode) 122 { 123 struct vm_dt_info *info = (struct vm_dt_info *)dev->info; 124 struct dt_pbus_reg base; 125 u32 magic; 126 int ret; 127 128 dt_device_bind_node((struct dt_device *)dev, fdtnode); 129 130 ret = dt_pbus_get_base(dev, &base); 131 assert(ret == 0); 132 info->base = ioremap(base.addr, base.size); 133 134 magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE); 135 if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) 136 return false; 137 138 return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid; 139 } 140 141 static struct virtio_device *virtio_mmio_dt_bind(u32 devid) 142 { 143 struct virtio_mmio_device *vm_dev; 144 struct dt_device dt_dev; 145 struct dt_bus dt_bus; 146 struct vm_dt_info info; 147 int node; 148 149 if (!dt_available()) 150 return NULL; 151 152 dt_bus_init_defaults(&dt_bus); 153 dt_bus.match = vm_dt_match; 154 155 info.devid = devid; 156 157 dt_device_init(&dt_dev, &dt_bus, &info); 158 159 node = dt_device_find_compatible(&dt_dev, "virtio,mmio"); 160 assert(node >= 0 || node == -FDT_ERR_NOTFOUND); 161 162 if (node == -FDT_ERR_NOTFOUND) 163 return NULL; 164 165 vm_dev = calloc(1, sizeof(*vm_dev)); 166 assert(vm_dev != NULL); 167 168 vm_dev->base = info.base; 169 vm_device_init(vm_dev); 170 171 return &vm_dev->vdev; 172 } 173 174 struct virtio_device *virtio_mmio_bind(u32 devid) 175 { 176 return virtio_mmio_dt_bind(devid); 177 } 178