[e16e8f2] | 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 | |
---|
| 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 | |
---|