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  * This work is licensed under the terms of the GNU GPL, version 2 or later.
12  * See the COPYING file in the top-level directory.
13  *
14  *
15  */
16 
17 #include "etherboot.h"
18 #include "gpxe/io.h"
19 #include "gpxe/virtio-ring.h"
20 #include "gpxe/virtio-pci.h"
21 
22 #define BUG() do { \
23    printf("BUG: failure at %s:%d/%s()!\n", \
24           __FILE__, __LINE__, __FUNCTION__); \
25    while(1); \
26 } while (0)
27 #define BUG_ON(condition) do { if (condition) BUG(); } while (0)
28 
29 /*
30  * vring_free
31  *
32  * put at the begin of the free list the current desc[head]
33  */
34 
vring_detach(struct vring_virtqueue * vq,unsigned int head)35 void vring_detach(struct vring_virtqueue *vq, unsigned int head)
36 {
37    struct vring *vr = &vq->vring;
38    unsigned int i;
39 
40    /* find end of given descriptor */
41 
42    i = head;
43    while (vr->desc[i].flags & VRING_DESC_F_NEXT)
44            i = vr->desc[i].next;
45 
46    /* link it with free list and point to it */
47 
48    vr->desc[i].next = vq->free_head;
49    wmb();
50    vq->free_head = head;
51 }
52 
53 /*
54  * vring_get_buf
55  *
56  * get a buffer from the used list
57  *
58  */
59 
vring_get_buf(struct vring_virtqueue * vq,unsigned int * len)60 int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len)
61 {
62    struct vring *vr = &vq->vring;
63    struct vring_used_elem *elem;
64    u32 id;
65    int ret;
66 
67    BUG_ON(!vring_more_used(vq));
68 
69    elem = &vr->used->ring[vq->last_used_idx % vr->num];
70    wmb();
71    id = elem->id;
72    if (len != NULL)
73            *len = elem->len;
74 
75    ret = vq->vdata[id];
76 
77    vring_detach(vq, id);
78 
79    vq->last_used_idx++;
80 
81    return ret;
82 }
83 
vring_add_buf(struct vring_virtqueue * vq,struct vring_list list[],unsigned int out,unsigned int in,int index,int num_added)84 void vring_add_buf(struct vring_virtqueue *vq,
85 		   struct vring_list list[],
86 		   unsigned int out, unsigned int in,
87 		   int index, int num_added)
88 {
89    struct vring *vr = &vq->vring;
90    int i, avail, head, prev;
91 
92    BUG_ON(out + in == 0);
93 
94    prev = 0;
95    head = vq->free_head;
96    for (i = head; out; i = vr->desc[i].next, out--) {
97 
98            vr->desc[i].flags = VRING_DESC_F_NEXT;
99            vr->desc[i].addr = (u64)virt_to_phys(list->addr);
100            vr->desc[i].len = list->length;
101            prev = i;
102            list++;
103    }
104    for ( ; in; i = vr->desc[i].next, in--) {
105 
106            vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
107            vr->desc[i].addr = (u64)virt_to_phys(list->addr);
108            vr->desc[i].len = list->length;
109            prev = i;
110            list++;
111    }
112    vr->desc[prev].flags &= ~VRING_DESC_F_NEXT;
113 
114    vq->free_head = i;
115 
116    vq->vdata[head] = index;
117 
118    avail = (vr->avail->idx + num_added) % vr->num;
119    vr->avail->ring[avail] = head;
120    wmb();
121 }
122 
vring_kick(unsigned int ioaddr,struct vring_virtqueue * vq,int num_added)123 void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added)
124 {
125    struct vring *vr = &vq->vring;
126 
127    wmb();
128    vr->avail->idx += num_added;
129 
130    mb();
131    if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY))
132            vp_notify(ioaddr, vq->queue_index);
133 }
134 
135