xref: /kvm-unit-tests/lib/virtio-mmio.c (revision 49f758b8a983e49b4537ea2726e8a83a0d5632ad)
143402ba5SAndrew Jones /*
2d73e4521SAndrew Jones  * virtqueue support adapted from the Linux kernel.
3d73e4521SAndrew Jones  *
4*49f758b8SAndrew Jones  * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
543402ba5SAndrew Jones  *
6*49f758b8SAndrew Jones  * This work is licensed under the terms of the GNU GPL, version 2.
743402ba5SAndrew Jones  */
843402ba5SAndrew Jones #include "libcflat.h"
943402ba5SAndrew Jones #include "devicetree.h"
1043402ba5SAndrew Jones #include "alloc.h"
11d73e4521SAndrew Jones #include "asm/page.h"
1243402ba5SAndrew Jones #include "asm/io.h"
1343402ba5SAndrew Jones #include "virtio.h"
1443402ba5SAndrew Jones #include "virtio-mmio.h"
1543402ba5SAndrew Jones 
1643402ba5SAndrew Jones static void vm_get(struct virtio_device *vdev, unsigned offset,
1743402ba5SAndrew Jones 		   void *buf, unsigned len)
1843402ba5SAndrew Jones {
1943402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
2043402ba5SAndrew Jones 	u8 *p = buf;
2143402ba5SAndrew Jones 	unsigned i;
2243402ba5SAndrew Jones 
2343402ba5SAndrew Jones 	for (i = 0; i < len; ++i)
2443402ba5SAndrew Jones 		p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
2543402ba5SAndrew Jones }
2643402ba5SAndrew Jones 
2743402ba5SAndrew Jones static void vm_set(struct virtio_device *vdev, unsigned offset,
2843402ba5SAndrew Jones 		   const void *buf, unsigned len)
2943402ba5SAndrew Jones {
3043402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
3143402ba5SAndrew Jones 	const u8 *p = buf;
3243402ba5SAndrew Jones 	unsigned i;
3343402ba5SAndrew Jones 
3443402ba5SAndrew Jones 	for (i = 0; i < len; ++i)
3543402ba5SAndrew Jones 		writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
3643402ba5SAndrew Jones }
3743402ba5SAndrew Jones 
38d73e4521SAndrew Jones static bool vm_notify(struct virtqueue *vq)
39d73e4521SAndrew Jones {
40d73e4521SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
41d73e4521SAndrew Jones 	writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
42d73e4521SAndrew Jones 	return true;
43d73e4521SAndrew Jones }
44d73e4521SAndrew Jones 
45d73e4521SAndrew Jones static struct virtqueue *vm_setup_vq(struct virtio_device *vdev,
46d73e4521SAndrew Jones 				     unsigned index,
47d73e4521SAndrew Jones 				     void (*callback)(struct virtqueue *vq),
48d73e4521SAndrew Jones 				     const char *name)
49d73e4521SAndrew Jones {
50d73e4521SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
51d73e4521SAndrew Jones 	struct vring_virtqueue *vq;
52d73e4521SAndrew Jones 	void *queue;
53d73e4521SAndrew Jones 	unsigned num = VIRTIO_MMIO_QUEUE_NUM_MIN;
54d73e4521SAndrew Jones 
55d73e4521SAndrew Jones 	vq = calloc(1, sizeof(*vq));
56d73e4521SAndrew Jones 	queue = memalign(PAGE_SIZE, VIRTIO_MMIO_QUEUE_SIZE_MIN);
57d14aa7cfSAndrew Jones 	assert(vq && queue);
58d73e4521SAndrew Jones 
59d73e4521SAndrew Jones 	writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
60d73e4521SAndrew Jones 
61d73e4521SAndrew Jones 	assert(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX) >= num);
62d73e4521SAndrew Jones 
63d73e4521SAndrew Jones 	if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN) != 0) {
64d73e4521SAndrew Jones 		printf("%s: virtqueue %d already setup! base=%p\n",
65d73e4521SAndrew Jones 				__func__, index, vm_dev->base);
66d73e4521SAndrew Jones 		return NULL;
67d73e4521SAndrew Jones 	}
68d73e4521SAndrew Jones 
69d73e4521SAndrew Jones 	writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
70d73e4521SAndrew Jones 	writel(VIRTIO_MMIO_VRING_ALIGN,
71d73e4521SAndrew Jones 			vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
72d73e4521SAndrew Jones 	writel(virt_to_pfn(queue), vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
73d73e4521SAndrew Jones 
74d73e4521SAndrew Jones 	vring_init_virtqueue(vq, index, num, VIRTIO_MMIO_VRING_ALIGN,
75d73e4521SAndrew Jones 			     vdev, queue, vm_notify, callback, name);
76d73e4521SAndrew Jones 
77d73e4521SAndrew Jones 	return &vq->vq;
78d73e4521SAndrew Jones }
79d73e4521SAndrew Jones 
80d73e4521SAndrew Jones static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
81d73e4521SAndrew Jones 		       struct virtqueue *vqs[], vq_callback_t *callbacks[],
82d73e4521SAndrew Jones 		       const char *names[])
83d73e4521SAndrew Jones {
84d73e4521SAndrew Jones 	unsigned i;
85d73e4521SAndrew Jones 
86d73e4521SAndrew Jones 	for (i = 0; i < nvqs; ++i) {
87355e4f48SAndrew Jones 		vqs[i] = vm_setup_vq(vdev, i,
88355e4f48SAndrew Jones 				     callbacks ? callbacks[i] : NULL,
89355e4f48SAndrew Jones 				     names ? names[i] : "");
90d73e4521SAndrew Jones 		if (vqs[i] == NULL)
91d73e4521SAndrew Jones 			return -1;
92d73e4521SAndrew Jones 	}
93d73e4521SAndrew Jones 
94d73e4521SAndrew Jones 	return 0;
95d73e4521SAndrew Jones }
96d73e4521SAndrew Jones 
9743402ba5SAndrew Jones static const struct virtio_config_ops vm_config_ops = {
9843402ba5SAndrew Jones 	.get = vm_get,
9943402ba5SAndrew Jones 	.set = vm_set,
100d73e4521SAndrew Jones 	.find_vqs = vm_find_vqs,
10143402ba5SAndrew Jones };
10243402ba5SAndrew Jones 
10343402ba5SAndrew Jones static void vm_device_init(struct virtio_mmio_device *vm_dev)
10443402ba5SAndrew Jones {
10543402ba5SAndrew Jones 	vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
10643402ba5SAndrew Jones 	vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
10743402ba5SAndrew Jones 	vm_dev->vdev.config = &vm_config_ops;
108d73e4521SAndrew Jones 
109d73e4521SAndrew Jones 	writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
11043402ba5SAndrew Jones }
11143402ba5SAndrew Jones 
11243402ba5SAndrew Jones /******************************************************
11343402ba5SAndrew Jones  * virtio-mmio device tree support
11443402ba5SAndrew Jones  ******************************************************/
11543402ba5SAndrew Jones 
11643402ba5SAndrew Jones struct vm_dt_info {
11743402ba5SAndrew Jones 	u32 devid;
11843402ba5SAndrew Jones 	void *base;
11943402ba5SAndrew Jones };
12043402ba5SAndrew Jones 
12143402ba5SAndrew Jones static int vm_dt_match(const struct dt_device *dev, int fdtnode)
12243402ba5SAndrew Jones {
12343402ba5SAndrew Jones 	struct vm_dt_info *info = (struct vm_dt_info *)dev->info;
12443402ba5SAndrew Jones 	struct dt_pbus_reg base;
12543402ba5SAndrew Jones 	u32 magic;
12618ab6cadSAndrew Jones 	int ret;
12743402ba5SAndrew Jones 
12843402ba5SAndrew Jones 	dt_device_bind_node((struct dt_device *)dev, fdtnode);
12943402ba5SAndrew Jones 
13018ab6cadSAndrew Jones 	ret = dt_pbus_get_base(dev, &base);
13118ab6cadSAndrew Jones 	assert(ret == 0);
13243402ba5SAndrew Jones 	info->base = ioremap(base.addr, base.size);
13343402ba5SAndrew Jones 
13443402ba5SAndrew Jones 	magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE);
13543402ba5SAndrew Jones 	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24))
13643402ba5SAndrew Jones 		return false;
13743402ba5SAndrew Jones 
13843402ba5SAndrew Jones 	return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid;
13943402ba5SAndrew Jones }
14043402ba5SAndrew Jones 
14143402ba5SAndrew Jones static struct virtio_device *virtio_mmio_dt_bind(u32 devid)
14243402ba5SAndrew Jones {
14343402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev;
14443402ba5SAndrew Jones 	struct dt_device dt_dev;
14543402ba5SAndrew Jones 	struct dt_bus dt_bus;
14643402ba5SAndrew Jones 	struct vm_dt_info info;
14743402ba5SAndrew Jones 	int node;
14843402ba5SAndrew Jones 
14943402ba5SAndrew Jones 	if (!dt_available())
15043402ba5SAndrew Jones 		return NULL;
15143402ba5SAndrew Jones 
15243402ba5SAndrew Jones 	dt_bus_init_defaults(&dt_bus);
15343402ba5SAndrew Jones 	dt_bus.match = vm_dt_match;
15443402ba5SAndrew Jones 
15543402ba5SAndrew Jones 	info.devid = devid;
15643402ba5SAndrew Jones 
15743402ba5SAndrew Jones 	dt_device_init(&dt_dev, &dt_bus, &info);
15843402ba5SAndrew Jones 
15943402ba5SAndrew Jones 	node = dt_device_find_compatible(&dt_dev, "virtio,mmio");
16043402ba5SAndrew Jones 	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
16143402ba5SAndrew Jones 
16243402ba5SAndrew Jones 	if (node == -FDT_ERR_NOTFOUND)
16343402ba5SAndrew Jones 		return NULL;
16443402ba5SAndrew Jones 
16543402ba5SAndrew Jones 	vm_dev = calloc(1, sizeof(*vm_dev));
166d14aa7cfSAndrew Jones 	assert(vm_dev != NULL);
16743402ba5SAndrew Jones 
16843402ba5SAndrew Jones 	vm_dev->base = info.base;
16943402ba5SAndrew Jones 	vm_device_init(vm_dev);
17043402ba5SAndrew Jones 
17143402ba5SAndrew Jones 	return &vm_dev->vdev;
17243402ba5SAndrew Jones }
17343402ba5SAndrew Jones 
17443402ba5SAndrew Jones struct virtio_device *virtio_mmio_bind(u32 devid)
17543402ba5SAndrew Jones {
17643402ba5SAndrew Jones 	return virtio_mmio_dt_bind(devid);
17743402ba5SAndrew Jones }
178