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