[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