Skip to content

Commit 633e1be

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 0e776de commit 633e1be

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) {
@@ -710,8 +848,13 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
710848
if (ret)
711849
goto err;
712850

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

716859
err:
717860
for_each_stream_map(i, cfg, stream_map)
@@ -795,8 +938,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
795938

796939
static void apple_dart_release_device(struct device *dev)
797940
{
941+
int i, j;
942+
struct apple_dart_stream_map *stream_map;
798943
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
799944

945+
for_each_stream_map(j, cfg, stream_map) {
946+
if (stream_map->dart->locked)
947+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
948+
apple_dart_hw_unmap_locked_ttbr(stream_map, i);
949+
}
950+
800951
kfree(cfg);
801952
}
802953

@@ -1073,6 +1224,11 @@ static const struct iommu_ops apple_dart_iommu_no_bypass_ops = {
10731224
APPLE_DART_IOMMU_COMMON_OPS()
10741225
};
10751226

1227+
static const struct iommu_ops apple_dart_iommu_locked_ops = {
1228+
.def_domain_type = apple_dart_def_domain_type_dma,
1229+
APPLE_DART_IOMMU_COMMON_OPS()
1230+
};
1231+
10761232
static irqreturn_t apple_dart_t8020_irq(int irq, void *dev)
10771233
{
10781234
struct apple_dart *dart = dev;
@@ -1282,7 +1438,9 @@ static int apple_dart_probe(struct platform_device *pdev)
12821438
if (ret)
12831439
goto err_free_irq;
12841440

1285-
if (!dart->supports_bypass)
1441+
if (dart->locked)
1442+
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_locked_ops, dev);
1443+
else if (!dart->supports_bypass)
12861444
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_no_bypass_ops, dev);
12871445
else
12881446
ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_ops, dev);

0 commit comments

Comments
 (0)