xref: /kvm-unit-tests/lib/virtio-mmio.c (revision 4363f1d9a646a5c7ea673bee8fc33ca6f2cddbd8)
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