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 35void 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 60int 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 84void 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 123void 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