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
commitcc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56 (patch)
treea56b706f73b4a109ec7d786848cddd8fcac32606 /hw/arm/smmuv3.c
parenthw/arm/smmuv3: Cache/invalidate config data (diff)
downloadqemu-cc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56.tar.gz
qemu-cc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56.tar.xz
qemu-cc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56.zip
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/smmuv3.c')
-rw-r--r--hw/arm/smmuv3.c98
1 files changed, 94 insertions, 4 deletions
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index df704c26f8..b6dc7ed6de 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -605,6 +605,10 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid};
SMMUPTWEventInfo ptw_info = {};
SMMUTranslationStatus status;
+ SMMUState *bs = ARM_SMMU(s);
+ uint64_t page_mask, aligned_addr;
+ IOMMUTLBEntry *cached_entry = NULL;
+ SMMUTransTableInfo *tt;
SMMUTransCfg *cfg = NULL;
IOMMUTLBEntry entry = {
.target_as = &address_space_memory,
@@ -613,6 +617,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
.addr_mask = ~(hwaddr)0,
.perm = IOMMU_NONE,
};
+ SMMUIOTLBKey key, *new_key;
qemu_mutex_lock(&s->mutex);
@@ -637,7 +642,57 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
goto epilogue;
}
- if (smmu_ptw(cfg, addr, flag, &entry, &ptw_info)) {
+ tt = select_tt(cfg, addr);
+ if (!tt) {
+ if (event.record_trans_faults) {
+ event.type = SMMU_EVT_F_TRANSLATION;
+ event.u.f_translation.addr = addr;
+ event.u.f_translation.rnw = flag & 0x1;
+ }
+ status = SMMU_TRANS_ERROR;
+ goto epilogue;
+ }
+
+ page_mask = (1ULL << (tt->granule_sz)) - 1;
+ aligned_addr = addr & ~page_mask;
+
+ key.asid = cfg->asid;
+ key.iova = aligned_addr;
+
+ cached_entry = g_hash_table_lookup(bs->iotlb, &key);
+ if (cached_entry) {
+ cfg->iotlb_hits++;
+ trace_smmu_iotlb_cache_hit(cfg->asid, aligned_addr,
+ cfg->iotlb_hits, cfg->iotlb_misses,
+ 100 * cfg->iotlb_hits /
+ (cfg->iotlb_hits + cfg->iotlb_misses));
+ if ((flag & IOMMU_WO) && !(cached_entry->perm & IOMMU_WO)) {
+ status = SMMU_TRANS_ERROR;
+ if (event.record_trans_faults) {
+ event.type = SMMU_EVT_F_PERMISSION;
+ event.u.f_permission.addr = addr;
+ event.u.f_permission.rnw = flag & 0x1;
+ }
+ } else {
+ status = SMMU_TRANS_SUCCESS;
+ }
+ goto epilogue;
+ }
+
+ cfg->iotlb_misses++;
+ trace_smmu_iotlb_cache_miss(cfg->asid, addr & ~page_mask,
+ cfg->iotlb_hits, cfg->iotlb_misses,
+ 100 * cfg->iotlb_hits /
+ (cfg->iotlb_hits + cfg->iotlb_misses));
+
+ if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) {
+ smmu_iotlb_inv_all(bs);
+ }
+
+ cached_entry = g_new0(IOMMUTLBEntry, 1);
+
+ if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) {
+ g_free(cached_entry);
switch (ptw_info.type) {
case SMMU_PTW_ERR_WALK_EABT:
event.type = SMMU_EVT_F_WALK_EABT;
@@ -679,6 +734,10 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
}
status = SMMU_TRANS_ERROR;
} else {
+ new_key = g_new0(SMMUIOTLBKey, 1);
+ new_key->asid = cfg->asid;
+ new_key->iova = aligned_addr;
+ g_hash_table_insert(bs->iotlb, new_key, cached_entry);
status = SMMU_TRANS_SUCCESS;
}
@@ -687,6 +746,9 @@ epilogue:
switch (status) {
case SMMU_TRANS_SUCCESS:
entry.perm = flag;
+ entry.translated_addr = cached_entry->translated_addr +
+ (addr & page_mask);
+ entry.addr_mask = cached_entry->addr_mask;
trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr,
entry.translated_addr, entry.perm);
break;
@@ -832,10 +894,39 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
smmuv3_flush_config(sdev);
break;
}
- case SMMU_CMD_TLBI_NH_ALL:
case SMMU_CMD_TLBI_NH_ASID:
- case SMMU_CMD_TLBI_NH_VA:
+ {
+ uint16_t asid = CMD_ASID(&cmd);
+
+ trace_smmuv3_cmdq_tlbi_nh_asid(asid);
+ 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_iotlb_inv_all(bs);
+ break;
case SMMU_CMD_TLBI_NH_VAA:
+ {
+ dma_addr_t addr = CMD_ADDR(&cmd);
+ uint16_t vmid = CMD_VMID(&cmd);
+
+ trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr);
+ smmu_iotlb_inv_all(bs);
+ break;
+ }
+ case SMMU_CMD_TLBI_NH_VA:
+ {
+ uint16_t asid = CMD_ASID(&cmd);
+ uint16_t vmid = CMD_VMID(&cmd);
+ dma_addr_t addr = CMD_ADDR(&cmd);
+ bool leaf = CMD_LEAF(&cmd);
+
+ trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf);
+ smmu_iotlb_inv_iova(bs, asid, addr);
+ break;
+ }
case SMMU_CMD_TLBI_EL3_ALL:
case SMMU_CMD_TLBI_EL3_VA:
case SMMU_CMD_TLBI_EL2_ALL:
@@ -844,7 +935,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
case SMMU_CMD_TLBI_EL2_VAA:
case SMMU_CMD_TLBI_S12_VMALL:
case SMMU_CMD_TLBI_S2_IPA:
- case SMMU_CMD_TLBI_NSNH_ALL:
case SMMU_CMD_ATC_INV:
case SMMU_CMD_PRI_RESP:
case SMMU_CMD_RESUME: