aboutsummaryrefslogtreecommitdiff
path: root/hw/arm/smmu-common.c
diff options
context:
space:
mode:
authorEric Auger <eric.auger@redhat.com>2018-06-26 17:50:42 +0100
committerPeter Maydell <peter.maydell@linaro.org>2018-06-26 17:50:42 +0100
commitcc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56 (patch)
treea56b706f73b4a109ec7d786848cddd8fcac32606 /hw/arm/smmu-common.c
parent32cfd7f39e0811036efd3a7a12d0f975ef57fdb3 (diff)
hw/arm/smmuv3: IOTLB emulation
We emulate a TLB cache of size SMMU_IOTLB_MAX_SIZE=256. It is implemented as a hash table whose key is a combination of the 16b asid and 48b IOVA (Jenkins hash). Entries are invalidated on TLB invalidation commands, either globally, or per asid, or per asid/iova. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 1529653501-15358-4-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/arm/smmu-common.c')
-rw-r--r--hw/arm/smmu-common.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index db242c73df..f66e444f6f 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -24,11 +24,43 @@
#include "qom/cpu.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
+#include "qemu/jhash.h"
#include "qemu/error-report.h"
#include "hw/arm/smmu-common.h"
#include "smmu-internal.h"
+/* IOTLB Management */
+
+inline void smmu_iotlb_inv_all(SMMUState *s)
+{
+ trace_smmu_iotlb_inv_all();
+ g_hash_table_remove_all(s->iotlb);
+}
+
+static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ uint16_t asid = *(uint16_t *)user_data;
+ SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
+
+ return iotlb_key->asid == asid;
+}
+
+inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova)
+{
+ SMMUIOTLBKey key = {.asid = asid, .iova = iova};
+
+ trace_smmu_iotlb_inv_iova(asid, iova);
+ g_hash_table_remove(s->iotlb, &key);
+}
+
+inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
+{
+ trace_smmu_iotlb_inv_asid(asid);
+ g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid);
+}
+
/* VMSAv8-64 Translation */
/**
@@ -328,6 +360,31 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid)
return NULL;
}
+static guint smmu_iotlb_key_hash(gconstpointer v)
+{
+ SMMUIOTLBKey *key = (SMMUIOTLBKey *)v;
+ uint32_t a, b, c;
+
+ /* Jenkins hash */
+ a = b = c = JHASH_INITVAL + sizeof(*key);
+ a += key->asid;
+ b += extract64(key->iova, 0, 32);
+ c += extract64(key->iova, 32, 32);
+
+ __jhash_mix(a, b, c);
+ __jhash_final(a, b, c);
+
+ return c;
+}
+
+static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2)
+{
+ const SMMUIOTLBKey *k1 = v1;
+ const SMMUIOTLBKey *k2 = v2;
+
+ return (k1->asid == k2->asid) && (k1->iova == k2->iova);
+}
+
static void smmu_base_realize(DeviceState *dev, Error **errp)
{
SMMUState *s = ARM_SMMU(dev);
@@ -340,6 +397,8 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
return;
}
s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free);
+ s->iotlb = g_hash_table_new_full(smmu_iotlb_key_hash, smmu_iotlb_key_equal,
+ g_free, g_free);
s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
if (s->primary_bus) {
@@ -354,6 +413,7 @@ static void smmu_base_reset(DeviceState *dev)
SMMUState *s = ARM_SMMU(dev);
g_hash_table_remove_all(s->configs);
+ g_hash_table_remove_all(s->iotlb);
}
static Property smmu_dev_properties[] = {