/* * Calculate the right shift amount to get to the portion describing level l * in a virtual address mapped by the pagetable in d.
*/ #define ARM_LPAE_LVL_SHIFT(l,d) \
(((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level) + \
ilog2(sizeof(arm_lpae_iopte)))
/* * Calculate the index at level l used to map virtual address a using the * pagetable in d.
*/ #define ARM_LPAE_PGD_IDX(l,d) \
((l) == (d)->start_level ? (d)->pgd_bits - (d)->bits_per_level : 0)
if (ARM_LPAE_GRANULE(data) < SZ_64K) return paddr;
/* Rotate the packed high-order bits back to the top */ return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
}
/* * Convert an index returned by ARM_LPAE_PGD_IDX(), which can point into * a concatenated PGD, into the maximum number of entries that can be * mapped in the same table page.
*/ staticinlineint arm_lpae_max_entries(int i, struct arm_lpae_io_pgtable *data)
{ int ptes_per_table = ARM_LPAE_PTES_PER_TABLE(data);
return ptes_per_table - (i & (ptes_per_table - 1));
}
/* * Check if concatenated PGDs are mandatory according to Arm DDI0487 (K.a) * 1) R_DXBSH: For 16KB, and 48-bit input size, use level 1 instead of 0. * 2) R_SRKBC: After de-ciphering the table for PA size and valid initial lookup * a) 40 bits PA size with 4K: use level 1 instead of level 0 (2 tables for ias = oas) * b) 40 bits PA size with 16K: use level 2 instead of level 1 (16 tables for ias = oas) * c) 42 bits PA size with 4K: use level 1 instead of level 0 (8 tables for ias = oas) * d) 48 bits PA size with 16K: use level 1 instead of level 0 (2 tables for ias = oas)
*/ staticinlinebool arm_lpae_concat_mandatory(struct io_pgtable_cfg *cfg, struct arm_lpae_io_pgtable *data)
{ unsignedint ias = cfg->ias; unsignedint oas = cfg->oas;
/* * For very small starting-level translation tables the HW requires a * minimum alignment of at least 64 to cover all cases.
*/
alloc_size = max(size, 64); if (cfg->alloc)
pages = cfg->alloc(cookie, alloc_size, gfp); else
pages = iommu_alloc_pages_node_sz(dev_to_node(dev), gfp,
alloc_size);
if (!pages) return NULL;
if (!cfg->coherent_walk) {
dma = dma_map_single(dev, pages, size, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma)) goto out_free; /* * We depend on the IOMMU being able to work with any physical * address directly, so if the DMA layer suggests otherwise by * translating or truncating them, that bodes very badly...
*/ if (dma != virt_to_phys(pages)) goto out_unmap;
}
for (i = 0; i < num_entries; i++)
ptep[i] = pte | paddr_to_iopte(paddr + i * sz, data);
if (!cfg->coherent_walk)
__arm_lpae_sync_pte(ptep, num_entries, cfg);
}
staticint arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, unsignedlong iova, phys_addr_t paddr,
arm_lpae_iopte prot, int lvl, int num_entries,
arm_lpae_iopte *ptep)
{ int i;
for (i = 0; i < num_entries; i++) if (iopte_leaf(ptep[i], lvl, data->iop.fmt)) { /* We require an unmap first */
WARN_ON(!(data->iop.cfg.quirks & IO_PGTABLE_QUIRK_NO_WARN)); return -EEXIST;
} elseif (iopte_type(ptep[i]) == ARM_LPAE_PTE_TYPE_TABLE) { /* * We need to unmap and free the old table before * overwriting it with a block entry.
*/
arm_lpae_iopte *tblp;
size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
new = paddr_to_iopte(__pa(table), data) | ARM_LPAE_PTE_TYPE_TABLE; if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS) new |= ARM_LPAE_PTE_NSTABLE;
/* * Ensure the table itself is visible before its PTE can be. * Whilst we could get away with cmpxchg64_release below, this * doesn't have any ordering semantics when !CONFIG_SMP.
*/
dma_wmb();
old = cmpxchg64_relaxed(ptep, curr, new);
if (cfg->coherent_walk || (old & ARM_LPAE_PTE_SW_SYNC)) return old;
/* Even if it's not ours, there's no point waiting; just kick it */
__arm_lpae_sync_pte(ptep, 1, cfg); if (old == curr)
WRITE_ONCE(*ptep, new | ARM_LPAE_PTE_SW_SYNC);
/* Find our entry at the current level */
map_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
ptep += map_idx_start;
/* If we can install a leaf entry at this level, then do so */ if (size == block_size) {
max_entries = arm_lpae_max_entries(map_idx_start, data);
num_entries = min_t(int, pgcount, max_entries);
ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl, num_entries, ptep); if (!ret)
*mapped += num_entries * size;
return ret;
}
/* We can't allocate tables at the final level */ if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) return -EINVAL;
/* Grab a pointer to the next level */
pte = READ_ONCE(*ptep); if (!pte) {
cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg, data->iop.cookie); if (!cptep) return -ENOMEM;
/* * Note that this logic is structured to accommodate Mali LPAE * having stage-1-like attributes but stage-2-like permissions.
*/ if (data->iop.fmt == ARM_64_LPAE_S2 ||
data->iop.fmt == ARM_32_LPAE_S2) { if (prot & IOMMU_MMIO) {
pte |= ARM_LPAE_PTE_MEMATTR_DEV;
} elseif (prot & IOMMU_CACHE) { if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_S2FWB)
pte |= ARM_LPAE_PTE_MEMATTR_FWB_WB; else
pte |= ARM_LPAE_PTE_MEMATTR_OIWB;
} else {
pte |= ARM_LPAE_PTE_MEMATTR_NC;
}
} else { if (prot & IOMMU_MMIO)
pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
<< ARM_LPAE_PTE_ATTRINDX_SHIFT); elseif (prot & IOMMU_CACHE)
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
}
/* * Also Mali has its own notions of shareability wherein its Inner * domain covers the cores within the GPU, and its Outer domain is * "outside the GPU" (i.e. either the Inner or System domain in CPU * terms, depending on coherency).
*/ if (prot & IOMMU_CACHE && data->iop.fmt != ARM_MALI_LPAE)
pte |= ARM_LPAE_PTE_SH_IS; else
pte |= ARM_LPAE_PTE_SH_OS;
if (prot & IOMMU_NOEXEC)
pte |= ARM_LPAE_PTE_XN;
if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
pte |= ARM_LPAE_PTE_NS;
if (data->iop.fmt != ARM_MALI_LPAE)
pte |= ARM_LPAE_PTE_AF;
if (WARN_ON(!pgsize || (pgsize & cfg->pgsize_bitmap) != pgsize)) return -EINVAL;
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)
iaext = ~iaext; if (WARN_ON(iaext || paddr >> cfg->oas)) return -ERANGE;
if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) return -EINVAL;
prot = arm_lpae_prot_to_pte(data, iommu_prot);
ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
ptep, gfp, mapped); /* * Synchronise all PTE updates for the new mapping before there's * a chance for anything to kick off a table walk for the new iova.
*/
wmb();
/* If the size matches this level, we're in the right place */ if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
num_entries = min_t(int, pgcount, max_entries);
/* Find and handle non-leaf entries */ for (i = 0; i < num_entries; i++) {
pte = READ_ONCE(ptep[i]); if (!pte) {
WARN_ON(!(data->iop.cfg.quirks & IO_PGTABLE_QUIRK_NO_WARN)); break;
}
if (!iopte_leaf(pte, lvl, iop->fmt)) {
__arm_lpae_clear_pte(&ptep[i], &iop->cfg, 1);
/* Also flush any partial walks */
io_pgtable_tlb_flush_walk(iop, iova + i * size, size,
ARM_LPAE_GRANULE(data));
__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
}
}
/* Clear the remaining entries */
__arm_lpae_clear_pte(ptep, &iop->cfg, i);
/* * We need to restrict the supported page sizes to match the * translation regime for a particular granule. Aim to match * the CPU page size if possible, otherwise prefer smaller sizes. * While we're at it, restrict the block sizes to match the * chosen granule.
*/ if (cfg->pgsize_bitmap & PAGE_SIZE)
granule = PAGE_SIZE; elseif (cfg->pgsize_bitmap & ~PAGE_MASK)
granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK); elseif (cfg->pgsize_bitmap & PAGE_MASK)
granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK); else
granule = 0;
switch (ARM_LPAE_GRANULE(data)) { case SZ_4K:
vtcr->tg = ARM_LPAE_TCR_TG0_4K;
sl++; /* SL0 format is different for 4K granule size */ break; case SZ_16K:
vtcr->tg = ARM_LPAE_TCR_TG0_16K; break; case SZ_64K:
vtcr->tg = ARM_LPAE_TCR_TG0_64K; break;
}
switch (cfg->oas) { case 32:
vtcr->ps = ARM_LPAE_TCR_PS_32_BIT; break; case 36:
vtcr->ps = ARM_LPAE_TCR_PS_36_BIT; break; case 40:
vtcr->ps = ARM_LPAE_TCR_PS_40_BIT; break; case 42:
vtcr->ps = ARM_LPAE_TCR_PS_42_BIT; break; case 44:
vtcr->ps = ARM_LPAE_TCR_PS_44_BIT; break; case 48:
vtcr->ps = ARM_LPAE_TCR_PS_48_BIT; break; case 52:
vtcr->ps = ARM_LPAE_TCR_PS_52_BIT; break; default: goto out_free_data;
}
/* No quirks for Mali (hopefully) */ if (cfg->quirks) return NULL;
if (cfg->ias > 48 || cfg->oas > 40) return NULL;
cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
data = arm_lpae_alloc_pgtable(cfg); if (!data) return NULL;
/* Mali seems to need a full 4-level table regardless of IAS */ if (data->start_level > 0) {
data->start_level = 0;
data->pgd_bits = 0;
} /* * MEMATTR: Mali has no actual notion of a non-cacheable type, so the * best we can do is mimic the out-of-tree driver and hope that the * "implementation-defined caching policy" is good enough. Similarly, * we'll use it for the sake of a valid attribute for our 'device' * index, although callers should never request that in practice.
*/
cfg->arm_mali_lpae_cfg.memattr =
(ARM_MALI_LPAE_MEMATTR_IMP_DEF
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
(ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
(ARM_MALI_LPAE_MEMATTR_IMP_DEF
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV));
data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
cfg, cookie); if (!data->pgd) goto out_free_data;
/* Ensure the empty pgd is visible before TRANSTAB can be written */
wmb();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.