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