summaryrefslogtreecommitdiffstats
path: root/hw/arm/smmuv3.c
diff options
context:
space:
mode:
authorEric Auger2018-06-26 18:50:42 +0200
committerPeter Maydell2018-06-26 18:50:42 +0200
commit832e4222c82071e4399cffdecd605abed5ac0c27 (patch)
treed48f9f61abf26ee9b2bd77aa3fbfca3f3d4213b8 /hw/arm/smmuv3.c
parenthw/arm/smmuv3: IOTLB emulation (diff)
downloadqemu-832e4222c82071e4399cffdecd605abed5ac0c27.tar.gz
qemu-832e4222c82071e4399cffdecd605abed5ac0c27.tar.xz
qemu-832e4222c82071e4399cffdecd605abed5ac0c27.zip
hw/arm/smmuv3: Add notifications on invalidation
On TLB invalidation commands, let's call registered IOMMU notifiers. Those can only be UNMAP notifiers. SMMUv3 does not support notification on MAP (VFIO). This patch allows vhost use case where IOTLB API is notified on each guest IOTLB invalidation. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1529653501-15358-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
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;
+ }
}
}