aboutsummaryrefslogtreecommitdiff
path: root/hw/virtio/virtio-iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/virtio/virtio-iommu.c')
-rw-r--r--hw/virtio/virtio-iommu.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index 78e07aa40a..fc5c75d693 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -899,6 +899,55 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr,
return 0;
}
+/*
+ * The default mask (TARGET_PAGE_MASK) is the smallest supported guest granule,
+ * for example 0xfffffffffffff000. When an assigned device has page size
+ * restrictions due to the hardware IOMMU configuration, apply this restriction
+ * to the mask.
+ */
+static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr,
+ uint64_t new_mask,
+ Error **errp)
+{
+ IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr);
+ VirtIOIOMMU *s = sdev->viommu;
+ uint64_t cur_mask = s->config.page_size_mask;
+
+ trace_virtio_iommu_set_page_size_mask(mr->parent_obj.name, cur_mask,
+ new_mask);
+
+ if ((cur_mask & new_mask) == 0) {
+ error_setg(errp, "virtio-iommu page mask 0x%"PRIx64
+ " is incompatible with mask 0x%"PRIx64, cur_mask, new_mask);
+ return -1;
+ }
+
+ /*
+ * After the machine is finalized, we can't change the mask anymore. If by
+ * chance the hotplugged device supports the same granule, we can still
+ * accept it. Having a different masks is possible but the guest will use
+ * sub-optimal block sizes, so warn about it.
+ */
+ if (qdev_hotplug) {
+ int new_granule = ctz64(new_mask);
+ int cur_granule = ctz64(cur_mask);
+
+ if (new_granule != cur_granule) {
+ error_setg(errp, "virtio-iommu page mask 0x%"PRIx64
+ " is incompatible with mask 0x%"PRIx64, cur_mask,
+ new_mask);
+ return -1;
+ } else if (new_mask != cur_mask) {
+ warn_report("virtio-iommu page mask 0x%"PRIx64
+ " does not match 0x%"PRIx64, cur_mask, new_mask);
+ }
+ return 0;
+ }
+
+ s->config.page_size_mask &= new_mask;
+ return 0;
+}
+
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -1130,6 +1179,7 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass,
imrc->translate = virtio_iommu_translate;
imrc->replay = virtio_iommu_replay;
imrc->notify_flag_changed = virtio_iommu_notify_flag_changed;
+ imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask;
}
static const TypeInfo virtio_iommu_info = {