1 /* virtio-pci.c - virtio ring management
2  *
3  * (c) Copyright 2008 Bull S.A.S.
4  *
5  *  Author: Laurent Vivier <Laurent.Vivier@bull.net>
6  *
7  *  some parts from Linux Virtio Ring
8  *
9  *  Copyright Rusty Russell IBM Corporation 2007
10  *
11  *  Adopted for Seabios: Gleb Natapov <gleb@redhat.com>
12  *
13  * This work is licensed under the terms of the GNU LGPLv3
14  * See the COPYING file in the top-level directory.
15  *
16  *
17  */
18 
19 #include "output.h" // panic
20 #include "virtio-ring.h"
21 #include "virtio-pci.h"
22 
23 #define BUG() do {                                                      \
24             panic("BUG: failure at %d/%s()!\n", __LINE__, __func__);    \
25         } while (0)
26 #define BUG_ON(condition) do { if (condition) BUG(); } while (0)
27 
28 /*
29  * vring_more_used
30  *
31  * is there some used buffers ?
32  *
33  */
34 
vring_more_used(struct vring_virtqueue * vq)35 int vring_more_used(struct vring_virtqueue *vq)
36 {
37     struct vring_used *used = vq->vring.used;
38     int more = vq->last_used_idx != used->idx;
39     /* Make sure ring reads are done after idx read above. */
40     smp_rmb();
41     return more;
42 }
43 
44 /*
45  * vring_free
46  *
47  * put at the begin of the free list the current desc[head]
48  */
49 
vring_detach(struct vring_virtqueue * vq,unsigned int head)50 void vring_detach(struct vring_virtqueue *vq, unsigned int head)
51 {
52     struct vring *vr = &vq->vring;
53     struct vring_desc *desc = GET_LOWFLAT(vr->desc);
54     unsigned int i;
55 
56     /* find end of given descriptor */
57 
58     i = head;
59     while (desc[i].flags & VRING_DESC_F_NEXT)
60         i = desc[i].next;
61 
62     /* link it with free list and point to it */
63 
64     desc[i].next = vq->free_head;
65     vq->free_head = head;
66 }
67 
68 /*
69  * vring_get_buf
70  *
71  * get a buffer from the used list
72  *
73  */
74 
vring_get_buf(struct vring_virtqueue * vq,unsigned int * len)75 int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len)
76 {
77     struct vring *vr = &vq->vring;
78     struct vring_used_elem *elem;
79     struct vring_used *used = vq->vring.used;
80     u32 id;
81     int ret;
82 
83 //    BUG_ON(!vring_more_used(vq));
84 
85     elem = &used->ring[vq->last_used_idx % vr->num];
86     id = elem->id;
87     if (len != NULL)
88         *len = elem->len;
89 
90     ret = vq->vdata[id];
91 
92     vring_detach(vq, id);
93 
94     vq->last_used_idx = vq->last_used_idx + 1;
95 
96     return ret;
97 }
98 
vring_add_buf(struct vring_virtqueue * vq,struct vring_list list[],unsigned int out,unsigned int in,int index,int num_added)99 void vring_add_buf(struct vring_virtqueue *vq,
100                    struct vring_list list[],
101                    unsigned int out, unsigned int in,
102                    int index, int num_added)
103 {
104     struct vring *vr = &vq->vring;
105     int i, av, head, prev;
106     struct vring_desc *desc = vr->desc;
107     struct vring_avail *avail = vr->avail;
108 
109     BUG_ON(out + in == 0);
110 
111     prev = 0;
112     head = vq->free_head;
113     for (i = head; out; i = desc[i].next, out--) {
114         desc[i].flags = VRING_DESC_F_NEXT;
115         desc[i].addr = (u64)virt_to_phys(list->addr);
116         desc[i].len = list->length;
117         prev = i;
118         list++;
119     }
120     for ( ; in; i = desc[i].next, in--) {
121         desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
122         desc[i].addr = (u64)virt_to_phys(list->addr);
123         desc[i].len = list->length;
124         prev = i;
125         list++;
126     }
127     desc[prev].flags = desc[prev].flags & ~VRING_DESC_F_NEXT;
128 
129     vq->free_head = i;
130 
131     vq->vdata[head] = index;
132 
133     av = (avail->idx + num_added) % vr->num;
134     avail->ring[av] = head;
135 }
136 
vring_kick(struct vp_device * vp,struct vring_virtqueue * vq,int num_added)137 void vring_kick(struct vp_device *vp, struct vring_virtqueue *vq, int num_added)
138 {
139     struct vring *vr = &vq->vring;
140     struct vring_avail *avail = vr->avail;
141 
142     /* Make sure idx update is done after ring write. */
143     smp_wmb();
144     avail->idx = avail->idx + num_added;
145 
146     vp_notify(vp, vq);
147 }
148