Skip to content

Commit 64f7c48

Browse files
committed
iommu/dart: Add iommu_ops for locked DARTs
A locked DART has partially read-only MMIO registers. Most importantly the TTBR registers are read-only. Apple's bootloader sets the DART up for its intended use before locking it. The single used streams has a L1 translation table allocated in carved out memory and its TTBRs point to this table. In addition translation and bypass can not be disabled or enabled so a locked DART must not offer default identity or blocked domains. The only observed locked DART is for the display coprocessor. It requires careful handling as translation errors result in unrecoverable crashes of the display coprocessor. Signed-off-by: Janne Grunau <j@jannau.net>
1 parent e57db50 commit 64f7c48

1 file changed

Lines changed: 140 additions & 3 deletions

File tree

drivers/iommu/apple-dart.c

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ struct apple_dart {
225225

226226
u32 save_tcr[DART_MAX_STREAMS];
227227
u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
228+
229+
u64 *locked_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
228230
};
229231

230232
/*
@@ -369,6 +371,82 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
369371
apple_dart_hw_clear_ttbr(stream_map, i);
370372
}
371373

374+
static int
375+
apple_dart_hw_map_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx)
376+
{
377+
struct apple_dart *dart = stream_map->dart;
378+
int sid;
379+
380+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
381+
u32 ttbr;
382+
phys_addr_t phys;
383+
u64 *l1_tbl;
384+
385+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, idx));
386+
387+
if (!(ttbr & dart->hw->ttbr_valid)) {
388+
dev_err(dart->dev, "Invalid ttbr[%u] for locked dart\n",
389+
idx);
390+
return -EIO;
391+
}
392+
393+
ttbr &= ~dart->hw->ttbr_valid;
394+
395+
if (dart->hw->ttbr_addr_field_shift)
396+
ttbr >>= dart->hw->ttbr_addr_field_shift;
397+
phys = ((phys_addr_t) ttbr) << dart->hw->ttbr_shift;
398+
399+
l1_tbl = devm_memremap(dart->dev, phys, dart->pgsize,
400+
MEMREMAP_WB);
401+
if (!l1_tbl)
402+
return -ENOMEM;
403+
404+
dart->locked_ttbr[sid][idx] = l1_tbl;
405+
}
406+
407+
return 0;
408+
}
409+
410+
static int
411+
apple_dart_hw_unmap_locked_ttbr(struct apple_dart_stream_map *stream_map,
412+
u8 idx)
413+
{
414+
struct apple_dart *dart = stream_map->dart;
415+
int sid;
416+
417+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
418+
/* TODO: locked L1 table might need to be restored to boot state */
419+
if (dart->locked_ttbr[sid][idx]) {
420+
memset(dart->locked_ttbr[sid][idx], 0, dart->pgsize);
421+
devm_memunmap(dart->dev, dart->locked_ttbr[sid][idx]);
422+
}
423+
dart->locked_ttbr[sid][idx] = NULL;
424+
}
425+
426+
return 0;
427+
}
428+
429+
static int
430+
apple_dart_hw_sync_locked(struct io_pgtable_cfg *cfg,
431+
struct apple_dart_stream_map *stream_map)
432+
{
433+
struct apple_dart *dart = stream_map->dart;
434+
int sid;
435+
436+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
437+
for (int idx = 0; idx < dart->hw->ttbr_count; idx++) {
438+
u64 *ttbrep = dart->locked_ttbr[sid][idx];
439+
u64 *ptep = cfg->apple_dart_cfg.ttbr[idx];
440+
if (!ttbrep || !ptep)
441+
continue;
442+
for (int entry = 0; entry < dart->pgsize / sizeof(*ptep); entry++)
443+
ttbrep[entry] = ptep[entry];
444+
}
445+
}
446+
447+
return 0;
448+
}
449+
372450
static int
373451
apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map,
374452
u32 command)
@@ -490,13 +568,18 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
490568
int i, j;
491569
struct apple_dart_atomic_stream_map *domain_stream_map;
492570
struct apple_dart_stream_map stream_map;
571+
struct io_pgtable_cfg *pgtbl_cfg =
572+
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
493573

494574
for_each_stream_map(i, domain, domain_stream_map) {
495575
stream_map.dart = domain_stream_map->dart;
496576

497577
for (j = 0; j < BITS_TO_LONGS(stream_map.dart->num_streams); j++)
498578
stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]);
499579

580+
if (stream_map.dart->locked)
581+
apple_dart_hw_sync_locked(pgtbl_cfg, &stream_map);
582+
500583
stream_map.dart->hw->invalidate_tlb(&stream_map);
501584
}
502585
}
@@ -576,6 +659,24 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
576659
stream_map->dart->hw->invalidate_tlb(stream_map);
577660
}
578661

662+
static void
663+
apple_dart_setup_translation_locked(struct apple_dart_domain *domain,
664+
struct apple_dart_stream_map *stream_map)
665+
{
666+
int i;
667+
struct io_pgtable_cfg *pgtbl_cfg =
668+
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
669+
670+
/* Locked DARTs are set up by the bootloader. */
671+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
672+
apple_dart_hw_map_locked_ttbr(stream_map, i);
673+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
674+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
675+
676+
apple_dart_hw_sync_locked(pgtbl_cfg, stream_map);
677+
stream_map->dart->hw->invalidate_tlb(stream_map);
678+
}
679+
579680
static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
580681
struct apple_dart_master_cfg *cfg)
581682
{
@@ -607,6 +708,21 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
607708
.iommu_dev = dart->dev,
608709
};
609710

711+
if (dart->locked) {
712+
unsigned long *sidmap;
713+
int sid;
714+
u32 ttbr;
715+
716+
/* Locked DARTs can only have a single stream bound */
717+
sidmap = cfg->stream_maps[0].sidmap;
718+
sid = find_first_bit(sidmap, dart->num_streams);
719+
720+
WARN_ON((sid < 0) || bitmap_weight(sidmap, dart->num_streams) > 1);
721+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0));
722+
723+
WARN_ON(!(ttbr & dart->hw->ttbr_valid));
724+
}
725+
610726
dart_domain->pgtbl_ops = alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg,
611727
&dart_domain->domain);
612728
if (!dart_domain->pgtbl_ops) {
@@ -678,8 +794,14 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
678794
if (ret)
679795
return ret;
680796

681-
for_each_stream_map(i, cfg, stream_map)
682-
apple_dart_setup_translation(dart_domain, stream_map);
797+
for_each_stream_map(i, cfg, stream_map) {
798+
if (!stream_map->dart->locked)
799+
apple_dart_setup_translation(dart_domain, stream_map);
800+
else
801+
apple_dart_setup_translation_locked(dart_domain,
802+
stream_map);
803+
}
804+
683805
return 0;
684806
}
685807

@@ -747,8 +869,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
747869

748870
static void apple_dart_release_device(struct device *dev)
749871
{
872+
int i, j;
873+
struct apple_dart_stream_map *stream_map;
750874
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
751875

876+
for_each_stream_map(j, cfg, stream_map) {
877+
if (stream_map->dart->locked)
878+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
879+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
880+
}
881+
752882
kfree(cfg);
753883
}
754884

@@ -1025,6 +1155,11 @@ static const struct iommu_ops apple_dart_iommu_no_bypass_ops = {
10251155
APPLE_DART_IOMMU_COMMON_OPS()
10261156
};
10271157

1158+
static const struct iommu_ops apple_dart_iommu_locked_ops = {
1159+
.def_domain_type = apple_dart_def_domain_type_dma,
1160+
APPLE_DART_IOMMU_COMMON_OPS()
1161+
};
1162+
10281163
static irqreturn_t apple_dart_t8020_irq(int irq, void *dev)
10291164
{
10301165
struct apple_dart *dart = dev;
@@ -1190,7 +1325,9 @@ static int apple_dart_probe(struct platform_device *pdev)
11901325
if (ret)
11911326
goto err_free_irq;
11921327

1193-
if (!dart->supports_bypass)
1328+
if (dart->locked)
1329+
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_locked_ops, dev);
1330+
else if (!dart->supports_bypass)
11941331
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_no_bypass_ops, dev);
11951332
else
11961333
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_ops, dev);

0 commit comments

Comments
 (0)