xref: /kvm-unit-tests/lib/virtio.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"
9d73e4521SAndrew Jones #include "asm/io.h"
1043402ba5SAndrew Jones #include "virtio.h"
1143402ba5SAndrew Jones #include "virtio-mmio.h"
1243402ba5SAndrew Jones 
vring_init(struct vring * vr,unsigned int num,void * p,unsigned long align)13d73e4521SAndrew Jones void vring_init(struct vring *vr, unsigned int num, void *p,
14d73e4521SAndrew Jones 		       unsigned long align)
15d73e4521SAndrew Jones {
16d73e4521SAndrew Jones 	vr->num = num;
17d73e4521SAndrew Jones 	vr->desc = p;
18d73e4521SAndrew Jones 	vr->avail = p + num*sizeof(struct vring_desc);
19d73e4521SAndrew Jones 	vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16)
20d73e4521SAndrew Jones 		+ align-1) & ~(align - 1));
21d73e4521SAndrew Jones }
22d73e4521SAndrew Jones 
vring_init_virtqueue(struct vring_virtqueue * vq,unsigned index,unsigned num,unsigned vring_align,struct virtio_device * vdev,void * pages,bool (* notify)(struct virtqueue *),void (* callback)(struct virtqueue *),const char * name)23d73e4521SAndrew Jones void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index,
24d73e4521SAndrew Jones 			  unsigned num, unsigned vring_align,
25d73e4521SAndrew Jones 			  struct virtio_device *vdev, void *pages,
26d73e4521SAndrew Jones 			  bool (*notify)(struct virtqueue *),
27d73e4521SAndrew Jones 			  void (*callback)(struct virtqueue *),
28d73e4521SAndrew Jones 			  const char *name)
29d73e4521SAndrew Jones {
30d73e4521SAndrew Jones 	unsigned i;
31d73e4521SAndrew Jones 
32d73e4521SAndrew Jones 	vring_init(&vq->vring, num, pages, vring_align);
33d73e4521SAndrew Jones 	vq->vq.callback = callback;
34d73e4521SAndrew Jones 	vq->vq.vdev = vdev;
35d73e4521SAndrew Jones 	vq->vq.name = name;
36d73e4521SAndrew Jones 	vq->vq.num_free = num;
37d73e4521SAndrew Jones 	vq->vq.index = index;
38d73e4521SAndrew Jones 	vq->notify = notify;
39d73e4521SAndrew Jones 	vq->last_used_idx = 0;
40d73e4521SAndrew Jones 	vq->num_added = 0;
41d73e4521SAndrew Jones 	vq->free_head = 0;
42d73e4521SAndrew Jones 
43d73e4521SAndrew Jones 	for (i = 0; i < num-1; i++) {
44d73e4521SAndrew Jones 		vq->vring.desc[i].next = i+1;
45d73e4521SAndrew Jones 		vq->data[i] = NULL;
46d73e4521SAndrew Jones 	}
47d73e4521SAndrew Jones 	vq->data[i] = NULL;
48d73e4521SAndrew Jones }
49d73e4521SAndrew Jones 
virtqueue_add_outbuf(struct virtqueue * _vq,char * buf,unsigned int len)50368819a9SAndrew Jones int virtqueue_add_outbuf(struct virtqueue *_vq, char *buf, unsigned int len)
51d73e4521SAndrew Jones {
52d73e4521SAndrew Jones 	struct vring_virtqueue *vq = to_vvq(_vq);
53d73e4521SAndrew Jones 	unsigned avail;
54d73e4521SAndrew Jones 	int head;
55d73e4521SAndrew Jones 
56d73e4521SAndrew Jones 	assert(buf != NULL);
57d73e4521SAndrew Jones 	assert(len != 0);
58d73e4521SAndrew Jones 
59d73e4521SAndrew Jones 	if (!vq->vq.num_free)
60d73e4521SAndrew Jones 		return -1;
61d73e4521SAndrew Jones 
62d73e4521SAndrew Jones 	--vq->vq.num_free;
63d73e4521SAndrew Jones 
64d73e4521SAndrew Jones 	head = vq->free_head;
65d73e4521SAndrew Jones 
66d73e4521SAndrew Jones 	vq->vring.desc[head].flags = 0;
67d73e4521SAndrew Jones 	vq->vring.desc[head].addr = virt_to_phys(buf);
68d73e4521SAndrew Jones 	vq->vring.desc[head].len = len;
69d73e4521SAndrew Jones 
70d73e4521SAndrew Jones 	vq->free_head = vq->vring.desc[head].next;
71d73e4521SAndrew Jones 
72d73e4521SAndrew Jones 	vq->data[head] = buf;
73d73e4521SAndrew Jones 
74d73e4521SAndrew Jones 	avail = (vq->vring.avail->idx & (vq->vring.num-1));
75d73e4521SAndrew Jones 	vq->vring.avail->ring[avail] = head;
76d73e4521SAndrew Jones 	wmb();
77d73e4521SAndrew Jones 	vq->vring.avail->idx++;
78d73e4521SAndrew Jones 	vq->num_added++;
79d73e4521SAndrew Jones 
80d73e4521SAndrew Jones 	return 0;
81d73e4521SAndrew Jones }
82d73e4521SAndrew Jones 
virtqueue_kick(struct virtqueue * _vq)83d73e4521SAndrew Jones bool virtqueue_kick(struct virtqueue *_vq)
84d73e4521SAndrew Jones {
85d73e4521SAndrew Jones 	struct vring_virtqueue *vq = to_vvq(_vq);
86d73e4521SAndrew Jones 	mb();
87d73e4521SAndrew Jones 	return vq->notify(_vq);
88d73e4521SAndrew Jones }
89d73e4521SAndrew Jones 
detach_buf(struct vring_virtqueue * vq,unsigned head)90d73e4521SAndrew Jones void detach_buf(struct vring_virtqueue *vq, unsigned head)
91d73e4521SAndrew Jones {
92d73e4521SAndrew Jones 	unsigned i = head;
93d73e4521SAndrew Jones 
94d73e4521SAndrew Jones 	vq->data[head] = NULL;
95d73e4521SAndrew Jones 
96d73e4521SAndrew Jones 	while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
97d73e4521SAndrew Jones 		i = vq->vring.desc[i].next;
98d73e4521SAndrew Jones 		vq->vq.num_free++;
99d73e4521SAndrew Jones 	}
100d73e4521SAndrew Jones 
101d73e4521SAndrew Jones 	vq->vring.desc[i].next = vq->free_head;
102d73e4521SAndrew Jones 	vq->free_head = head;
103d73e4521SAndrew Jones 	vq->vq.num_free++;
104d73e4521SAndrew Jones }
105d73e4521SAndrew Jones 
virtqueue_get_buf(struct virtqueue * _vq,unsigned int * len)106d73e4521SAndrew Jones void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
107d73e4521SAndrew Jones {
108d73e4521SAndrew Jones 	struct vring_virtqueue *vq = to_vvq(_vq);
109d73e4521SAndrew Jones 	u16 last_used;
110d73e4521SAndrew Jones 	unsigned i;
111d73e4521SAndrew Jones 	void *ret;
112d73e4521SAndrew Jones 
113d73e4521SAndrew Jones 	rmb();
114d73e4521SAndrew Jones 
115d73e4521SAndrew Jones 	last_used = (vq->last_used_idx & (vq->vring.num-1));
116d73e4521SAndrew Jones 	i = vq->vring.used->ring[last_used].id;
117d73e4521SAndrew Jones 	*len = vq->vring.used->ring[last_used].len;
118d73e4521SAndrew Jones 
119d73e4521SAndrew Jones 	ret = vq->data[i];
120d73e4521SAndrew Jones 	detach_buf(vq, i);
121d73e4521SAndrew Jones 
122d73e4521SAndrew Jones 	vq->last_used_idx++;
123d73e4521SAndrew Jones 
124d73e4521SAndrew Jones 	return ret;
125d73e4521SAndrew Jones }
126d73e4521SAndrew Jones 
virtio_bind(u32 devid)12743402ba5SAndrew Jones struct virtio_device *virtio_bind(u32 devid)
12843402ba5SAndrew Jones {
12943402ba5SAndrew Jones 	return virtio_mmio_bind(devid);
13043402ba5SAndrew Jones }
131