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.c123
1 files changed, 62 insertions, 61 deletions
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index 7c54c6b..33ae61c 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -563,10 +563,15 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
int ret = -EINVAL;
if (!sbus) {
- error_report("%s no sbus", __func__);
+ error_setg(errp, "%s: no IOMMUPciBus found!", __func__);
+ return ret;
}
sdev = sbus->pbdev[devfn];
+ if (!sdev) {
+ error_setg(errp, "%s: no IOMMUDevice found!", __func__);
+ return ret;
+ }
current_ranges = sdev->host_resv_ranges;
@@ -613,9 +618,39 @@ out:
return ret;
}
+static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask,
+ Error **errp)
+{
+ uint64_t cur_mask = viommu->config.page_size_mask;
+
+ if ((cur_mask & new_mask) == 0) {
+ error_setg(errp, "virtio-iommu reports a page size mask 0x%"PRIx64
+ " incompatible with currently supported mask 0x%"PRIx64,
+ new_mask, cur_mask);
+ return false;
+ }
+ /*
+ * Once the granule is frozen we can't change the mask anymore. If by
+ * chance the hotplugged device supports the same granule, we can still
+ * accept it.
+ */
+ if (viommu->granule_frozen) {
+ int cur_granule = ctz64(cur_mask);
+
+ if (!(BIT_ULL(cur_granule) & new_mask)) {
+ error_setg(errp,
+ "virtio-iommu does not support frozen granule 0x%llx",
+ BIT_ULL(cur_granule));
+ return false;
+ }
+ }
+ return true;
+}
+
static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
HostIOMMUDevice *hiod, Error **errp)
{
+ ERRP_GUARD();
VirtIOIOMMU *viommu = opaque;
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod);
struct hiod_key *new_key;
@@ -630,7 +665,7 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
if (hiodc->get_iova_ranges) {
int ret;
- host_iova_ranges = hiodc->get_iova_ranges(hiod, errp);
+ host_iova_ranges = hiodc->get_iova_ranges(hiod);
if (!host_iova_ranges) {
return true; /* some old kernels may not support that capability */
}
@@ -638,8 +673,28 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
hiod->aliased_devfn,
host_iova_ranges, errp);
if (ret) {
- g_list_free_full(host_iova_ranges, g_free);
- return false;
+ goto error;
+ }
+ }
+ if (hiodc->get_page_size_mask) {
+ uint64_t new_mask = hiodc->get_page_size_mask(hiod);
+
+ if (check_page_size_mask(viommu, new_mask, errp)) {
+ /*
+ * The default mask depends on the "granule" property. For example,
+ * with 4k granule, it is -(4 * KiB). When an assigned device has
+ * page size restrictions due to the hardware IOMMU configuration,
+ * apply this restriction to the mask.
+ */
+ trace_virtio_iommu_update_page_size_mask(hiod->name,
+ viommu->config.page_size_mask,
+ new_mask);
+ if (!viommu->granule_frozen) {
+ viommu->config.page_size_mask &= new_mask;
+ }
+ } else {
+ error_prepend(errp, "%s: ", hiod->name);
+ goto error;
}
}
@@ -652,6 +707,9 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
g_list_free_full(host_iova_ranges, g_free);
return true;
+error:
+ g_list_free_full(host_iova_ranges, g_free);
+ return false;
}
static void
@@ -1350,50 +1408,6 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr,
return 0;
}
-/*
- * The default mask depends on the "granule" property. For example, with
- * 4k granule, it is -(4 * KiB). 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 %s reports a page size mask 0x%"PRIx64
- " incompatible with currently supported mask 0x%"PRIx64,
- mr->parent_obj.name, new_mask, cur_mask);
- return -1;
- }
-
- /*
- * Once the granule is frozen we can't change the mask anymore. If by
- * chance the hotplugged device supports the same granule, we can still
- * accept it.
- */
- if (s->granule_frozen) {
- int cur_granule = ctz64(cur_mask);
-
- if (!(BIT_ULL(cur_granule) & new_mask)) {
- error_setg(errp, "virtio-iommu %s does not support frozen granule 0x%llx",
- mr->parent_obj.name, BIT_ULL(cur_granule));
- return -1;
- }
- return 0;
- }
-
- s->config.page_size_mask &= new_mask;
- return 0;
-}
-
static void virtio_iommu_system_reset(void *opaque)
{
VirtIOIOMMU *s = opaque;
@@ -1416,18 +1430,6 @@ static void virtio_iommu_freeze_granule(Notifier *notifier, void *data)
VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done);
int granule;
- if (likely(s->config.bypass)) {
- /*
- * Transient IOMMU MR enable to collect page_size_mask requirements
- * through memory_region_iommu_set_page_size_mask() called by
- * VFIO region_add() callback
- */
- s->config.bypass = false;
- virtio_iommu_switch_address_space_all(s);
- /* restore default */
- s->config.bypass = true;
- virtio_iommu_switch_address_space_all(s);
- }
s->granule_frozen = true;
granule = ctz64(s->config.page_size_mask);
trace_virtio_iommu_freeze_granule(BIT_ULL(granule));
@@ -1718,7 +1720,6 @@ 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 = {