aboutsummaryrefslogtreecommitdiff
path: root/hw/arm/smmuv3.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm/smmuv3.c')
-rw-r--r--hw/arm/smmuv3.c99
1 files changed, 97 insertions, 2 deletions
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index b6dc7ed6de..39fbcbf577 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -780,6 +780,68 @@ epilogue:
return entry;
}
+/**
+ * smmuv3_notify_iova - call the notifier @n for a given
+ * @asid and @iova tuple.
+ *
+ * @mr: IOMMU mr region handle
+ * @n: notifier to be called
+ * @asid: address space ID or negative value if we don't care
+ * @iova: iova
+ */
+static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
+ IOMMUNotifier *n,
+ int asid,
+ dma_addr_t iova)
+{
+ SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
+ SMMUEventInfo event = {};
+ SMMUTransTableInfo *tt;
+ SMMUTransCfg *cfg;
+ IOMMUTLBEntry entry;
+
+ cfg = smmuv3_get_config(sdev, &event);
+ if (!cfg) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s error decoding the configuration for iommu mr=%s\n",
+ __func__, mr->parent_obj.name);
+ return;
+ }
+
+ if (asid >= 0 && cfg->asid != asid) {
+ return;
+ }
+
+ tt = select_tt(cfg, iova);
+ if (!tt) {
+ return;
+ }
+
+ entry.target_as = &address_space_memory;
+ entry.iova = iova;
+ entry.addr_mask = (1 << tt->granule_sz) - 1;
+ entry.perm = IOMMU_NONE;
+
+ memory_region_notify_one(n, &entry);
+}
+
+/* invalidate an asid/iova tuple in all mr's */
+static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
+{
+ SMMUNotifierNode *node;
+
+ QLIST_FOREACH(node, &s->notifiers_list, next) {
+ IOMMUMemoryRegion *mr = &node->sdev->iommu;
+ IOMMUNotifier *n;
+
+ trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova);
+
+ IOMMU_NOTIFIER_FOREACH(n, mr) {
+ smmuv3_notify_iova(mr, n, asid, iova);
+ }
+ }
+}
+
static int smmuv3_cmdq_consume(SMMUv3State *s)
{
SMMUState *bs = ARM_SMMU(s);
@@ -899,12 +961,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
uint16_t asid = CMD_ASID(&cmd);
trace_smmuv3_cmdq_tlbi_nh_asid(asid);
+ smmu_inv_notifiers_all(&s->smmu_state);
smmu_iotlb_inv_asid(bs, asid);
break;
}
case SMMU_CMD_TLBI_NH_ALL:
case SMMU_CMD_TLBI_NSNH_ALL:
trace_smmuv3_cmdq_tlbi_nh();
+ smmu_inv_notifiers_all(&s->smmu_state);
smmu_iotlb_inv_all(bs);
break;
case SMMU_CMD_TLBI_NH_VAA:
@@ -913,6 +977,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
uint16_t vmid = CMD_VMID(&cmd);
trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr);
+ smmuv3_inv_notifiers_iova(bs, -1, addr);
smmu_iotlb_inv_all(bs);
break;
}
@@ -924,6 +989,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
bool leaf = CMD_LEAF(&cmd);
trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf);
+ smmuv3_inv_notifiers_iova(bs, asid, addr);
smmu_iotlb_inv_iova(bs, asid, addr);
break;
}
@@ -1402,9 +1468,38 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
IOMMUNotifierFlag old,
IOMMUNotifierFlag new)
{
+ SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu);
+ SMMUv3State *s3 = sdev->smmu;
+ SMMUState *s = &(s3->smmu_state);
+ SMMUNotifierNode *node = NULL;
+ SMMUNotifierNode *next_node = NULL;
+
+ if (new & IOMMU_NOTIFIER_MAP) {
+ int bus_num = pci_bus_num(sdev->bus);
+ PCIDevice *pcidev = pci_find_device(sdev->bus, bus_num, sdev->devfn);
+
+ warn_report("SMMUv3 does not support notification on MAP: "
+ "device %s will not function properly", pcidev->name);
+ }
+
if (old == IOMMU_NOTIFIER_NONE) {
- warn_report("SMMUV3 does not support vhost/vfio integration yet: "
- "devices of those types will not function properly");
+ trace_smmuv3_notify_flag_add(iommu->parent_obj.name);
+ node = g_malloc0(sizeof(*node));
+ node->sdev = sdev;
+ QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
+ return;
+ }
+
+ /* update notifier node with new flags */
+ QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
+ if (node->sdev == sdev) {
+ if (new == IOMMU_NOTIFIER_NONE) {
+ trace_smmuv3_notify_flag_del(iommu->parent_obj.name);
+ QLIST_REMOVE(node, next);
+ g_free(node);
+ }
+ return;
+ }
}
}