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