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