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