1527773eeSEric Auger /* 2527773eeSEric Auger * Copyright (C) 2014-2016 Broadcom Corporation 3527773eeSEric Auger * Copyright (c) 2017 Red Hat, Inc. 4527773eeSEric Auger * Written by Prem Mallappa, Eric Auger 5527773eeSEric Auger * 6527773eeSEric Auger * This program is free software; you can redistribute it and/or modify 7527773eeSEric Auger * it under the terms of the GNU General Public License version 2 as 8527773eeSEric Auger * published by the Free Software Foundation. 9527773eeSEric Auger * 10527773eeSEric Auger * This program is distributed in the hope that it will be useful, 11527773eeSEric Auger * but WITHOUT ANY WARRANTY; without even the implied warranty of 12527773eeSEric Auger * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13527773eeSEric Auger * GNU General Public License for more details. 14527773eeSEric Auger * 15527773eeSEric Auger * Author: Prem Mallappa <pmallapp@broadcom.com> 16527773eeSEric Auger * 17527773eeSEric Auger */ 18527773eeSEric Auger 19527773eeSEric Auger #include "qemu/osdep.h" 20527773eeSEric Auger #include "trace.h" 21527773eeSEric Auger #include "exec/target_page.h" 222e5b09fdSMarkus Armbruster #include "hw/core/cpu.h" 23527773eeSEric Auger #include "hw/qdev-properties.h" 24527773eeSEric Auger #include "qapi/error.h" 25cc27ed81SEric Auger #include "qemu/jhash.h" 260b8fa32fSMarkus Armbruster #include "qemu/module.h" 27527773eeSEric Auger 28527773eeSEric Auger #include "qemu/error-report.h" 29527773eeSEric Auger #include "hw/arm/smmu-common.h" 3093641948SEric Auger #include "smmu-internal.h" 3193641948SEric Auger 32cc27ed81SEric Auger /* IOTLB Management */ 33cc27ed81SEric Auger 3460a61f1bSEric Auger static guint smmu_iotlb_key_hash(gconstpointer v) 3560a61f1bSEric Auger { 3660a61f1bSEric Auger SMMUIOTLBKey *key = (SMMUIOTLBKey *)v; 3760a61f1bSEric Auger uint32_t a, b, c; 3860a61f1bSEric Auger 3960a61f1bSEric Auger /* Jenkins hash */ 4060a61f1bSEric Auger a = b = c = JHASH_INITVAL + sizeof(*key); 412eaeb7d5SMostafa Saleh a += key->asid + key->vmid + key->level + key->tg; 4260a61f1bSEric Auger b += extract64(key->iova, 0, 32); 4360a61f1bSEric Auger c += extract64(key->iova, 32, 32); 4460a61f1bSEric Auger 4560a61f1bSEric Auger __jhash_mix(a, b, c); 4660a61f1bSEric Auger __jhash_final(a, b, c); 4760a61f1bSEric Auger 4860a61f1bSEric Auger return c; 4960a61f1bSEric Auger } 5060a61f1bSEric Auger 5160a61f1bSEric Auger static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) 5260a61f1bSEric Auger { 539e54dee7SEric Auger SMMUIOTLBKey *k1 = (SMMUIOTLBKey *)v1, *k2 = (SMMUIOTLBKey *)v2; 5460a61f1bSEric Auger 559e54dee7SEric Auger return (k1->asid == k2->asid) && (k1->iova == k2->iova) && 562eaeb7d5SMostafa Saleh (k1->level == k2->level) && (k1->tg == k2->tg) && 572eaeb7d5SMostafa Saleh (k1->vmid == k2->vmid); 5860a61f1bSEric Auger } 5960a61f1bSEric Auger 60d8838226SMostafa Saleh SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, 619e54dee7SEric Auger uint8_t tg, uint8_t level) 6260a61f1bSEric Auger { 632eaeb7d5SMostafa Saleh SMMUIOTLBKey key = {.asid = asid, .vmid = vmid, .iova = iova, 642eaeb7d5SMostafa Saleh .tg = tg, .level = level}; 6560a61f1bSEric Auger 6660a61f1bSEric Auger return key; 6760a61f1bSEric Auger } 6860a61f1bSEric Auger 697eb57be1SMostafa Saleh static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs, 707eb57be1SMostafa Saleh SMMUTransCfg *cfg, 717eb57be1SMostafa Saleh SMMUTransTableInfo *tt, 727eb57be1SMostafa Saleh hwaddr iova) 736808bca9SEric Auger { 749e54dee7SEric Auger uint8_t tg = (tt->granule_sz - 10) / 2; 759e54dee7SEric Auger uint8_t inputsize = 64 - tt->tsz; 769e54dee7SEric Auger uint8_t stride = tt->granule_sz - 3; 779e54dee7SEric Auger uint8_t level = 4 - (inputsize - 4) / stride; 789e54dee7SEric Auger SMMUTLBEntry *entry = NULL; 799e54dee7SEric Auger 809e54dee7SEric Auger while (level <= 3) { 819e54dee7SEric Auger uint64_t subpage_size = 1ULL << level_shift(level, tt->granule_sz); 829e54dee7SEric Auger uint64_t mask = subpage_size - 1; 839e54dee7SEric Auger SMMUIOTLBKey key; 849e54dee7SEric Auger 852eaeb7d5SMostafa Saleh key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid, 862eaeb7d5SMostafa Saleh iova & ~mask, tg, level); 879e54dee7SEric Auger entry = g_hash_table_lookup(bs->iotlb, &key); 889e54dee7SEric Auger if (entry) { 899e54dee7SEric Auger break; 909e54dee7SEric Auger } 919e54dee7SEric Auger level++; 929e54dee7SEric Auger } 937eb57be1SMostafa Saleh return entry; 947eb57be1SMostafa Saleh } 957eb57be1SMostafa Saleh 967eb57be1SMostafa Saleh /** 977eb57be1SMostafa Saleh * smmu_iotlb_lookup - Look up for a TLB entry. 987eb57be1SMostafa Saleh * @bs: SMMU state which includes the TLB instance 997eb57be1SMostafa Saleh * @cfg: Configuration of the translation 1007eb57be1SMostafa Saleh * @tt: Translation table info (granule and tsz) 1017eb57be1SMostafa Saleh * @iova: IOVA address to lookup 1027eb57be1SMostafa Saleh * 1037eb57be1SMostafa Saleh * returns a valid entry on success, otherwise NULL. 1047eb57be1SMostafa Saleh * In case of nested translation, tt can be updated to include 1057eb57be1SMostafa Saleh * the granule of the found entry as it might different from 1067eb57be1SMostafa Saleh * the IOVA granule. 1077eb57be1SMostafa Saleh */ 1087eb57be1SMostafa Saleh SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, 1097eb57be1SMostafa Saleh SMMUTransTableInfo *tt, hwaddr iova) 1107eb57be1SMostafa Saleh { 1117eb57be1SMostafa Saleh SMMUTLBEntry *entry = NULL; 1127eb57be1SMostafa Saleh 1137eb57be1SMostafa Saleh entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); 1147eb57be1SMostafa Saleh /* 1157eb57be1SMostafa Saleh * For nested translation also try the s2 granule, as the TLB will insert 1167eb57be1SMostafa Saleh * it if the size of s2 tlb entry was smaller. 1177eb57be1SMostafa Saleh */ 1187eb57be1SMostafa Saleh if (!entry && (cfg->stage == SMMU_NESTED) && 1197eb57be1SMostafa Saleh (cfg->s2cfg.granule_sz != tt->granule_sz)) { 1207eb57be1SMostafa Saleh tt->granule_sz = cfg->s2cfg.granule_sz; 1217eb57be1SMostafa Saleh entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); 1227eb57be1SMostafa Saleh } 1236808bca9SEric Auger 1246808bca9SEric Auger if (entry) { 1256808bca9SEric Auger cfg->iotlb_hits++; 1262eaeb7d5SMostafa Saleh trace_smmu_iotlb_lookup_hit(cfg->asid, cfg->s2cfg.vmid, iova, 1276808bca9SEric Auger cfg->iotlb_hits, cfg->iotlb_misses, 1286808bca9SEric Auger 100 * cfg->iotlb_hits / 1296808bca9SEric Auger (cfg->iotlb_hits + cfg->iotlb_misses)); 1306808bca9SEric Auger } else { 1316808bca9SEric Auger cfg->iotlb_misses++; 1322eaeb7d5SMostafa Saleh trace_smmu_iotlb_lookup_miss(cfg->asid, cfg->s2cfg.vmid, iova, 1336808bca9SEric Auger cfg->iotlb_hits, cfg->iotlb_misses, 1346808bca9SEric Auger 100 * cfg->iotlb_hits / 1356808bca9SEric Auger (cfg->iotlb_hits + cfg->iotlb_misses)); 1366808bca9SEric Auger } 1376808bca9SEric Auger return entry; 1386808bca9SEric Auger } 1396808bca9SEric Auger 140a7550158SEric Auger void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new) 1416808bca9SEric Auger { 1426808bca9SEric Auger SMMUIOTLBKey *key = g_new0(SMMUIOTLBKey, 1); 1439e54dee7SEric Auger uint8_t tg = (new->granule - 10) / 2; 1446808bca9SEric Auger 1456808bca9SEric Auger if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) { 1466808bca9SEric Auger smmu_iotlb_inv_all(bs); 1476808bca9SEric Auger } 1486808bca9SEric Auger 1492eaeb7d5SMostafa Saleh *key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid, new->entry.iova, 1502eaeb7d5SMostafa Saleh tg, new->level); 1512eaeb7d5SMostafa Saleh trace_smmu_iotlb_insert(cfg->asid, cfg->s2cfg.vmid, new->entry.iova, 1522eaeb7d5SMostafa Saleh tg, new->level); 153a7550158SEric Auger g_hash_table_insert(bs->iotlb, key, new); 1546808bca9SEric Auger } 1556808bca9SEric Auger 1569de9fa5cSPhilippe Mathieu-Daudé void smmu_iotlb_inv_all(SMMUState *s) 157cc27ed81SEric Auger { 158cc27ed81SEric Auger trace_smmu_iotlb_inv_all(); 159cc27ed81SEric Auger g_hash_table_remove_all(s->iotlb); 160cc27ed81SEric Auger } 161cc27ed81SEric Auger 162cc27ed81SEric Auger static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, 163cc27ed81SEric Auger gpointer user_data) 164cc27ed81SEric Auger { 165d8838226SMostafa Saleh int asid = *(int *)user_data; 166cc27ed81SEric Auger SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; 167cc27ed81SEric Auger 16860a61f1bSEric Auger return SMMU_IOTLB_ASID(*iotlb_key) == asid; 169cc27ed81SEric Auger } 170ccc3ee38SMostafa Saleh 171ccc3ee38SMostafa Saleh static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value, 172ccc3ee38SMostafa Saleh gpointer user_data) 173ccc3ee38SMostafa Saleh { 174d8838226SMostafa Saleh int vmid = *(int *)user_data; 175ccc3ee38SMostafa Saleh SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; 176ccc3ee38SMostafa Saleh 177ccc3ee38SMostafa Saleh return SMMU_IOTLB_VMID(*iotlb_key) == vmid; 178ccc3ee38SMostafa Saleh } 179ccc3ee38SMostafa Saleh 1802eaeb7d5SMostafa Saleh static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, 1819e54dee7SEric Auger gpointer user_data) 182cc27ed81SEric Auger { 1839e54dee7SEric Auger SMMUTLBEntry *iter = (SMMUTLBEntry *)value; 1849e54dee7SEric Auger IOMMUTLBEntry *entry = &iter->entry; 1859e54dee7SEric Auger SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; 1869e54dee7SEric Auger SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key; 1879e54dee7SEric Auger 1889e54dee7SEric Auger if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) { 1899e54dee7SEric Auger return false; 1909e54dee7SEric Auger } 1912eaeb7d5SMostafa Saleh if (info->vmid >= 0 && info->vmid != SMMU_IOTLB_VMID(iotlb_key)) { 1922eaeb7d5SMostafa Saleh return false; 1932eaeb7d5SMostafa Saleh } 194d5291561SEric Auger return ((info->iova & ~entry->addr_mask) == entry->iova) || 195d5291561SEric Auger ((entry->iova & ~info->mask) == info->iova); 1969e54dee7SEric Auger } 1979e54dee7SEric Auger 1982eaeb7d5SMostafa Saleh void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, 199d5291561SEric Auger uint8_t tg, uint64_t num_pages, uint8_t ttl) 2009e54dee7SEric Auger { 2016d9cd115SEric Auger /* if tg is not set we use 4KB range invalidation */ 2026d9cd115SEric Auger uint8_t granule = tg ? tg * 2 + 10 : 12; 2036d9cd115SEric Auger 204a4b6e1beSEric Auger if (ttl && (num_pages == 1) && (asid >= 0)) { 2052eaeb7d5SMostafa Saleh SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, iova, tg, ttl); 206cc27ed81SEric Auger 2076d9cd115SEric Auger if (g_hash_table_remove(s->iotlb, &key)) { 2086d9cd115SEric Auger return; 2096d9cd115SEric Auger } 2106d9cd115SEric Auger /* 2116d9cd115SEric Auger * if the entry is not found, let's see if it does not 2126d9cd115SEric Auger * belong to a larger IOTLB entry 2136d9cd115SEric Auger */ 2146d9cd115SEric Auger } 215d5291561SEric Auger 216d5291561SEric Auger SMMUIOTLBPageInvInfo info = { 217d5291561SEric Auger .asid = asid, .iova = iova, 2182eaeb7d5SMostafa Saleh .vmid = vmid, 219d5291561SEric Auger .mask = (num_pages * 1 << granule) - 1}; 220d5291561SEric Auger 221d5291561SEric Auger g_hash_table_foreach_remove(s->iotlb, 2222eaeb7d5SMostafa Saleh smmu_hash_remove_by_asid_vmid_iova, 223d5291561SEric Auger &info); 224d5291561SEric Auger } 225cc27ed81SEric Auger 226d8838226SMostafa Saleh void smmu_iotlb_inv_asid(SMMUState *s, int asid) 227cc27ed81SEric Auger { 228cc27ed81SEric Auger trace_smmu_iotlb_inv_asid(asid); 229cc27ed81SEric Auger g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); 230cc27ed81SEric Auger } 231cc27ed81SEric Auger 232d8838226SMostafa Saleh void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) 233ccc3ee38SMostafa Saleh { 234ccc3ee38SMostafa Saleh trace_smmu_iotlb_inv_vmid(vmid); 235ccc3ee38SMostafa Saleh g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); 236ccc3ee38SMostafa Saleh } 237ccc3ee38SMostafa Saleh 23893641948SEric Auger /* VMSAv8-64 Translation */ 23993641948SEric Auger 24093641948SEric Auger /** 24193641948SEric Auger * get_pte - Get the content of a page table entry located at 24293641948SEric Auger * @base_addr[@index] 24393641948SEric Auger */ 24493641948SEric Auger static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte, 24593641948SEric Auger SMMUPTWEventInfo *info) 24693641948SEric Auger { 24793641948SEric Auger int ret; 24893641948SEric Auger dma_addr_t addr = baseaddr + index * sizeof(*pte); 24993641948SEric Auger 25093641948SEric Auger /* TODO: guarantee 64-bit single-copy atomicity */ 251c6445544SPeter Maydell ret = ldq_le_dma(&address_space_memory, addr, pte, MEMTXATTRS_UNSPECIFIED); 25293641948SEric Auger 25393641948SEric Auger if (ret != MEMTX_OK) { 25493641948SEric Auger info->type = SMMU_PTW_ERR_WALK_EABT; 25593641948SEric Auger info->addr = addr; 25693641948SEric Auger return -EINVAL; 25793641948SEric Auger } 25893641948SEric Auger trace_smmu_get_pte(baseaddr, index, addr, *pte); 25993641948SEric Auger return 0; 26093641948SEric Auger } 26193641948SEric Auger 26293641948SEric Auger /* VMSAv8-64 Translation Table Format Descriptor Decoding */ 26393641948SEric Auger 26493641948SEric Auger /** 26593641948SEric Auger * get_page_pte_address - returns the L3 descriptor output address, 26693641948SEric Auger * ie. the page frame 26793641948SEric Auger * ARM ARM spec: Figure D4-17 VMSAv8-64 level 3 descriptor format 26893641948SEric Auger */ 26993641948SEric Auger static inline hwaddr get_page_pte_address(uint64_t pte, int granule_sz) 27093641948SEric Auger { 27193641948SEric Auger return PTE_ADDRESS(pte, granule_sz); 27293641948SEric Auger } 27393641948SEric Auger 27493641948SEric Auger /** 27593641948SEric Auger * get_table_pte_address - return table descriptor output address, 27693641948SEric Auger * ie. address of next level table 27793641948SEric Auger * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats 27893641948SEric Auger */ 27993641948SEric Auger static inline hwaddr get_table_pte_address(uint64_t pte, int granule_sz) 28093641948SEric Auger { 28193641948SEric Auger return PTE_ADDRESS(pte, granule_sz); 28293641948SEric Auger } 28393641948SEric Auger 28493641948SEric Auger /** 28593641948SEric Auger * get_block_pte_address - return block descriptor output address and block size 28693641948SEric Auger * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats 28793641948SEric Auger */ 28893641948SEric Auger static inline hwaddr get_block_pte_address(uint64_t pte, int level, 28993641948SEric Auger int granule_sz, uint64_t *bsz) 29093641948SEric Auger { 291118eee6cSEric Auger int n = level_shift(level, granule_sz); 29293641948SEric Auger 293118eee6cSEric Auger *bsz = 1ULL << n; 29493641948SEric Auger return PTE_ADDRESS(pte, n); 29593641948SEric Auger } 29693641948SEric Auger 29793641948SEric Auger SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) 29893641948SEric Auger { 29993641948SEric Auger bool tbi = extract64(iova, 55, 1) ? TBI1(cfg->tbi) : TBI0(cfg->tbi); 30093641948SEric Auger uint8_t tbi_byte = tbi * 8; 30193641948SEric Auger 30293641948SEric Auger if (cfg->tt[0].tsz && 30393641948SEric Auger !extract64(iova, 64 - cfg->tt[0].tsz, cfg->tt[0].tsz - tbi_byte)) { 30493641948SEric Auger /* there is a ttbr0 region and we are in it (high bits all zero) */ 30593641948SEric Auger return &cfg->tt[0]; 30693641948SEric Auger } else if (cfg->tt[1].tsz && 307e431b8f6SJean-Philippe Brucker sextract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte) == -1) { 30893641948SEric Auger /* there is a ttbr1 region and we are in it (high bits all one) */ 30993641948SEric Auger return &cfg->tt[1]; 31093641948SEric Auger } else if (!cfg->tt[0].tsz) { 31193641948SEric Auger /* ttbr0 region is "everything not in the ttbr1 region" */ 31293641948SEric Auger return &cfg->tt[0]; 31393641948SEric Auger } else if (!cfg->tt[1].tsz) { 31493641948SEric Auger /* ttbr1 region is "everything not in the ttbr0 region" */ 31593641948SEric Auger return &cfg->tt[1]; 31693641948SEric Auger } 31793641948SEric Auger /* in the gap between the two regions, this is a Translation fault */ 31893641948SEric Auger return NULL; 31993641948SEric Auger } 32093641948SEric Auger 321*f42a0a57SMostafa Saleh /* Translate stage-1 table address using stage-2 page table. */ 322*f42a0a57SMostafa Saleh static inline int translate_table_addr_ipa(SMMUState *bs, 323*f42a0a57SMostafa Saleh dma_addr_t *table_addr, 324*f42a0a57SMostafa Saleh SMMUTransCfg *cfg, 325*f42a0a57SMostafa Saleh SMMUPTWEventInfo *info) 326*f42a0a57SMostafa Saleh { 327*f42a0a57SMostafa Saleh dma_addr_t addr = *table_addr; 328*f42a0a57SMostafa Saleh SMMUTLBEntry *cached_entry; 329*f42a0a57SMostafa Saleh int asid; 330*f42a0a57SMostafa Saleh 331*f42a0a57SMostafa Saleh /* 332*f42a0a57SMostafa Saleh * The translation table walks performed from TTB0 or TTB1 are always 333*f42a0a57SMostafa Saleh * performed in IPA space if stage 2 translations are enabled. 334*f42a0a57SMostafa Saleh */ 335*f42a0a57SMostafa Saleh asid = cfg->asid; 336*f42a0a57SMostafa Saleh cfg->stage = SMMU_STAGE_2; 337*f42a0a57SMostafa Saleh cfg->asid = -1; 338*f42a0a57SMostafa Saleh cached_entry = smmu_translate(bs, cfg, addr, IOMMU_RO, info); 339*f42a0a57SMostafa Saleh cfg->asid = asid; 340*f42a0a57SMostafa Saleh cfg->stage = SMMU_NESTED; 341*f42a0a57SMostafa Saleh 342*f42a0a57SMostafa Saleh if (cached_entry) { 343*f42a0a57SMostafa Saleh *table_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr); 344*f42a0a57SMostafa Saleh return 0; 345*f42a0a57SMostafa Saleh } 346*f42a0a57SMostafa Saleh 347*f42a0a57SMostafa Saleh info->stage = SMMU_STAGE_2; 348*f42a0a57SMostafa Saleh info->addr = addr; 349*f42a0a57SMostafa Saleh info->is_ipa_descriptor = true; 350*f42a0a57SMostafa Saleh return -EINVAL; 351*f42a0a57SMostafa Saleh } 352*f42a0a57SMostafa Saleh 35393641948SEric Auger /** 354bcc919e7SMostafa Saleh * smmu_ptw_64_s1 - VMSAv8-64 Walk of the page tables for a given IOVA 355*f42a0a57SMostafa Saleh * @bs: smmu state which includes TLB instance 35693641948SEric Auger * @cfg: translation config 35793641948SEric Auger * @iova: iova to translate 35893641948SEric Auger * @perm: access type 359a7550158SEric Auger * @tlbe: SMMUTLBEntry (out) 36093641948SEric Auger * @info: handle to an error info 36193641948SEric Auger * 36293641948SEric Auger * Return 0 on success, < 0 on error. In case of error, @info is filled 36393641948SEric Auger * and tlbe->perm is set to IOMMU_NONE. 36493641948SEric Auger * Upon success, @tlbe is filled with translated_addr and entry 36593641948SEric Auger * permission rights. 36693641948SEric Auger */ 367*f42a0a57SMostafa Saleh static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg, 36893641948SEric Auger dma_addr_t iova, IOMMUAccessFlags perm, 369a7550158SEric Auger SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) 37093641948SEric Auger { 37193641948SEric Auger dma_addr_t baseaddr, indexmask; 372f6cc1980SMostafa Saleh SMMUStage stage = cfg->stage; 37393641948SEric Auger SMMUTransTableInfo *tt = select_tt(cfg, iova); 37493641948SEric Auger uint8_t level, granule_sz, inputsize, stride; 37593641948SEric Auger 37693641948SEric Auger if (!tt || tt->disabled) { 37793641948SEric Auger info->type = SMMU_PTW_ERR_TRANSLATION; 37893641948SEric Auger goto error; 37993641948SEric Auger } 38093641948SEric Auger 38193641948SEric Auger granule_sz = tt->granule_sz; 382bcc919e7SMostafa Saleh stride = VMSA_STRIDE(granule_sz); 38393641948SEric Auger inputsize = 64 - tt->tsz; 38493641948SEric Auger level = 4 - (inputsize - 4) / stride; 385bcc919e7SMostafa Saleh indexmask = VMSA_IDXMSK(inputsize, stride, level); 38693641948SEric Auger baseaddr = extract64(tt->ttb, 0, 48); 38793641948SEric Auger baseaddr &= ~indexmask; 38893641948SEric Auger 389bcc919e7SMostafa Saleh while (level < VMSA_LEVELS) { 39093641948SEric Auger uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); 39193641948SEric Auger uint64_t mask = subpage_size - 1; 39293641948SEric Auger uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz); 3931733837dSEric Auger uint64_t pte, gpa; 39493641948SEric Auger dma_addr_t pte_addr = baseaddr + offset * sizeof(pte); 39593641948SEric Auger uint8_t ap; 39693641948SEric Auger 39793641948SEric Auger if (get_pte(baseaddr, offset, &pte, info)) { 39893641948SEric Auger goto error; 39993641948SEric Auger } 400bcc919e7SMostafa Saleh trace_smmu_ptw_level(stage, level, iova, subpage_size, 40193641948SEric Auger baseaddr, offset, pte); 40293641948SEric Auger 40393641948SEric Auger if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { 40493641948SEric Auger trace_smmu_ptw_invalid_pte(stage, level, baseaddr, 40593641948SEric Auger pte_addr, offset, pte); 4061733837dSEric Auger break; 40793641948SEric Auger } 40893641948SEric Auger 4091733837dSEric Auger if (is_table_pte(pte, level)) { 41093641948SEric Auger ap = PTE_APTABLE(pte); 41193641948SEric Auger 412e7c3b9d9SEric Auger if (is_permission_fault(ap, perm) && !tt->had) { 41393641948SEric Auger info->type = SMMU_PTW_ERR_PERMISSION; 41493641948SEric Auger goto error; 41593641948SEric Auger } 41693641948SEric Auger baseaddr = get_table_pte_address(pte, granule_sz); 417*f42a0a57SMostafa Saleh if (cfg->stage == SMMU_NESTED) { 418*f42a0a57SMostafa Saleh if (translate_table_addr_ipa(bs, &baseaddr, cfg, info)) { 419*f42a0a57SMostafa Saleh goto error; 420*f42a0a57SMostafa Saleh } 421*f42a0a57SMostafa Saleh } 42293641948SEric Auger level++; 4231733837dSEric Auger continue; 4241733837dSEric Auger } else if (is_page_pte(pte, level)) { 4251733837dSEric Auger gpa = get_page_pte_address(pte, granule_sz); 4261733837dSEric Auger trace_smmu_ptw_page_pte(stage, level, iova, 4271733837dSEric Auger baseaddr, pte_addr, pte, gpa); 4281733837dSEric Auger } else { 4291733837dSEric Auger uint64_t block_size; 4301733837dSEric Auger 4311733837dSEric Auger gpa = get_block_pte_address(pte, level, granule_sz, 4321733837dSEric Auger &block_size); 4331733837dSEric Auger trace_smmu_ptw_block_pte(stage, level, baseaddr, 4341733837dSEric Auger pte_addr, pte, iova, gpa, 4351733837dSEric Auger block_size >> 20); 4361733837dSEric Auger } 43715f6c16eSLuc Michel 43815f6c16eSLuc Michel /* 43915f6c16eSLuc Michel * QEMU does not currently implement HTTU, so if AFFD and PTE.AF 44015f6c16eSLuc Michel * are 0 we take an Access flag fault. (5.4. Context Descriptor) 44115f6c16eSLuc Michel * An Access flag fault takes priority over a Permission fault. 44215f6c16eSLuc Michel */ 44315f6c16eSLuc Michel if (!PTE_AF(pte) && !cfg->affd) { 44415f6c16eSLuc Michel info->type = SMMU_PTW_ERR_ACCESS; 44515f6c16eSLuc Michel goto error; 44615f6c16eSLuc Michel } 44715f6c16eSLuc Michel 4481733837dSEric Auger ap = PTE_AP(pte); 4491733837dSEric Auger if (is_permission_fault(ap, perm)) { 4501733837dSEric Auger info->type = SMMU_PTW_ERR_PERMISSION; 4511733837dSEric Auger goto error; 45293641948SEric Auger } 45393641948SEric Auger 454bde809f0SMostafa Saleh /* 455bde809f0SMostafa Saleh * The address output from the translation causes a stage 1 Address 456bde809f0SMostafa Saleh * Size fault if it exceeds the range of the effective IPA size for 457bde809f0SMostafa Saleh * the given CD. 458bde809f0SMostafa Saleh */ 459bde809f0SMostafa Saleh if (gpa >= (1ULL << cfg->oas)) { 460bde809f0SMostafa Saleh info->type = SMMU_PTW_ERR_ADDR_SIZE; 461bde809f0SMostafa Saleh goto error; 462bde809f0SMostafa Saleh } 463bde809f0SMostafa Saleh 4649e54dee7SEric Auger tlbe->entry.translated_addr = gpa; 4659e54dee7SEric Auger tlbe->entry.iova = iova & ~mask; 4669e54dee7SEric Auger tlbe->entry.addr_mask = mask; 467d7cdf89cSMostafa Saleh tlbe->parent_perm = PTE_AP_TO_PERM(ap); 468d7cdf89cSMostafa Saleh tlbe->entry.perm = tlbe->parent_perm; 469a7550158SEric Auger tlbe->level = level; 470a7550158SEric Auger tlbe->granule = granule_sz; 4711733837dSEric Auger return 0; 4721733837dSEric Auger } 47393641948SEric Auger info->type = SMMU_PTW_ERR_TRANSLATION; 47493641948SEric Auger 47593641948SEric Auger error: 476f6cc1980SMostafa Saleh info->stage = SMMU_STAGE_1; 477a7550158SEric Auger tlbe->entry.perm = IOMMU_NONE; 47893641948SEric Auger return -EINVAL; 47993641948SEric Auger } 48093641948SEric Auger 48193641948SEric Auger /** 482e703f707SMostafa Saleh * smmu_ptw_64_s2 - VMSAv8-64 Walk of the page tables for a given ipa 483e703f707SMostafa Saleh * for stage-2. 484e703f707SMostafa Saleh * @cfg: translation config 485e703f707SMostafa Saleh * @ipa: ipa to translate 486e703f707SMostafa Saleh * @perm: access type 487e703f707SMostafa Saleh * @tlbe: SMMUTLBEntry (out) 488e703f707SMostafa Saleh * @info: handle to an error info 489e703f707SMostafa Saleh * 490e703f707SMostafa Saleh * Return 0 on success, < 0 on error. In case of error, @info is filled 491e703f707SMostafa Saleh * and tlbe->perm is set to IOMMU_NONE. 492e703f707SMostafa Saleh * Upon success, @tlbe is filled with translated_addr and entry 493e703f707SMostafa Saleh * permission rights. 494e703f707SMostafa Saleh */ 495e703f707SMostafa Saleh static int smmu_ptw_64_s2(SMMUTransCfg *cfg, 496e703f707SMostafa Saleh dma_addr_t ipa, IOMMUAccessFlags perm, 497e703f707SMostafa Saleh SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) 498e703f707SMostafa Saleh { 499f6cc1980SMostafa Saleh const SMMUStage stage = SMMU_STAGE_2; 500e703f707SMostafa Saleh int granule_sz = cfg->s2cfg.granule_sz; 501e703f707SMostafa Saleh /* ARM DDI0487I.a: Table D8-7. */ 502e703f707SMostafa Saleh int inputsize = 64 - cfg->s2cfg.tsz; 503e703f707SMostafa Saleh int level = get_start_level(cfg->s2cfg.sl0, granule_sz); 504e703f707SMostafa Saleh int stride = VMSA_STRIDE(granule_sz); 505e703f707SMostafa Saleh int idx = pgd_concat_idx(level, granule_sz, ipa); 506e703f707SMostafa Saleh /* 507e703f707SMostafa Saleh * Get the ttb from concatenated structure. 508e703f707SMostafa Saleh * The offset is the idx * size of each ttb(number of ptes * (sizeof(pte)) 509e703f707SMostafa Saleh */ 510e703f707SMostafa Saleh uint64_t baseaddr = extract64(cfg->s2cfg.vttb, 0, 48) + (1 << stride) * 511e703f707SMostafa Saleh idx * sizeof(uint64_t); 512e703f707SMostafa Saleh dma_addr_t indexmask = VMSA_IDXMSK(inputsize, stride, level); 513e703f707SMostafa Saleh 514e703f707SMostafa Saleh baseaddr &= ~indexmask; 515e703f707SMostafa Saleh 516e703f707SMostafa Saleh /* 517e703f707SMostafa Saleh * On input, a stage 2 Translation fault occurs if the IPA is outside the 518e703f707SMostafa Saleh * range configured by the relevant S2T0SZ field of the STE. 519e703f707SMostafa Saleh */ 520e703f707SMostafa Saleh if (ipa >= (1ULL << inputsize)) { 521e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_TRANSLATION; 52248f9e9ebSMostafa Saleh goto error_ipa; 523e703f707SMostafa Saleh } 524e703f707SMostafa Saleh 525e703f707SMostafa Saleh while (level < VMSA_LEVELS) { 526e703f707SMostafa Saleh uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); 527e703f707SMostafa Saleh uint64_t mask = subpage_size - 1; 528e703f707SMostafa Saleh uint32_t offset = iova_level_offset(ipa, inputsize, level, granule_sz); 529e703f707SMostafa Saleh uint64_t pte, gpa; 530e703f707SMostafa Saleh dma_addr_t pte_addr = baseaddr + offset * sizeof(pte); 531e703f707SMostafa Saleh uint8_t s2ap; 532e703f707SMostafa Saleh 533e703f707SMostafa Saleh if (get_pte(baseaddr, offset, &pte, info)) { 534e703f707SMostafa Saleh goto error; 535e703f707SMostafa Saleh } 536e703f707SMostafa Saleh trace_smmu_ptw_level(stage, level, ipa, subpage_size, 537e703f707SMostafa Saleh baseaddr, offset, pte); 538e703f707SMostafa Saleh if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { 539e703f707SMostafa Saleh trace_smmu_ptw_invalid_pte(stage, level, baseaddr, 540e703f707SMostafa Saleh pte_addr, offset, pte); 541e703f707SMostafa Saleh break; 542e703f707SMostafa Saleh } 543e703f707SMostafa Saleh 544e703f707SMostafa Saleh if (is_table_pte(pte, level)) { 545e703f707SMostafa Saleh baseaddr = get_table_pte_address(pte, granule_sz); 546e703f707SMostafa Saleh level++; 547e703f707SMostafa Saleh continue; 548e703f707SMostafa Saleh } else if (is_page_pte(pte, level)) { 549e703f707SMostafa Saleh gpa = get_page_pte_address(pte, granule_sz); 550e703f707SMostafa Saleh trace_smmu_ptw_page_pte(stage, level, ipa, 551e703f707SMostafa Saleh baseaddr, pte_addr, pte, gpa); 552e703f707SMostafa Saleh } else { 553e703f707SMostafa Saleh uint64_t block_size; 554e703f707SMostafa Saleh 555e703f707SMostafa Saleh gpa = get_block_pte_address(pte, level, granule_sz, 556e703f707SMostafa Saleh &block_size); 557e703f707SMostafa Saleh trace_smmu_ptw_block_pte(stage, level, baseaddr, 558e703f707SMostafa Saleh pte_addr, pte, ipa, gpa, 559e703f707SMostafa Saleh block_size >> 20); 560e703f707SMostafa Saleh } 561e703f707SMostafa Saleh 562e703f707SMostafa Saleh /* 563e703f707SMostafa Saleh * If S2AFFD and PTE.AF are 0 => fault. (5.2. Stream Table Entry) 564e703f707SMostafa Saleh * An Access fault takes priority over a Permission fault. 565e703f707SMostafa Saleh */ 566e703f707SMostafa Saleh if (!PTE_AF(pte) && !cfg->s2cfg.affd) { 567e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_ACCESS; 56848f9e9ebSMostafa Saleh goto error_ipa; 569e703f707SMostafa Saleh } 570e703f707SMostafa Saleh 571e703f707SMostafa Saleh s2ap = PTE_AP(pte); 572e703f707SMostafa Saleh if (is_permission_fault_s2(s2ap, perm)) { 573e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_PERMISSION; 57448f9e9ebSMostafa Saleh goto error_ipa; 575e703f707SMostafa Saleh } 576e703f707SMostafa Saleh 577e703f707SMostafa Saleh /* 578e703f707SMostafa Saleh * The address output from the translation causes a stage 2 Address 579e703f707SMostafa Saleh * Size fault if it exceeds the effective PA output range. 580e703f707SMostafa Saleh */ 581e703f707SMostafa Saleh if (gpa >= (1ULL << cfg->s2cfg.eff_ps)) { 582e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_ADDR_SIZE; 58348f9e9ebSMostafa Saleh goto error_ipa; 584e703f707SMostafa Saleh } 585e703f707SMostafa Saleh 586e703f707SMostafa Saleh tlbe->entry.translated_addr = gpa; 587e703f707SMostafa Saleh tlbe->entry.iova = ipa & ~mask; 588e703f707SMostafa Saleh tlbe->entry.addr_mask = mask; 589d7cdf89cSMostafa Saleh tlbe->parent_perm = s2ap; 590d7cdf89cSMostafa Saleh tlbe->entry.perm = tlbe->parent_perm; 591e703f707SMostafa Saleh tlbe->level = level; 592e703f707SMostafa Saleh tlbe->granule = granule_sz; 593e703f707SMostafa Saleh return 0; 594e703f707SMostafa Saleh } 595e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_TRANSLATION; 596e703f707SMostafa Saleh 59748f9e9ebSMostafa Saleh error_ipa: 59848f9e9ebSMostafa Saleh info->addr = ipa; 599e703f707SMostafa Saleh error: 600f6cc1980SMostafa Saleh info->stage = SMMU_STAGE_2; 601e703f707SMostafa Saleh tlbe->entry.perm = IOMMU_NONE; 602e703f707SMostafa Saleh return -EINVAL; 603e703f707SMostafa Saleh } 604e703f707SMostafa Saleh 605d7cdf89cSMostafa Saleh /* 606d7cdf89cSMostafa Saleh * combine S1 and S2 TLB entries into a single entry. 607d7cdf89cSMostafa Saleh * As a result the S1 entry is overriden with combined data. 608d7cdf89cSMostafa Saleh */ 609*f42a0a57SMostafa Saleh static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2, 610*f42a0a57SMostafa Saleh dma_addr_t iova, SMMUTransCfg *cfg) 611d7cdf89cSMostafa Saleh { 612d7cdf89cSMostafa Saleh if (tlbe_s2->entry.addr_mask < tlbe->entry.addr_mask) { 613d7cdf89cSMostafa Saleh tlbe->entry.addr_mask = tlbe_s2->entry.addr_mask; 614d7cdf89cSMostafa Saleh tlbe->granule = tlbe_s2->granule; 615d7cdf89cSMostafa Saleh tlbe->level = tlbe_s2->level; 616d7cdf89cSMostafa Saleh } 617d7cdf89cSMostafa Saleh 618d7cdf89cSMostafa Saleh tlbe->entry.translated_addr = CACHED_ENTRY_TO_ADDR(tlbe_s2, 619d7cdf89cSMostafa Saleh tlbe->entry.translated_addr); 620d7cdf89cSMostafa Saleh 621d7cdf89cSMostafa Saleh tlbe->entry.iova = iova & ~tlbe->entry.addr_mask; 622d7cdf89cSMostafa Saleh /* parent_perm has s2 perm while perm keeps s1 perm. */ 623d7cdf89cSMostafa Saleh tlbe->parent_perm = tlbe_s2->entry.perm; 624d7cdf89cSMostafa Saleh return; 625d7cdf89cSMostafa Saleh } 626d7cdf89cSMostafa Saleh 627e703f707SMostafa Saleh /** 62893641948SEric Auger * smmu_ptw - Walk the page tables for an IOVA, according to @cfg 62993641948SEric Auger * 630*f42a0a57SMostafa Saleh * @bs: smmu state which includes TLB instance 63193641948SEric Auger * @cfg: translation configuration 63293641948SEric Auger * @iova: iova to translate 63393641948SEric Auger * @perm: tentative access type 63493641948SEric Auger * @tlbe: returned entry 63593641948SEric Auger * @info: ptw event handle 63693641948SEric Auger * 63793641948SEric Auger * return 0 on success 63893641948SEric Auger */ 639*f42a0a57SMostafa Saleh int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, 640*f42a0a57SMostafa Saleh IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) 64193641948SEric Auger { 642*f42a0a57SMostafa Saleh int ret; 643*f42a0a57SMostafa Saleh SMMUTLBEntry tlbe_s2; 644*f42a0a57SMostafa Saleh dma_addr_t ipa; 645*f42a0a57SMostafa Saleh 646f6cc1980SMostafa Saleh if (cfg->stage == SMMU_STAGE_1) { 647*f42a0a57SMostafa Saleh return smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); 648f6cc1980SMostafa Saleh } else if (cfg->stage == SMMU_STAGE_2) { 649e703f707SMostafa Saleh /* 650e703f707SMostafa Saleh * If bypassing stage 1(or unimplemented), the input address is passed 651e703f707SMostafa Saleh * directly to stage 2 as IPA. If the input address of a transaction 652e703f707SMostafa Saleh * exceeds the size of the IAS, a stage 1 Address Size fault occurs. 653e703f707SMostafa Saleh * For AA64, IAS = OAS according to (IHI 0070.E.a) "3.4 Address sizes" 654e703f707SMostafa Saleh */ 655e703f707SMostafa Saleh if (iova >= (1ULL << cfg->oas)) { 656e703f707SMostafa Saleh info->type = SMMU_PTW_ERR_ADDR_SIZE; 657f6cc1980SMostafa Saleh info->stage = SMMU_STAGE_1; 658e703f707SMostafa Saleh tlbe->entry.perm = IOMMU_NONE; 659e703f707SMostafa Saleh return -EINVAL; 660e703f707SMostafa Saleh } 661e703f707SMostafa Saleh 662e703f707SMostafa Saleh return smmu_ptw_64_s2(cfg, iova, perm, tlbe, info); 663e703f707SMostafa Saleh } 664e703f707SMostafa Saleh 665*f42a0a57SMostafa Saleh /* SMMU_NESTED. */ 666*f42a0a57SMostafa Saleh ret = smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); 667*f42a0a57SMostafa Saleh if (ret) { 668*f42a0a57SMostafa Saleh return ret; 669*f42a0a57SMostafa Saleh } 670*f42a0a57SMostafa Saleh 671*f42a0a57SMostafa Saleh ipa = CACHED_ENTRY_TO_ADDR(tlbe, iova); 672*f42a0a57SMostafa Saleh ret = smmu_ptw_64_s2(cfg, ipa, perm, &tlbe_s2, info); 673*f42a0a57SMostafa Saleh if (ret) { 674*f42a0a57SMostafa Saleh return ret; 675*f42a0a57SMostafa Saleh } 676*f42a0a57SMostafa Saleh 677*f42a0a57SMostafa Saleh combine_tlb(tlbe, &tlbe_s2, iova, cfg); 678*f42a0a57SMostafa Saleh return 0; 67993641948SEric Auger } 680527773eeSEric Auger 681a9e3f4c1SMostafa Saleh SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, 682a9e3f4c1SMostafa Saleh IOMMUAccessFlags flag, SMMUPTWEventInfo *info) 683a9e3f4c1SMostafa Saleh { 684a9e3f4c1SMostafa Saleh SMMUTLBEntry *cached_entry = NULL; 685a9e3f4c1SMostafa Saleh SMMUTransTableInfo *tt; 686a9e3f4c1SMostafa Saleh int status; 687a9e3f4c1SMostafa Saleh 688a9e3f4c1SMostafa Saleh /* 6897eb57be1SMostafa Saleh * Combined attributes used for TLB lookup, holds the attributes for 6907eb57be1SMostafa Saleh * the input stage. 691a9e3f4c1SMostafa Saleh */ 692a9e3f4c1SMostafa Saleh SMMUTransTableInfo tt_combined; 693a9e3f4c1SMostafa Saleh 6947eb57be1SMostafa Saleh if (cfg->stage == SMMU_STAGE_2) { 6957eb57be1SMostafa Saleh /* Stage2. */ 6967eb57be1SMostafa Saleh tt_combined.granule_sz = cfg->s2cfg.granule_sz; 6977eb57be1SMostafa Saleh tt_combined.tsz = cfg->s2cfg.tsz; 6987eb57be1SMostafa Saleh } else { 699a9e3f4c1SMostafa Saleh /* Select stage1 translation table. */ 700a9e3f4c1SMostafa Saleh tt = select_tt(cfg, addr); 701a9e3f4c1SMostafa Saleh if (!tt) { 702a9e3f4c1SMostafa Saleh info->type = SMMU_PTW_ERR_TRANSLATION; 703a9e3f4c1SMostafa Saleh info->stage = SMMU_STAGE_1; 704a9e3f4c1SMostafa Saleh return NULL; 705a9e3f4c1SMostafa Saleh } 706a9e3f4c1SMostafa Saleh tt_combined.granule_sz = tt->granule_sz; 707a9e3f4c1SMostafa Saleh tt_combined.tsz = tt->tsz; 708a9e3f4c1SMostafa Saleh } 709a9e3f4c1SMostafa Saleh 7107eb57be1SMostafa Saleh cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr); 711a9e3f4c1SMostafa Saleh if (cached_entry) { 712d7cdf89cSMostafa Saleh if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & 713d7cdf89cSMostafa Saleh cached_entry->parent_perm & IOMMU_WO)) { 714a9e3f4c1SMostafa Saleh info->type = SMMU_PTW_ERR_PERMISSION; 715d7cdf89cSMostafa Saleh info->stage = !(cached_entry->entry.perm & IOMMU_WO) ? 716d7cdf89cSMostafa Saleh SMMU_STAGE_1 : 717d7cdf89cSMostafa Saleh SMMU_STAGE_2; 718a9e3f4c1SMostafa Saleh return NULL; 719a9e3f4c1SMostafa Saleh } 720a9e3f4c1SMostafa Saleh return cached_entry; 721a9e3f4c1SMostafa Saleh } 722a9e3f4c1SMostafa Saleh 723a9e3f4c1SMostafa Saleh cached_entry = g_new0(SMMUTLBEntry, 1); 724*f42a0a57SMostafa Saleh status = smmu_ptw(bs, cfg, addr, flag, cached_entry, info); 725a9e3f4c1SMostafa Saleh if (status) { 726a9e3f4c1SMostafa Saleh g_free(cached_entry); 727a9e3f4c1SMostafa Saleh return NULL; 728a9e3f4c1SMostafa Saleh } 729a9e3f4c1SMostafa Saleh smmu_iotlb_insert(bs, cfg, cached_entry); 730a9e3f4c1SMostafa Saleh return cached_entry; 731a9e3f4c1SMostafa Saleh } 732a9e3f4c1SMostafa Saleh 733cac994efSEric Auger /** 734cac994efSEric Auger * The bus number is used for lookup when SID based invalidation occurs. 735cac994efSEric Auger * In that case we lazily populate the SMMUPciBus array from the bus hash 736cac994efSEric Auger * table. At the time the SMMUPciBus is created (smmu_find_add_as), the bus 737cac994efSEric Auger * numbers may not be always initialized yet. 738cac994efSEric Auger */ 739cac994efSEric Auger SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num) 740cac994efSEric Auger { 741cac994efSEric Auger SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num]; 742cac994efSEric Auger GHashTableIter iter; 743cac994efSEric Auger 7445ca0e6feSPhilippe Mathieu-Daudé if (smmu_pci_bus) { 7455ca0e6feSPhilippe Mathieu-Daudé return smmu_pci_bus; 7465ca0e6feSPhilippe Mathieu-Daudé } 7475ca0e6feSPhilippe Mathieu-Daudé 748cac994efSEric Auger g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr); 749cac994efSEric Auger while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) { 750cac994efSEric Auger if (pci_bus_num(smmu_pci_bus->bus) == bus_num) { 751cac994efSEric Auger s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus; 752cac994efSEric Auger return smmu_pci_bus; 753cac994efSEric Auger } 754cac994efSEric Auger } 7555ca0e6feSPhilippe Mathieu-Daudé 7565ca0e6feSPhilippe Mathieu-Daudé return NULL; 757cac994efSEric Auger } 758cac994efSEric Auger 759cac994efSEric Auger static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) 760cac994efSEric Auger { 761cac994efSEric Auger SMMUState *s = opaque; 762cac994efSEric Auger SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus); 763cac994efSEric Auger SMMUDevice *sdev; 7646ce9297bSEric Auger static unsigned int index; 765cac994efSEric Auger 766cac994efSEric Auger if (!sbus) { 767cac994efSEric Auger sbus = g_malloc0(sizeof(SMMUPciBus) + 768cac994efSEric Auger sizeof(SMMUDevice *) * SMMU_PCI_DEVFN_MAX); 769cac994efSEric Auger sbus->bus = bus; 770cac994efSEric Auger g_hash_table_insert(s->smmu_pcibus_by_busptr, bus, sbus); 771cac994efSEric Auger } 772cac994efSEric Auger 773cac994efSEric Auger sdev = sbus->pbdev[devfn]; 774cac994efSEric Auger if (!sdev) { 7756ce9297bSEric Auger char *name = g_strdup_printf("%s-%d-%d", s->mrtypename, devfn, index++); 7766ce9297bSEric Auger 777cac994efSEric Auger sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1); 778cac994efSEric Auger 779cac994efSEric Auger sdev->smmu = s; 780cac994efSEric Auger sdev->bus = bus; 781cac994efSEric Auger sdev->devfn = devfn; 782cac994efSEric Auger 783cac994efSEric Auger memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu), 784cac994efSEric Auger s->mrtypename, 785ca3fbed8SJean-Philippe Brucker OBJECT(s), name, UINT64_MAX); 786cac994efSEric Auger address_space_init(&sdev->as, 787cac994efSEric Auger MEMORY_REGION(&sdev->iommu), name); 788cac994efSEric Auger trace_smmu_add_mr(name); 789cac994efSEric Auger g_free(name); 790cac994efSEric Auger } 791cac994efSEric Auger 792cac994efSEric Auger return &sdev->as; 793cac994efSEric Auger } 794cac994efSEric Auger 795ba7d12ebSYi Liu static const PCIIOMMUOps smmu_ops = { 796ba7d12ebSYi Liu .get_address_space = smmu_find_add_as, 797ba7d12ebSYi Liu }; 798ba7d12ebSYi Liu 79969970205SNicolin Chen SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid) 80032cfd7f3SEric Auger { 80132cfd7f3SEric Auger uint8_t bus_n, devfn; 80232cfd7f3SEric Auger SMMUPciBus *smmu_bus; 80332cfd7f3SEric Auger 80432cfd7f3SEric Auger bus_n = PCI_BUS_NUM(sid); 80532cfd7f3SEric Auger smmu_bus = smmu_find_smmu_pcibus(s, bus_n); 80632cfd7f3SEric Auger if (smmu_bus) { 807b78aae9bSEric Auger devfn = SMMU_PCI_DEVFN(sid); 80869970205SNicolin Chen return smmu_bus->pbdev[devfn]; 80932cfd7f3SEric Auger } 81032cfd7f3SEric Auger return NULL; 81132cfd7f3SEric Auger } 81232cfd7f3SEric Auger 813832e4222SEric Auger /* Unmap all notifiers attached to @mr */ 8141e793dd6SPhilippe Mathieu-Daudé static void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) 815832e4222SEric Auger { 816832e4222SEric Auger IOMMUNotifier *n; 817832e4222SEric Auger 818832e4222SEric Auger trace_smmu_inv_notifiers_mr(mr->parent_obj.name); 819832e4222SEric Auger IOMMU_NOTIFIER_FOREACH(n, mr) { 82098332f64SJason Wang memory_region_unmap_iommu_notifier_range(n); 821832e4222SEric Auger } 822832e4222SEric Auger } 823832e4222SEric Auger 824832e4222SEric Auger /* Unmap all notifiers of all mr's */ 825832e4222SEric Auger void smmu_inv_notifiers_all(SMMUState *s) 826832e4222SEric Auger { 827c6370441SEric Auger SMMUDevice *sdev; 828832e4222SEric Auger 829c6370441SEric Auger QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) { 830c6370441SEric Auger smmu_inv_notifiers_mr(&sdev->iommu); 831832e4222SEric Auger } 832832e4222SEric Auger } 833832e4222SEric Auger 834527773eeSEric Auger static void smmu_base_realize(DeviceState *dev, Error **errp) 835527773eeSEric Auger { 836cac994efSEric Auger SMMUState *s = ARM_SMMU(dev); 837527773eeSEric Auger SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); 838527773eeSEric Auger Error *local_err = NULL; 839527773eeSEric Auger 840527773eeSEric Auger sbc->parent_realize(dev, &local_err); 841527773eeSEric Auger if (local_err) { 842527773eeSEric Auger error_propagate(errp, local_err); 843527773eeSEric Auger return; 844527773eeSEric Auger } 84532cfd7f3SEric Auger s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free); 846cc27ed81SEric Auger s->iotlb = g_hash_table_new_full(smmu_iotlb_key_hash, smmu_iotlb_key_equal, 847cc27ed81SEric Auger g_free, g_free); 848cac994efSEric Auger s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); 849cac994efSEric Auger 850cac994efSEric Auger if (s->primary_bus) { 851ba7d12ebSYi Liu pci_setup_iommu(s->primary_bus, &smmu_ops, s); 852cac994efSEric Auger } else { 853cac994efSEric Auger error_setg(errp, "SMMU is not attached to any PCI bus!"); 854cac994efSEric Auger } 855527773eeSEric Auger } 856527773eeSEric Auger 857ad80e367SPeter Maydell static void smmu_base_reset_hold(Object *obj, ResetType type) 858527773eeSEric Auger { 8593c1a7c41SPeter Maydell SMMUState *s = ARM_SMMU(obj); 86032cfd7f3SEric Auger 8618a6b3f4dSZhenzhong Duan memset(s->smmu_pcibus_by_bus_num, 0, sizeof(s->smmu_pcibus_by_bus_num)); 8628a6b3f4dSZhenzhong Duan 86332cfd7f3SEric Auger g_hash_table_remove_all(s->configs); 864cc27ed81SEric Auger g_hash_table_remove_all(s->iotlb); 865527773eeSEric Auger } 866527773eeSEric Auger 867527773eeSEric Auger static Property smmu_dev_properties[] = { 868527773eeSEric Auger DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), 869c45e7619SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, 870c45e7619SPhilippe Mathieu-Daudé TYPE_PCI_BUS, PCIBus *), 871527773eeSEric Auger DEFINE_PROP_END_OF_LIST(), 872527773eeSEric Auger }; 873527773eeSEric Auger 874527773eeSEric Auger static void smmu_base_class_init(ObjectClass *klass, void *data) 875527773eeSEric Auger { 876527773eeSEric Auger DeviceClass *dc = DEVICE_CLASS(klass); 8773c1a7c41SPeter Maydell ResettableClass *rc = RESETTABLE_CLASS(klass); 878527773eeSEric Auger SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass); 879527773eeSEric Auger 8804f67d30bSMarc-André Lureau device_class_set_props(dc, smmu_dev_properties); 881527773eeSEric Auger device_class_set_parent_realize(dc, smmu_base_realize, 882527773eeSEric Auger &sbc->parent_realize); 8833c1a7c41SPeter Maydell rc->phases.hold = smmu_base_reset_hold; 884527773eeSEric Auger } 885527773eeSEric Auger 886527773eeSEric Auger static const TypeInfo smmu_base_info = { 887527773eeSEric Auger .name = TYPE_ARM_SMMU, 888527773eeSEric Auger .parent = TYPE_SYS_BUS_DEVICE, 889527773eeSEric Auger .instance_size = sizeof(SMMUState), 890527773eeSEric Auger .class_data = NULL, 891527773eeSEric Auger .class_size = sizeof(SMMUBaseClass), 892527773eeSEric Auger .class_init = smmu_base_class_init, 893527773eeSEric Auger .abstract = true, 894527773eeSEric Auger }; 895527773eeSEric Auger 896527773eeSEric Auger static void smmu_base_register_types(void) 897527773eeSEric Auger { 898527773eeSEric Auger type_register_static(&smmu_base_info); 899527773eeSEric Auger } 900527773eeSEric Auger 901527773eeSEric Auger type_init(smmu_base_register_types) 902527773eeSEric Auger 903