iommu: io-pgtable: Add ARM Mali midgard MMU page table format
ARM Mali midgard GPU is similar to standard 64-bit stage 1 page tables, but have a few differences. Add a new format type to represent the format. The input address size is 48-bits and the output address size is 40-bits (and possibly less?). Note that the later bifrost GPUs follow the standard 64-bit stage 1 format. The differences in the format compared to 64-bit stage 1 format are: The 3rd level page entry bits are 0x1 instead of 0x3 for page entries. The access flags are not read-only and unprivileged, but read and write. This is similar to stage 2 entries, but the memory attributes field matches stage 1 being an index. The nG bit is not set by the vendor driver. This one didn't seem to matter, but we'll keep it aligned to the vendor driver. Cc: Will Deacon <will.deacon@arm.com> Acked-by: Robin Murphy <robin.murphy@arm.com> Cc: linux-arm-kernel@lists.infradead.org Cc: iommu@lists.linux-foundation.org Acked-by: Alyssa Rosenzweig <alyssa@rosenzweig.io> Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Rob Herring <robh@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20190409205427.6943-2-robh@kernel.org
This commit is contained in:
parent
0eb2766dd6
commit
d08d42de64
|
@ -172,6 +172,10 @@
|
|||
#define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1
|
||||
#define ARM_LPAE_MAIR_ATTR_IDX_DEV 2
|
||||
|
||||
#define ARM_MALI_LPAE_TTBR_ADRMODE_TABLE (3u << 0)
|
||||
#define ARM_MALI_LPAE_TTBR_READ_INNER BIT(2)
|
||||
#define ARM_MALI_LPAE_TTBR_SHARE_OUTER BIT(4)
|
||||
|
||||
/* IOPTE accessors */
|
||||
#define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
|
||||
|
||||
|
@ -180,11 +184,6 @@
|
|||
|
||||
#define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK)
|
||||
|
||||
#define iopte_leaf(pte,l) \
|
||||
(l == (ARM_LPAE_MAX_LEVELS - 1) ? \
|
||||
(iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \
|
||||
(iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK))
|
||||
|
||||
struct arm_lpae_io_pgtable {
|
||||
struct io_pgtable iop;
|
||||
|
||||
|
@ -198,6 +197,15 @@ struct arm_lpae_io_pgtable {
|
|||
|
||||
typedef u64 arm_lpae_iopte;
|
||||
|
||||
static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl,
|
||||
enum io_pgtable_fmt fmt)
|
||||
{
|
||||
if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE)
|
||||
return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE;
|
||||
|
||||
return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK;
|
||||
}
|
||||
|
||||
static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr,
|
||||
struct arm_lpae_io_pgtable *data)
|
||||
{
|
||||
|
@ -303,12 +311,14 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|||
if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
||||
pte |= ARM_LPAE_PTE_NS;
|
||||
|
||||
if (lvl == ARM_LPAE_MAX_LEVELS - 1)
|
||||
if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1)
|
||||
pte |= ARM_LPAE_PTE_TYPE_PAGE;
|
||||
else
|
||||
pte |= ARM_LPAE_PTE_TYPE_BLOCK;
|
||||
|
||||
pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS;
|
||||
if (data->iop.fmt != ARM_MALI_LPAE)
|
||||
pte |= ARM_LPAE_PTE_AF;
|
||||
pte |= ARM_LPAE_PTE_SH_IS;
|
||||
pte |= paddr_to_iopte(paddr, data);
|
||||
|
||||
__arm_lpae_set_pte(ptep, pte, &data->iop.cfg);
|
||||
|
@ -321,7 +331,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|||
{
|
||||
arm_lpae_iopte pte = *ptep;
|
||||
|
||||
if (iopte_leaf(pte, lvl)) {
|
||||
if (iopte_leaf(pte, lvl, data->iop.fmt)) {
|
||||
/* We require an unmap first */
|
||||
WARN_ON(!selftest_running);
|
||||
return -EEXIST;
|
||||
|
@ -409,7 +419,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
|||
__arm_lpae_sync_pte(ptep, cfg);
|
||||
}
|
||||
|
||||
if (pte && !iopte_leaf(pte, lvl)) {
|
||||
if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) {
|
||||
cptep = iopte_deref(pte, data);
|
||||
} else if (pte) {
|
||||
/* We require an unmap first */
|
||||
|
@ -429,31 +439,37 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
|
|||
if (data->iop.fmt == ARM_64_LPAE_S1 ||
|
||||
data->iop.fmt == ARM_32_LPAE_S1) {
|
||||
pte = ARM_LPAE_PTE_nG;
|
||||
|
||||
if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
|
||||
pte |= ARM_LPAE_PTE_AP_RDONLY;
|
||||
|
||||
if (!(prot & IOMMU_PRIV))
|
||||
pte |= ARM_LPAE_PTE_AP_UNPRIV;
|
||||
|
||||
if (prot & IOMMU_MMIO)
|
||||
pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
|
||||
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
||||
else if (prot & IOMMU_CACHE)
|
||||
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
|
||||
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
||||
} else {
|
||||
pte = ARM_LPAE_PTE_HAP_FAULT;
|
||||
if (prot & IOMMU_READ)
|
||||
pte |= ARM_LPAE_PTE_HAP_READ;
|
||||
if (prot & IOMMU_WRITE)
|
||||
pte |= ARM_LPAE_PTE_HAP_WRITE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
else if (prot & IOMMU_CACHE)
|
||||
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);
|
||||
else if (prot & IOMMU_CACHE)
|
||||
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
|
||||
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
||||
}
|
||||
|
||||
if (prot & IOMMU_NOEXEC)
|
||||
|
@ -511,7 +527,7 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
|
|||
while (ptep != end) {
|
||||
arm_lpae_iopte pte = *ptep++;
|
||||
|
||||
if (!pte || iopte_leaf(pte, lvl))
|
||||
if (!pte || iopte_leaf(pte, lvl, data->iop.fmt))
|
||||
continue;
|
||||
|
||||
__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
|
||||
|
@ -602,7 +618,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|||
if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
|
||||
__arm_lpae_set_pte(ptep, 0, &iop->cfg);
|
||||
|
||||
if (!iopte_leaf(pte, lvl)) {
|
||||
if (!iopte_leaf(pte, lvl, iop->fmt)) {
|
||||
/* Also flush any partial walks */
|
||||
io_pgtable_tlb_add_flush(iop, iova, size,
|
||||
ARM_LPAE_GRANULE(data), false);
|
||||
|
@ -621,7 +637,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|||
}
|
||||
|
||||
return size;
|
||||
} else if (iopte_leaf(pte, lvl)) {
|
||||
} else if (iopte_leaf(pte, lvl, iop->fmt)) {
|
||||
/*
|
||||
* Insert a table at the next level to map the old region,
|
||||
* minus the part we want to unmap
|
||||
|
@ -669,7 +685,7 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
|
|||
return 0;
|
||||
|
||||
/* Leaf entry? */
|
||||
if (iopte_leaf(pte,lvl))
|
||||
if (iopte_leaf(pte, lvl, data->iop.fmt))
|
||||
goto found_translation;
|
||||
|
||||
/* Take it to the next level */
|
||||
|
@ -995,6 +1011,32 @@ arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
|
|||
return iop;
|
||||
}
|
||||
|
||||
static struct io_pgtable *
|
||||
arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
|
||||
{
|
||||
struct io_pgtable *iop;
|
||||
|
||||
if (cfg->ias != 48 || cfg->oas > 40)
|
||||
return NULL;
|
||||
|
||||
cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
|
||||
iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie);
|
||||
if (iop) {
|
||||
u64 mair, ttbr;
|
||||
|
||||
/* Copy values as union fields overlap */
|
||||
mair = cfg->arm_lpae_s1_cfg.mair[0];
|
||||
ttbr = cfg->arm_lpae_s1_cfg.ttbr[0];
|
||||
|
||||
cfg->arm_mali_lpae_cfg.memattr = mair;
|
||||
cfg->arm_mali_lpae_cfg.transtab = ttbr |
|
||||
ARM_MALI_LPAE_TTBR_READ_INNER |
|
||||
ARM_MALI_LPAE_TTBR_ADRMODE_TABLE;
|
||||
}
|
||||
|
||||
return iop;
|
||||
}
|
||||
|
||||
struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
|
||||
.alloc = arm_64_lpae_alloc_pgtable_s1,
|
||||
.free = arm_lpae_free_pgtable,
|
||||
|
@ -1015,6 +1057,11 @@ struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = {
|
|||
.free = arm_lpae_free_pgtable,
|
||||
};
|
||||
|
||||
struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
|
||||
.alloc = arm_mali_lpae_alloc_pgtable,
|
||||
.free = arm_lpae_free_pgtable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
|
||||
|
||||
static struct io_pgtable_cfg *cfg_cookie;
|
||||
|
|
|
@ -30,6 +30,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
|
|||
[ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns,
|
||||
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
|
||||
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
|
||||
[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
|
||||
#endif
|
||||
#ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
|
||||
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
|
||||
|
|
|
@ -12,6 +12,7 @@ enum io_pgtable_fmt {
|
|||
ARM_64_LPAE_S1,
|
||||
ARM_64_LPAE_S2,
|
||||
ARM_V7S,
|
||||
ARM_MALI_LPAE,
|
||||
IO_PGTABLE_NUM_FMTS,
|
||||
};
|
||||
|
||||
|
@ -108,6 +109,11 @@ struct io_pgtable_cfg {
|
|||
u32 nmrr;
|
||||
u32 prrr;
|
||||
} arm_v7s_cfg;
|
||||
|
||||
struct {
|
||||
u64 transtab;
|
||||
u64 memattr;
|
||||
} arm_mali_lpae_cfg;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -209,5 +215,6 @@ extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns;
|
|||
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
|
||||
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
|
||||
extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
|
||||
extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
|
||||
|
||||
#endif /* __IO_PGTABLE_H */
|
||||
|
|
Loading…
Reference in New Issue