Home | History | Annotate | Download | only in bus
      1 /* virtio-pci.c - virtio ring management
      2  *
      3  * (c) Copyright 2008 Bull S.A.S.
      4  *
      5  *  Author: Laurent Vivier <Laurent.Vivier (at) 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 
     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 
     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 
     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 
    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