Spaces:
Runtime error
Runtime error
| /* | |
| * 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. | |
| */ | |
| 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); | |
| } | |