/* virtio-pci.c - virtio ring management * * (c) Copyright 2008 Bull S.A.S. * * Author: Laurent Vivier * * some parts from Linux Virtio Ring * * Copyright Rusty Russell IBM Corporation 2007 * * Adopted for Seabios: Gleb Natapov * * This work is licensed under the terms of the GNU LGPLv3 * See the COPYING file in the top-level directory. * * */ #include "biosvar.h" // GET_GLOBAL #include "output.h" // panic #include "virtio-ring.h" #include "virtio-pci.h" #define BUG() do { \ panic("BUG: failure at %d/%s()!\n", __LINE__, __func__); \ } while (0) #define BUG_ON(condition) do { if (condition) BUG(); } while (0) /* * vring_more_used * * is there some used buffers ? * */ int vring_more_used(struct vring_virtqueue *vq) { struct vring_used *used = vq->vring.used; int more = vq->last_used_idx != used->idx; /* Make sure ring reads are done after idx read above. */ smp_rmb(); return more; } /* * vring_free * * put at the begin of the free list the current desc[head] */ void vring_detach(struct vring_virtqueue *vq, unsigned int head) { struct vring *vr = &vq->vring; struct vring_desc *desc = GET_LOWFLAT(vr->desc); unsigned int i; /* find end of given descriptor */ i = head; while (desc[i].flags & VRING_DESC_F_NEXT) i = desc[i].next; /* link it with free list and point to it */ desc[i].next = vq->free_head; vq->free_head = head; } /* * vring_get_buf * * get a buffer from the used list * */ int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) { struct vring *vr = &vq->vring; struct vring_used_elem *elem; struct vring_used *used = vq->vring.used; u32 id; int ret; // BUG_ON(!vring_more_used(vq)); elem = &used->ring[vq->last_used_idx % vr->num]; id = elem->id; if (len != NULL) *len = elem->len; ret = vq->vdata[id]; vring_detach(vq, id); vq->last_used_idx = vq->last_used_idx + 1; return ret; } void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], unsigned int out, unsigned int in, int index, int num_added) { struct vring *vr = &vq->vring; int i, av, head, prev; struct vring_desc *desc = vr->desc; struct vring_avail *avail = vr->avail; BUG_ON(out + in == 0); prev = 0; head = vq->free_head; for (i = head; out; i = desc[i].next, out--) { desc[i].flags = VRING_DESC_F_NEXT; desc[i].addr = (u64)virt_to_phys(list->addr); desc[i].len = list->length; prev = i; list++; } for ( ; in; i = desc[i].next, in--) { desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; desc[i].addr = (u64)virt_to_phys(list->addr); desc[i].len = list->length; prev = i; list++; } desc[prev].flags = desc[prev].flags & ~VRING_DESC_F_NEXT; vq->free_head = i; vq->vdata[head] = index; av = (avail->idx + num_added) % vr->num; avail->ring[av] = head; } void vring_kick(struct vp_device *vp, struct vring_virtqueue *vq, int num_added) { struct vring *vr = &vq->vring; struct vring_avail *avail = vr->avail; /* Make sure idx update is done after ring write. */ smp_wmb(); avail->idx = avail->idx + num_added; vp_notify(vp, vq); }