[SLOF] [RFC PATCH slof 2/2] virtio-net: Enable IOMMU

Alexey Kardashevskiy aik at ozlabs.ru
Fri Nov 8 19:40:06 AEDT 2019


When QEMU is started with iommu_platform=on, the guest driver must accept
it so do just this and use IOMMU.

Without the change in virtio-net.fs, SLOF_dma_map_in() invocation from
virtionet_xmit() fails - "dma-map-in" does not return as the node is not
the ethernet device.

Signed-off-by: Alexey Kardashevskiy <aik at ozlabs.ru>
---
 lib/libvirtio/virtio.h        |  3 ++
 lib/libvirtio/virtio-net.c    | 34 ++++++++++-------
 lib/libvirtio/virtio.c        | 69 ++++++++++++++++++++++++++++++++++-
 board-qemu/slof/virtio-net.fs |  1 +
 4 files changed, 93 insertions(+), 14 deletions(-)

diff --git a/lib/libvirtio/virtio.h b/lib/libvirtio/virtio.h
index 4927a97f9f5f..5c9f1e01a497 100644
--- a/lib/libvirtio/virtio.h
+++ b/lib/libvirtio/virtio.h
@@ -29,6 +29,7 @@
 #define VIRTIO_F_RING_INDIRECT_DESC	BIT(28)
 #define VIRTIO_F_RING_EVENT_IDX		BIT(29)
 #define VIRTIO_F_VERSION_1		BIT(32)
+#define VIRTIO_F_IOMMU_PLATFORM         BIT(33)
 
 #define VIRTIO_TIMEOUT		        5000 /* 5 sec timeout */
 
@@ -84,6 +85,8 @@ struct vqs {
 	struct vring_desc *desc;
 	struct vring_avail *avail;
 	struct vring_used *used;
+	uint64_t *desc_gpas; /* to get gpa from desc->addr (which is ioba) */
+	uint64_t bus_desc;
 };
 
 struct virtio_device {
diff --git a/lib/libvirtio/virtio-net.c b/lib/libvirtio/virtio-net.c
index 2290b2d74765..fa6769892571 100644
--- a/lib/libvirtio/virtio-net.c
+++ b/lib/libvirtio/virtio-net.c
@@ -84,18 +84,6 @@ static int virtionet_init_pci(struct virtio_net *vnet, struct virtio_device *dev
 	/* Reset device */
 	virtio_reset_device(vdev);
 
-	/* The queue information can be retrieved via the virtio header that
-	 * can be found in the I/O BAR. First queue is the receive queue,
-	 * second the transmit queue, and the forth is the control queue for
-	 * networking options.
-	 * We are only interested in the receive and transmit queue here. */
-	if (!virtio_queue_init_vq(vdev, VQ_RX) ||
-	    !virtio_queue_init_vq(vdev, VQ_TX)) {
-		virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
-				  |VIRTIO_STAT_FAILED);
-		return -1;
-	}
-
 	/* Acknowledge device. */
 	virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE);
 
@@ -137,6 +125,21 @@ static int virtionet_init(struct virtio_net *vnet)
 		virtio_set_guest_features(vdev,  0);
 	}
 
+
+
+	/* The queue information can be retrieved via the virtio header that
+	 * can be found in the I/O BAR. First queue is the receive queue,
+	 * second the transmit queue, and the forth is the control queue for
+	 * networking options.
+	 * We are only interested in the receive and transmit queue here. */
+	vq_rx = virtio_queue_init_vq(vdev, VQ_RX);
+	vq_tx = virtio_queue_init_vq(vdev, VQ_TX);
+	if (!vq_rx || !vq_tx) {
+		virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
+				  |VIRTIO_STAT_FAILED);
+		return -1;
+	}
+
 	/* Allocate memory for one transmit an multiple receive buffers */
 	vq_rx->buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+net_hdr_size)
 				   * RX_QUEUE_SIZE);
@@ -283,6 +286,7 @@ static int virtionet_receive(struct virtio_net *vnet, char *buf, int maxlen)
 	uint16_t avail_idx;
 	struct virtio_device *vdev = &vnet->vdev;
 	struct vqs *vq_rx = &vnet->vdev.vq[VQ_RX];
+	uint64_t gpa;
 
 	idx = virtio_modern16_to_cpu(vdev, vq_rx->used->idx);
 
@@ -316,7 +320,11 @@ static int virtionet_receive(struct virtio_net *vnet, char *buf, int maxlen)
 #endif
 
 	/* Copy data to destination buffer */
-	memcpy(buf, (void *)virtio_modern64_to_cpu(vdev, vq_rx->desc[id].addr), len);
+	if (vq_rx->desc_gpas)
+		gpa = vq_rx->desc_gpas[id];
+	else
+		gpa = virtio_modern64_to_cpu(vdev, vq_rx->desc[id].addr);
+	memcpy(buf, (void *) gpa, len);
 
 	/* Move indices to next entries */
 	last_rx_idx = last_rx_idx + 1;
diff --git a/lib/libvirtio/virtio.c b/lib/libvirtio/virtio.c
index fff0c7e54c19..e816e2d319be 100644
--- a/lib/libvirtio/virtio.c
+++ b/lib/libvirtio/virtio.c
@@ -273,6 +273,22 @@ void virtio_fill_desc(struct vqs *vq, int id, uint64_t features,
 	next %= vq->size;
 
 	if (features & VIRTIO_F_VERSION_1) {
+		if (features & VIRTIO_F_IOMMU_PLATFORM) {
+			uint64_t olddma, gpa = addr;
+
+			if (!vq->desc_gpas) {
+				fprintf(stderr, "IOMMU setup has not been done!\n");
+				return;
+			}
+
+			olddma = le64_to_cpu(desc->addr);
+			if (olddma)
+				SLOF_dma_map_out(le64_to_cpu(desc->addr),
+					(void *) vq->desc_gpas[id], len);
+
+			addr = SLOF_dma_map_in((void *)gpa, len, 0);
+			vq->desc_gpas[id] = gpa;
+		}
 		desc->addr = cpu_to_le64(addr);
 		desc->len = cpu_to_le32(len);
 		desc->flags = cpu_to_le16(flags);
@@ -326,6 +342,19 @@ static void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long
 		uint64_t q_used;
 		uint32_t q_size = virtio_get_qsize(dev, queue);
 
+		if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+			unsigned long cb;
+
+			cb = q_size * sizeof(struct vring_desc);
+			cb += sizeof(struct vring_avail) + sizeof(uint16_t) * q_size;
+			cb = VQ_ALIGN(cb);
+			cb += sizeof(struct vring_used) + sizeof(uint16_t) * q_size;
+			cb = VQ_ALIGN(cb);
+			q_desc = SLOF_dma_map_in((void *)q_desc, cb, 0);
+
+			dev->vq[queue].bus_desc = q_desc;
+		}
+
 		virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_desc), q_desc);
 		q_avail = q_desc + q_size * sizeof(struct vring_desc);
 		virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_avail), q_avail);
@@ -356,6 +385,7 @@ struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id)
 	memset(vq, 0, sizeof(*vq));
 
 	vq->size = virtio_get_qsize(dev, id);
+	vq->desc_gpas = NULL;
 	vq->desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq->size), 4096);
 	if (!vq->desc) {
 		printf("memory allocation failed!\n");
@@ -373,14 +403,47 @@ struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id)
 
 	vq->avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
 	vq->avail->idx = 0;
+	if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+		vq->desc_gpas = SLOF_alloc_mem_aligned(
+			vq->size * sizeof(vq->desc_gpas[0]), 4096);
+	}
 
 	return vq;
 }
 
 void virtio_queue_term_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id)
 {
-	if (vq->desc)
+	if (vq->desc_gpas) {
+		int i;
+
+		for (i = 0; i < vq->size; ++i) {
+		    struct vring_desc *desc = &vq->desc[i];
+
+		    if (!desc->addr)
+			continue;
+
+		    SLOF_dma_map_out(le64_to_cpu(desc->addr),
+				     (void *) vq->desc_gpas[i],
+				     le32_to_cpu(desc->len));
+		}
+	    memset(vq->desc_gpas, 0, vq->size * sizeof(vq->desc_gpas[0]));
+	}
+	if (vq->desc) {
+		if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+			unsigned long cb;
+			uint32_t q_size = virtio_get_qsize(dev, vq->id);
+
+			cb = q_size * sizeof(struct vring_desc);
+			cb += sizeof(struct vring_avail) + sizeof(uint16_t) * q_size;
+			cb = VQ_ALIGN(cb);
+			cb += sizeof(struct vring_used) + sizeof(uint16_t) * q_size;
+			cb = VQ_ALIGN(cb);
+
+			SLOF_dma_map_out(vq->bus_desc, 0, cb);
+		}
+
 		SLOF_free_mem(vq->desc, virtio_vring_size(vq->size));
+	}
 	memset(vq, 0, sizeof(*vq));
 }
 
@@ -474,6 +537,9 @@ int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features
 		return -1;
 	}
 
+	if (host_features & VIRTIO_F_IOMMU_PLATFORM)
+		features |= VIRTIO_F_IOMMU_PLATFORM;
+
 	virtio_set_guest_features(dev,  features);
 	host_features = virtio_get_host_features(dev);
 	if ((host_features & features) != features) {
@@ -491,6 +557,7 @@ int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features
 		return -1;
 
 	dev->features = features;
+	printf("Negotiated %llx\n", dev->features);
 
 	return 0;
 }
diff --git a/board-qemu/slof/virtio-net.fs b/board-qemu/slof/virtio-net.fs
index ff6fcafac4cf..7760499e1d29 100644
--- a/board-qemu/slof/virtio-net.fs
+++ b/board-qemu/slof/virtio-net.fs
@@ -72,6 +72,7 @@ virtio-setup-vd VALUE virtiodev
 
 : write ( buf len -- actual )
    dup IF
+      my-phandle set-node
       virtio-net-priv virtio-net-write
    ELSE
       nip
-- 
2.17.1



More information about the SLOF mailing list