Skip to content

Commit 988ffc8

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 c56a808 commit 988ffc8

1 file changed

Lines changed: 161 additions & 3 deletions

File tree

drivers/iommu/apple-dart.c

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ struct apple_dart {
235235

236236
u32 save_tcr[DART_MAX_STREAMS];
237237
u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
238+
239+
u64 *locked_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
238240
};
239241

240242
/*
@@ -383,6 +385,82 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
383385
apple_dart_hw_clear_ttbr(stream_map, i);
384386
}
385387

388+
static int
389+
apple_dart_hw_map_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx)
390+
{
391+
struct apple_dart *dart = stream_map->dart;
392+
int sid;
393+
394+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
395+
u32 ttbr;
396+
phys_addr_t phys;
397+
u64 *l1_tbl;
398+
399+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, idx));
400+
401+
if (!(ttbr & dart->hw->ttbr_valid)) {
402+
dev_err(dart->dev, "Invalid ttbr[%u] for locked dart\n",
403+
idx);
404+
return -EIO;
405+
}
406+
407+
ttbr &= ~dart->hw->ttbr_valid;
408+
409+
if (dart->hw->ttbr_addr_field_shift)
410+
ttbr >>= dart->hw->ttbr_addr_field_shift;
411+
phys = ((phys_addr_t) ttbr) << dart->hw->ttbr_shift;
412+
413+
l1_tbl = devm_memremap(dart->dev, phys, dart->pgsize,
414+
MEMREMAP_WB);
415+
if (!l1_tbl)
416+
return -ENOMEM;
417+
418+
dart->locked_ttbr[sid][idx] = l1_tbl;
419+
}
420+
421+
return 0;
422+
}
423+
424+
static int
425+
apple_dart_hw_unmap_locked_ttbr(struct apple_dart_stream_map *stream_map,
426+
u8 idx)
427+
{
428+
struct apple_dart *dart = stream_map->dart;
429+
int sid;
430+
431+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
432+
/* TODO: locked L1 table might need to be restored to boot state */
433+
if (dart->locked_ttbr[sid][idx]) {
434+
memset(dart->locked_ttbr[sid][idx], 0, dart->pgsize);
435+
devm_memunmap(dart->dev, dart->locked_ttbr[sid][idx]);
436+
}
437+
dart->locked_ttbr[sid][idx] = NULL;
438+
}
439+
440+
return 0;
441+
}
442+
443+
static int
444+
apple_dart_hw_sync_locked(struct io_pgtable_cfg *cfg,
445+
struct apple_dart_stream_map *stream_map)
446+
{
447+
struct apple_dart *dart = stream_map->dart;
448+
int sid;
449+
450+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
451+
for (int idx = 0; idx < dart->hw->ttbr_count; idx++) {
452+
u64 *ttbrep = dart->locked_ttbr[sid][idx];
453+
u64 *ptep = cfg->apple_dart_cfg.ttbr[idx];
454+
if (!ttbrep || !ptep)
455+
continue;
456+
for (int entry = 0; entry < dart->pgsize / sizeof(*ptep); entry++)
457+
ttbrep[entry] = ptep[entry];
458+
}
459+
}
460+
461+
return 0;
462+
}
463+
386464
static int
387465
apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map,
388466
u32 command)
@@ -504,6 +582,8 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
504582
int i, j;
505583
struct apple_dart_atomic_stream_map *domain_stream_map;
506584
struct apple_dart_stream_map stream_map;
585+
struct io_pgtable_cfg *pgtbl_cfg =
586+
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
507587

508588
for_each_stream_map(i, domain, domain_stream_map) {
509589
stream_map.dart = domain_stream_map->dart;
@@ -512,6 +592,10 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
512592
stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]);
513593

514594
WARN_ON(pm_runtime_get_sync(stream_map.dart->dev) < 0);
595+
596+
if (stream_map.dart->locked)
597+
apple_dart_hw_sync_locked(pgtbl_cfg, &stream_map);
598+
515599
stream_map.dart->hw->invalidate_tlb(&stream_map);
516600
pm_runtime_put(stream_map.dart->dev);
517601
}
@@ -594,6 +678,24 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
594678
stream_map->dart->hw->invalidate_tlb(stream_map);
595679
}
596680

681+
static void
682+
apple_dart_setup_translation_locked(struct apple_dart_domain *domain,
683+
struct apple_dart_stream_map *stream_map)
684+
{
685+
int i;
686+
struct io_pgtable_cfg *pgtbl_cfg =
687+
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
688+
689+
/* Locked DARTs are set up by the bootloader. */
690+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
691+
apple_dart_hw_map_locked_ttbr(stream_map, i);
692+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
693+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
694+
695+
apple_dart_hw_sync_locked(pgtbl_cfg, stream_map);
696+
stream_map->dart->hw->invalidate_tlb(stream_map);
697+
}
698+
597699
static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
598700
struct apple_dart_master_cfg *cfg)
599701
{
@@ -627,6 +729,42 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
627729
.iommu_dev = dart->dev,
628730
};
629731

732+
if (dart->locked) {
733+
unsigned long *sidmap;
734+
int sid;
735+
u32 ttbr;
736+
737+
/* Locked DARTs can only have a single stream bound */
738+
sidmap = cfg->stream_maps[0].sidmap;
739+
sid = find_first_bit(sidmap, dart->num_streams);
740+
741+
WARN_ON((sid < 0) || bitmap_weight(sidmap, dart->num_streams) > 1);
742+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0));
743+
744+
WARN_ON(!(ttbr & dart->hw->ttbr_valid));
745+
746+
/* If the DART is locked, we need to keep the translation level count. */
747+
if (dart->hw->tcr_4level && dart->ias > 36) {
748+
if (readl(dart->regs + DART_TCR(dart, sid)) & dart->hw->tcr_4level) {
749+
if (ias < 37) {
750+
dev_info(dart->dev, "Expanded to ias=37 due to lock\n");
751+
pgtbl_cfg.ias = 37;
752+
}
753+
} else if (ias > 36) {
754+
dev_info(dart->dev, "Limited to ias=36 due to lock\n");
755+
pgtbl_cfg.ias = 36;
756+
if (dart->dma_min == 0 && dma_max == DMA_BIT_MASK(dart->ias)) {
757+
dma_max = DMA_BIT_MASK(pgtbl_cfg.ias);
758+
} else if ((dart->dma_min ^ dma_max) & ~DMA_BIT_MASK(36)) {
759+
dev_err(dart->dev,
760+
"Invalid DMA range for locked 3-level PT\n");
761+
ret = -ENOMEM;
762+
goto done;
763+
}
764+
}
765+
}
766+
}
767+
630768
dart_domain->pgtbl_ops = alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg,
631769
&dart_domain->domain);
632770
if (!dart_domain->pgtbl_ops) {
@@ -707,8 +845,13 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
707845
if (ret)
708846
goto err;
709847

710-
for_each_stream_map(i, cfg, stream_map)
711-
apple_dart_setup_translation(dart_domain, stream_map);
848+
for_each_stream_map(i, cfg, stream_map) {
849+
if (!stream_map->dart->locked)
850+
apple_dart_setup_translation(dart_domain, stream_map);
851+
else
852+
apple_dart_setup_translation_locked(dart_domain,
853+
stream_map);
854+
}
712855

713856
err:
714857
for_each_stream_map(i, cfg, stream_map)
@@ -792,8 +935,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
792935

793936
static void apple_dart_release_device(struct device *dev)
794937
{
938+
int i, j;
939+
struct apple_dart_stream_map *stream_map;
795940
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
796941

942+
for_each_stream_map(j, cfg, stream_map) {
943+
if (stream_map->dart->locked)
944+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
945+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
946+
}
947+
797948
kfree(cfg);
798949
}
799950

@@ -1070,6 +1221,11 @@ static const struct iommu_ops apple_dart_iommu_no_bypass_ops = {
10701221
APPLE_DART_IOMMU_COMMON_OPS()
10711222
};
10721223

1224+
static const struct iommu_ops apple_dart_iommu_locked_ops = {
1225+
.def_domain_type = apple_dart_def_domain_type_dma,
1226+
APPLE_DART_IOMMU_COMMON_OPS()
1227+
};
1228+
10731229
static irqreturn_t apple_dart_t8020_irq(int irq, void *dev)
10741230
{
10751231
struct apple_dart *dart = dev;
@@ -1280,7 +1436,9 @@ static int apple_dart_probe(struct platform_device *pdev)
12801436
if (ret)
12811437
goto err_free_irq;
12821438

1283-
if (!dart->supports_bypass)
1439+
if (dart->locked)
1440+
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_locked_ops, dev);
1441+
else if (!dart->supports_bypass)
12841442
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_no_bypass_ops, dev);
12851443
else
12861444
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_ops, dev);

0 commit comments

Comments
 (0)