diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index c50602a3..402bf4f1 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -21,11 +21,37 @@ #include "ipxe/virtio-pci.h" #include "ipxe/virtio-ring.h" +static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num) +{ + size_t queue_size = PAGE_MASK + vring_size(num); + size_t vdata_size = num * sizeof(void *); + + vq->queue = zalloc(queue_size + vdata_size); + if (!vq->queue) { + return -ENOMEM; + } + + /* vdata immediately follows the ring */ + vq->vdata = (void **)(vq->queue + queue_size); + + return 0; +} + +void vp_free_vq(struct vring_virtqueue *vq) +{ + if (vq->queue) { + free(vq->queue); + vq->queue = NULL; + vq->vdata = NULL; + } +} + int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq) { struct vring * vr = &vq->vring; u16 num; + int rc; /* select the queue */ @@ -39,11 +65,6 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, return -1; } - if (num > MAX_QUEUE_NUM) { - DBG("VIRTIO-PCI ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); - return -1; - } - /* check if the queue is already active */ if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { @@ -54,8 +75,12 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, vq->queue_index = queue_index; /* initialize the queue */ - - vring_init(vr, num, (unsigned char*)&vq->queue); + rc = vp_alloc_vq(vq, num); + if (rc) { + DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n"); + return rc; + } + vring_init(vr, num, vq->queue); /* activate the queue * @@ -354,7 +379,7 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, return -ENOENT; if (size & (size - 1)) { - DBG("VIRTIO-PCI %p: bad queue size %d", vdev, size); + DBG("VIRTIO-PCI %p: bad queue size %d\n", vdev, size); return -EINVAL; } @@ -371,7 +396,12 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, /* get offset of notification word for this vq */ off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); - vring_init(&vq->vring, size, (unsigned char *)vq->queue); + err = vp_alloc_vq(vq, size); + if (err) { + DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev); + return err; + } + vring_init(&vq->vring, size, vq->queue); /* activate the queue */ vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index de3333d2..eaf6ed38 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -185,6 +185,7 @@ static void virtnet_free_virtqueues ( struct net_device *netdev ) { for ( i = 0; i < QUEUE_NB; i++ ) { virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); + vp_free_vq ( &virtnet->virtqueue[i] ); } free ( virtnet->virtqueue ); diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index f3c9b17c..f3074f14 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -196,9 +196,11 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index) struct vring_virtqueue; +void vp_free_vq(struct vring_virtqueue *vq); int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq); + /* Virtio 1.0 I/O routines abstract away the three possible HW access * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in * are endianness conversions - to LE on write and from LE on read. */ diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index d2ded300..e608e624 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -71,14 +71,12 @@ struct vring { + PAGE_MASK) & ~PAGE_MASK) + \ (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) -typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)]; - struct vring_virtqueue { - virtio_queue_t queue; + unsigned char *queue; struct vring vring; u16 free_head; u16 last_used_idx; - void *vdata[MAX_QUEUE_NUM]; + void **vdata; /* PCI */ int queue_index; struct virtio_pci_region notification;