+ if (cfg->bypassed) {
+ status = SMMU_TRANS_BYPASS;
+ goto epilogue;
+ }
+
+ 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);