|
@@ -618,9 +618,39 @@ out:
|
|
return ret;
|
|
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,
|
|
static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
|
|
HostIOMMUDevice *hiod, Error **errp)
|
|
HostIOMMUDevice *hiod, Error **errp)
|
|
{
|
|
{
|
|
|
|
+ ERRP_GUARD();
|
|
VirtIOIOMMU *viommu = opaque;
|
|
VirtIOIOMMU *viommu = opaque;
|
|
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod);
|
|
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod);
|
|
struct hiod_key *new_key;
|
|
struct hiod_key *new_key;
|
|
@@ -643,8 +673,28 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
|
|
hiod->aliased_devfn,
|
|
hiod->aliased_devfn,
|
|
host_iova_ranges, errp);
|
|
host_iova_ranges, errp);
|
|
if (ret) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -657,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);
|
|
g_list_free_full(host_iova_ranges, g_free);
|
|
|
|
|
|
return true;
|
|
return true;
|
|
|
|
+error:
|
|
|
|
+ g_list_free_full(host_iova_ranges, g_free);
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|