1 /* 2 * virtqueue support adapted from the Linux kernel. 3 * 4 * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2. 7 */ 8 #include "libcflat.h" 9 #include "asm/io.h" 10 #include "virtio.h" 11 #include "virtio-mmio.h" 12 13 void vring_init(struct vring *vr, unsigned int num, void *p, 14 unsigned long align) 15 { 16 vr->num = num; 17 vr->desc = p; 18 vr->avail = p + num*sizeof(struct vring_desc); 19 vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16) 20 + align-1) & ~(align - 1)); 21 } 22 23 void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index, 24 unsigned num, unsigned vring_align, 25 struct virtio_device *vdev, void *pages, 26 bool (*notify)(struct virtqueue *), 27 void (*callback)(struct virtqueue *), 28 const char *name) 29 { 30 unsigned i; 31 32 vring_init(&vq->vring, num, pages, vring_align); 33 vq->vq.callback = callback; 34 vq->vq.vdev = vdev; 35 vq->vq.name = name; 36 vq->vq.num_free = num; 37 vq->vq.index = index; 38 vq->notify = notify; 39 vq->last_used_idx = 0; 40 vq->num_added = 0; 41 vq->free_head = 0; 42 43 for (i = 0; i < num-1; i++) { 44 vq->vring.desc[i].next = i+1; 45 vq->data[i] = NULL; 46 } 47 vq->data[i] = NULL; 48 } 49 50 int virtqueue_add_outbuf(struct virtqueue *_vq, char *buf, unsigned int len) 51 { 52 struct vring_virtqueue *vq = to_vvq(_vq); 53 unsigned avail; 54 int head; 55 56 assert(buf != NULL); 57 assert(len != 0); 58 59 if (!vq->vq.num_free) 60 return -1; 61 62 --vq->vq.num_free; 63 64 head = vq->free_head; 65 66 vq->vring.desc[head].flags = 0; 67 vq->vring.desc[head].addr = virt_to_phys(buf); 68 vq->vring.desc[head].len = len; 69 70 vq->free_head = vq->vring.desc[head].next; 71 72 vq->data[head] = buf; 73 74 avail = (vq->vring.avail->idx & (vq->vring.num-1)); 75 vq->vring.avail->ring[avail] = head; 76 wmb(); 77 vq->vring.avail->idx++; 78 vq->num_added++; 79 80 return 0; 81 } 82 83 bool virtqueue_kick(struct virtqueue *_vq) 84 { 85 struct vring_virtqueue *vq = to_vvq(_vq); 86 mb(); 87 return vq->notify(_vq); 88 } 89 90 void detach_buf(struct vring_virtqueue *vq, unsigned head) 91 { 92 unsigned i = head; 93 94 vq->data[head] = NULL; 95 96 while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) { 97 i = vq->vring.desc[i].next; 98 vq->vq.num_free++; 99 } 100 101 vq->vring.desc[i].next = vq->free_head; 102 vq->free_head = head; 103 vq->vq.num_free++; 104 } 105 106 void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) 107 { 108 struct vring_virtqueue *vq = to_vvq(_vq); 109 u16 last_used; 110 unsigned i; 111 void *ret; 112 113 rmb(); 114 115 last_used = (vq->last_used_idx & (vq->vring.num-1)); 116 i = vq->vring.used->ring[last_used].id; 117 *len = vq->vring.used->ring[last_used].len; 118 119 ret = vq->data[i]; 120 detach_buf(vq, i); 121 122 vq->last_used_idx++; 123 124 return ret; 125 } 126 127 struct virtio_device *virtio_bind(u32 devid) 128 { 129 return virtio_mmio_bind(devid); 130 } 131