123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /*
- * virtqueue support adapted from the Linux kernel.
- *
- * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- */
- #include "libcflat.h"
- #include "asm/io.h"
- #include "virtio.h"
- #include "virtio-mmio.h"
- void vring_init(struct vring *vr, unsigned int num, void *p,
- unsigned long align)
- {
- vr->num = num;
- vr->desc = p;
- vr->avail = p + num*sizeof(struct vring_desc);
- vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16)
- + align-1) & ~(align - 1));
- }
- void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index,
- unsigned num, unsigned vring_align,
- struct virtio_device *vdev, void *pages,
- bool (*notify)(struct virtqueue *),
- void (*callback)(struct virtqueue *),
- const char *name)
- {
- unsigned i;
- vring_init(&vq->vring, num, pages, vring_align);
- vq->vq.callback = callback;
- vq->vq.vdev = vdev;
- vq->vq.name = name;
- vq->vq.num_free = num;
- vq->vq.index = index;
- vq->notify = notify;
- vq->last_used_idx = 0;
- vq->num_added = 0;
- vq->free_head = 0;
- for (i = 0; i < num-1; i++) {
- vq->vring.desc[i].next = i+1;
- vq->data[i] = NULL;
- }
- vq->data[i] = NULL;
- }
- int virtqueue_add_outbuf(struct virtqueue *_vq, char *buf, unsigned int len)
- {
- struct vring_virtqueue *vq = to_vvq(_vq);
- unsigned avail;
- int head;
- assert(buf != NULL);
- assert(len != 0);
- if (!vq->vq.num_free)
- return -1;
- --vq->vq.num_free;
- head = vq->free_head;
- vq->vring.desc[head].flags = 0;
- vq->vring.desc[head].addr = virt_to_phys(buf);
- vq->vring.desc[head].len = len;
- vq->free_head = vq->vring.desc[head].next;
- vq->data[head] = buf;
- avail = (vq->vring.avail->idx & (vq->vring.num-1));
- vq->vring.avail->ring[avail] = head;
- wmb();
- vq->vring.avail->idx++;
- vq->num_added++;
- return 0;
- }
- bool virtqueue_kick(struct virtqueue *_vq)
- {
- struct vring_virtqueue *vq = to_vvq(_vq);
- mb();
- return vq->notify(_vq);
- }
- void detach_buf(struct vring_virtqueue *vq, unsigned head)
- {
- unsigned i = head;
- vq->data[head] = NULL;
- while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
- i = vq->vring.desc[i].next;
- vq->vq.num_free++;
- }
- vq->vring.desc[i].next = vq->free_head;
- vq->free_head = head;
- vq->vq.num_free++;
- }
- void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
- {
- struct vring_virtqueue *vq = to_vvq(_vq);
- u16 last_used;
- unsigned i;
- void *ret;
- rmb();
- last_used = (vq->last_used_idx & (vq->vring.num-1));
- i = vq->vring.used->ring[last_used].id;
- *len = vq->vring.used->ring[last_used].len;
- ret = vq->data[i];
- detach_buf(vq, i);
- vq->last_used_idx++;
- return ret;
- }
- struct virtio_device *virtio_bind(u32 devid)
- {
- return virtio_mmio_bind(devid);
- }
|