iommu/io-pgtable-dart: Add 4-level page table support

DARTs on t602x SoCs are of the t8110 variant but have an IAS of 42,
which means optional support for an extra page table level.

Refactor the PTE management to support an arbitrary level count, and
then calculate how many levels we need for any given configuration.

Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Janne Grunau <j@jannau.net>
Reviewed-by: Sven Peter <sven@kernel.org>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Link: https://lore.kernel.org/r/20250821-apple-dart-4levels-v2-2-e39af79daa37@jannau.net
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
pull/1354/merge
Hector Martin 2025-08-21 12:15:59 +02:00 committed by Joerg Roedel
parent 1268890086
commit 74a0e72f03
2 changed files with 89 additions and 55 deletions

View File

@ -27,8 +27,9 @@
#define DART1_MAX_ADDR_BITS 36
#define DART_MAX_TABLES 4
#define DART_LEVELS 2
#define DART_MAX_TABLE_BITS 2
#define DART_MAX_TABLES BIT(DART_MAX_TABLE_BITS)
#define DART_MAX_LEVELS 4 /* Includes TTBR level */
/* Struct accessors */
#define io_pgtable_to_data(x) \
@ -68,6 +69,7 @@
struct dart_io_pgtable {
struct io_pgtable iop;
int levels;
int tbl_bits;
int bits_per_level;
@ -156,44 +158,45 @@ static dart_iopte dart_install_table(dart_iopte *table,
return old;
}
static int dart_get_table(struct dart_io_pgtable *data, unsigned long iova)
static int dart_get_index(struct dart_io_pgtable *data, unsigned long iova, int level)
{
return (iova >> (3 * data->bits_per_level + ilog2(sizeof(dart_iopte)))) &
((1 << data->tbl_bits) - 1);
}
static int dart_get_l1_index(struct dart_io_pgtable *data, unsigned long iova)
{
return (iova >> (2 * data->bits_per_level + ilog2(sizeof(dart_iopte)))) &
return (iova >> (level * data->bits_per_level + ilog2(sizeof(dart_iopte)))) &
((1 << data->bits_per_level) - 1);
}
static int dart_get_l2_index(struct dart_io_pgtable *data, unsigned long iova)
static int dart_get_last_index(struct dart_io_pgtable *data, unsigned long iova)
{
return (iova >> (data->bits_per_level + ilog2(sizeof(dart_iopte)))) &
((1 << data->bits_per_level) - 1);
}
static dart_iopte *dart_get_l2(struct dart_io_pgtable *data, unsigned long iova)
static dart_iopte *dart_get_last(struct dart_io_pgtable *data, unsigned long iova)
{
dart_iopte pte, *ptep;
int tbl = dart_get_table(data, iova);
int level = data->levels;
int tbl = dart_get_index(data, iova, level);
if (tbl > (1 << data->tbl_bits))
return NULL;
ptep = data->pgd[tbl];
if (!ptep)
return NULL;
ptep += dart_get_l1_index(data, iova);
while (--level > 1) {
ptep += dart_get_index(data, iova, level);
pte = READ_ONCE(*ptep);
/* Valid entry? */
if (!pte)
return NULL;
/* Deref to get level 2 table */
return iopte_deref(pte, data);
/* Deref to get next level table */
ptep = iopte_deref(pte, data);
}
return ptep;
}
static dart_iopte dart_prot_to_pte(struct dart_io_pgtable *data,
@ -230,6 +233,7 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
int ret = 0, tbl, num_entries, max_entries, map_idx_start;
dart_iopte pte, *cptep, *ptep;
dart_iopte prot;
int level = data->levels;
if (WARN_ON(pgsize != cfg->pgsize_bitmap))
return -EINVAL;
@ -240,13 +244,17 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
return -EINVAL;
tbl = dart_get_table(data, iova);
tbl = dart_get_index(data, iova, level);
if (tbl > (1 << data->tbl_bits))
return -ENOMEM;
ptep = data->pgd[tbl];
ptep += dart_get_l1_index(data, iova);
while (--level > 1) {
ptep += dart_get_index(data, iova, level);
pte = READ_ONCE(*ptep);
/* no L2 table present */
/* no table present */
if (!pte) {
cptep = iommu_alloc_pages_sz(gfp, tblsz);
if (!cptep)
@ -261,10 +269,11 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
}
ptep = iopte_deref(pte, data);
}
/* install a leaf entries into L2 table */
prot = dart_prot_to_pte(data, iommu_prot);
map_idx_start = dart_get_l2_index(data, iova);
map_idx_start = dart_get_last_index(data, iova);
max_entries = DART_PTES_PER_TABLE(data) - map_idx_start;
num_entries = min_t(int, pgcount, max_entries);
ptep += map_idx_start;
@ -293,13 +302,13 @@ static size_t dart_unmap_pages(struct io_pgtable_ops *ops, unsigned long iova,
if (WARN_ON(pgsize != cfg->pgsize_bitmap || !pgcount))
return 0;
ptep = dart_get_l2(data, iova);
ptep = dart_get_last(data, iova);
/* Valid L2 IOPTE pointer? */
if (WARN_ON(!ptep))
return 0;
unmap_idx_start = dart_get_l2_index(data, iova);
unmap_idx_start = dart_get_last_index(data, iova);
ptep += unmap_idx_start;
max_entries = DART_PTES_PER_TABLE(data) - unmap_idx_start;
@ -330,13 +339,13 @@ static phys_addr_t dart_iova_to_phys(struct io_pgtable_ops *ops,
struct dart_io_pgtable *data = io_pgtable_ops_to_data(ops);
dart_iopte pte, *ptep;
ptep = dart_get_l2(data, iova);
ptep = dart_get_last(data, iova);
/* Valid L2 IOPTE pointer? */
if (!ptep)
return 0;
ptep += dart_get_l2_index(data, iova);
ptep += dart_get_last_index(data, iova);
pte = READ_ONCE(*ptep);
/* Found translation */
@ -353,21 +362,37 @@ static struct dart_io_pgtable *
dart_alloc_pgtable(struct io_pgtable_cfg *cfg)
{
struct dart_io_pgtable *data;
int tbl_bits, bits_per_level, va_bits, pg_shift;
int levels, max_tbl_bits, tbl_bits, bits_per_level, va_bits, pg_shift;
/*
* Old 4K page DARTs can use up to 4 top-level tables.
* Newer ones only ever use a maximum of 1.
*/
if (cfg->pgsize_bitmap == SZ_4K)
max_tbl_bits = DART_MAX_TABLE_BITS;
else
max_tbl_bits = 0;
pg_shift = __ffs(cfg->pgsize_bitmap);
bits_per_level = pg_shift - ilog2(sizeof(dart_iopte));
va_bits = cfg->ias - pg_shift;
tbl_bits = max_t(int, 0, va_bits - (bits_per_level * DART_LEVELS));
if ((1 << tbl_bits) > DART_MAX_TABLES)
levels = max_t(int, 2, (va_bits - max_tbl_bits + bits_per_level - 1) / bits_per_level);
if (levels > (DART_MAX_LEVELS - 1))
return NULL;
tbl_bits = max_t(int, 0, va_bits - (bits_per_level * levels));
if (tbl_bits > max_tbl_bits)
return NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
data->levels = levels + 1; /* Table level counts as one level */
data->tbl_bits = tbl_bits;
data->bits_per_level = bits_per_level;
@ -403,6 +428,7 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
return NULL;
cfg->apple_dart_cfg.n_ttbrs = 1 << data->tbl_bits;
cfg->apple_dart_cfg.n_levels = data->levels;
for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i) {
data->pgd[i] =
@ -422,24 +448,31 @@ out_free_data:
return NULL;
}
static void apple_dart_free_pgtable(struct io_pgtable *iop)
static void apple_dart_free_pgtables(struct dart_io_pgtable *data, dart_iopte *ptep, int level)
{
struct dart_io_pgtable *data = io_pgtable_to_data(iop);
dart_iopte *ptep, *end;
int i;
dart_iopte *end;
dart_iopte *start = ptep;
for (i = 0; i < (1 << data->tbl_bits) && data->pgd[i]; ++i) {
ptep = data->pgd[i];
if (level > 1) {
end = (void *)ptep + DART_GRANULE(data);
while (ptep != end) {
dart_iopte pte = *ptep++;
if (pte)
iommu_free_pages(iopte_deref(pte, data));
apple_dart_free_pgtables(data, iopte_deref(pte, data), level - 1);
}
iommu_free_pages(data->pgd[i]);
}
iommu_free_pages(start);
}
static void apple_dart_free_pgtable(struct io_pgtable *iop)
{
struct dart_io_pgtable *data = io_pgtable_to_data(iop);
int i;
for (i = 0; i < (1 << data->tbl_bits) && data->pgd[i]; ++i)
apple_dart_free_pgtables(data, data->pgd[i], data->levels - 1);
kfree(data);
}

View File

@ -180,6 +180,7 @@ struct io_pgtable_cfg {
struct {
u64 ttbr[4];
u32 n_ttbrs;
u32 n_levels;
} apple_dart_cfg;
struct {