Skip to content

Commit b94ca3c

Browse files
jannaumarcan
authored andcommitted
iommu: apple-dart: Install IOMMU_RESV_TRANSLATED mappings
[insert rant about locked TTBR] The iommus for the display processors on Apple silicon machines have locked TTBR registers. This makes it a little akward to install the boot loader's translated reserved mappings on iommu domain init. Handle this via a shadow L1 pgtable apple_dart_alloc_pgtable(). apple-dart will after that map the reserved regions and let io-pgtable-dart transfer the shadow L1 table to the locked HW L1 table. This has to be done this way since HW L1 table still holds the bootloader allocated L2 tables. Switching the L2 PTE has to happen atomically since the display processor is the whole time active with scan out of the boot frame buffer. TODO: find a less hacky way for upstream Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 891eae8 commit b94ca3c

4 files changed

Lines changed: 102 additions & 16 deletions

File tree

drivers/iommu/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ config APPLE_DART
311311
depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_DART
312312
select IOMMU_API
313313
select IOMMU_IO_PGTABLE_DART
314+
select OF_IOMMU
314315
default ARCH_APPLE
315316
help
316317
Support for Apple DART (Device Address Resolution Table) IOMMUs

drivers/iommu/apple-dart.c

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/types.h>
3434

3535
#include "dma-iommu.h"
36+
#include "io-pgtable-dart.h"
3637

3738
#define DART_MAX_STREAMS 256
3839
#define DART_MAX_TTBR 4
@@ -557,17 +558,55 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
557558
struct io_pgtable_cfg *pgtbl_cfg =
558559
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
559560

560-
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
561-
apple_dart_hw_set_ttbr(stream_map, i,
562-
pgtbl_cfg->apple_dart_cfg.ttbr[i]);
563-
for (; i < stream_map->dart->hw->ttbr_count; ++i)
564-
apple_dart_hw_clear_ttbr(stream_map, i);
561+
/* Locked DARTs are set up by the bootloader. */
562+
if (!stream_map->dart->locked) {
563+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
564+
apple_dart_hw_set_ttbr(stream_map, i,
565+
pgtbl_cfg->apple_dart_cfg.ttbr[i]);
566+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
567+
apple_dart_hw_clear_ttbr(stream_map, i);
565568

566-
apple_dart_hw_enable_translation(stream_map);
569+
apple_dart_hw_enable_translation(stream_map);
570+
}
567571
stream_map->dart->hw->invalidate_tlb(stream_map);
568572
}
569573

574+
static int apple_dart_setup_resv_locked(struct iommu_domain *domain,
575+
struct device *dev, size_t pgsize)
576+
{
577+
struct iommu_resv_region *region;
578+
LIST_HEAD(resv_regions);
579+
int ret = 0;
580+
581+
of_iommu_get_resv_regions(dev, &resv_regions);
582+
list_for_each_entry(region, &resv_regions, list) {
583+
size_t mapped = 0;
584+
585+
/* Only map translated reserved regions */
586+
if (region->type != IOMMU_RESV_TRANSLATED)
587+
continue;
588+
589+
while (mapped < region->length) {
590+
phys_addr_t paddr = region->start + mapped;
591+
unsigned long iova = region->dva + mapped;
592+
size_t length = region->length - mapped;
593+
size_t pgcount = length / pgsize;
594+
595+
ret = apple_dart_map_pages(domain, iova,
596+
paddr, pgsize, pgcount,
597+
region->prot, GFP_KERNEL, &mapped);
598+
599+
if (ret)
600+
goto end_put;
601+
}
602+
}
603+
end_put:
604+
iommu_put_resv_regions(dev, &resv_regions);
605+
return ret;
606+
}
607+
570608
static int apple_dart_finalize_domain(struct iommu_domain *domain,
609+
struct device *dev,
571610
struct apple_dart_master_cfg *cfg)
572611
{
573612
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
@@ -633,6 +672,11 @@ static int apple_dart_finalize_domain(struct iommu_domain *domain,
633672

634673
dart_domain->finalized = true;
635674

675+
if (dart->locked) {
676+
/* TODO: error handling */
677+
ret = apple_dart_setup_resv_locked(domain, dev, dart->pgsize);
678+
io_pgtable_dart_setup_locked(dart_domain->pgtbl_ops);
679+
}
636680
done:
637681
mutex_unlock(&dart_domain->init_lock);
638682
return ret;
@@ -689,7 +733,7 @@ static int apple_dart_attach_dev(struct iommu_domain *domain,
689733
if (dart0->locked && domain->type != IOMMU_DOMAIN_DMA)
690734
return -EINVAL;
691735

692-
ret = apple_dart_finalize_domain(domain, cfg);
736+
ret = apple_dart_finalize_domain(domain, dev, cfg);
693737
if (ret)
694738
return ret;
695739

drivers/iommu/io-pgtable-dart.c

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/atomic.h>
1717
#include <linux/bitfield.h>
1818
#include <linux/bitops.h>
19+
#include "linux/export.h"
1920
#include <linux/io.h>
2021
#include <linux/io-pgtable.h>
2122
#include <linux/kernel.h>
@@ -25,6 +26,8 @@
2526

2627
#include <asm/barrier.h>
2728

29+
#include "io-pgtable-dart.h"
30+
2831
#define DART1_MAX_ADDR_BITS 36
2932

3033
#define DART_MAX_TABLES 4
@@ -363,6 +366,32 @@ static phys_addr_t dart_iova_to_phys(struct io_pgtable_ops *ops,
363366
return 0;
364367
}
365368

369+
int io_pgtable_dart_setup_locked(struct io_pgtable_ops *ops)
370+
{
371+
void *l1tbl;
372+
struct dart_io_pgtable *data = io_pgtable_ops_to_data(ops);
373+
struct io_pgtable_cfg *cfg = &data->iop.cfg;
374+
size_t size;
375+
376+
if (!(cfg->quirks & IO_PGTABLE_QUIRK_APPLE_LOCKED))
377+
return 0;
378+
379+
size = cfg->pgsize_bitmap;
380+
l1tbl = devm_memremap(cfg->iommu_dev, cfg->apple_dart_cfg.ttbr[0], size,
381+
MEMREMAP_WB);
382+
if (!l1tbl)
383+
return -ENOMEM;
384+
385+
for (int entry = 0; entry < DART_PTES_PER_TABLE(data); entry++)
386+
((dart_iopte *)l1tbl)[entry] = ((dart_iopte *)data->pgd[0])[entry];
387+
388+
free_pages((unsigned long)data->pgd[0], get_order(DART_GRANULE(data)));
389+
data->pgd[0] = l1tbl;
390+
391+
return 0;
392+
}
393+
EXPORT_SYMBOL(io_pgtable_dart_setup_locked);
394+
366395
static struct dart_io_pgtable *
367396
dart_alloc_pgtable(struct io_pgtable_cfg *cfg)
368397
{
@@ -418,22 +447,19 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
418447

419448
cfg->apple_dart_cfg.n_ttbrs = 1 << data->tbl_bits;
420449

421-
/* Locked DARTs can not modify the TTBR registers. The known locked
422-
* DARTs (dcp, dcpext0) use just a single TTBR so we do not have to
423-
* worry whether the pages are consecutive.
450+
/* Locked DARTs can not modify the TTBR registers. Allocate first a shadow
451+
* page table so locked DARTs (disp0, dcp, dcpext*) can map their reserved
452+
* memory regions. They will be later in io_pgtable_dart_setup_locked()
453+
* copied to the locked L1 table.
424454
*/
425455
if (cfg->quirks & IO_PGTABLE_QUIRK_APPLE_LOCKED) {
426-
size_t size = cfg->pgsize_bitmap;
427456
if (cfg->apple_dart_cfg.n_ttbrs > 1)
428457
goto out_free_data;
429458

430-
data->pgd[0] = devm_memremap(cfg->iommu_dev,
431-
cfg->apple_dart_cfg.ttbr[0],
432-
size, MEMREMAP_WB);
459+
data->pgd[0] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL);
433460
if (!data->pgd[0])
434461
goto out_free_data;
435-
/* start with an empty table */
436-
memset(data->pgd[0], 0, size);
462+
437463
return &data->iop;
438464
}
439465

drivers/iommu/io-pgtable-dart.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Apple DART page table allocator.
4+
*
5+
* Copyright (C) 2022 The Asahi Linux Contributors
6+
*/
7+
8+
/* This will go away on the next iteration of locked DART handling */
9+
10+
#ifndef IO_PGTABLE_DART_H_
11+
#define IO_PGTABLE_DART_H_
12+
13+
int io_pgtable_dart_setup_locked(struct io_pgtable_ops *ops);
14+
15+
#endif /* IO_PGTABLE_DART_H_ */

0 commit comments

Comments
 (0)