diff options
author | Eric Auger | 2021-03-09 11:27:38 +0100 |
---|---|---|
committer | Peter Maydell | 2021-03-12 13:40:10 +0100 |
commit | dde3f08b5cab24e570fc0ccbbbab86b6b50aad23 (patch) | |
tree | 72fca0e7a8992d390e867594cbfcf953d15b7e92 | |
parent | dma: Introduce dma_aligned_pow2_mask() (diff) | |
download | qemu-dde3f08b5cab24e570fc0ccbbbab86b6b50aad23.tar.gz qemu-dde3f08b5cab24e570fc0ccbbbab86b6b50aad23.tar.xz qemu-dde3f08b5cab24e570fc0ccbbbab86b6b50aad23.zip |
virtio-iommu: Handle non power of 2 range invalidations
Unmap notifiers work with an address mask assuming an
invalidation range of a power of 2. Nothing mandates this
in the VIRTIO-IOMMU spec.
So in case the range is not a power of 2, split it into
several invalidations.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Message-id: 20210309102742.30442-4-eric.auger@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | hw/virtio/virtio-iommu.c | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index c2883a2f6c..1b23e8e18c 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -155,6 +155,7 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end) { IOMMUTLBEvent event; + uint64_t delta = virt_end - virt_start; if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { return; @@ -164,12 +165,24 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, event.type = IOMMU_NOTIFIER_UNMAP; event.entry.target_as = &address_space_memory; - event.entry.addr_mask = virt_end - virt_start; - event.entry.iova = virt_start; event.entry.perm = IOMMU_NONE; event.entry.translated_addr = 0; + event.entry.addr_mask = delta; + event.entry.iova = virt_start; - memory_region_notify_iommu(mr, 0, event); + if (delta == UINT64_MAX) { + memory_region_notify_iommu(mr, 0, event); + } + + + while (virt_start != virt_end + 1) { + uint64_t mask = dma_aligned_pow2_mask(virt_start, virt_end, 64); + + event.entry.addr_mask = mask; + event.entry.iova = virt_start; + memory_region_notify_iommu(mr, 0, event); + virt_start += mask + 1; + } } static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value, |