Skip to content

Commit 57c3621

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 64a1493 commit 57c3621

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
/*
@@ -386,6 +388,82 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
386388
apple_dart_hw_clear_ttbr(stream_map, i);
387389
}
388390

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

511591
for_each_stream_map(i, domain, domain_stream_map) {
512592
stream_map.dart = domain_stream_map->dart;
@@ -515,6 +595,10 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
515595
stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]);
516596

517597
WARN_ON(pm_runtime_get_sync(stream_map.dart->dev) < 0);
598+
599+
if (stream_map.dart->locked)
600+
apple_dart_hw_sync_locked(pgtbl_cfg, &stream_map);
601+
518602
stream_map.dart->hw->invalidate_tlb(&stream_map);
519603
pm_runtime_put(stream_map.dart->dev);
520604
}
@@ -597,6 +681,24 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
597681
stream_map->dart->hw->invalidate_tlb(stream_map);
598682
}
599683

684+
static void
685+
apple_dart_setup_translation_locked(struct apple_dart_domain *domain,
686+
struct apple_dart_stream_map *stream_map)
687+
{
688+
int i;
689+
struct io_pgtable_cfg *pgtbl_cfg =
690+
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
691+
692+
/* Locked DARTs are set up by the bootloader. */
693+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
694+
apple_dart_hw_map_locked_ttbr(stream_map, i);
695+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
696+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
697+
698+
apple_dart_hw_sync_locked(pgtbl_cfg, stream_map);
699+
stream_map->dart->hw->invalidate_tlb(stream_map);
700+
}
701+
600702
static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
601703
struct apple_dart_master_cfg *cfg)
602704
{
@@ -630,6 +732,42 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
630732
.iommu_dev = dart->dev,
631733
};
632734

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

714-
for_each_stream_map(i, cfg, stream_map)
715-
apple_dart_setup_translation(dart_domain, stream_map);
852+
for_each_stream_map(i, cfg, stream_map) {
853+
if (!stream_map->dart->locked)
854+
apple_dart_setup_translation(dart_domain, stream_map);
855+
else
856+
apple_dart_setup_translation_locked(dart_domain,
857+
stream_map);
858+
}
716859

717860
err:
718861
for_each_stream_map(i, cfg, stream_map)
@@ -798,8 +941,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
798941

799942
static void apple_dart_release_device(struct device *dev)
800943
{
944+
int i, j;
945+
struct apple_dart_stream_map *stream_map;
801946
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
802947

948+
for_each_stream_map(j, cfg, stream_map) {
949+
if (stream_map->dart->locked)
950+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
951+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
952+
}
953+
803954
kfree(cfg);
804955
}
805956

@@ -1078,6 +1229,11 @@ static const struct iommu_ops apple_dart_iommu_no_bypass_ops = {
10781229
APPLE_DART_IOMMU_COMMON_OPS()
10791230
};
10801231

1232+
static const struct iommu_ops apple_dart_iommu_locked_ops = {
1233+
.def_domain_type = apple_dart_def_domain_type_dma,
1234+
APPLE_DART_IOMMU_COMMON_OPS()
1235+
};
1236+
10811237
static irqreturn_t apple_dart_t8020_irq(int irq, void *dev)
10821238
{
10831239
struct apple_dart *dart = dev;
@@ -1287,7 +1443,9 @@ static int apple_dart_probe(struct platform_device *pdev)
12871443
if (ret)
12881444
goto err_free_irq;
12891445

1290-
if (!dart->supports_bypass)
1446+
if (dart->locked)
1447+
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_locked_ops, dev);
1448+
else if (!dart->supports_bypass)
12911449
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_no_bypass_ops, dev);
12921450
else
12931451
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_ops, dev);

0 commit comments

Comments
 (0)