Re: [PATCH] iommu: dart: Add missing module owner to ops structure

2022-05-02 Thread Sven Peter
On Mon, May 2, 2022, at 11:22, Hector Martin wrote:
> This is required to make loading this as a module work.
>
> Signed-off-by: Hector Martin 

this could probably use a 
Fixes: 46d1fb072e76 ("iommu/dart: Add DART iommu driver")

but otherwise
Reviewed-by: Sven Peter 


Thanks,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH] iommu/dart: check return value after calling platform_get_resource()

2022-04-26 Thread Sven Peter via iommu
> On 25. Apr 2022, at 10:56, Yang Yingliang  wrote:
> 
> It will cause null-ptr-deref in resource_size(), if platform_get_resource()
> returns NULL, move calling resource_size() after devm_ioremap_resource() that
> will check 'res' to avoid null-ptr-deref.
> And use devm_platform_get_and_ioremap_resource() to simplify code.
> 
> Fixes: 46d1fb072e76 ("iommu/dart: Add DART iommu driver")
> Signed-off-by: Yang Yingliang 

Reviewed-by: Sven Peter 


Thanks,

Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Re: [PATCH 06/13] iommu/dart: Clean up bus_set_iommu()

2022-04-15 Thread Sven Peter via iommu
On Thu, Apr 14, 2022, at 14:42, Robin Murphy wrote:
> Stop calling bus_set_iommu() since it's now unnecessary, and simplify
> the probe failure path accordingly.
>
> Signed-off-by: Robin Murphy 

Tested-by: Sven Peter 
Reviewed-by: Sven Peter 

Can't wait until that saga is completed :)


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] MAINTAINERS: merge DART into ARM/APPLE MACHINE

2022-04-12 Thread Sven Peter via iommu
It's the same people anyway.

Signed-off-by: Sven Peter 
---
 MAINTAINERS | 10 ++
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index fd768d43e048..5af879de869c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1375,14 +1375,6 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c
 
-APPLE DART IOMMU DRIVER
-M: Sven Peter 
-R: Alyssa Rosenzweig 
-L: iommu@lists.linux-foundation.org
-S: Maintained
-F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
-F: drivers/iommu/apple-dart.c
-
 APPLE PCIE CONTROLLER DRIVER
 M: Alyssa Rosenzweig 
 M: Marc Zyngier 
@@ -1836,6 +1828,7 @@ F:Documentation/devicetree/bindings/arm/apple/*
 F: Documentation/devicetree/bindings/clock/apple,nco.yaml
 F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
 F: Documentation/devicetree/bindings/interrupt-controller/apple,*
+F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
 F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
 F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
 F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
@@ -1845,6 +1838,7 @@ F:arch/arm64/boot/dts/apple/
 F: drivers/clk/clk-apple-nco.c
 F: drivers/i2c/busses/i2c-pasemi-core.c
 F: drivers/i2c/busses/i2c-pasemi-platform.c
+F: drivers/iommu/apple-dart.c
 F: drivers/irqchip/irq-apple-aic.c
 F: drivers/mailbox/apple-mailbox.c
 F: drivers/pinctrl/pinctrl-apple-gpio.c
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 4/4] iommu: dart: Support t6000 variant

2021-11-17 Thread Sven Peter via iommu
The M1 Pro/Max SoCs come with a new variant of DART which supports a
larger physical address space with a slightly different PTE format.
Pass through the correct paddr address space size to the io-pgtable code
which will take care of the rest.

Signed-off-by: Sven Peter 
---
 drivers/iommu/apple-dart.c | 19 +--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 565ef5598811..c04648dfd747 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -81,10 +81,15 @@
 #define DART_TTBR_VALID BIT(31)
 #define DART_TTBR_SHIFT 12
 
+struct apple_dart_hw {
+   u32 oas;
+};
+
 /*
  * Private structure associated with each DART device.
  *
  * @dev: device struct
+ * @hw: SoC-specific hardware data
  * @regs: mapped MMIO region
  * @irq: interrupt number, can be shared with other DARTs
  * @clks: clocks associated with this DART
@@ -98,6 +103,7 @@
  */
 struct apple_dart {
struct device *dev;
+   const struct apple_dart_hw *hw;
 
void __iomem *regs;
 
@@ -421,7 +427,7 @@ static int apple_dart_finalize_domain(struct iommu_domain 
*domain,
pgtbl_cfg = (struct io_pgtable_cfg){
.pgsize_bitmap = dart->pgsize,
.ias = 32,
-   .oas = 36,
+   .oas = dart->hw->oas,
.coherent_walk = 1,
.iommu_dev = dart->dev,
};
@@ -855,6 +861,7 @@ static int apple_dart_probe(struct platform_device *pdev)
return -ENOMEM;
 
dart->dev = dev;
+   dart->hw = of_device_get_match_data(dev);
spin_lock_init(&dart->lock);
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -944,8 +951,16 @@ static int apple_dart_remove(struct platform_device *pdev)
return 0;
 }
 
+static const struct apple_dart_hw apple_dart_hw_t8103 = {
+   .oas = 36,
+};
+static const struct apple_dart_hw apple_dart_hw_t6000 = {
+   .oas = 42,
+};
+
 static const struct of_device_id apple_dart_of_match[] = {
-   { .compatible = "apple,t8103-dart", .data = NULL },
+   { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 },
+   { .compatible = "apple,t6000-dart", .data = &apple_dart_hw_t6000 },
{},
 };
 MODULE_DEVICE_TABLE(of, apple_dart_of_match);
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/4] dt-bindings: iommu: dart: add t6000 compatible

2021-11-17 Thread Sven Peter via iommu
The M1 Max/Pro SoCs come with a new DART variant that is incompatible with
the previous one. Add a new compatible for those.

Signed-off-by: Sven Peter 
---
 Documentation/devicetree/bindings/iommu/apple,dart.yaml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
index 94aa9e9afa59..ca2cbde9f3c9 100644
--- a/Documentation/devicetree/bindings/iommu/apple,dart.yaml
+++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
@@ -22,7 +22,9 @@ description: |+
 
 properties:
   compatible:
-const: apple,t8103-dart
+enum:
+  - apple,t8103-dart
+  - apple,t6000-dart
 
   reg:
 maxItems: 1
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 3/4] iommu/io-pgtable: Add DART PTE support for t6000

2021-11-17 Thread Sven Peter via iommu
The DARTs present in the M1 Pro/Max SoC support a 42bit physical address
space by shifting the paddr and extending its mask inside the PTE.

Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 30 +-
 include/linux/io-pgtable.h |  2 ++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index a8c660b8b3e9..be66774aaf70 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -137,6 +137,11 @@
 #define APPLE_DART_PTE_SUBPAGE_START   GENMASK_ULL(63, 52)
 #define APPLE_DART_PTE_SUBPAGE_END GENMASK_ULL(51, 40)
 
+#define APPLE_DART_PADDR_MASK_PS_36BIT GENMASK_ULL(35, 12)
+#define APPLE_DART_PADDR_SHIFT_PS_36BIT(0)
+#define APPLE_DART_PADDR_MASK_PS_42BIT GENMASK_ULL(37, 10)
+#define APPLE_DART_PADDR_SHIFT_PS_42BIT(4)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -171,6 +176,13 @@ static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr,
 {
arm_lpae_iopte pte = paddr;
 
+   if (data->iop.fmt == APPLE_DART) {
+   pte = paddr >> data->iop.cfg.apple_dart_cfg.paddr_shift;
+   pte &= data->iop.cfg.apple_dart_cfg.paddr_mask;
+
+   return pte;
+   }
+
/* Of the bits which overlap, either 51:48 or 15:12 are always RES0 */
return (pte | (pte >> (48 - 12))) & ARM_LPAE_PTE_ADDR_MASK;
 }
@@ -180,6 +192,12 @@ static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
 {
u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK;
 
+   if (data->iop.fmt == APPLE_DART) {
+   paddr = pte & data->iop.cfg.apple_dart_cfg.paddr_mask;
+   paddr <<= data->iop.cfg.apple_dart_cfg.paddr_shift;
+   return paddr;
+   }
+
if (ARM_LPAE_GRANULE(data) < SZ_64K)
return paddr;
 
@@ -1122,8 +1140,18 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
struct arm_lpae_io_pgtable *data;
int i;
 
-   if (cfg->oas > 36)
+   switch (cfg->oas) {
+   case 36:
+   cfg->apple_dart_cfg.paddr_shift = 
APPLE_DART_PADDR_SHIFT_PS_36BIT;
+   cfg->apple_dart_cfg.paddr_mask = APPLE_DART_PADDR_MASK_PS_36BIT;
+   break;
+   case 42:
+   cfg->apple_dart_cfg.paddr_shift = 
APPLE_DART_PADDR_SHIFT_PS_42BIT;
+   cfg->apple_dart_cfg.paddr_mask = APPLE_DART_PADDR_MASK_PS_42BIT;
+   break;
+   default:
return NULL;
+   }
 
data = arm_lpae_alloc_pgtable(cfg);
if (!data)
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index 86af6f0a00a2..4e26ebb0be93 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -136,6 +136,8 @@ struct io_pgtable_cfg {
struct {
u64 ttbr[4];
u32 n_ttbrs;
+   u32 paddr_shift;
+   u64 paddr_mask;
} apple_dart_cfg;
};
 };
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 2/4] iommu/io-pgtable: Add DART subpage protection support

2021-11-17 Thread Sven Peter via iommu
DART allows to only expose a subpage to the device. While this is an
optional feature on the M1 DARTs the new ones present on the Pro/Max
models require this field in every PTE.

Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index dd9e47189d0d..a8c660b8b3e9 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -10,6 +10,7 @@
 #define pr_fmt(fmt)"arm-lpae io-pgtable: " fmt
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -133,6 +134,9 @@
 #define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
 #define APPLE_DART_PTE_PROT_NO_READ (1<<8)
 
+#define APPLE_DART_PTE_SUBPAGE_START   GENMASK_ULL(63, 52)
+#define APPLE_DART_PTE_SUBPAGE_END GENMASK_ULL(51, 40)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -273,6 +277,12 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable 
*data,
else
pte |= ARM_LPAE_PTE_TYPE_BLOCK;
 
+   if (data->iop.fmt == APPLE_DART) {
+   /* subpage protection: always allow access to the entire page */
+   pte |= FIELD_PREP(APPLE_DART_PTE_SUBPAGE_START, 0);
+   pte |= FIELD_PREP(APPLE_DART_PTE_SUBPAGE_END, 0xfff);
+   }
+
for (i = 0; i < num_entries; i++)
ptep[i] = pte | paddr_to_iopte(paddr + i * sz, data);
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 0/4] iommu: M1 Pro/Max DART support

2021-11-17 Thread Sven Peter via iommu
Hi,

This is a fairly brief series to add support for the DARTs present in the
M1 Pro/Max. They have two differences that make them incompatible with
those in the M1:

  - the physical addresses are shifted left by 4 bits and and have 2 more
bits inside the PTE entries
  - the subpage protection feature is now mandatory. For Linux we can
just configure it to always allow access to the entire page.

Note that this needs a fix to the core pagetable code. Hector already
sent a first version separately to the mailing list since the problem
is (at least in theory) also present on other SoCs using the LPAE format
with a large physical address space [1].

Sven

[1] 
https://lore.kernel.org/linux-iommu/a2b45243-7e0a-a2ac-4e14-5256a3e7a...@arm.com/T/#t

Sven Peter (4):
  dt-bindings: iommu: dart: add t6000 compatible
  iommu/io-pgtable: Add DART subpage protection support
  iommu/io-pgtable: Add DART PTE support for t6000
  iommu: dart: Support t6000 variant

 .../devicetree/bindings/iommu/apple,dart.yaml |  4 +-
 drivers/iommu/apple-dart.c| 19 -
 drivers/iommu/io-pgtable-arm.c| 40 ++-
 include/linux/io-pgtable.h|  2 +
 4 files changed, 61 insertions(+), 4 deletions(-)

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 4/6] iommu: Move IOMMU pagesize check to attach_device

2021-10-23 Thread Sven Peter via iommu



On Fri, Oct 22, 2021, at 15:39, Robin Murphy wrote:
> On 2021-10-22 09:06, Marc Zyngier wrote:
>> On Fri, 22 Oct 2021 03:52:38 +0100,
>> Lu Baolu  wrote:
>>>
>>> On 10/21/21 4:10 PM, Marc Zyngier wrote:
>>>> On Thu, 21 Oct 2021 03:22:30 +0100,
>>>> Lu Baolu  wrote:
>>>>>
>>>>> On 10/20/21 10:22 PM, Marc Zyngier wrote:
>>>>>> On Wed, 20 Oct 2021 06:21:44 +0100,
>>>>>> Lu Baolu  wrote:
>>>>>>>
>>>>>>> On 2021/10/20 0:37, Sven Peter via iommu wrote:
>>>>>>>> +  /*
>>>>>>>> +   * Check that CPU pages can be represented by the IOVA 
>>>>>>>> granularity.
>>>>>>>> +   * This has to be done after ops->attach_dev since many IOMMU 
>>>>>>>> drivers
>>>>>>>> +   * only limit domain->pgsize_bitmap after having attached the 
>>>>>>>> first
>>>>>>>> +   * device.
>>>>>>>> +   */
>>>>>>>> +  ret = iommu_check_page_size(domain);
>>>>>>>> +  if (ret) {
>>>>>>>> +  __iommu_detach_device(domain, dev);
>>>>>>>> +  return ret;
>>>>>>>> +  }
>>>>>>>
>>>>>>> It looks odd. __iommu_attach_device() attaches an I/O page table for a
>>>>>>> device. How does it relate to CPU pages? Why is it a failure case if CPU
>>>>>>> page size is not covered?
>>>>>>
>>>>>> If you allocate a CPU PAGE_SIZE'd region, and point it at a device
>>>>>> that now can DMA to more than what you have allocated because the
>>>>>> IOMMU's own page size is larger, the device has now access to data it
>>>>>> shouldn't see. In my book, that's a pretty bad thing.
>>>>>
>>>>> But even you enforce the CPU page size check here, this problem still
>>>>> exists unless all DMA buffers are PAGE_SIZE aligned and sized, right?
>>>>
>>>> Let me take a CPU analogy: you have a page that contains some user
>>>> data *and* a kernel secret. How do you map this page into userspace
>>>> without leaking the kernel secret?
>>>>
>>>> PAGE_SIZE allocations are the unit of isolation, and this applies to
>>>> both CPU and IOMMU. If you have allocated a DMA buffer that is less
>>>> than a page, you then have to resort to bounce buffering, or accept
>>>> that your data isn't safe.
>>>
>>> I can understand the problems when IOMMU page sizes is larger than CPU
>>> page size. But the code itself is not clean. The vendor iommu drivers
>>> know more hardware details than the iommu core. It looks odd that the
>>> vendor iommu says "okay, I can attach this I/O page table to the
>>> device", but the iommu core says "no, you can't" and rolls everything
>>> back.
>> 
>> If your IOMMU driver can do things behind the core's back and
>> contradict the view that the core has, then it is probably time to fix
>> your IOMMU driver and make the core aware of what is going on.
>> Supported page sizes is one of these things.
>> 
>> In general, keeping the IOMMU driver as dumb as possible is a worthy
>> design goal, and this is why we have these abstractions.
>
> In this case it's the abstractions that are the problem, though. Any 
> driver which supports heterogeneous IOMMU instances with potentially 
> differing page sizes currently has no choice but to do horrible bodges 
> to make the bus-based iommu_domain_alloc() paradigm work *at all*. 
> Fixing that from the fundamental API level upwards has been on the to-do 
> list for some time now, but won't be straightforward.

That does sound like a rather big change.

But it also sounds like I can just limit DART to 16K pages for now and
kick the problem down the road until either Apple decides to do DARTs with
heterogeneous page sizes again or until those changes are done :-)

That at least gets rid of the weird atttach/check/maybe-detach-again hack
here.


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 4/6] iommu: Move IOMMU pagesize check to attach_device

2021-10-21 Thread Sven Peter via iommu



On Wed, Oct 20, 2021, at 07:21, Lu Baolu wrote:
> On 2021/10/20 0:37, Sven Peter via iommu wrote:
>> The iova allocator is capable of handling any granularity which is a power
>> of two. Remove the much stronger condition that the granularity must be
>> smaller or equal to the CPU page size from a BUG_ON there.
>> Instead, check this condition during __iommu_attach_device and fail
>> gracefully.
>> 
>> Signed-off-by: Sven Peter
>> ---
>>   drivers/iommu/iommu.c | 35 ---
>>   drivers/iommu/iova.c  |  7 ---
>>   include/linux/iommu.h |  5 +
>>   3 files changed, 41 insertions(+), 6 deletions(-)
>> 
>> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
>> index dd7863e453a5..28896739964b 100644
>> --- a/drivers/iommu/iommu.c
>> +++ b/drivers/iommu/iommu.c
>> @@ -80,6 +80,8 @@ static struct iommu_domain *__iommu_domain_alloc(struct 
>> bus_type *bus,
>>   unsigned type);
>>   static int __iommu_attach_device(struct iommu_domain *domain,
>>   struct device *dev);
>> +static void __iommu_detach_device(struct iommu_domain *domain,
>> +  struct device *dev);
>>   static int __iommu_attach_group(struct iommu_domain *domain,
>>  struct iommu_group *group);
>>   static void __iommu_detach_group(struct iommu_domain *domain,
>> @@ -1974,6 +1976,19 @@ void iommu_domain_free(struct iommu_domain *domain)
>>   }
>>   EXPORT_SYMBOL_GPL(iommu_domain_free);
>>   
>> +static int iommu_check_page_size(struct iommu_domain *domain)
>> +{
>> +if (!iommu_is_paging_domain(domain))
>> +return 0;
>> +
>> +if (!(domain->pgsize_bitmap & (PAGE_SIZE | (PAGE_SIZE - 1 {
>> +pr_warn("IOMMU pages cannot exactly represent CPU pages.\n");
>> +return -EFAULT;
>> +}
>> +
>> +return 0;
>> +}
>> +
>>   static int __iommu_attach_device(struct iommu_domain *domain,
>>   struct device *dev)
>>   {
>> @@ -1983,9 +1998,23 @@ static int __iommu_attach_device(struct iommu_domain 
>> *domain,
>>  return -ENODEV;
>>   
>>  ret = domain->ops->attach_dev(domain, dev);
>> -if (!ret)
>> -trace_attach_device_to_domain(dev);
>> -return ret;
>> +if (ret)
>> +return ret;
>> +
>> +/*
>> + * Check that CPU pages can be represented by the IOVA granularity.
>> + * This has to be done after ops->attach_dev since many IOMMU drivers
>> + * only limit domain->pgsize_bitmap after having attached the first
>> + * device.
>> + */
>> +ret = iommu_check_page_size(domain);
>> +if (ret) {
>> +__iommu_detach_device(domain, dev);
>> +return ret;
>> +}
>
> It looks odd. __iommu_attach_device() attaches an I/O page table for a
> device. How does it relate to CPU pages? Why is it a failure case if CPU
> page size is not covered?

Ideally, I'd only allow allocating DMA domains (which are going to be able
to handle larger IOMMU page sizes) while disallowing UNMANAGED domains
(which can theoretically read the granule but I doubt any client right now
considers this situation and will just run into odd issues) when the I/O
page size is bigger than the CPU page size. There was a brief previous
discussion about this [1,2,3].

Unfortunately, Apple's DART IOMMU is hardwired to either support 4K or
16K pages but never both. And to make things worse there was at least one
SoC used in the iPhones that mixed 4K and 16K DARTs on the same bus. Ugh.
That's why this awkward check is here because this is the earliest
place where I know which I/O page size will be used.


But I guess I could just limit the DART driver to 16K pages for now
(since every instance on the M1 is hard wired for that anyway) and then
just disallow allocating UNMANAGED domains when granule > PAGE_SIZE.

I'd still need a similar check here (at least for now) to prevent attaching
untrusted devices since I haven't changed swiotlb yet to support aligning
buffers correctly to more than PAGE_SIZE. That's possible but the interaction
with min_align_mask is a bit tricky to get right.
If there really shouldn't be any check here I can also do that for the next
version but I'd really like to keep that as a separate series - especially
since Thunderbolt support is still far away.


Thanks,


Sven

[1] 
https://lore.kernel.org/linux-iommu/7261df01-34a9-4e53-37cd-ae1aa15b1...@arm.com/
[2] 
https://lore.kernel.org/linux-iommu/CAK8P3a18XK2mfMGbZ+M32Mbabhbkd+=DNrnzampOah_j=rw...@mail.gmail.com/
[3] https://lore.kernel.org/linux-iommu/yo%2fbmuaolrgoj...@8bytes.org/
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 6/6] iommu/dart: Remove force_bypass logic

2021-10-19 Thread Sven Peter via iommu
Now that the dma-iommu API supports IOMMU granules which are larger than
the CPU page size and that the kernel no longer runs into a BUG_ON when
devices are attached to a domain with such a granule there's no need to
force bypass mode anymore.

Signed-off-by: Sven Peter 
---
 drivers/iommu/apple-dart.c | 14 ++
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 280ff8df728d..ce92195db638 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -90,7 +90,6 @@
  * @lock: lock for hardware operations involving this dart
  * @pgsize: pagesize supported by this DART
  * @supports_bypass: indicates if this DART supports bypass mode
- * @force_bypass: force bypass mode due to pagesize mismatch?
  * @sid2group: maps stream ids to iommu_groups
  * @iommu: iommu core device
  */
@@ -107,7 +106,6 @@ struct apple_dart {
 
u32 pgsize;
u32 supports_bypass : 1;
-   u32 force_bypass : 1;
 
struct iommu_group *sid2group[DART_MAX_STREAMS];
struct iommu_device iommu;
@@ -488,9 +486,6 @@ static int apple_dart_attach_dev(struct iommu_domain 
*domain,
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
 
-   if (cfg->stream_maps[0].dart->force_bypass &&
-   domain->type != IOMMU_DOMAIN_IDENTITY)
-   return -EINVAL;
if (!cfg->stream_maps[0].dart->supports_bypass &&
domain->type == IOMMU_DOMAIN_IDENTITY)
return -EINVAL;
@@ -619,8 +614,6 @@ static int apple_dart_of_xlate(struct device *dev, struct 
of_phandle_args *args)
if (cfg_dart) {
if (cfg_dart->supports_bypass != dart->supports_bypass)
return -EINVAL;
-   if (cfg_dart->force_bypass != dart->force_bypass)
-   return -EINVAL;
if (cfg_dart->pgsize != dart->pgsize)
return -EINVAL;
}
@@ -726,8 +719,6 @@ static int apple_dart_def_domain_type(struct device *dev)
 {
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
 
-   if (cfg->stream_maps[0].dart->force_bypass)
-   return IOMMU_DOMAIN_IDENTITY;
if (!cfg->stream_maps[0].dart->supports_bypass)
return IOMMU_DOMAIN_DMA;
 
@@ -884,7 +875,6 @@ static int apple_dart_probe(struct platform_device *pdev)
dart_params[1] = readl(dart->regs + DART_PARAMS2);
dart->pgsize = 1 << FIELD_GET(DART_PARAMS_PAGE_SHIFT, dart_params[0]);
dart->supports_bypass = dart_params[1] & DART_PARAMS_BYPASS_SUPPORT;
-   dart->force_bypass = dart->pgsize > PAGE_SIZE;
 
ret = request_irq(dart->irq, apple_dart_irq, IRQF_SHARED,
  "apple-dart fault handler", dart);
@@ -908,8 +898,8 @@ static int apple_dart_probe(struct platform_device *pdev)
 
dev_info(
&pdev->dev,
-   "DART [pagesize %x, bypass support: %d, bypass forced: %d] 
initialized\n",
-   dart->pgsize, dart->supports_bypass, dart->force_bypass);
+   "DART [pagesize %x, bypass support: %d] initialized\n",
+   dart->pgsize, dart->supports_bypass);
return 0;
 
 err_sysfs_remove:
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 5/6] iommu: Introduce __IOMMU_DOMAIN_LP

2021-10-19 Thread Sven Peter via iommu
__IOMMU_DOMAIN_LP (large pages) indicates that a domain can handle
conditions where PAGE_SIZE might be smaller than the IOMMU page size.
Always allow attaching trusted devices to such domains and set the flag for
IOMMU_DOMAIN_DMA, which can now handle these situations.

Note that untrusted devices are not yet supported. Those require
additional changes to allow aligning swiotlb buffers to granularities
larger than PAGE_SIZE.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c |  9 +++--
 include/linux/iommu.h | 13 +++--
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 28896739964b..66bba6a6bb28 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1976,10 +1976,15 @@ void iommu_domain_free(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL_GPL(iommu_domain_free);
 
-static int iommu_check_page_size(struct iommu_domain *domain)
+static int iommu_check_page_size(struct iommu_domain *domain,
+   struct device *dev)
 {
+   bool trusted = !(dev_is_pci(dev) && to_pci_dev(dev)->untrusted);
+
if (!iommu_is_paging_domain(domain))
return 0;
+   if (iommu_is_large_pages_domain(domain) && trusted)
+   return 0;
 
if (!(domain->pgsize_bitmap & (PAGE_SIZE | (PAGE_SIZE - 1 {
pr_warn("IOMMU pages cannot exactly represent CPU pages.\n");
@@ -2007,7 +2012,7 @@ static int __iommu_attach_device(struct iommu_domain 
*domain,
 * only limit domain->pgsize_bitmap after having attached the first
 * device.
 */
-   ret = iommu_check_page_size(domain);
+   ret = iommu_check_page_size(domain, dev);
if (ret) {
__iommu_detach_device(domain, dev);
return ret;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index cabd25879613..1f1af59d0522 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -62,6 +62,8 @@ struct iommu_domain_geometry {
  implementation  */
 #define __IOMMU_DOMAIN_PT  (1U << 2)  /* Domain is identity mapped   */
 #define __IOMMU_DOMAIN_DMA_FQ  (1U << 3)  /* DMA-API uses flush queue*/
+#define __IOMMU_DOMAIN_LP  (1U << 4)  /* Support for PAGE_SIZE smaller
+ than IOMMU page size*/
 
 /*
  * This are the possible domain-types
@@ -81,10 +83,12 @@ struct iommu_domain_geometry {
 #define IOMMU_DOMAIN_IDENTITY  (__IOMMU_DOMAIN_PT)
 #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
 #define IOMMU_DOMAIN_DMA   (__IOMMU_DOMAIN_PAGING |\
-__IOMMU_DOMAIN_DMA_API)
+__IOMMU_DOMAIN_DMA_API |   \
+__IOMMU_DOMAIN_LP)
 #define IOMMU_DOMAIN_DMA_FQ(__IOMMU_DOMAIN_PAGING |\
 __IOMMU_DOMAIN_DMA_API |   \
-__IOMMU_DOMAIN_DMA_FQ)
+__IOMMU_DOMAIN_DMA_FQ |\
+__IOMMU_DOMAIN_LP)
 
 struct iommu_domain {
unsigned type;
@@ -106,6 +110,11 @@ static inline bool iommu_is_paging_domain(struct 
iommu_domain *domain)
return domain->type & __IOMMU_DOMAIN_PAGING;
 }
 
+static inline bool iommu_is_large_pages_domain(struct iommu_domain *domain)
+{
+   return domain->type & __IOMMU_DOMAIN_LP;
+}
+
 enum iommu_cap {
IOMMU_CAP_CACHE_COHERENCY,  /* IOMMU can enforce cache coherent DMA
   transactions */
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 4/6] iommu: Move IOMMU pagesize check to attach_device

2021-10-19 Thread Sven Peter via iommu
The iova allocator is capable of handling any granularity which is a power
of two. Remove the much stronger condition that the granularity must be
smaller or equal to the CPU page size from a BUG_ON there.
Instead, check this condition during __iommu_attach_device and fail
gracefully.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c | 35 ---
 drivers/iommu/iova.c  |  7 ---
 include/linux/iommu.h |  5 +
 3 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index dd7863e453a5..28896739964b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -80,6 +80,8 @@ static struct iommu_domain *__iommu_domain_alloc(struct 
bus_type *bus,
 unsigned type);
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev);
+static void __iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev);
 static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
 static void __iommu_detach_group(struct iommu_domain *domain,
@@ -1974,6 +1976,19 @@ void iommu_domain_free(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL_GPL(iommu_domain_free);
 
+static int iommu_check_page_size(struct iommu_domain *domain)
+{
+   if (!iommu_is_paging_domain(domain))
+   return 0;
+
+   if (!(domain->pgsize_bitmap & (PAGE_SIZE | (PAGE_SIZE - 1 {
+   pr_warn("IOMMU pages cannot exactly represent CPU pages.\n");
+   return -EFAULT;
+   }
+
+   return 0;
+}
+
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev)
 {
@@ -1983,9 +1998,23 @@ static int __iommu_attach_device(struct iommu_domain 
*domain,
return -ENODEV;
 
ret = domain->ops->attach_dev(domain, dev);
-   if (!ret)
-   trace_attach_device_to_domain(dev);
-   return ret;
+   if (ret)
+   return ret;
+
+   /*
+* Check that CPU pages can be represented by the IOVA granularity.
+* This has to be done after ops->attach_dev since many IOMMU drivers
+* only limit domain->pgsize_bitmap after having attached the first
+* device.
+*/
+   ret = iommu_check_page_size(domain);
+   if (ret) {
+   __iommu_detach_device(domain, dev);
+   return ret;
+   }
+
+   trace_attach_device_to_domain(dev);
+   return 0;
 }
 
 int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 9e8bc802ac05..707eb0ceb29f 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -50,10 +50,11 @@ init_iova_domain(struct iova_domain *iovad, unsigned long 
granule,
 {
/*
 * IOVA granularity will normally be equal to the smallest
-* supported IOMMU page size; both *must* be capable of
-* representing individual CPU pages exactly.
+* supported IOMMU page size; while both usually are capable of
+* representing individual CPU pages exactly the IOVA allocator
+* supports any granularities that are an exact power of two.
 */
-   BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
+   BUG_ON(!is_power_of_2(granule));
 
spin_lock_init(&iovad->iova_rbtree_lock);
iovad->rbroot = RB_ROOT;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index d2f3435e7d17..cabd25879613 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -101,6 +101,11 @@ static inline bool iommu_is_dma_domain(struct iommu_domain 
*domain)
return domain->type & __IOMMU_DOMAIN_DMA_API;
 }
 
+static inline bool iommu_is_paging_domain(struct iommu_domain *domain)
+{
+   return domain->type & __IOMMU_DOMAIN_PAGING;
+}
+
 enum iommu_cap {
IOMMU_CAP_CACHE_COHERENCY,  /* IOMMU can enforce cache coherent DMA
   transactions */
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 3/6] iommu/dma: Support granule > PAGE_SIZE allocations

2021-10-19 Thread Sven Peter via iommu
Noncontiguous allocations must be made up of individual blocks
in a way that allows those blocks to be mapped contiguously in IOVA space.
For IOMMU page sizes larger than the CPU page size this can be done
by allocating all individual blocks from pools with
order >= get_order(iovad->granule). Some spillover pages might be
allocated at the end, which can however immediately be freed.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 103 ++
 1 file changed, 93 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index ea799e70fc98..579a5a89d1ec 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -547,6 +548,9 @@ static struct page **__iommu_dma_alloc_pages(struct device 
*dev,
 {
struct page **pages;
unsigned int i = 0, nid = dev_to_node(dev);
+   unsigned int j;
+   unsigned long min_order = __fls(order_mask);
+   unsigned int min_order_size = 1U << min_order;
 
order_mask &= (2U << MAX_ORDER) - 1;
if (!order_mask)
@@ -586,15 +590,37 @@ static struct page **__iommu_dma_alloc_pages(struct 
device *dev,
split_page(page, order);
break;
}
-   if (!page) {
-   __iommu_dma_free_pages(pages, i);
-   return NULL;
+
+   /*
+* If we have no valid page here we might be trying to allocate
+* the last block consisting of 1<pgsize_bitmap;
+   struct sg_append_table sgt_append = {};
+   struct scatterlist *last_sg;
struct page **pages;
dma_addr_t iova;
+   phys_addr_t orig_s_phys;
+   size_t orig_s_len, orig_s_off, s_iova_off, iova_size;
 
if (static_branch_unlikely(&iommu_deferred_attach_enabled) &&
iommu_deferred_attach(dev, domain))
return NULL;
 
min_size = alloc_sizes & -alloc_sizes;
-   if (min_size < PAGE_SIZE) {
+   if (iovad->granule > PAGE_SIZE) {
+   if (size < iovad->granule) {
+   /* ensure a single contiguous allocation */
+   min_size = ALIGN(size, PAGE_SIZE*(1U<coherent_dma_mask, dev);
+   iova_size = iova_align(iovad, size);
+   iova = iommu_dma_alloc_iova(domain, iova_size, dev->coherent_dma_mask, 
dev);
if (!iova)
goto out_free_pages;
 
-   if (sg_alloc_table_from_pages(sgt, pages, count, 0, size, GFP_KERNEL))
+   /* append_table is only used to get a pointer to the last entry */
+   if (sg_alloc_append_table_from_pages(&sgt_append, pages, count, 0,
+   iova_size, UINT_MAX, 0, GFP_KERNEL))
goto out_free_iova;
+   memcpy(sgt, &sgt_append.sgt, sizeof(*sgt));
+   last_sg = sgt_append.prv;
 
if (!(ioprot & IOMMU_CACHE)) {
struct scatterlist *sg;
@@ -650,18 +692,59 @@ static struct page 
**__iommu_dma_alloc_noncontiguous(struct device *dev,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
 
+   if (iovad->granule > PAGE_SIZE) {
+   if (size < iovad->granule) {
+   /*
+* we only have a single sg list entry here that is
+* likely not aligned to iovad->granule. adjust the
+* entry to represent the encapsulating IOMMU page
+* and then later restore everything to its original
+* values, similar to the impedance matching done in
+* iommu_dma_map_sg.
+*/
+   orig_s_phys = sg_phys(sgt->sgl);
+   orig_s_len = sgt->sgl->length;
+   orig_s_off = sgt->sgl->offset;
+   s_iova_off = iova_offset(iovad, orig_s_phys);
+
+   sg_set_page(sgt->sgl,
+   pfn_to_page(PHYS_PFN(orig_s_phys - s_iova_off)),
+   iova_align(iovad, orig_s_len + s_iova_off),
+   sgt->sgl->offset & ~s_iova_off);
+   } else {
+   /*
+* convince iommu_map_sg_atomic to map the last block
+* even though it may be too small.
+*/
+   orig_s_len = last_sg->length;
+   last_sg->length = iova_align(iovad, last_sg->length);
+   }
+   }
+
if (iommu_map_sg_atomic(domain, iova, sgt->sgl, sgt->orig_nents, ioprot)
-   < size)
+ 

[PATCH v3 2/6] iommu/dma: Support granule > PAGE_SIZE in dma_map_sg

2021-10-19 Thread Sven Peter via iommu
Add support to iommu_dma_map_sg's impedance matching to also align
sg_lists correctly when the IOMMU granule is larger than PAGE_SIZE.

Co-developed-by: Robin Murphy 
Signed-off-by: Robin Murphy 
Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 25 -
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 17f25632a0d6..ea799e70fc98 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -878,8 +879,9 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;
 
-   s->offset += s_iova_off;
-   s->length = s_length;
+   sg_set_page(s,
+   pfn_to_page(PHYS_PFN(sg_phys(s) + s_iova_off)),
+   s_length, s_iova_off & ~PAGE_MASK);
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
 
@@ -920,13 +922,17 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
 static void __invalidate_sg(struct scatterlist *sg, int nents)
 {
struct scatterlist *s;
+   phys_addr_t orig_paddr;
int i;
 
for_each_sg(sg, s, nents, i) {
-   if (sg_dma_address(s) != DMA_MAPPING_ERROR)
-   s->offset += sg_dma_address(s);
-   if (sg_dma_len(s))
-   s->length = sg_dma_len(s);
+   if (sg_dma_len(s)) {
+   orig_paddr = sg_phys(s) + sg_dma_address(s);
+   sg_set_page(s,
+   pfn_to_page(PHYS_PFN(orig_paddr)),
+   sg_dma_len(s),
+   sg_dma_address(s) & ~PAGE_MASK);
+   }
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
}
@@ -1003,15 +1009,16 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 * stashing the unaligned parts in the as-yet-unused DMA fields.
 */
for_each_sg(sg, s, nents, i) {
-   size_t s_iova_off = iova_offset(iovad, s->offset);
+   phys_addr_t s_phys = sg_phys(s);
+   size_t s_iova_off = iova_offset(iovad, s_phys);
size_t s_length = s->length;
size_t pad_len = (mask - iova_len + 1) & mask;
 
sg_dma_address(s) = s_iova_off;
sg_dma_len(s) = s_length;
-   s->offset -= s_iova_off;
s_length = iova_align(iovad, s_length + s_iova_off);
-   s->length = s_length;
+   sg_set_page(s, pfn_to_page(PHYS_PFN(s_phys - s_iova_off)),
+   s_length, s->offset & ~s_iova_off);
 
/*
 * Due to the alignment of our single IOVA allocation, we can
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 1/6] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-10-19 Thread Sven Peter via iommu
While this function *probably* works correctly without any changes for
granule > PAGE_SIZE I don't have any code to actually test it and cannot
reason about how the function is supposed to work.
Disable it instead until we run into a use case where it's required.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index de5040b65529..17f25632a0d6 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1249,9 +1249,15 @@ static int iommu_dma_get_sgtable(struct device *dev, 
struct sg_table *sgt,
void *cpu_addr, dma_addr_t dma_addr, size_t size,
unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
struct page *page;
int ret;
 
+   if (iovad->granule > PAGE_SIZE)
+   return -ENXIO;
+
if (IS_ENABLED(CONFIG_DMA_REMAP) && is_vmalloc_addr(cpu_addr)) {
struct page **pages = dma_common_find_pages(cpu_addr);
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 0/6] Support IOMMU page sizes larger than the CPU page size

2021-10-19 Thread Sven Peter via iommu
Hi,

RFC: 
https://lore.kernel.org/linux-iommu/20210806155523.50429-1-s...@svenpeter.dev/
 v2: 
https://lore.kernel.org/linux-iommu/20210828153642.19396-1-s...@svenpeter.dev/

Time to revive this series:

v2 -> v3:
  - Dropped support for untrusted devices since swiotlb currently does not
allow aligning buffers to granularities bigger than PAGE_SIZE.
Getting this to work is possibly but a bit tricky together with 
min_align_mask.
Right now there are no untrusted device on the M1 anyway and this series 
already
feels big enough. I've therefore decided to address this in a follow up.
  - Replaced phys_to_page with pfn_to_page(PHYS_PFN(..)) since not all 
architectures
define phys_to_page
  - Replaced the PAGE_SIZE > granule check in iommu_check_page_size with
domain->pgsize_bitmap & (PAGE_SIZE | (PAGE_SIZE - 1)) as suggested by Robin
  - Rebased on the latest rc which required to introduce 
sg_alloc_append_table_from_pages
since __sg_alloc_table_from_pages no longer exists 

RFC -> v2:
  - essentially a comlpetely rewrite of the first approach which just padded 
every
allocation

Some background: On the Apple M1 the IOMMUs are hardwired to only support 16 KB 
pages.
We'd still like to boot Linux with 4KB pages though because that's what most 
distros
ship these days. I've been told this also helps with Android userspace 
compatibility
and x86 emulation.
This patch series adds support for that setup to the IOMMU DMA API.

This is essentially done by always mapping the encapsulating IOMMU page and 
adjusting
the returned iova offset. There are also changes to only allow DMA domains to 
make use
of this and prevent UNMANAGED domains from encountering unexpected situations.


Best,

Sven

Sven Peter (6):
  iommu/dma: Disable get_sgtable for granule > PAGE_SIZE
  iommu/dma: Support granule > PAGE_SIZE in dma_map_sg
  iommu/dma: Support granule > PAGE_SIZE allocations
  iommu: Move IOMMU pagesize check to attach_device
  iommu: Introduce __IOMMU_DOMAIN_LP
  iommu/dart: Remove force_bypass logic

 drivers/iommu/apple-dart.c |  14 +---
 drivers/iommu/dma-iommu.c  | 134 +++--
 drivers/iommu/iommu.c  |  40 ++-
 drivers/iommu/iova.c   |   7 +-
 include/linux/iommu.h  |  18 -
 5 files changed, 174 insertions(+), 39 deletions(-)

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/dart: Initialize DART_STREAMS_ENABLE

2021-10-19 Thread Sven Peter via iommu
DART has an additional global register to control which streams are
isolated. This register is a bit redundant since DART_TCR can already
be used to control isolation and is usually initialized to DART_STREAM_ALL
by the time we get control. Some DARTs (namely the one used for the audio
controller) however have some streams disabled initially. Make sure those
work by initializing DART_STREAMS_ENABLE during reset.

Reported-by: Martin Povišer 
Signed-off-by: Sven Peter 
---

While this could technically count as a fix I don't think it needs to go to
5.15 since no driver that requires this is in there. The first driver
that needs this will likely only be ready for the 5.17 merge window.

 drivers/iommu/apple-dart.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index ce92195db638..6f8c240d8d40 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -70,6 +70,8 @@
 #define DART_ERROR_ADDR_HI 0x54
 #define DART_ERROR_ADDR_LO 0x50
 
+#define DART_STREAMS_ENABLE 0xfc
+
 #define DART_TCR(sid) (0x100 + 4 * (sid))
 #define DART_TCR_TRANSLATE_ENABLE BIT(7)
 #define DART_TCR_BYPASS0_ENABLE BIT(8)
@@ -299,6 +301,9 @@ static int apple_dart_hw_reset(struct apple_dart *dart)
apple_dart_hw_disable_dma(&stream_map);
apple_dart_hw_clear_all_ttbrs(&stream_map);
 
+   /* enable all streams globally since TCR is used to control isolation */
+   writel(DART_STREAM_ALL, dart->regs + DART_STREAMS_ENABLE);
+
/* clear any pending errors before the interrupt is unmasked */
writel(readl(dart->regs + DART_ERROR), dart->regs + DART_ERROR);
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Re: [PATCH] iommu/dart: use kmemdup instead of kzalloc and memcpy

2021-10-16 Thread Sven Peter via iommu



On Wed, Oct 13, 2021, at 08:34, Wan Jiabing wrote:
> Fix following coccicheck warning:
> drivers/iommu/apple-dart.c:704:20-27: WARNING opportunity for kmemdup
>
> Signed-off-by: Wan Jiabing 
> ---
>  drivers/iommu/apple-dart.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)

Looks good to me, thanks!

Acked-by: Sven Peter 


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/dart: Clear sid2group entry when a group is freed

2021-09-24 Thread Sven Peter via iommu
sid2groups keeps track of which stream id combinations belong to a
iommu_group to assign those correctly to devices.
When a iommu_group is freed a stale pointer will however remain in
sid2groups. This prevents devices with the same stream id combination
to ever be attached again (see below).
Fix that by creating a shadow copy of the stream id configuration
when a group is allocated for the first time and clear the sid2group
entry when that group is freed.

  # echo 1 >/sys/bus/pci/devices/\:03\:00.0/remove
  pci :03:00.0: Removing from iommu group 1
  # echo 1 >/sys/bus/pci/rescan
  [...]
  pci :03:00.0: BAR 0: assigned [mem 0x6a000-0x6a000 64bit pref]
  pci :03:00.0: BAR 2: assigned [mem 0x6a001-0x6a001 64bit pref]
  pci :03:00.0: BAR 6: assigned [mem 0x6c010-0x6c01007ff pref]
  tg3 :03:00.0: Failed to add to iommu group 1: -2
  [...]

Fixes: 46d1fb072e76b161 ("iommu/dart: Add DART iommu driver")
Reported-by: Marc Zyngier 
Signed-off-by: Sven Peter 
---
 drivers/iommu/apple-dart.c | 38 +++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 47ffe9e49abb..f82b2c46493a 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -636,16 +636,34 @@ static int apple_dart_of_xlate(struct device *dev, struct 
of_phandle_args *args)
return -EINVAL;
 }
 
+static DEFINE_MUTEX(apple_dart_groups_lock);
+
+static void apple_dart_release_group(void *iommu_data)
+{
+   int i, sid;
+   struct apple_dart_stream_map *stream_map;
+   struct apple_dart_master_cfg *group_master_cfg = iommu_data;
+
+   mutex_lock(&apple_dart_groups_lock);
+
+   for_each_stream_map(i, group_master_cfg, stream_map)
+   for_each_set_bit(sid, &stream_map->sidmap, DART_MAX_STREAMS)
+   stream_map->dart->sid2group[sid] = NULL;
+
+   kfree(iommu_data);
+   mutex_unlock(&apple_dart_groups_lock);
+}
+
 static struct iommu_group *apple_dart_device_group(struct device *dev)
 {
-   static DEFINE_MUTEX(lock);
int i, sid;
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_stream_map *stream_map;
+   struct apple_dart_master_cfg *group_master_cfg;
struct iommu_group *group = NULL;
struct iommu_group *res = ERR_PTR(-EINVAL);
 
-   mutex_lock(&lock);
+   mutex_lock(&apple_dart_groups_lock);
 
for_each_stream_map(i, cfg, stream_map) {
for_each_set_bit(sid, &stream_map->sidmap, DART_MAX_STREAMS) {
@@ -673,6 +691,20 @@ static struct iommu_group *apple_dart_device_group(struct 
device *dev)
 #endif
group = generic_device_group(dev);
 
+   res = ERR_PTR(-ENOMEM);
+   if (!group)
+   goto out;
+
+   group_master_cfg = kzalloc(sizeof(*group_master_cfg), GFP_KERNEL);
+   if (!group_master_cfg) {
+   iommu_group_put(group);
+   goto out;
+   }
+
+   memcpy(group_master_cfg, cfg, sizeof(*group_master_cfg));
+   iommu_group_set_iommudata(group, group_master_cfg,
+   apple_dart_release_group);
+
for_each_stream_map(i, cfg, stream_map)
for_each_set_bit(sid, &stream_map->sidmap, DART_MAX_STREAMS)
stream_map->dart->sid2group[sid] = group;
@@ -680,7 +712,7 @@ static struct iommu_group *apple_dart_device_group(struct 
device *dev)
res = group;
 
 out:
-   mutex_unlock(&lock);
+   mutex_unlock(&apple_dart_groups_lock);
return res;
 }
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/dart: Remove iommu_flush_ops

2021-09-21 Thread Sven Peter via iommu
apple_dart_tlb_flush_{all,walk} expect to get a struct apple_dart_domain
but instead get a struct iommu_domain right now. This breaks those two
functions and can lead to kernel panics like the one below.
DART can only invalidate the entire TLB and apple_dart_iotlb_sync will
already flush everything. There's no need to do that again inside those
two functions. Let's just drop them.

  pci :03:00.0: Removing from iommu group 1
  Unable to handle kernel paging request at virtual address 00010023
  [...]
  Call trace:
   _raw_spin_lock_irqsave+0x54/0xbc
   apple_dart_hw_stream_command.constprop.0+0x2c/0x130
   apple_dart_tlb_flush_all+0x48/0x90
   free_io_pgtable_ops+0x40/0x70
   apple_dart_domain_free+0x2c/0x44
   iommu_group_release+0x68/0xac
   kobject_cleanup+0x4c/0x1fc
   kobject_cleanup+0x14c/0x1fc
   kobject_put+0x64/0x84
   iommu_group_remove_device+0x110/0x180
   iommu_release_device+0x50/0xa0
  [...]

Fixes: 46d1fb072e76b161 ("iommu/dart: Add DART iommu driver")
Reported-by: Marc Zyngier 
Signed-off-by: Sven Peter 
---
 drivers/iommu/apple-dart.c | 18 --
 1 file changed, 18 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index c37fb4790e8a..47ffe9e49abb 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -181,7 +181,6 @@ struct apple_dart_master_cfg {
 
 static struct platform_driver apple_dart_driver;
 static const struct iommu_ops apple_dart_iommu_ops;
-static const struct iommu_flush_ops apple_dart_tlb_ops;
 
 static struct apple_dart_domain *to_dart_domain(struct iommu_domain *dom)
 {
@@ -336,22 +335,6 @@ static void apple_dart_iotlb_sync_map(struct iommu_domain 
*domain,
apple_dart_domain_flush_tlb(to_dart_domain(domain));
 }
 
-static void apple_dart_tlb_flush_all(void *cookie)
-{
-   apple_dart_domain_flush_tlb(cookie);
-}
-
-static void apple_dart_tlb_flush_walk(unsigned long iova, size_t size,
- size_t granule, void *cookie)
-{
-   apple_dart_domain_flush_tlb(cookie);
-}
-
-static const struct iommu_flush_ops apple_dart_tlb_ops = {
-   .tlb_flush_all = apple_dart_tlb_flush_all,
-   .tlb_flush_walk = apple_dart_tlb_flush_walk,
-};
-
 static phys_addr_t apple_dart_iova_to_phys(struct iommu_domain *domain,
   dma_addr_t iova)
 {
@@ -433,7 +416,6 @@ static int apple_dart_finalize_domain(struct iommu_domain 
*domain,
.ias = 32,
.oas = 36,
.coherent_walk = 1,
-   .tlb = &apple_dart_tlb_ops,
.iommu_dev = dart->dev,
};
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 3/8] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-09-03 Thread Sven Peter via iommu



On Fri, Sep 3, 2021, at 17:45, Robin Murphy wrote:
> On 2021-09-03 16:16, Sven Peter wrote:
> > 
> > 
> > On Thu, Sep 2, 2021, at 21:42, Robin Murphy wrote:
> >> On 2021-09-02 19:19, Sven Peter wrote:
> >>>
> >>>
> >>> On Wed, Sep 1, 2021, at 23:10, Alyssa Rosenzweig wrote:
> >>>>> My biggest issue is that I do not understand how this function is 
> >>>>> supposed
> >>>>> to be used correctly. It would work fine as-is if it only ever gets 
> >>>>> passed buffers
> >>>>> allocated by the coherent API but there's not way to check or guarantee 
> >>>>> that.
> >>>>> There may also be callers making assumptions that no longer hold when
> >>>>> iovad->granule > PAGE_SIZE.
> >>>>>
> >>>>> Regarding your case: I'm not convinced the function is meant to be used 
> >>>>> there.
> >>>>> If I understand it correctly, your code first allocates memory with 
> >>>>> dma_alloc_coherent
> >>>>> (which possibly creates a sgt internally and then maps it with 
> >>>>> iommu_map_sg),
> >>>>> then coerces that back into a sgt with dma_get_sgtable, and then maps 
> >>>>> that sgt to
> >>>>> another iommu domain with dma_map_sg while assuming that the result 
> >>>>> will be contiguous
> >>>>> in IOVA space. It'll work out because dma_alloc_coherent is the very 
> >>>>> thing
> >>>>> meant to allocate pages that can be mapped into kernel and device VA 
> >>>>> space
> >>>>> as a single contiguous block and because both of your IOMMUs are 
> >>>>> different
> >>>>> instances of the same HW block. Anything allocated by 
> >>>>> dma_alloc_coherent for the
> >>>>> first IOMMU will have the right shape that will allow it to be mapped as
> >>>>> a single contiguous block for the second IOMMU.
> >>>>>
> >>>>> What could be done in your case is to instead use the IOMMU API,
> >>>>> allocate the pages yourself (while ensuring the sgt your create is made 
> >>>>> up
> >>>>> of blocks with size and physaddr aligned to max(domain_a->granule, 
> >>>>> domain_b->granule))
> >>>>> and then just use iommu_map_sg for both domains which actually comes 
> >>>>> with the
> >>>>> guarantee that the result will be a single contiguous block in IOVA 
> >>>>> space and
> >>>>> doesn't required the sgt roundtrip.
> >>>>
> >>>> In principle I agree. I am getting the sense this function can't be used
> >>>> correctly in general, and yet is the function that's meant to be used.
> >>>> If my interpretation of prior LKML discussion holds, the problems are
> >>>> far deeper than my code or indeed page size problems...
> >>>
> >>> Right, which makes reasoning about this function and its behavior if the
> >>> IOMMU pages size is unexpected very hard for me. I'm not opposed to just
> >>> keeping this function as-is when there's a mismatch between PAGE_SIZE and
> >>> the IOMMU page size (and it will probably work that way) but I'd like to
> >>> be sure that won't introduce unexpected behavior.
> >>>
> >>>>
> >>>> If the right way to handle this is with the IOMMU and IOVA APIs, I 
> >>>> really wish
> >>>> that dance were wrapped up in a safe helper function instead of open
> >>>> coding it in every driver that does cross device sharing.
> >>>>
> >>>> We might even call that helper... hmm... dma_map_sg *ducks*
> >>>>
> >>>
> >>> There might be another way to do this correctly. I'm likely just a little
> >>> bit biased because I've spent the past weeks wrapping my head around the
> >>> IOMMU and DMA APIs and when all you have is a hammer everything looks like
> >>> a nail.
> >>>
> >>> But dma_map_sg operates at the DMA API level and at that point the dma-ops
> >>> for two different devices could be vastly different.
> >>> In the worst case one of them could be behind an IOMMU that can easily map
> >>> non-contiguous p

Re: [PATCH v2 3/8] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-09-03 Thread Sven Peter via iommu



On Thu, Sep 2, 2021, at 21:42, Robin Murphy wrote:
> On 2021-09-02 19:19, Sven Peter wrote:
> > 
> > 
> > On Wed, Sep 1, 2021, at 23:10, Alyssa Rosenzweig wrote:
> >>> My biggest issue is that I do not understand how this function is supposed
> >>> to be used correctly. It would work fine as-is if it only ever gets 
> >>> passed buffers
> >>> allocated by the coherent API but there's not way to check or guarantee 
> >>> that.
> >>> There may also be callers making assumptions that no longer hold when
> >>> iovad->granule > PAGE_SIZE.
> >>>
> >>> Regarding your case: I'm not convinced the function is meant to be used 
> >>> there.
> >>> If I understand it correctly, your code first allocates memory with 
> >>> dma_alloc_coherent
> >>> (which possibly creates a sgt internally and then maps it with 
> >>> iommu_map_sg),
> >>> then coerces that back into a sgt with dma_get_sgtable, and then maps 
> >>> that sgt to
> >>> another iommu domain with dma_map_sg while assuming that the result will 
> >>> be contiguous
> >>> in IOVA space. It'll work out because dma_alloc_coherent is the very thing
> >>> meant to allocate pages that can be mapped into kernel and device VA space
> >>> as a single contiguous block and because both of your IOMMUs are different
> >>> instances of the same HW block. Anything allocated by dma_alloc_coherent 
> >>> for the
> >>> first IOMMU will have the right shape that will allow it to be mapped as
> >>> a single contiguous block for the second IOMMU.
> >>>
> >>> What could be done in your case is to instead use the IOMMU API,
> >>> allocate the pages yourself (while ensuring the sgt your create is made up
> >>> of blocks with size and physaddr aligned to max(domain_a->granule, 
> >>> domain_b->granule))
> >>> and then just use iommu_map_sg for both domains which actually comes with 
> >>> the
> >>> guarantee that the result will be a single contiguous block in IOVA space 
> >>> and
> >>> doesn't required the sgt roundtrip.
> >>
> >> In principle I agree. I am getting the sense this function can't be used
> >> correctly in general, and yet is the function that's meant to be used.
> >> If my interpretation of prior LKML discussion holds, the problems are
> >> far deeper than my code or indeed page size problems...
> > 
> > Right, which makes reasoning about this function and its behavior if the
> > IOMMU pages size is unexpected very hard for me. I'm not opposed to just
> > keeping this function as-is when there's a mismatch between PAGE_SIZE and
> > the IOMMU page size (and it will probably work that way) but I'd like to
> > be sure that won't introduce unexpected behavior.
> > 
> >>
> >> If the right way to handle this is with the IOMMU and IOVA APIs, I really 
> >> wish
> >> that dance were wrapped up in a safe helper function instead of open
> >> coding it in every driver that does cross device sharing.
> >>
> >> We might even call that helper... hmm... dma_map_sg *ducks*
> >>
> > 
> > There might be another way to do this correctly. I'm likely just a little
> > bit biased because I've spent the past weeks wrapping my head around the
> > IOMMU and DMA APIs and when all you have is a hammer everything looks like
> > a nail.
> > 
> > But dma_map_sg operates at the DMA API level and at that point the dma-ops
> > for two different devices could be vastly different.
> > In the worst case one of them could be behind an IOMMU that can easily map
> > non-contiguous pages while the other one is directly connected to the bus 
> > and
> > can't even access >4G pages without swiotlb support.
> > It's really only possible to guarantee that it will map N buffers to <= N
> > DMA-addressable buffers (possibly by using an IOMMU or swiotlb internally) 
> > at
> > that point.
> > 
> > On the IOMMU API level you have much more information available about the 
> > actual
> > hardware and can prepare the buffers in a way that makes both devices happy.
> > That's why iommu_map_sgtable combined with iovad->granule aligned sgt 
> > entries
> > can actually guarantee to map the entire list to a single contiguous IOVA 
> > block.
> 
> Essentially there are two reasonable o

Re: [PATCH v2 3/8] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-09-02 Thread Sven Peter via iommu



On Wed, Sep 1, 2021, at 23:10, Alyssa Rosenzweig wrote:
> > My biggest issue is that I do not understand how this function is supposed
> > to be used correctly. It would work fine as-is if it only ever gets passed 
> > buffers
> > allocated by the coherent API but there's not way to check or guarantee 
> > that.
> > There may also be callers making assumptions that no longer hold when
> > iovad->granule > PAGE_SIZE.
> > 
> > Regarding your case: I'm not convinced the function is meant to be used 
> > there.
> > If I understand it correctly, your code first allocates memory with 
> > dma_alloc_coherent
> > (which possibly creates a sgt internally and then maps it with 
> > iommu_map_sg),
> > then coerces that back into a sgt with dma_get_sgtable, and then maps that 
> > sgt to
> > another iommu domain with dma_map_sg while assuming that the result will be 
> > contiguous
> > in IOVA space. It'll work out because dma_alloc_coherent is the very thing
> > meant to allocate pages that can be mapped into kernel and device VA space
> > as a single contiguous block and because both of your IOMMUs are different
> > instances of the same HW block. Anything allocated by dma_alloc_coherent 
> > for the
> > first IOMMU will have the right shape that will allow it to be mapped as
> > a single contiguous block for the second IOMMU.
> > 
> > What could be done in your case is to instead use the IOMMU API,
> > allocate the pages yourself (while ensuring the sgt your create is made up
> > of blocks with size and physaddr aligned to max(domain_a->granule, 
> > domain_b->granule))
> > and then just use iommu_map_sg for both domains which actually comes with 
> > the
> > guarantee that the result will be a single contiguous block in IOVA space 
> > and
> > doesn't required the sgt roundtrip.
> 
> In principle I agree. I am getting the sense this function can't be used
> correctly in general, and yet is the function that's meant to be used.
> If my interpretation of prior LKML discussion holds, the problems are
> far deeper than my code or indeed page size problems...

Right, which makes reasoning about this function and its behavior if the
IOMMU pages size is unexpected very hard for me. I'm not opposed to just
keeping this function as-is when there's a mismatch between PAGE_SIZE and
the IOMMU page size (and it will probably work that way) but I'd like to
be sure that won't introduce unexpected behavior. 

> 
> If the right way to handle this is with the IOMMU and IOVA APIs, I really wish
> that dance were wrapped up in a safe helper function instead of open
> coding it in every driver that does cross device sharing.
> 
> We might even call that helper... hmm... dma_map_sg *ducks*
> 

There might be another way to do this correctly. I'm likely just a little
bit biased because I've spent the past weeks wrapping my head around the
IOMMU and DMA APIs and when all you have is a hammer everything looks like
a nail.

But dma_map_sg operates at the DMA API level and at that point the dma-ops
for two different devices could be vastly different. 
In the worst case one of them could be behind an IOMMU that can easily map
non-contiguous pages while the other one is directly connected to the bus and
can't even access >4G pages without swiotlb support.
It's really only possible to guarantee that it will map N buffers to <= N
DMA-addressable buffers (possibly by using an IOMMU or swiotlb internally) at
that point.

On the IOMMU API level you have much more information available about the actual
hardware and can prepare the buffers in a way that makes both devices happy.
That's why iommu_map_sgtable combined with iovad->granule aligned sgt entries
can actually guarantee to map the entire list to a single contiguous IOVA block.


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 6/8] iommu: Move IOMMU pagesize check to attach_device

2021-09-01 Thread Sven Peter via iommu



On Tue, Aug 31, 2021, at 23:39, Alyssa Rosenzweig wrote:
> > +   if ((1 << __ffs(domain->pgsize_bitmap)) > PAGE_SIZE) {
> 
> Not a fan of this construction. Could you assign `(1 <<
> __ffs(domain->pgsize_bitmap))` to an appropriately named temporary (e.g
> min_io_pgsize) so it's clearer what's going on?

Good point, will do that for the next version.

> 
> > +   pr_warn("IOMMU page size cannot represent CPU pages.\n");
> 
> "Represent" how?
> 

Looks like I dropped an "exactly" there when taking this line from iova.c :)



Thanks,


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 3/8] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-09-01 Thread Sven Peter via iommu



On Tue, Aug 31, 2021, at 23:30, Alyssa Rosenzweig wrote:
> I use this function for cross-device sharing on the M1 display driver.
> Arguably this is unsafe but it works on 16k kernels and if you want to
> test the function on 4k, you know where my code is.
> 

My biggest issue is that I do not understand how this function is supposed
to be used correctly. It would work fine as-is if it only ever gets passed 
buffers
allocated by the coherent API but there's not way to check or guarantee that.
There may also be callers making assumptions that no longer hold when
iovad->granule > PAGE_SIZE.


Regarding your case: I'm not convinced the function is meant to be used there.
If I understand it correctly, your code first allocates memory with 
dma_alloc_coherent
(which possibly creates a sgt internally and then maps it with iommu_map_sg),
then coerces that back into a sgt with dma_get_sgtable, and then maps that sgt 
to
another iommu domain with dma_map_sg while assuming that the result will be 
contiguous
in IOVA space. It'll work out because dma_alloc_coherent is the very thing
meant to allocate pages that can be mapped into kernel and device VA space
as a single contiguous block and because both of your IOMMUs are different
instances of the same HW block. Anything allocated by dma_alloc_coherent for the
first IOMMU will have the right shape that will allow it to be mapped as
a single contiguous block for the second IOMMU.

What could be done in your case is to instead use the IOMMU API,
allocate the pages yourself (while ensuring the sgt your create is made up
of blocks with size and physaddr aligned to max(domain_a->granule, 
domain_b->granule))
and then just use iommu_map_sg for both domains which actually comes with the
guarantee that the result will be a single contiguous block in IOVA space and
doesn't required the sgt roundtrip.



Sven


> On Sat, Aug 28, 2021 at 05:36:37PM +0200, Sven Peter wrote:
> > Pretend that iommu_dma_get_sgtable is not implemented when
> > granule > PAGE_SIZE since I can neither test this function right now
> > nor do I fully understand how it is used.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >  drivers/iommu/dma-iommu.c | 6 ++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index d6e273ec3de6..64fbd9236820 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -1315,9 +1315,15 @@ static int iommu_dma_get_sgtable(struct device *dev, 
> > struct sg_table *sgt,
> > void *cpu_addr, dma_addr_t dma_addr, size_t size,
> > unsigned long attrs)
> >  {
> > +   struct iommu_domain *domain = iommu_get_dma_domain(dev);
> > +   struct iommu_dma_cookie *cookie = domain->iova_cookie;
> > +   struct iova_domain *iovad = &cookie->iovad;
> > struct page *page;
> > int ret;
> >  
> > +   if (iovad->granule > PAGE_SIZE)
> > +   return -ENXIO;
> > +
> > if (IS_ENABLED(CONFIG_DMA_REMAP) && is_vmalloc_addr(cpu_addr)) {
> > struct page **pages = dma_common_find_pages(cpu_addr);
> >  
> > -- 
> > 2.25.1
> > 
> 


-- 
Sven Peter
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 2/8] iommu/dma: Fail unaligned map requests for untrusted devs

2021-08-28 Thread Sven Peter via iommu
and ofc shortly after submitting this series I realized this doesn't quite work 
yet:
swiotlb_tbl_map_single can return a 16KB buffer that's only aligned to a 4KB 
boundary. 
v3 will need at least another change to ensure that the result will be aligned 
to
a 16KB boundary as well.


Sven


On Sat, Aug 28, 2021, at 17:36, Sven Peter wrote:
> If swiotlb is enabled we should never try to create any mappings that
> would expose more memory than requested to the device.
> WARN_ON and refuse those mappings just in case.
> 
> Signed-off-by: Sven Peter 
> ---
>  drivers/iommu/dma-iommu.c | 9 -
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index e8eae34e9e4f..d6e273ec3de6 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -534,13 +534,20 @@ static dma_addr_t __iommu_dma_map(struct device 
> *dev, phys_addr_t phys,
>   struct iommu_dma_cookie *cookie = domain->iova_cookie;
>   struct iova_domain *iovad = &cookie->iovad;
>   size_t iova_off = iova_offset(iovad, phys);
> + size_t size_aligned = iova_align(iovad, size + iova_off);
>   dma_addr_t iova;
>  
>   if (static_branch_unlikely(&iommu_deferred_attach_enabled) &&
>   iommu_deferred_attach(dev, domain))
>   return DMA_MAPPING_ERROR;
>  
> - size = iova_align(iovad, size + iova_off);
> + if (IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev)) {
> + if (WARN_ON(iova_off))
> + return DMA_MAPPING_ERROR;
> + if (WARN_ON(size_aligned != size))
> + return DMA_MAPPING_ERROR;
> + }
> +     size = size_aligned;
>  
>   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
>   if (!iova)
> -- 
> 2.25.1
> 
> 


-- 
Sven Peter
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 8/8] iommu/dart: Remove force_bypass logic

2021-08-28 Thread Sven Peter via iommu
Now that the dma-iommu API supports IOMMU granules which are larger than
the CPU page size and that the kernel no longer runs into a BUG_ON when
devices are attached to a domain with such a granule there's no need to
force bypass mode anymore.

Signed-off-by: Sven Peter 
---
 drivers/iommu/apple-dart.c | 14 ++
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 559db9259e65..c37fb4790e8a 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -90,7 +90,6 @@
  * @lock: lock for hardware operations involving this dart
  * @pgsize: pagesize supported by this DART
  * @supports_bypass: indicates if this DART supports bypass mode
- * @force_bypass: force bypass mode due to pagesize mismatch?
  * @sid2group: maps stream ids to iommu_groups
  * @iommu: iommu core device
  */
@@ -107,7 +106,6 @@ struct apple_dart {
 
u32 pgsize;
u32 supports_bypass : 1;
-   u32 force_bypass : 1;
 
struct iommu_group *sid2group[DART_MAX_STREAMS];
struct iommu_device iommu;
@@ -506,9 +504,6 @@ static int apple_dart_attach_dev(struct iommu_domain 
*domain,
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
 
-   if (cfg->stream_maps[0].dart->force_bypass &&
-   domain->type != IOMMU_DOMAIN_IDENTITY)
-   return -EINVAL;
if (!cfg->stream_maps[0].dart->supports_bypass &&
domain->type == IOMMU_DOMAIN_IDENTITY)
return -EINVAL;
@@ -638,8 +633,6 @@ static int apple_dart_of_xlate(struct device *dev, struct 
of_phandle_args *args)
if (cfg_dart) {
if (cfg_dart->supports_bypass != dart->supports_bypass)
return -EINVAL;
-   if (cfg_dart->force_bypass != dart->force_bypass)
-   return -EINVAL;
if (cfg_dart->pgsize != dart->pgsize)
return -EINVAL;
}
@@ -713,8 +706,6 @@ static int apple_dart_def_domain_type(struct device *dev)
 {
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
 
-   if (cfg->stream_maps[0].dart->force_bypass)
-   return IOMMU_DOMAIN_IDENTITY;
if (!cfg->stream_maps[0].dart->supports_bypass)
return IOMMU_DOMAIN_DMA;
 
@@ -844,7 +835,6 @@ static int apple_dart_probe(struct platform_device *pdev)
dart_params[1] = readl(dart->regs + DART_PARAMS2);
dart->pgsize = 1 << FIELD_GET(DART_PARAMS_PAGE_SHIFT, dart_params[0]);
dart->supports_bypass = dart_params[1] & DART_PARAMS_BYPASS_SUPPORT;
-   dart->force_bypass = dart->pgsize > PAGE_SIZE;
 
ret = request_irq(dart->irq, apple_dart_irq, IRQF_SHARED,
  "apple-dart fault handler", dart);
@@ -868,8 +858,8 @@ static int apple_dart_probe(struct platform_device *pdev)
 
dev_info(
&pdev->dev,
-   "DART [pagesize %x, bypass support: %d, bypass forced: %d] 
initialized\n",
-   dart->pgsize, dart->supports_bypass, dart->force_bypass);
+   "DART [pagesize %x, bypass support: %d] initialized\n",
+   dart->pgsize, dart->supports_bypass);
return 0;
 
 err_sysfs_remove:
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 7/8] iommu: Introduce __IOMMU_DOMAIN_LP

2021-08-28 Thread Sven Peter via iommu
__IOMMU_DOMAIN_LP (large pages) indicates that a domain can handle
conditions where PAGE_SIZE might be smaller than the IOMMU page size.
Always allow attaching devices to such domains and set the flag for
IOMMU_DOMAIN_DMA, which can now handle these situations.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c | 2 ++
 include/linux/iommu.h | 8 ++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index f02b727d3054..77d1ee14c7d0 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1980,6 +1980,8 @@ static int iommu_check_page_size(struct iommu_domain 
*domain)
 {
if (!(domain->type & __IOMMU_DOMAIN_PAGING))
return 0;
+   if (domain->type & __IOMMU_DOMAIN_LP)
+   return 0;
 
if ((1 << __ffs(domain->pgsize_bitmap)) > PAGE_SIZE) {
pr_warn("IOMMU page size cannot represent CPU pages.\n");
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 6633040a13f9..40c1ad6be4e7 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -62,6 +62,8 @@ struct iommu_domain_geometry {
  implementation  */
 #define __IOMMU_DOMAIN_PT  (1U << 2)  /* Domain is identity mapped   */
 #define __IOMMU_DOMAIN_DMA_FQ  (1U << 3)  /* DMA-API uses flush queue*/
+#define __IOMMU_DOMAIN_LP  (1U << 4)  /* Support for PAGE_SIZE smaller
+ than IOMMU page size*/
 
 /*
  * This are the possible domain-types
@@ -81,10 +83,12 @@ struct iommu_domain_geometry {
 #define IOMMU_DOMAIN_IDENTITY  (__IOMMU_DOMAIN_PT)
 #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
 #define IOMMU_DOMAIN_DMA   (__IOMMU_DOMAIN_PAGING |\
-__IOMMU_DOMAIN_DMA_API)
+__IOMMU_DOMAIN_DMA_API |   \
+__IOMMU_DOMAIN_LP)
 #define IOMMU_DOMAIN_DMA_FQ(__IOMMU_DOMAIN_PAGING |\
 __IOMMU_DOMAIN_DMA_API |   \
-__IOMMU_DOMAIN_DMA_FQ)
+__IOMMU_DOMAIN_DMA_FQ |\
+__IOMMU_DOMAIN_LP)
 
 struct iommu_domain {
unsigned type;
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 6/8] iommu: Move IOMMU pagesize check to attach_device

2021-08-28 Thread Sven Peter via iommu
The iova allocator is capable of handling any granularity which is a power
of two. Remove the much stronger condition that the granularity must be
smaller or equal to the CPU page size from a BUG_ON there.
Instead, check this condition during __iommu_attach_device and fail
gracefully.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c | 34 +++---
 drivers/iommu/iova.c  |  7 ---
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b4499b1915fa..f02b727d3054 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -79,6 +79,8 @@ static struct iommu_domain *__iommu_domain_alloc(struct 
bus_type *bus,
 unsigned type);
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev);
+static void __iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev);
 static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
 static void __iommu_detach_group(struct iommu_domain *domain,
@@ -1974,6 +1976,18 @@ void iommu_domain_free(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL_GPL(iommu_domain_free);
 
+static int iommu_check_page_size(struct iommu_domain *domain)
+{
+   if (!(domain->type & __IOMMU_DOMAIN_PAGING))
+   return 0;
+
+   if ((1 << __ffs(domain->pgsize_bitmap)) > PAGE_SIZE) {
+   pr_warn("IOMMU page size cannot represent CPU pages.\n");
+   return -EFAULT;
+   }
+
+   return 0;
+}
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev)
 {
@@ -1983,9 +1997,23 @@ static int __iommu_attach_device(struct iommu_domain 
*domain,
return -ENODEV;
 
ret = domain->ops->attach_dev(domain, dev);
-   if (!ret)
-   trace_attach_device_to_domain(dev);
-   return ret;
+   if (ret)
+   return ret;
+
+   /*
+* Check that CPU pages can be represented by the IOVA granularity.
+* This has to be done after ops->attach_dev since many IOMMU drivers
+* only limit domain->pgsize_bitmap after having attached the first
+* device.
+*/
+   ret = iommu_check_page_size(domain);
+   if (ret) {
+   __iommu_detach_device(domain, dev);
+   return ret;
+   }
+
+   trace_attach_device_to_domain(dev);
+   return 0;
 }
 
 int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 0af42fb93a49..302e6dfa7cdc 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -50,10 +50,11 @@ init_iova_domain(struct iova_domain *iovad, unsigned long 
granule,
 {
/*
 * IOVA granularity will normally be equal to the smallest
-* supported IOMMU page size; both *must* be capable of
-* representing individual CPU pages exactly.
+* supported IOMMU page size; while both usually are capable of
+* representing individual CPU pages exactly the IOVA allocator
+* supports any granularities that are an exact power of two.
 */
-   BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
+   BUG_ON(!is_power_of_2(granule));
 
spin_lock_init(&iovad->iova_rbtree_lock);
iovad->rbroot = RB_ROOT;
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 4/8] iommu/dma: Support granule > PAGE_SIZE in dma_map_sg

2021-08-28 Thread Sven Peter via iommu
Add support to iommu_dma_map_sg's impedance matching to also align
sg_lists correctly when the IOMMU granule is larger than PAGE_SIZE.

Co-developed-by: Robin Murphy 
Signed-off-by: Robin Murphy 
Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 18 ++
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 64fbd9236820..a091cff5829d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -932,8 +932,8 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;
 
-   s->offset += s_iova_off;
-   s->length = s_length;
+   sg_set_page(s, phys_to_page(sg_phys(s) + s_iova_off), s_length,
+   s_iova_off & ~PAGE_MASK);
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
 
@@ -977,10 +977,11 @@ static void __invalidate_sg(struct scatterlist *sg, int 
nents)
int i;
 
for_each_sg(sg, s, nents, i) {
-   if (sg_dma_address(s) != DMA_MAPPING_ERROR)
-   s->offset += sg_dma_address(s);
if (sg_dma_len(s))
-   s->length = sg_dma_len(s);
+   sg_set_page(s,
+   phys_to_page(sg_phys(s) + 
sg_dma_address(s)),
+   sg_dma_len(s),
+   sg_dma_address(s) & ~PAGE_MASK);
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
}
@@ -1056,15 +1057,16 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 * stashing the unaligned parts in the as-yet-unused DMA fields.
 */
for_each_sg(sg, s, nents, i) {
-   size_t s_iova_off = iova_offset(iovad, s->offset);
+   phys_addr_t s_phys = sg_phys(s);
+   size_t s_iova_off = iova_offset(iovad, s_phys);
size_t s_length = s->length;
size_t pad_len = (mask - iova_len + 1) & mask;
 
sg_dma_address(s) = s_iova_off;
sg_dma_len(s) = s_length;
-   s->offset -= s_iova_off;
s_length = iova_align(iovad, s_length + s_iova_off);
-   s->length = s_length;
+   sg_set_page(s, phys_to_page(s_phys - s_iova_off),
+   s_length, s->offset & ~s_iova_off);
 
/*
 * Due to the alignment of our single IOVA allocation, we can
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 5/8] iommu/dma: Support PAGE_SIZE < iovad->granule allocations

2021-08-28 Thread Sven Peter via iommu
Noncontiguous allocations must be made up of individual blocks
in a way that allows those blocks to be mapped contiguously in IOVA space.
For IOMMU page sizes larger than the CPU page size this can be done
by allocating all individual blocks from pools with
order >= get_order(iovad->granule). Some spillover pages might be
allocated at the end, which can however immediately be freed.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 99 +++
 1 file changed, 89 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index a091cff5829d..e57966bcfae1 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -618,6 +619,9 @@ static struct page **__iommu_dma_alloc_pages(struct device 
*dev,
 {
struct page **pages;
unsigned int i = 0, nid = dev_to_node(dev);
+   unsigned int j;
+   unsigned long min_order = __fls(order_mask);
+   unsigned int min_order_size = 1U << min_order;
 
order_mask &= (2U << MAX_ORDER) - 1;
if (!order_mask)
@@ -657,15 +661,37 @@ static struct page **__iommu_dma_alloc_pages(struct 
device *dev,
split_page(page, order);
break;
}
-   if (!page) {
-   __iommu_dma_free_pages(pages, i);
-   return NULL;
+
+   /*
+* If we have no valid page here we might be trying to allocate
+* the last block consisting of 1<pgsize_bitmap;
+   struct scatterlist *last_sg;
struct page **pages;
dma_addr_t iova;
+   phys_addr_t orig_s_phys;
+   size_t orig_s_len, orig_s_off, s_iova_off, iova_size;
 
if (static_branch_unlikely(&iommu_deferred_attach_enabled) &&
iommu_deferred_attach(dev, domain))
return NULL;
 
min_size = alloc_sizes & -alloc_sizes;
-   if (min_size < PAGE_SIZE) {
+   if (iovad->granule > PAGE_SIZE) {
+   if (size < iovad->granule) {
+   /* ensure a single contiguous allocation */
+   min_size = ALIGN(size, PAGE_SIZE*(1U<coherent_dma_mask, dev);
+   iova_size = iova_align(iovad, size);
+   iova = iommu_dma_alloc_iova(domain, iova_size, dev->coherent_dma_mask, 
dev);
if (!iova)
goto out_free_pages;
 
-   if (sg_alloc_table_from_pages(sgt, pages, count, 0, size, GFP_KERNEL))
+   last_sg = __sg_alloc_table_from_pages(sgt, pages, count, 0, iova_size,
+ UINT_MAX, NULL, 0, GFP_KERNEL);
+   if (IS_ERR(last_sg))
goto out_free_iova;
 
if (!(ioprot & IOMMU_CACHE)) {
@@ -721,18 +760,58 @@ static struct page 
**__iommu_dma_alloc_noncontiguous(struct device *dev,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
 
+   if (iovad->granule > PAGE_SIZE) {
+   if (size < iovad->granule) {
+   /*
+* we only have a single sg list entry here that is
+* likely not aligned to iovad->granule. adjust the
+* entry to represent the encapsulating IOMMU page
+* and then later restore everything to its original
+* values, similar to the impedance matching done in
+* iommu_dma_map_sg.
+*/
+   orig_s_phys = sg_phys(sgt->sgl);
+   orig_s_len = sgt->sgl->length;
+   orig_s_off = sgt->sgl->offset;
+   s_iova_off = iova_offset(iovad, orig_s_phys);
+
+   sg_set_page(sgt->sgl,
+   phys_to_page(orig_s_phys - s_iova_off),
+   iova_align(iovad, orig_s_len + s_iova_off),
+   sgt->sgl->offset & ~s_iova_off);
+   } else {
+   /*
+* convince iommu_map_sg_atomic to map the last block
+* even though it may be too small.
+*/
+   orig_s_len = last_sg->length;
+   last_sg->length = iova_align(iovad, last_sg->length);
+   }
+   }
+
if (iommu_map_sg_atomic(domain, iova, sgt->sgl, sgt->orig_nents, ioprot)
-   < size)
+   < iova_size)
goto out_free_sg;
 
+   if (iovad->granule > PAGE_SIZE) {
+   if (size < iovad->granule) {
+   sg_set_page(sgt->sgl, phys_to_page(orig_s_phys),
+  

[PATCH v2 3/8] iommu/dma: Disable get_sgtable for granule > PAGE_SIZE

2021-08-28 Thread Sven Peter via iommu
Pretend that iommu_dma_get_sgtable is not implemented when
granule > PAGE_SIZE since I can neither test this function right now
nor do I fully understand how it is used.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d6e273ec3de6..64fbd9236820 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1315,9 +1315,15 @@ static int iommu_dma_get_sgtable(struct device *dev, 
struct sg_table *sgt,
void *cpu_addr, dma_addr_t dma_addr, size_t size,
unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
struct page *page;
int ret;
 
+   if (iovad->granule > PAGE_SIZE)
+   return -ENXIO;
+
if (IS_ENABLED(CONFIG_DMA_REMAP) && is_vmalloc_addr(cpu_addr)) {
struct page **pages = dma_common_find_pages(cpu_addr);
 
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 1/8] iommu/dma: Align size for untrusted devs to IOVA granule

2021-08-28 Thread Sven Peter via iommu
Up until now PAGE_SIZE was always a multiple of iovad->granule
such that adjacent pages were never exposed to untrusted devices
due to allocations done as part of the coherent DMA API.
With PAGE_SIZE < iovad->granule however all these allocations
must also be aligned to iovad->granule.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 40 ++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d0bc8c06e1a4..e8eae34e9e4f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -735,10 +735,16 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
dma_addr_t *dma_handle, gfp_t gfp, pgprot_t prot,
unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
struct page **pages;
struct sg_table sgt;
void *vaddr;
 
+   if (dev_is_untrusted(dev))
+   size = iova_align(iovad, size);
+
pages = __iommu_dma_alloc_noncontiguous(dev, size, &sgt, gfp, prot,
attrs);
if (!pages)
@@ -762,12 +768,18 @@ static struct sg_table 
*iommu_dma_alloc_noncontiguous(struct device *dev,
size_t size, enum dma_data_direction dir, gfp_t gfp,
unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
struct dma_sgt_handle *sh;
 
sh = kmalloc(sizeof(*sh), gfp);
if (!sh)
return NULL;
 
+   if (dev_is_untrusted(dev))
+   size = iova_align(iovad, size);
+
sh->pages = __iommu_dma_alloc_noncontiguous(dev, size, &sh->sgt, gfp,
PAGE_KERNEL, attrs);
if (!sh->pages) {
@@ -780,8 +792,15 @@ static struct sg_table 
*iommu_dma_alloc_noncontiguous(struct device *dev,
 static void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
struct sg_table *sgt, enum dma_data_direction dir)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
struct dma_sgt_handle *sh = sgt_handle(sgt);
 
+
+   if (dev_is_untrusted(dev))
+   size = iova_align(iovad, size);
+
__iommu_dma_unmap(dev, sgt->sgl->dma_address, size);
__iommu_dma_free_pages(sh->pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
sg_free_table(&sh->sgt);
@@ -1127,10 +1146,17 @@ static void iommu_dma_unmap_resource(struct device 
*dev, dma_addr_t handle,
 
 static void __iommu_dma_free(struct device *dev, size_t size, void *cpu_addr)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
size_t alloc_size = PAGE_ALIGN(size);
-   int count = alloc_size >> PAGE_SHIFT;
+   int count;
struct page *page = NULL, **pages = NULL;
 
+   if (dev_is_untrusted(dev))
+   alloc_size = iova_align(iovad, alloc_size);
+   count = alloc_size >> PAGE_SHIFT;
+
/* Non-coherent atomic allocation? Easy */
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
dma_free_from_pool(dev, cpu_addr, alloc_size))
@@ -1166,12 +1192,18 @@ static void iommu_dma_free(struct device *dev, size_t 
size, void *cpu_addr,
 static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
struct page **pagep, gfp_t gfp, unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
bool coherent = dev_is_dma_coherent(dev);
size_t alloc_size = PAGE_ALIGN(size);
int node = dev_to_node(dev);
struct page *page = NULL;
void *cpu_addr;
 
+   if (dev_is_untrusted(dev))
+   alloc_size = iova_align(iovad, alloc_size);
+
page = dma_alloc_contiguous(dev, alloc_size, gfp);
if (!page)
page = alloc_pages_node(node, gfp, get_order(alloc_size));
@@ -1203,6 +1235,9 @@ static void *iommu_dma_alloc_pages(struct device *dev, 
size_t size,
 static void *iommu_dma_alloc(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct 

[PATCH v2 2/8] iommu/dma: Fail unaligned map requests for untrusted devs

2021-08-28 Thread Sven Peter via iommu
If swiotlb is enabled we should never try to create any mappings that
would expose more memory than requested to the device.
WARN_ON and refuse those mappings just in case.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 9 -
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index e8eae34e9e4f..d6e273ec3de6 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -534,13 +534,20 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = &cookie->iovad;
size_t iova_off = iova_offset(iovad, phys);
+   size_t size_aligned = iova_align(iovad, size + iova_off);
dma_addr_t iova;
 
if (static_branch_unlikely(&iommu_deferred_attach_enabled) &&
iommu_deferred_attach(dev, domain))
return DMA_MAPPING_ERROR;
 
-   size = iova_align(iovad, size + iova_off);
+   if (IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev)) {
+   if (WARN_ON(iova_off))
+   return DMA_MAPPING_ERROR;
+   if (WARN_ON(size_aligned != size))
+   return DMA_MAPPING_ERROR;
+   }
+   size = size_aligned;
 
iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
if (!iova)
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 0/8] Support IOMMU page sizes larger than the CPU page size

2021-08-28 Thread Sven Peter via iommu
RFC Patch: 
https://lore.kernel.org/linux-iommu/20210806155523.50429-1-s...@svenpeter.dev/

Hi,

After a very helpful discussion with Robin Murphy on the RFC, here's v2 that is 
slowly
starting to look sane.
I've been running this code for two weeks now and mainly tested it with usb 
storage devices
connected to dwc3 and to xhci over pcie on the M1.

Some background: On the Apple M1 the IOMMUs are hardwired to only support 16 KB 
pages.
We'd still like to boot Linux with 4KB pages though because that's what most 
distros
ship these days. This patch series adds support for that setup to the IOMMU DMA 
API.

This is essentially done by always mapping the encapsulating IOMMU page and 
adjusting
the returned iova offset. There are also changes to only allow DMA domains to 
make use
of this and prevent UNMANAGED domains from encountering unexpected situations.

For untrusted devices the allocation size is simply aligned to iovad->granule 
if they
don't already go through the swiotlb path. I have not been able to test that 
part
so far though since there's no Thunderbolt support for the M1 yet.

The series is based on top of iommu/next (and without the last commit probably 
also on
iommu/core). It won't apply cleanly on apple/dart since it already takes 
Robin's DMA domain
cleanup series into account.


Best,

Sven
 
Sven Peter (8):
  iommu/dma: Align size for untrusted devs to IOVA granule
  iommu/dma: Fail unaligned map requests for untrusted devs
  iommu/dma: Disable get_sgtable for granule > PAGE_SIZE
  iommu/dma: Support granule > PAGE_SIZE in dma_map_sg
  iommu/dma: Support PAGE_SIZE < iovad->granule allocations
  iommu: Move IOMMU pagesize check to attach_device
  iommu: Introduce __IOMMU_DOMAIN_LP
  iommu/dart: Remove force_bypass logic

 drivers/iommu/apple-dart.c |  14 +--
 drivers/iommu/dma-iommu.c  | 172 -
 drivers/iommu/iommu.c  |  36 +++-
 drivers/iommu/iova.c   |   7 +-
 include/linux/iommu.h  |   8 +-
 5 files changed, 197 insertions(+), 40 deletions(-)

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 00/24] iommu: Refactor DMA domain strictness

2021-08-21 Thread Sven Peter via iommu



On Wed, Aug 18, 2021, at 17:13, Robin Murphy wrote:
> Sven - I've prepared the follow-up patches already[1], so consider 
> yourself off the hook (I see no point in trying to fix the nominal DART 
> cookie bugs between now and then) :)
> 

Great, thanks for taking care of that! :)
Just tested your branch and everything works. Feel free to add 
Acked-by: Sven Peter 
Tested-by: Sven Peter 


Best,


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [iommu:apple/dart 3/3] drivers/iommu/apple-dart.c:730:17: error: initialization of 'size_t (*)(struct iommu_domain *, long unsigned int, size_t, struct iommu_iotlb_gather *)' {aka 'long unsigned

2021-08-15 Thread Sven Peter via iommu



On Thu, Aug 12, 2021, at 13:29, Joerg Roedel wrote:
> Hi Sven,
> 
> On Tue, Aug 10, 2021 at 08:09:53AM +0200, Sven Peter wrote:
> > This happens because apple/dart is missing the "Optimizing 
> > iommu_[map/unmap] performance"
> > series which is already in the core branch [1].
> > The same commit works fine in iommu/next since that branch merges both 
> > iommu/core and
> > apple/dart.
> 
> Okay, thanks. I re-based the DART patches on-top of my core branch,
> which contains the changes for iommu_[map/unmap] performance. I
> generally don't like rebasing topic branches, but made an exception here
> to not break bisectability.
> 
> Thanks,
> 
>   Joerg
> 

Hi Joerg,

Thanks, and sorry about that! I'll try to make it more clear if anything depends
on another series in the future or just try to avoid it altogether if possible.


Just a heads up about a similar situation you may already be aware of: Once 
Robin's
DMA domain strictness refactoring [1] is merged, the current DART driver will 
fail due
to patch 12 there, which unexports iommu_get_dma_cookie. It'll need a small
adjustment just like all the other drivers (which will also fix two small bugs
it just made me notice: I never use iommu_put_dma_cookie and also 
unconditionally
grab a DMA cookie for all domain types).

Unless I'm mistaken I can't make that adjustment before the first patch of
that series has been merged, and Robin can't make that adjustment in his series
because it'll presumably go through another topic branch.


Best,


Sven

[1] 
https://lore.kernel.org/linux-iommu/cover.1628682048.git.robin.mur...@arm.com/


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-11 Thread Sven Peter via iommu



On Tue, Aug 10, 2021, at 11:51, Robin Murphy wrote:
> On 2021-08-09 21:45, Sven Peter wrote:
> > 
> > 
> > On Mon, Aug 9, 2021, at 19:41, Robin Murphy wrote:
> >> On 2021-08-07 12:47, Sven Peter via iommu wrote:
> >>>
> >>>
> >>> On Fri, Aug 6, 2021, at 20:04, Robin Murphy wrote:
> >>>> On 2021-08-06 16:55, Sven Peter via iommu wrote:
> >>>>> @@ -1006,6 +1019,31 @@ static int iommu_dma_map_sg(struct device *dev, 
> >>>>> struct scatterlist *sg,
> >>>>> if (dev_is_untrusted(dev))
> >>>>> return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, 
> >>>>> attrs);
> >>>>> 
> >>>>> +   /*
> >>>>> +* If the IOMMU pagesize is larger than the CPU pagesize we will
> >>>>> +* very likely run into sgs with a physical address that is not 
> >>>>> aligned
> >>>>> +* to an IOMMU page boundary. Fall back to just mapping every 
> >>>>> entry
> >>>>> +* independently with __iommu_dma_map then.
> >>>>
> >>>> Scatterlist segments often don't have nicely aligned ends, which is why
> >>>> we already align things to IOVA granules in main loop here. I think in
> >>>> principle we'd just need to move the non-IOVA-aligned part of the
> >>>> address from sg->page to sg->offset in the temporary transformation for
> >>>> the rest of the assumptions to hold. I don't blame you for being timid
> >>>> about touching that, though - it took me 3 tries to get right when I
> >>>> first wrote it...
> >>>>
> >>>
> >>>
> >>> I've spent some time with that code now and I think we cannot use it
> >>> but have to fall back to iommu_dma_map_sg_swiotlb (even though that 
> >>> swiotlb
> >>> part is a lie then):
> >>>
> >>> When we have sg_phys(s) = 0x802e65000 with s->offset = 0 the paddr
> >>> is aligned to PAGE_SIZE but has an offset of 0x1000 from something
> >>> the IOMMU can map.
> >>> Now this would result in s->offset = -0x1000 which is already weird
> >>> enough.
> >>> Offset is unsigned (and 32bit) so this will actually look like
> >>> s->offset = 0xf000 then, which isn't much better.
> >>> And then sg_phys(s) = 0x902e64000 (instead of 0x802e64000) and
> >>> we'll map some random memory in iommu_map_sg_atomic and a little bit later
> >>> everything explodes.
> >>>
> >>> Now I could probably adjust the phys addr backwards and make sure offset 
> >>> is
> >>> always positive (and possibly larger than PAGE_SIZE) and later restore it
> >>> in __finalise_sg then but I feel like that's pushing this a little bit 
> >>> too far.
> >>
> >> Yes, that's what I meant. At a quick guess, something like the
> >> completely untested diff below.
> > 
> > That unfortunately results in unaligned mappings
> 
> You mean it even compiles!? :D

I was more impressed that it already almost worked correctly :)

> 
> > [9.630334] iommu: unaligned: iova 0xbff4 pa 0x000801a3b000 size 
> > 0x4000 min_pagesz 0x4000
> > 
> > I'll take a closer look later this week and see if I can fix it.
> 
> On reflection, "s->offset ^ s_iova_off" is definitely wrong, that more 
> likely wants to be "s->offset & ~s_iova_off".
> 
> Robin.
> 


If I change

sg_set_page(s, phys_to_page(sg_phys(s)), s_length,
s_iova_off & ~PAGE_MASK);

in __finalise_sg (and the same thing in __invalidate_sg) to

sg_set_page(s, phys_to_page(sg_phys(s) + s_iova_off), s_length,
s_iova_off & ~PAGE_MASK);

then it also restores the original fields correctly.


What is the proper way to credit you for coming up with this?
Do you create the commit and I apply it to my local tree and
include it in my submission once I have fixed the other
issues? Or do I create the commit and put a Suggested-by
in the message?


Either way, here's the patch that I have right now:

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7ce74476699d..ba31dc59566d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -907,8 +907,8 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
   

Re: [PATCH] iommu: APPLE_DART should depend on ARCH_APPLE

2021-08-11 Thread Sven Peter via iommu
Good catch, thanks!

Acked-by: Sven Peter 

Sven

On Tue, Aug 10, 2021, at 15:47, Geert Uytterhoeven wrote:
> The Apple DART (Device Address Resolution Table) IOMMU is only present
> on Apple ARM SoCs like the M1.  Hence add a dependency on ARCH_APPLE, to
> prevent asking the user about this driver when configuring a kernel
> without support for the Apple Silicon SoC family.
> 
> Fixes: 05ce9d20d699b093 ("iommu/dart: Add DART iommu driver")
> Signed-off-by: Geert Uytterhoeven 
> ---
>  drivers/iommu/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index dfe81da483e9e073..e908b8222e4ed679 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -292,7 +292,7 @@ config SPAPR_TCE_IOMMU
>  
>  config APPLE_DART
>   tristate "Apple DART IOMMU Support"
> - depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
> + depends on ARCH_APPLE || (COMPILE_TEST && !GENERIC_ATOMIC64)
>   select IOMMU_API
>   select IOMMU_IO_PGTABLE_LPAE
>   default ARCH_APPLE
> -- 
> 2.25.1
> 
> 
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [iommu:apple/dart 3/3] drivers/iommu/apple-dart.c:730:17: error: initialization of 'size_t (*)(struct iommu_domain *, long unsigned int, size_t, struct iommu_iotlb_gather *)' {aka 'long unsigned

2021-08-09 Thread Sven Peter via iommu
;drivers/iommu/apple-dart.c:382:1: error: control reaches end of 
> non-void function [-Werror=return-type]
>  382 | }
>  | ^
>cc1: some warnings being treated as errors
> 
> 
> vim +730 drivers/iommu/apple-dart.c
> 
>723
>724static const struct iommu_ops apple_dart_iommu_ops = {
>725.domain_alloc = apple_dart_domain_alloc,
>726.domain_free = apple_dart_domain_free,
>727.attach_dev = apple_dart_attach_dev,
>728.detach_dev = apple_dart_detach_dev,
>  > 729.map_pages = apple_dart_map_pages,
>  > 730.unmap_pages = apple_dart_unmap_pages,
>731.flush_iotlb_all = apple_dart_flush_iotlb_all,
>732.iotlb_sync = apple_dart_iotlb_sync,
>733.iotlb_sync_map = apple_dart_iotlb_sync_map,
>734.iova_to_phys = apple_dart_iova_to_phys,
>735.probe_device = apple_dart_probe_device,
>736.release_device = apple_dart_release_device,
>737.device_group = apple_dart_device_group,
>738.of_xlate = apple_dart_of_xlate,
>739.def_domain_type = apple_dart_def_domain_type,
>740.pgsize_bitmap = -1UL, /* Restricted during dart probe 
> */
>741};
>742
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org
> 
> Attachments:
> * .config.gz


-- 
Sven Peter
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-09 Thread Sven Peter via iommu



On Mon, Aug 9, 2021, at 19:41, Robin Murphy wrote:
> On 2021-08-07 12:47, Sven Peter via iommu wrote:
> > 
> > 
> > On Fri, Aug 6, 2021, at 20:04, Robin Murphy wrote:
> >> On 2021-08-06 16:55, Sven Peter via iommu wrote:
> >>> @@ -1006,6 +1019,31 @@ static int iommu_dma_map_sg(struct device *dev, 
> >>> struct scatterlist *sg,
> >>>   if (dev_is_untrusted(dev))
> >>>   return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, 
> >>> attrs);
> >>>
> >>> + /*
> >>> +  * If the IOMMU pagesize is larger than the CPU pagesize we will
> >>> +  * very likely run into sgs with a physical address that is not aligned
> >>> +  * to an IOMMU page boundary. Fall back to just mapping every entry
> >>> +  * independently with __iommu_dma_map then.
> >>
> >> Scatterlist segments often don't have nicely aligned ends, which is why
> >> we already align things to IOVA granules in main loop here. I think in
> >> principle we'd just need to move the non-IOVA-aligned part of the
> >> address from sg->page to sg->offset in the temporary transformation for
> >> the rest of the assumptions to hold. I don't blame you for being timid
> >> about touching that, though - it took me 3 tries to get right when I
> >> first wrote it...
> >>
> > 
> > 
> > I've spent some time with that code now and I think we cannot use it
> > but have to fall back to iommu_dma_map_sg_swiotlb (even though that swiotlb
> > part is a lie then):
> > 
> > When we have sg_phys(s) = 0x802e65000 with s->offset = 0 the paddr
> > is aligned to PAGE_SIZE but has an offset of 0x1000 from something
> > the IOMMU can map.
> > Now this would result in s->offset = -0x1000 which is already weird
> > enough.
> > Offset is unsigned (and 32bit) so this will actually look like
> > s->offset = 0xf000 then, which isn't much better.
> > And then sg_phys(s) = 0x902e64000 (instead of 0x802e64000) and
> > we'll map some random memory in iommu_map_sg_atomic and a little bit later
> > everything explodes.
> > 
> > Now I could probably adjust the phys addr backwards and make sure offset is
> > always positive (and possibly larger than PAGE_SIZE) and later restore it
> > in __finalise_sg then but I feel like that's pushing this a little bit too 
> > far.
> 
> Yes, that's what I meant. At a quick guess, something like the
> completely untested diff below.

That unfortunately results in unaligned mappings

[9.630334] iommu: unaligned: iova 0xbff4 pa 0x000801a3b000 size 
0x4000 min_pagesz 0x4000

I'll take a closer look later this week and see if I can fix it.

> It really comes down to what we want to
> achieve here - if it's just to make this thing work at all, then I'd
> favour bolting on the absolute minimum changes, possibly even cheating
> by tainting the kernel and saying all bets are off instead of trying to
> handle the more involved corners really properly. However if you want to
> work towards this being a properly-supported thing, then I think it's
> worth generalising the existing assumptions of page alignment from the
> beginning.

I'd like to try and see if we can make this a properly-supported thing.

That will likely take a few iterations but realistically the rest of the drivers
required to make this platform actually useful (and especially the display 
controller
and GPU drivers) won't be ready for a few more months anyway. And even on 4KB 
PAGE_SIZE
kernels half the USB ports and NVMe will work fine, which should be enough to 
install
a distro and some third-party package that just ships the distro kernel with 
16KB
pages.




Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-09 Thread Sven Peter via iommu
Hi,

On Mon, Aug 9, 2021, at 20:37, Robin Murphy wrote:
> On 2021-08-07 09:41, Sven Peter wrote:
> > Hi,
> > 
> > Thanks a lot for quick reply!
> > 
> > On Fri, Aug 6, 2021, at 20:04, Robin Murphy wrote:
> >> On 2021-08-06 16:55, Sven Peter via iommu wrote:
> >>> DMA IOMMU domains can support hardware where the IOMMU page size is
> >>> larger than the CPU page size.
> >>> Alignments need to be done with respect to both PAGE_SIZE and
> >>> iovad->granule. Additionally, the sg list optimization to use a single
> >>> IOVA allocation cannot be used in those cases since the physical
> >>> addresses will very likely not be aligned to the larger IOMMU page size.
> >>>
> >>> Signed-off-by: Sven Peter 
> >>> ---
> >>>drivers/iommu/dma-iommu.c | 87 ++-
> >>>1 file changed, 77 insertions(+), 10 deletions(-)
> >>>
> >>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> >>> index 6f0df629353f..e072d9030d9f 100644
> >>> --- a/drivers/iommu/dma-iommu.c
> >>> +++ b/drivers/iommu/dma-iommu.c
> >>> @@ -8,6 +8,7 @@
> >>> * Copyright (C) 2000-2004 Russell King
> >>> */
> >>>
> >>> +#include 
> >>>#include 
> >>>#include 
> >>>#include 
> >>> @@ -51,6 +52,15 @@ struct iommu_dma_cookie {
> >>>   struct iommu_domain *fq_domain;
> >>>};
> >>>
> >>> +/* aligns size to CPU and IOMMU page size */
> >>> +static inline size_t iommu_page_align(struct device *dev, size_t size)
> >>> +{
> >>> + struct iommu_domain *domain = iommu_get_dma_domain(dev);
> >>> + struct iommu_dma_cookie *cookie = domain->iova_cookie;
> >>> +
> >>> + return iova_align(&cookie->iovad, PAGE_ALIGN(size));
> >>> +}
> >>> +
> >>>static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled);
> >>>bool iommu_dma_forcedac __read_mostly;
> >>>
> >>> @@ -647,6 +657,8 @@ static struct page **__iommu_dma_alloc_pages(struct 
> >>> device *dev,
> >>>/*
> >>> * If size is less than PAGE_SIZE, then a full CPU page will be 
> >>> allocated,
> >>> * but an IOMMU which supports smaller pages might not map the whole 
> >>> thing.
> >>> + * If the IOMMU page size is larger than the CPU page size, then the size
> >>> + * will be aligned to that granularity and some memory will be left 
> >>> unused.
> >>
> >> Why do we need to increase the actual memory allocation? The point here
> >> is that we allocate the smallest thing we can allocate and map the
> >> smallest thing we can map - I think that still works the "wrong" way
> >> round too, we should just need to start taking an IOVA offset into
> >> account as in dma_map_page() if we can no longer assume it's 0 for a CPU
> >> page. Sure we may expose some unrelated adjacent pages, but we'll
> >> already be doing that to excess for streaming DMA so whoop de do.
> > 
> > I agree for trusted devices, but untrusted ones (Thunderbolt, and depending 
> > on your
> > risk tolerance possibly even the broadcom wifi) might also end up calling 
> > this.
> 
> Oh, right, I hadn't considered actual untrusted device support at this 
> stage.


Me neither :-)
I did the alignment at first without thinking too much about it,
then read your reply, and only *then* realized that there are untrusted devices
for which this just happens to do the right thing (at the cost of wasting
memory for everyone else, but I'll fix that).

> 
> > For streaming DMA swiotlb will make sure that these won't see memory
> > they're not supposed to access.
> 
> I was slightly surprised to see that that does appear to work out OK, 
> but I guess SWIOTLB slots are already smaller than typical IOMMU pages, 
> so it falls out of that. Neat.
> 
> > But, at least as far as I understand it, no swiotlb is in the way to catch 
> > devices
> > who end up calling this function. That wasn't required because we used to 
> > get
> > PAGE_SIZE aligned allocation here and every IOMMU so far would be able to 
> > easily
> > map them without any spill overs.
> > But now we'll end up exposing three more unrelated pages if the allocation
> > is n

Re: [RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-07 Thread Sven Peter via iommu



On Fri, Aug 6, 2021, at 20:04, Robin Murphy wrote:
> On 2021-08-06 16:55, Sven Peter via iommu wrote:
> > @@ -1006,6 +1019,31 @@ static int iommu_dma_map_sg(struct device *dev, 
> > struct scatterlist *sg,
> > if (dev_is_untrusted(dev))
> > return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, attrs);
> >   
> > +   /*
> > +* If the IOMMU pagesize is larger than the CPU pagesize we will
> > +* very likely run into sgs with a physical address that is not aligned
> > +* to an IOMMU page boundary. Fall back to just mapping every entry
> > +* independently with __iommu_dma_map then.
> 
> Scatterlist segments often don't have nicely aligned ends, which is why 
> we already align things to IOVA granules in main loop here. I think in 
> principle we'd just need to move the non-IOVA-aligned part of the 
> address from sg->page to sg->offset in the temporary transformation for 
> the rest of the assumptions to hold. I don't blame you for being timid 
> about touching that, though - it took me 3 tries to get right when I 
> first wrote it...
> 


I've spent some time with that code now and I think we cannot use it
but have to fall back to iommu_dma_map_sg_swiotlb (even though that swiotlb
part is a lie then):

When we have sg_phys(s) = 0x802e65000 with s->offset = 0 the paddr
is aligned to PAGE_SIZE but has an offset of 0x1000 from something
the IOMMU can map.
Now this would result in s->offset = -0x1000 which is already weird
enough.
Offset is unsigned (and 32bit) so this will actually look like
s->offset = 0xf000 then, which isn't much better.
And then sg_phys(s) = 0x902e64000 (instead of 0x802e64000) and
we'll map some random memory in iommu_map_sg_atomic and a little bit later
everything explodes.

Now I could probably adjust the phys addr backwards and make sure offset is
always positive (and possibly larger than PAGE_SIZE) and later restore it
in __finalise_sg then but I feel like that's pushing this a little bit too far.



Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-07 Thread Sven Peter via iommu
Hi,

Thanks a lot for quick reply!

On Fri, Aug 6, 2021, at 20:04, Robin Murphy wrote:
> On 2021-08-06 16:55, Sven Peter via iommu wrote:
> > DMA IOMMU domains can support hardware where the IOMMU page size is
> > larger than the CPU page size.
> > Alignments need to be done with respect to both PAGE_SIZE and
> > iovad->granule. Additionally, the sg list optimization to use a single
> > IOVA allocation cannot be used in those cases since the physical
> > addresses will very likely not be aligned to the larger IOMMU page size.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >   drivers/iommu/dma-iommu.c | 87 ++-
> >   1 file changed, 77 insertions(+), 10 deletions(-)
> > 
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index 6f0df629353f..e072d9030d9f 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -8,6 +8,7 @@
> >* Copyright (C) 2000-2004 Russell King
> >*/
> >   
> > +#include 
> >   #include 
> >   #include 
> >   #include 
> > @@ -51,6 +52,15 @@ struct iommu_dma_cookie {
> > struct iommu_domain *fq_domain;
> >   };
> >   
> > +/* aligns size to CPU and IOMMU page size */
> > +static inline size_t iommu_page_align(struct device *dev, size_t size)
> > +{
> > +   struct iommu_domain *domain = iommu_get_dma_domain(dev);
> > +   struct iommu_dma_cookie *cookie = domain->iova_cookie;
> > +
> > +   return iova_align(&cookie->iovad, PAGE_ALIGN(size));
> > +}
> > +
> >   static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled);
> >   bool iommu_dma_forcedac __read_mostly;
> >   
> > @@ -647,6 +657,8 @@ static struct page **__iommu_dma_alloc_pages(struct 
> > device *dev,
> >   /*
> >* If size is less than PAGE_SIZE, then a full CPU page will be allocated,
> >* but an IOMMU which supports smaller pages might not map the whole 
> > thing.
> > + * If the IOMMU page size is larger than the CPU page size, then the size
> > + * will be aligned to that granularity and some memory will be left unused.
> 
> Why do we need to increase the actual memory allocation? The point here 
> is that we allocate the smallest thing we can allocate and map the 
> smallest thing we can map - I think that still works the "wrong" way 
> round too, we should just need to start taking an IOVA offset into 
> account as in dma_map_page() if we can no longer assume it's 0 for a CPU 
> page. Sure we may expose some unrelated adjacent pages, but we'll 
> already be doing that to excess for streaming DMA so whoop de do.

I agree for trusted devices, but untrusted ones (Thunderbolt, and depending on 
your
risk tolerance possibly even the broadcom wifi) might also end up calling this.
For streaming DMA swiotlb will make sure that these won't see memory
they're not supposed to access.
But, at least as far as I understand it, no swiotlb is in the way to catch 
devices
who end up calling this function. That wasn't required because we used to get
PAGE_SIZE aligned allocation here and every IOMMU so far would be able to easily
map them without any spill overs.
But now we'll end up exposing three more unrelated pages if the allocation
is not increased.


> 
> >*/
> >   static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
> > size_t size, struct sg_table *sgt, gfp_t gfp, pgprot_t prot,
> > @@ -736,7 +748,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
> > size_t size,
> >   
> >   out_unmap:
> > __iommu_dma_unmap(dev, *dma_handle, size);
> > -   __iommu_dma_free_pages(pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
> > +   __iommu_dma_free_pages(pages, iommu_page_align(dev, size) >> 
> > PAGE_SHIFT);
> > return NULL;
> >   }
> >   
> > @@ -766,7 +778,8 @@ static void iommu_dma_free_noncontiguous(struct device 
> > *dev, size_t size,
> > struct dma_sgt_handle *sh = sgt_handle(sgt);
> >   
> > __iommu_dma_unmap(dev, sgt->sgl->dma_address, size);
> > -   __iommu_dma_free_pages(sh->pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
> > +   __iommu_dma_free_pages(sh->pages,
> > +   iommu_page_align(dev, size) >> PAGE_SHIFT);
> > sg_free_table(&sh->sgt);
> > kfree(sh);
> >   }
> > @@ -1006,6 +1019,31 @@ static int iommu_dma_map_sg(struct device *dev, 
> > struct scatterlist *sg,
> > if (dev_is_untrusted(dev))
> > return iommu_dma_map_sg_swiotlb(dev, sg, nents,

[RFC PATCH 0/3] iommu/dma-iommu: Support IOMMU page size larger than the CPU page size

2021-08-06 Thread Sven Peter via iommu
Hi,

On the Apple M1 there's this slightly annoying situation where the DART IOMMU
has a hard-wired page size of 16KB. Additionally, the DARTs for some hardware
(USB A ports, WiFi, Ethernet, Thunderbolt PCIe) cannot be switched to bypass
mode and it's also not easily possible to program a software bypass mode.

This is a problem for kernels configured with 4K pages. Unfortunately,
most distributions ship with those by default.

There's not much that can be done for IOMMU_DOMAIN_UNMANAGED domains since
most API clients likely expect to be able to map single CPU pages.

For IOMMU_DOMAIN_DMA domains however, dma-iommu.c is the only code that
uses the raw IOMMU API to manage these domains and can possibly be adapted
to still work correctly.
Essentially, I changed some relevant alignments to happen with respect to both
PAGE_SIZE and iovad->granule. The sglist code also can't use the optimization
for a single IOVA allocation anymore since most phys_addrs will not be aligned
to the IOMMU page size.

I'd like to get some early feedback on this approach to see if it's feasible
to continue working on this or if a different approach will work better or if
this setup just won't be supported.

I'm not very confident I've covered all necessary cases but I'll take
a closer look at every function in dma-iommu.c if there's a chance that
this will be accepted eventually. The current changes are enough to boot
from a USB device and use the Ethernet adapter on my M1 Mini with 4kb pages
though.


One issue I see is that this will end up wasting memory. There's e.g.
dma_pool_*, which will dma_alloc_coherent PAGE_SIZE bytes and stuff the 
individual
allocations into those buffers. These will get padded to SZ_16K but dma_pool 
will
be completely unaware that it got 4x as much memory as requested and will leave
it unused :-(

The other issue I'm aware of is v4l2 which expects that a page-aligned sglist
can be represented contiguously in IOVA space [1].


Best,


Sven


[1] 
https://lore.kernel.org/linux-iommu/0d20bd6b-d0a1-019c-6398-b12f83f4f...@arm.com/

Sven Peter (3):
  iommu: Move IOMMU pagesize check to attach_device
  iommu/dma-iommu: Support iovad->granule > PAGE_SIZE
  iommu: Introduce __IOMMU_DOMAIN_LARGE_PAGES

 drivers/iommu/dma-iommu.c | 87 ++-
 drivers/iommu/iommu.c | 36 ++--
 drivers/iommu/iova.c  |  7 ++--
 include/linux/iommu.h | 14 ---
 4 files changed, 123 insertions(+), 21 deletions(-)

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC PATCH 3/3] iommu: Introduce __IOMMU_DOMAIN_LARGE_PAGES

2021-08-06 Thread Sven Peter via iommu
__IOMMU_DOMAIN_LARGE_PAGES indicates that a domain can handle
conditions where PAGE_SIZE might be smaller than the IOMMU page size.
Always allow attaching devices to such domains and set the flag for
IOMMU_DOMAIN_DMA, which can now handle these situations.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c |  2 ++
 include/linux/iommu.h | 14 +-
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 5854a4ef5681..f0bfd76187b1 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1970,6 +1970,8 @@ static int iommu_check_page_size(struct iommu_domain 
*domain)
 {
if (!(domain->type & __IOMMU_DOMAIN_PAGING))
return 0;
+   if (domain->type & __IOMMU_DOMAIN_LARGE_PAGES)
+   return 0;
 
if ((1 << __ffs(domain->pgsize_bitmap)) > PAGE_SIZE) {
pr_warn("IOMMU page size cannot represent CPU pages.\n");
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e552ecfefcf7..1f97eac8a4b0 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -56,10 +56,13 @@ struct iommu_domain_geometry {
 };
 
 /* Domain feature flags */
-#define __IOMMU_DOMAIN_PAGING  (1U << 0)  /* Support for iommu_map/unmap */
-#define __IOMMU_DOMAIN_DMA_API (1U << 1)  /* Domain for use in DMA-API
- implementation  */
-#define __IOMMU_DOMAIN_PT  (1U << 2)  /* Domain is identity mapped   */
+#define __IOMMU_DOMAIN_PAGING   (1U << 0)  /* Support for iommu_map/unmap 
*/
+#define __IOMMU_DOMAIN_DMA_API  (1U << 1)  /* Domain for use in DMA-API
+  implementation  
*/
+#define __IOMMU_DOMAIN_PT   (1U << 2)  /* Domain is identity mapped   
*/
+#define __IOMMU_DOMAIN_LARGE_PAGES  (1U << 3)  /* Domain can handle IOMMU page
+ sizes larger than the CPU
+ page size   */
 
 /*
  * This are the possible domain-types
@@ -77,7 +80,8 @@ struct iommu_domain_geometry {
 #define IOMMU_DOMAIN_IDENTITY  (__IOMMU_DOMAIN_PT)
 #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
 #define IOMMU_DOMAIN_DMA   (__IOMMU_DOMAIN_PAGING |\
-__IOMMU_DOMAIN_DMA_API)
+__IOMMU_DOMAIN_DMA_API |   \
+__IOMMU_DOMAIN_LARGE_PAGES)
 
 struct iommu_domain {
unsigned type;
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC PATCH 2/3] iommu/dma-iommu: Support iovad->granule > PAGE_SIZE

2021-08-06 Thread Sven Peter via iommu
DMA IOMMU domains can support hardware where the IOMMU page size is
larger than the CPU page size.
Alignments need to be done with respect to both PAGE_SIZE and
iovad->granule. Additionally, the sg list optimization to use a single
IOVA allocation cannot be used in those cases since the physical
addresses will very likely not be aligned to the larger IOMMU page size.

Signed-off-by: Sven Peter 
---
 drivers/iommu/dma-iommu.c | 87 ++-
 1 file changed, 77 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 6f0df629353f..e072d9030d9f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -8,6 +8,7 @@
  * Copyright (C) 2000-2004 Russell King
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -51,6 +52,15 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+/* aligns size to CPU and IOMMU page size */
+static inline size_t iommu_page_align(struct device *dev, size_t size)
+{
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+
+   return iova_align(&cookie->iovad, PAGE_ALIGN(size));
+}
+
 static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled);
 bool iommu_dma_forcedac __read_mostly;
 
@@ -647,6 +657,8 @@ static struct page **__iommu_dma_alloc_pages(struct device 
*dev,
 /*
  * If size is less than PAGE_SIZE, then a full CPU page will be allocated,
  * but an IOMMU which supports smaller pages might not map the whole thing.
+ * If the IOMMU page size is larger than the CPU page size, then the size
+ * will be aligned to that granularity and some memory will be left unused.
  */
 static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
size_t size, struct sg_table *sgt, gfp_t gfp, pgprot_t prot,
@@ -736,7 +748,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 
 out_unmap:
__iommu_dma_unmap(dev, *dma_handle, size);
-   __iommu_dma_free_pages(pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
+   __iommu_dma_free_pages(pages, iommu_page_align(dev, size) >> 
PAGE_SHIFT);
return NULL;
 }
 
@@ -766,7 +778,8 @@ static void iommu_dma_free_noncontiguous(struct device 
*dev, size_t size,
struct dma_sgt_handle *sh = sgt_handle(sgt);
 
__iommu_dma_unmap(dev, sgt->sgl->dma_address, size);
-   __iommu_dma_free_pages(sh->pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
+   __iommu_dma_free_pages(sh->pages,
+   iommu_page_align(dev, size) >> PAGE_SHIFT);
sg_free_table(&sh->sgt);
kfree(sh);
 }
@@ -1006,6 +1019,31 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
if (dev_is_untrusted(dev))
return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, attrs);
 
+   /*
+* If the IOMMU pagesize is larger than the CPU pagesize we will
+* very likely run into sgs with a physical address that is not aligned
+* to an IOMMU page boundary. Fall back to just mapping every entry
+* independently with __iommu_dma_map then.
+*/
+   if (iovad->granule > PAGE_SIZE) {
+   for_each_sg(sg, s, nents, i) {
+   sg_dma_address(s) = __iommu_dma_map(dev, sg_phys(s),
+   s->length, prot, dma_get_mask(dev));
+   if (sg_dma_address(s) == DMA_MAPPING_ERROR)
+   break;
+   sg_dma_len(s) = s->length;
+   }
+
+   if (unlikely(i != nents)) {
+   nents = i;
+   for_each_sg(sg, s, nents, i)
+   __iommu_dma_unmap(dev, sg_dma_address(s), 
sg_dma_len(s));
+   return 0;
+   }
+
+   return nents;
+   }
+
/*
 * Work out how much IOVA space we need, and align the segments to
 * IOVA granules for the IOMMU driver to handle. With some clever
@@ -1068,6 +1106,9 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 static void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir, unsigned long attrs)
 {
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = &cookie->iovad;
dma_addr_t start, end;
struct scatterlist *tmp;
int i;
@@ -1080,6 +1121,17 @@ static void iommu_dma_unmap_sg(struct device *dev, 
struct scatterlist *sg,
return;
}
 
+   /*
+* If the IOMMU pagesize is larger than the CPU pagesize we mapped
+* every entry indepedently with __iommu_dma_map then. Let's do the
+* opposite here.
+*/
+   

[RFC PATCH 1/3] iommu: Move IOMMU pagesize check to attach_device

2021-08-06 Thread Sven Peter via iommu
The iova allocator is capable of handling any granularity which is a power
of two. Remove the much stronger condition that the granularity must be
smaller or equal to the CPU page size from a BUG_ON there.
Instead, check this condition during __iommu_attach_device and fail
gracefully.

Signed-off-by: Sven Peter 
---
 drivers/iommu/iommu.c | 34 +++---
 drivers/iommu/iova.c  |  7 ---
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 1de503ddb343..5854a4ef5681 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -78,6 +78,8 @@ static struct iommu_domain *__iommu_domain_alloc(struct 
bus_type *bus,
 unsigned type);
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev);
+static void __iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev);
 static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
 static void __iommu_detach_group(struct iommu_domain *domain,
@@ -1964,6 +1966,18 @@ void iommu_domain_free(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL_GPL(iommu_domain_free);
 
+static int iommu_check_page_size(struct iommu_domain *domain)
+{
+   if (!(domain->type & __IOMMU_DOMAIN_PAGING))
+   return 0;
+
+   if ((1 << __ffs(domain->pgsize_bitmap)) > PAGE_SIZE) {
+   pr_warn("IOMMU page size cannot represent CPU pages.\n");
+   return -EFAULT;
+   }
+
+   return 0;
+}
 static int __iommu_attach_device(struct iommu_domain *domain,
 struct device *dev)
 {
@@ -1973,9 +1987,23 @@ static int __iommu_attach_device(struct iommu_domain 
*domain,
return -ENODEV;
 
ret = domain->ops->attach_dev(domain, dev);
-   if (!ret)
-   trace_attach_device_to_domain(dev);
-   return ret;
+   if (ret)
+   return ret;
+
+   /*
+* Check that CPU pages can be represented by the IOVA granularity.
+* This has to be done after ops->attach_dev since many IOMMU drivers
+* only limit domain->pgsize_bitmap after having attached the first
+* device.
+*/
+   ret = iommu_check_page_size(domain);
+   if (ret) {
+   __iommu_detach_device(domain, dev);
+   return ret;
+   }
+
+   trace_attach_device_to_domain(dev);
+   return 0;
 }
 
 int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index b6cf5f16123b..e0f8adde0f1b 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -50,10 +50,11 @@ init_iova_domain(struct iova_domain *iovad, unsigned long 
granule,
 {
/*
 * IOVA granularity will normally be equal to the smallest
-* supported IOMMU page size; both *must* be capable of
-* representing individual CPU pages exactly.
+* supported IOMMU page size; while both usually are capable of
+* representing individual CPU pages exactly the IOVA allocator
+* supports any granularities that are an exact power of two.
 */
-   BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
+   BUG_ON(!is_power_of_2(granule));
 
spin_lock_init(&iovad->iova_rbtree_lock);
iovad->rbroot = RB_ROOT;
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v5 3/3] iommu/dart: Add DART iommu driver

2021-08-03 Thread Sven Peter via iommu
Apple's new SoCs use iommus for almost all peripherals. These Device
Address Resolution Tables must be setup before these peripherals can
act as DMA masters.

Tested-by: Alyssa Rosenzweig 
Signed-off-by: Sven Peter 
---
 MAINTAINERS|   1 +
 drivers/iommu/Kconfig  |  14 +
 drivers/iommu/Makefile |   1 +
 drivers/iommu/apple-dart.c | 923 +
 4 files changed, 939 insertions(+)
 create mode 100644 drivers/iommu/apple-dart.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0f450f7d5336..5f3ef4298594 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1267,6 +1267,7 @@ M:    Sven Peter 
 L: iommu@lists.linux-foundation.org
 S: Maintained
 F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+F: drivers/iommu/apple-dart.c
 
 APPLE SMC DRIVER
 M: Henrik Rydberg 
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index c84da8205be7..dfe81da483e9 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -290,6 +290,20 @@ config SPAPR_TCE_IOMMU
  Enables bits of IOMMU API required by VFIO. The iommu_ops
  is not implemented as it is not necessary for VFIO.
 
+config APPLE_DART
+   tristate "Apple DART IOMMU Support"
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   select IOMMU_API
+   select IOMMU_IO_PGTABLE_LPAE
+   default ARCH_APPLE
+   help
+ Support for Apple DART (Device Address Resolution Table) IOMMUs
+ found in Apple ARM SoCs like the M1.
+ This IOMMU is required for most peripherals using DMA to access
+ the main memory.
+
+ Say Y here if you are using an Apple SoC.
+
 # ARM IOMMU support
 config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c0fb0ba88143..bc7f730edbb0 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
 obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
 obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o io-pgfault.o
 obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
+obj-$(CONFIG_APPLE_DART) += apple-dart.o
diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
new file mode 100644
index ..559db9259e65
--- /dev/null
+++ b/drivers/iommu/apple-dart.c
@@ -0,0 +1,923 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple DART (Device Address Resolution Table) IOMMU driver
+ *
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ *
+ * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
+ *  Copyright (C) 2013 ARM Limited
+ *  Copyright (C) 2015 ARM Limited
+ * and on exynos-iommu.c
+ *  Copyright (c) 2011,2016 Samsung Electronics Co., Ltd.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DART_MAX_STREAMS 16
+#define DART_MAX_TTBR 4
+#define MAX_DARTS_PER_DEVICE 2
+
+#define DART_STREAM_ALL 0x
+
+#define DART_PARAMS1 0x00
+#define DART_PARAMS_PAGE_SHIFT GENMASK(27, 24)
+
+#define DART_PARAMS2 0x04
+#define DART_PARAMS_BYPASS_SUPPORT BIT(0)
+
+#define DART_STREAM_COMMAND 0x20
+#define DART_STREAM_COMMAND_BUSY BIT(2)
+#define DART_STREAM_COMMAND_INVALIDATE BIT(20)
+
+#define DART_STREAM_SELECT 0x34
+
+#define DART_ERROR 0x40
+#define DART_ERROR_STREAM GENMASK(27, 24)
+#define DART_ERROR_CODE GENMASK(11, 0)
+#define DART_ERROR_FLAG BIT(31)
+
+#define DART_ERROR_READ_FAULT BIT(4)
+#define DART_ERROR_WRITE_FAULT BIT(3)
+#define DART_ERROR_NO_PTE BIT(2)
+#define DART_ERROR_NO_PMD BIT(1)
+#define DART_ERROR_NO_TTBR BIT(0)
+
+#define DART_CONFIG 0x60
+#define DART_CONFIG_LOCK BIT(15)
+
+#define DART_STREAM_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_ERROR_ADDR_HI 0x54
+#define DART_ERROR_ADDR_LO 0x50
+
+#define DART_TCR(sid) (0x100 + 4 * (sid))
+#define DART_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_TCR_BYPASS0_ENABLE BIT(8)
+#define DART_TCR_BYPASS1_ENABLE BIT(12)
+
+#define DART_TTBR(sid, idx) (0x200 + 16 * (sid) + 4 * (idx))
+#define DART_TTBR_VALID BIT(31)
+#define DART_TTBR_SHIFT 12
+
+/*
+ * Private structure associated with each DART device.
+ *
+ * @dev: device struct
+ * @regs: mapped MMIO region
+ * @irq: interrupt number, can be shared with other DARTs
+ * @clks: clocks associated with this DART
+ * @num_clks: number of @clks
+ * @lock: lock for hardware operations involving this dart
+ * @pgsize: pagesize supported by this DART
+ * @supports_bypass: indicates if this DART supports bypass mode
+ * @force_bypass: force bypass mode due to pagesize mismatch?
+ * @sid2group: maps stream ids to iommu_groups
+ * @iommu: iommu core device
+ */
+struct apple_dart {
+   struct device *dev;
+
+   void __iomem *regs;
+
+   int irq;
+   struct clk_bulk_data *clks;
+   int nu

[PATCH v5 1/3] iommu/io-pgtable: Add DART pagetable format

2021-08-03 Thread Sven Peter via iommu
Apple's DART iommu uses a pagetable format that shares some
similarities with the ones already implemented by io-pgtable.c.
Add a new format variant to support the required differences
so that we don't have to duplicate the pagetable handling code.

Reviewed-by: Alexander Graf 
Reviewed-by: Alyssa Rosenzweig 
Reviewed-by: Robin Murphy 
Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 63 ++
 drivers/iommu/io-pgtable.c |  1 +
 include/linux/io-pgtable.h |  7 
 3 files changed, 71 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 053df4048a29..0779eb96bd29 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -130,6 +130,9 @@
 #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
 #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
 
+#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
+#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -402,6 +405,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
arm_lpae_io_pgtable *data,
 {
arm_lpae_iopte pte;
 
+   if (data->iop.fmt == APPLE_DART) {
+   pte = 0;
+   if (!(prot & IOMMU_WRITE))
+   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
+   if (!(prot & IOMMU_READ))
+   pte |= APPLE_DART_PTE_PROT_NO_READ;
+   return pte;
+   }
+
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
pte = ARM_LPAE_PTE_nG;
@@ -1102,6 +1114,52 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
return NULL;
 }
 
+static struct io_pgtable *
+apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+   struct arm_lpae_io_pgtable *data;
+   int i;
+
+   if (cfg->oas > 36)
+   return NULL;
+
+   data = arm_lpae_alloc_pgtable(cfg);
+   if (!data)
+   return NULL;
+
+   /*
+* The table format itself always uses two levels, but the total VA
+* space is mapped by four separate tables, making the MMIO registers
+* an effective "level 1". For simplicity, though, we treat this
+* equivalently to LPAE stage 2 concatenation at level 2, with the
+* additional TTBRs each just pointing at consecutive pages.
+*/
+   if (data->start_level < 1)
+   goto out_free_data;
+   if (data->start_level == 1 && data->pgd_bits > 2)
+   goto out_free_data;
+   if (data->start_level > 1)
+   data->pgd_bits = 0;
+   data->start_level = 2;
+   cfg->apple_dart_cfg.n_ttbrs = 1 << data->pgd_bits;
+   data->pgd_bits += data->bits_per_level;
+
+   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
+  cfg);
+   if (!data->pgd)
+   goto out_free_data;
+
+   for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i)
+   cfg->apple_dart_cfg.ttbr[i] =
+   virt_to_phys(data->pgd + i * ARM_LPAE_GRANULE(data));
+
+   return &data->iop;
+
+out_free_data:
+   kfree(data);
+   return NULL;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
.alloc  = arm_64_lpae_alloc_pgtable_s1,
.free   = arm_lpae_free_pgtable,
@@ -1127,6 +1185,11 @@ struct io_pgtable_init_fns 
io_pgtable_arm_mali_lpae_init_fns = {
.free   = arm_lpae_free_pgtable,
 };
 
+struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns = {
+   .alloc  = apple_dart_alloc_pgtable,
+   .free   = arm_lpae_free_pgtable,
+};
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
 
 static struct io_pgtable_cfg *cfg_cookie __initdata;
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6e9917ce980f..f4bfcef98297 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
+   [APPLE_DART] = &io_pgtable_apple_dart_init_fns,
 #endif
 #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index c43f3b899d2a..a738483fb4da 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -16,6 +16,7 @@ enum io_pgtable_fmt {
ARM_V7S,
ARM_MALI_LPAE,
AMD_IOMMU_V1,
+   APPLE_DART,
IO_PGTABLE_NUM_FMTS,
 };
 
@@ -136,6 +137,11 @@ struct io_pgtable_cfg {
u64 

[PATCH v5 2/3] dt-bindings: iommu: add DART iommu bindings

2021-08-03 Thread Sven Peter via iommu
DART (Device Address Resolution Table) is the iommu found on Apple
ARM SoCs such as the M1.

Reviewed-by: Rob Herring 
Reviewed-by: Alyssa Rosenzweig 
Signed-off-by: Sven Peter 
---
 .../devicetree/bindings/iommu/apple,dart.yaml | 81 +++
 MAINTAINERS   |  6 ++
 2 files changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml

diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
new file mode 100644
index ..94aa9e9afa59
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/apple,dart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple DART IOMMU
+
+maintainers:
+  - Sven Peter 
+
+description: |+
+  Apple SoCs may contain an implementation of their Device Address
+  Resolution Table which provides a mandatory layer of address
+  translations for various masters.
+
+  Each DART instance is capable of handling up to 16 different streams
+  with individual pagetables and page-level read/write protection flags.
+
+  This DART IOMMU also raises interrupts in response to various
+  fault conditions.
+
+properties:
+  compatible:
+const: apple,t8103-dart
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+description:
+  Reference to the gate clock phandle if required for this IOMMU.
+  Optional since not all IOMMUs are attached to a clock gate.
+
+  '#iommu-cells':
+const: 1
+description:
+  Has to be one. The single cell describes the stream id emitted by
+  a master to the IOMMU.
+
+required:
+  - compatible
+  - reg
+  - '#iommu-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |+
+dart1: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master1 {
+  iommus = <&dart1 0>;
+};
+
+  - |+
+dart2a: iommu@82f0 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f0 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+dart2b: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master2 {
+  iommus = <&dart2a 0>, <&dart2b 1>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index c9467d2839f5..0f450f7d5336 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1262,6 +1262,12 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c
 
+APPLE DART IOMMU DRIVER
+M: Sven Peter 
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+
 APPLE SMC DRIVER
 M: Henrik Rydberg 
 L: linux-hw...@vger.kernel.org
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v5 0/3] Apple M1 DART IOMMU driver

2021-08-03 Thread Sven Peter via iommu
re specific checks from io-pgtable.c  as pointed out by
   Will Deacon
 - introduced a fake bypass mode by programming static linear pagetables
   if the DART does not support regular bypass mode as proposed by Alex
   Graf
 - added checks to enforce bypass mode if there is a pagesize mismatch
   between the DART HW and the CPU.
 - fixed usage of GFP_KERNEL during a held spinlock found by Julia Lawall
 - rebased on v5.13-rc3

Changes for v2:
 - fixed devicetree binding linting issues pointed out by Rob Herring and
   reworked that file.
 - made DART-specific code in io-pgtable.c unconditional and removed flag from
   Kconfig as proposed by Robin Murphy.
 - allowed multiple DART nodes in the "iommus" property as proposed by
   Rob Herring and Robin Murphy. this resulted in significant changes
   to apple-iommu-dart.c.
 - the domain aperture is now forced to 32bit if translation is enabled after
   the original suggestion to limit the aperture by Mark Kettenis and the
   follow-up discussion and investigation with Mark Kettenis, Arnd Bergmann,
   Robin Murphy and Rob Herring. This change also simplified the code
   in io-pgtable.c and made some of the improvements suggested during review
   not apply anymore.
 - added support for bypassed and isolated domain modes.
 - reject IOMMU_MMIO and IOMMU_NOEXEC since it's unknown how to set these up
   for now or if the hardware even supports these flags.
 - renamed some registers to be less confusing (mainly s/DOMAIN/STREAM/ to
   prevent confusion with linux's iommu domain concept).


[1] 
https://lore.kernel.org/linux-iommu/20210320151903.60759-1-s...@svenpeter.dev/
[2] 
https://lore.kernel.org/linux-iommu/20210328074009.95932-1-s...@svenpeter.dev/
[3] 
https://lore.kernel.org/linux-iommu/20210603085003.50465-1-s...@svenpeter.dev/
[4] https://github.com/AsahiLinux/docs/wiki/Developer-Quickstart
[5] 
https://github.com/AsahiLinux/linux/commit/7d4ebb0b22e9bfec849e2af86ddeb46ec29d7feb
[6] 
https://github.com/AsahiLinux/m1n1/commit/9529ec2b4fd6550f9cfd66d9f2448b90804699a1
[7] 
https://lore.kernel.org/linux-iommu/20210627143405.77298-1-s...@svenpeter.dev/

Sven Peter (3):
  iommu/io-pgtable: Add DART pagetable format
  dt-bindings: iommu: add DART iommu bindings
  iommu/dart: Add DART iommu driver

 .../devicetree/bindings/iommu/apple,dart.yaml |  81 ++
 MAINTAINERS   |   7 +
 drivers/iommu/Kconfig |  14 +
 drivers/iommu/Makefile|   1 +
 drivers/iommu/apple-dart.c| 923 ++
 drivers/iommu/io-pgtable-arm.c|  63 ++
 drivers/iommu/io-pgtable.c|   1 +
 include/linux/io-pgtable.h|   7 +
 8 files changed, 1097 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml
 create mode 100644 drivers/iommu/apple-dart.c

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 3/3] iommu: dart: Add DART iommu driver

2021-07-25 Thread Sven Peter via iommu



On Mon, Jul 19, 2021, at 20:15, Robin Murphy wrote:
> On 2021-07-15 17:41, Sven Peter via iommu wrote:
> [...]
> >>> + u64 sw_bypass_cpu_start;
> >>> + u64 sw_bypass_dma_start;
> >>> + u64 sw_bypass_len;
> >>> +
> >>> + struct list_head streams;
> >>
> >> I'm staring to think this could just be a bitmap, in a u16 even.
> > 
> > The problem is that these streams may come from two different
> > DART instances. That is required for e.g. the dwc3 controller which
> > has a weird quirk where DMA transactions go through two separate
> > DARTs with no clear pattern (e.g. some xhci control structures use the
> > first dart while other structures use the second one).
> 
> Ah right, I do remember discussing that situation, but I think I 
> misinterpreted dart_domain->dart representing "the DART instance" here 
> to mean we weren't trying to accommodate that just yet.
> 
> > Both of them need to point to the same pagetable.
> > In the device tree the node will have an entry like this:
> > 
> > dwc3_0: usb@38228{
> > ...
> > iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>;
> > };
> > 
> > There's no need for a linked list though once I do this properly with
> > groups. I can just use an array allocated when the first device is
> > attached, which just contains apple_dart* and streamid values.
> > 
> > 
> >>
> >>> +
> >>> + spinlock_t lock;
> >>> +
> >>> + struct iommu_domain domain;
> >>> +};
> >>> +
> >>> +/*
> >>> + * This structure is attached to devices with dev_iommu_priv_set() on 
> >>> of_xlate
> >>> + * and contains a list of streams bound to this device as defined in the
> >>> + * device tree. Multiple DART instances can be attached to a single 
> >>> device
> >>> + * and each stream is identified by its stream id.
> >>> + * It's usually reference by a pointer called *cfg.
> >>> + *
> >>> + * A dynamic array instead of a linked list is used here since in almost
> >>> + * all cases a device will just be attached to a single stream and 
> >>> streams
> >>> + * are never removed after they have been added.
> >>> + *
> >>> + * @num_streams: number of streams attached
> >>> + * @streams: array of structs to identify attached streams and the 
> >>> device link
> >>> + *   to the iommu
> >>> + */
> >>> +struct apple_dart_master_cfg {
> >>> + int num_streams;
> >>> + struct {
> >>> + struct apple_dart *dart;
> >>> + u32 sid;
> >>
> >> Can't you use the fwspec for this?
> > 
> > 
> > I'd be happy to use the fwspec code if that's somehow possible.
> > I'm not sure how though since I need to store both the reference to the DART
> > _and_ to the stream id. As far as I can tell the fwspec code would only 
> > allow
> > to store the stream ids.
> > (see also the previous comment regarding the dwc3 node which requires stream
> > ids from two separate DART instances)
> 
> Hmm, yes, as above I was overlooking that, although there are still 
> various ideas that come to mind; the question becomes whether they're 
> actually worthwhile or just too-clever-for-their-own-good hacks. The 
> exact format of fwspec->ids is not fixed (other than the ACPI IORT code 
> having a common understanding with the Arm SMMU drivers) so in principle 
> you could munge some sort of DART instance index or indeed anything, but 
> if it remains cleaner to manage your own data internally then by all 
> means keep doing that.

Yeah, I can think of some hacks as well (like storing a global id->apple_dart* 
map
or stuffing the 64bit pointer into two ints) and I've tried a few of them in 
the past
days but didn't like either of them.

I do like the idea to just put two (struct apple_dart *dart, u16 sidmap)
in there though which will be plenty for all current configurations.

> 
> >>> + struct device_link *link;
> >>
> >> Is it necessary to use stateless links, or could you use
> >> DL_FLAG_AUTOREMOVE_SUPPLIER and not have to keep track of them manually?
> > 
> > I'll just use DL_FLAG_AUTOREMOVE_SUPPLIER. No idea why I went for stateless 
> > links.
> > 
> >>
> > [...]
> >>> + /* restore stream identity map */
> >>> + writel(0x03020100,

Re: [PATCH v4 3/3] iommu: dart: Add DART iommu driver

2021-07-15 Thread Sven Peter via iommu
Hi,

Awesome, thanks a lot for the detailed review!


On Wed, Jul 14, 2021, at 01:23, Robin Murphy wrote:
> ^^ Nit: the subsystem style for the subject format should be 
> "iommu/dart: Add..." - similarly on patch #1, which I just realised I 
> missed (sorry!)

Sure!

> 
> On 2021-06-27 15:34, Sven Peter wrote:
> > Apple's new SoCs use iommus for almost all peripherals. These Device
> > Address Resolution Tables must be setup before these peripherals can
> > act as DMA masters.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >   MAINTAINERS  |1 +
> >   drivers/iommu/Kconfig|   15 +
> >   drivers/iommu/Makefile   |1 +
> >   drivers/iommu/apple-dart-iommu.c | 1058 ++
> 
> I'd be inclined to drop "-iommu" from the filename, unless there's some 
> other "apple-dart" functionality that might lead to a module name clash 
> in future?

Sure, DART should only be an iommu.

> 
> >   4 files changed, 1075 insertions(+)
> >   create mode 100644 drivers/iommu/apple-dart-iommu.c
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 29e5541c8f21..c1ffaa56b5f9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1245,6 +1245,7 @@ M:Sven Peter 
> >   L:iommu@lists.linux-foundation.org
> >   S:Maintained
> >   F:Documentation/devicetree/bindings/iommu/apple,dart.yaml
> > +F: drivers/iommu/apple-dart-iommu.c
> >   
> >   APPLE SMC DRIVER
> >   M:Henrik Rydberg 
> > diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> > index 1f111b399bca..87882c628b46 100644
> > --- a/drivers/iommu/Kconfig
> > +++ b/drivers/iommu/Kconfig
> > @@ -249,6 +249,21 @@ config SPAPR_TCE_IOMMU
> >   Enables bits of IOMMU API required by VFIO. The iommu_ops
> >   is not implemented as it is not necessary for VFIO.
> >   
> > +config IOMMU_APPLE_DART
> > +   tristate "Apple DART IOMMU Support"
> > +   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
> > +   select IOMMU_API
> > +   select IOMMU_IO_PGTABLE
> 
> This is redundant - the individual formats already select it.

Removed for the next version.

> 
[...]
> > +#include 
> 
> Redundant duplicate

Whoops, removed for the next version as well.

> 
> > +#define DART_MAX_STREAMS 16
[...]
> > +
> > +/*
> > + * This structure is used to identify a single stream attached to a domain.
> > + * It's used as a list inside that domain to be able to attach multiple
> > + * streams to a single domain. Since multiple devices can use a single 
> > stream
> > + * it additionally keeps track of how many devices are represented by this
> > + * stream. Once that number reaches zero it is detached from the IOMMU 
> > domain
> > + * and all translations from this stream are disabled.
> 
> That sounds a lot like something you should be doing properly with groups.

The hint to look at arm-smmu for a similar flow was very helpful, thanks!
Now that I understand how these groups works I completely agree that this
needs to be reworked and done properly.


> 
> > + * @dart: DART instance to which this stream belongs
> > + * @sid: stream id within the DART instance
> > + * @num_devices: count of devices attached to this stream
> > + * @stream_head: list head for the next stream
> > + */
> > +struct apple_dart_stream {
> > +   struct apple_dart *dart;
> > +   u32 sid;
> 
> What are the actual SID values like? If they're large and sparse then 
> maybe a list makes sense, but if they're small and dense then an array 
> hanging off the apple_dart structure itself might be more efficient. 
> Given DART_MAX_STREAMS, I'm thinking the latter, and considerably so.
> 
> The impression I'm getting so far is that this seems conceptually a bit 
> like arm-smmu with stream indexing.

There are two (very similar) types of DARTs.
The one supported with this series has up to 16 stream ids which will be
integers <16. There's another variant used for Thunderbolt for which I will
add support in a follow-up that supports up to 64 stream ids then. 
So at worst this is an array with 64 entries if this structure won't
disappear completely.

And yes, this is conceptually a bit like arm-smmu's stream indexing I think.


> 
> > +   u32 num_devices;
> > +
> > +   struct list_head stream_head;
> > +};
> > +
> > +/*
> > + * This structure is attached to each iommu domain handled by a DART.
> > + * A single domain is used to represent 

Re: [PATCH v4 1/3] iommu: io-pgtable: add DART pagetable format

2021-07-14 Thread Sven Peter via iommu
Hi,

On Tue, Jul 13, 2021, at 21:17, Robin Murphy wrote:
> On 2021-06-27 15:34, Sven Peter wrote:
> > Apple's DART iommu uses a pagetable format that shares some
> > similarities with the ones already implemented by io-pgtable.c.
> > Add a new format variant to support the required differences
> > so that we don't have to duplicate the pagetable handling code.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >   drivers/iommu/io-pgtable-arm.c | 62 ++
> >   drivers/iommu/io-pgtable.c |  1 +
> >   include/linux/io-pgtable.h |  7 
> >   3 files changed, 70 insertions(+)
> > 
> > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> > index 87def58e79b5..1dd5c45b4b5b 100644
> > --- a/drivers/iommu/io-pgtable-arm.c
> > +++ b/drivers/iommu/io-pgtable-arm.c
> > @@ -127,6 +127,9 @@
> >   #define ARM_MALI_LPAE_MEMATTR_IMP_DEF 0x88ULL
> >   #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
> >   
> > +#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
> > +#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
> > +
> >   /* IOPTE accessors */
> >   #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
> >   
> > @@ -381,6 +384,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
> > arm_lpae_io_pgtable *data,
> >   {
> > arm_lpae_iopte pte;
> >   
> > +   if (data->iop.fmt == ARM_APPLE_DART) {
> > +   pte = 0;
> > +   if (!(prot & IOMMU_WRITE))
> > +   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
> > +   if (!(prot & IOMMU_READ))
> > +   pte |= APPLE_DART_PTE_PROT_NO_READ;
> > +   return pte;
> > +   }
> > +
> > if (data->iop.fmt == ARM_64_LPAE_S1 ||
> > data->iop.fmt == ARM_32_LPAE_S1) {
> > pte = ARM_LPAE_PTE_nG;
> > @@ -1043,6 +1055,51 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg 
> > *cfg, void *cookie)
> > return NULL;
> >   }
> >   
> > +static struct io_pgtable *
> > +apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> > +{
> > +   struct arm_lpae_io_pgtable *data;
> > +   int i;
> > +
> > +   if (cfg->oas > 36)
> > +   return NULL;
> > +
> > +   data = arm_lpae_alloc_pgtable(cfg);
> > +   if (!data)
> > +   return NULL;
> > +
> > +   /*
> > +* Apple's DART always requires three levels with the first level being
> > +* stored in four MMIO registers. We always concatenate the first and
> > +* second level so that we only have to setup the MMIO registers once.
> > +* This results in an effective two level pagetable.
> > +*/
> 
> Nit: I appreciate the effort to document the weirdness, but this comment 
> did rather mislead me initially, and now that I (think I) understand how 
> things work it seems a bit backwards. Could we say something like:
> 
>"The table format itself always uses two levels, but the total VA
> space is mapped by four separate tables, making the MMIO registers
> an effective "level 1". For simplicity, though, we treat this
> equivalently to LPAE stage 2 concatenation at level 2, with the
> additional TTBRs each just pointing at consecutive pages."
> 
> ?
> 

Sure, your version is much easier to understand! Thanks.

> > +   if (data->start_level < 1)
> > +   return NULL;
> > +   if (data->start_level == 1 && data->pgd_bits > 2)
> > +   return NULL;
> > +   if (data->start_level > 1)
> > +   data->pgd_bits = 0;
> > +   data->start_level = 2;
> > +   cfg->apple_dart_cfg.n_ttbrs = 1 << data->pgd_bits;
> > +   data->pgd_bits += data->bits_per_level;
> > +
> > +   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
> > +  cfg);
> > +   if (!data->pgd)
> > +   goto out_free_data;
> > +
> > +   for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i)
> > +   cfg->apple_dart_cfg.ttbr[i] =
> > +   virt_to_phys(data->pgd + i * ARM_LPAE_GRANULE(data));
> > +
> > +   return &data->iop;
> > +
> > +out_free_data:
> > +   kfree(data);
> > +   return NULL;
> > +}
> > +
> >   struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
> > .alloc  = arm_64_lpae_alloc_pgtable_s1,
> > .free   = arm_lpae_free_pg

Re: [PATCH v4 3/3] iommu: dart: Add DART iommu driver

2021-07-12 Thread Sven Peter via iommu
Hi,


On Wed, Jun 30, 2021, at 15:49, Alyssa Rosenzweig wrote:
> Looks really good! Just a few minor comments. With them addressed,
> 
>   Reviewed-by: Alyssa Rosenzweig 

Thanks!

> 
> > + Say Y here if you are using an Apple SoC with a DART IOMMU.
> 
> Nit: Do we need to spell out "with a DART IOMMU"? Don't all the apple
> socs need DART?

Good point, I'll remove it.

> 
> > +/*
> > + * This structure is used to identify a single stream attached to a domain.
> > + * It's used as a list inside that domain to be able to attach multiple
> > + * streams to a single domain. Since multiple devices can use a single 
> > stream
> > + * it additionally keeps track of how many devices are represented by this
> > + * stream. Once that number reaches zero it is detached from the IOMMU 
> > domain
> > + * and all translations from this stream are disabled.
> > + *
> > + * @dart: DART instance to which this stream belongs
> > + * @sid: stream id within the DART instance
> > + * @num_devices: count of devices attached to this stream
> > + * @stream_head: list head for the next stream
> > + */
> > +struct apple_dart_stream {
> > +   struct apple_dart *dart;
> > +   u32 sid;
> > +
> > +   u32 num_devices;
> > +
> > +   struct list_head stream_head;
> > +};
> 
> It wasn't obvious to me why we can get away without reference counting.
> Looking ahead it looks like we assert locks in each case. Maybe add
> that to the comment?

Sure, I'll add that to the comment.

> 
> ```
> > +static void apple_dart_hw_set_ttbr(struct apple_dart *dart, u16 sid, u16 
> > idx,
> > +  phys_addr_t paddr)
> > +{
> > +   writel(DART_TTBR_VALID | (paddr >> DART_TTBR_SHIFT),
> > +  dart->regs + DART_TTBR(sid, idx));
> > +}
> ```
> 
> Should we be checking alignment here? Something like
> 
> BUG_ON(paddr & ((1 << DART_TTBR_SHIFT) - 1));
> 

Sure, right now paddr will always be aligned but adding that
BUG_ON doesn't hurt :)



Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 1/3] iommu: io-pgtable: add DART pagetable format

2021-06-29 Thread Sven Peter via iommu



On Mon, Jun 28, 2021, at 12:54, Alexander Graf wrote:
> 
> 
> On 27.06.21 16:34, Sven Peter wrote:
> > 
> > Apple's DART iommu uses a pagetable format that shares some
> > similarities with the ones already implemented by io-pgtable.c.
> > Add a new format variant to support the required differences
> > so that we don't have to duplicate the pagetable handling code.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >   drivers/iommu/io-pgtable-arm.c | 62 ++
> >   drivers/iommu/io-pgtable.c |  1 +
> >   include/linux/io-pgtable.h |  7 
> >   3 files changed, 70 insertions(+)
> > 
> > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> > index 87def58e79b5..1dd5c45b4b5b 100644
> > --- a/drivers/iommu/io-pgtable-arm.c
> > +++ b/drivers/iommu/io-pgtable-arm.c
> > @@ -127,6 +127,9 @@
> >   #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
> >   #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
> > 
> > +#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
> > +#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
> > +
> >   /* IOPTE accessors */
> >   #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
> > 
> > @@ -381,6 +384,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
> > arm_lpae_io_pgtable *data,
> >   {
> >  arm_lpae_iopte pte;
> > 
> > +   if (data->iop.fmt == ARM_APPLE_DART) {
> > +   pte = 0;
> > +   if (!(prot & IOMMU_WRITE))
> > +   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
> > +   if (!(prot & IOMMU_READ))
> > +   pte |= APPLE_DART_PTE_PROT_NO_READ;
> > +   return pte;
> 
> What about the other bits, such as sharability, XN, etc? Do they not 
> exist on DART? Or have they not been reverse engineered and 0s happen to 
> "just work"?

I'm fairly certain they don't exist (or are at least not used by XNU).

The co-processors that can run code also either use an entire separate iommu
(e.g. the GPU) or only use DART as a "second stage" and have their own
MMU which e.g. handles XN (e.g. the SEP or AOP).

> 
> > +   }
> > +
> >  if (data->iop.fmt == ARM_64_LPAE_S1 ||
> >  data->iop.fmt == ARM_32_LPAE_S1) {
> >  pte = ARM_LPAE_PTE_nG;
> > @@ -1043,6 +1055,51 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg 
> > *cfg, void *cookie)
> >  return NULL;
> >   }
> > 
> > +static struct io_pgtable *
> > +apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> > +{
> > +   struct arm_lpae_io_pgtable *data;
> > +   int i;
> > +
> > +   if (cfg->oas > 36)
> > +   return NULL;
> > +
> > +   data = arm_lpae_alloc_pgtable(cfg);
> > +   if (!data)
> > +   return NULL;
> > +
> > +   /*
> > +* Apple's DART always requires three levels with the first level 
> > being
> > +* stored in four MMIO registers. We always concatenate the first 
> > and
> > +* second level so that we only have to setup the MMIO registers 
> > once.
> > +* This results in an effective two level pagetable.
> > +*/
> > +   if (data->start_level < 1)
> > +   return NULL;
> > +   if (data->start_level == 1 && data->pgd_bits > 2)
> > +   return NULL;
> > +   if (data->start_level > 1)
> > +   data->pgd_bits = 0;
> > +   data->start_level = 2;
> > +   cfg->apple_dart_cfg.n_ttbrs = 1 << data->pgd_bits;
> 
> Maybe add a BUG_ON if n_ttbrs > ARRAY_SIZE(ttbr)? Or alternatively, do a 
> normal runtime check and bail out then.

n_ttbrs can't actually be larger than 4 at this point already due to the
previous checks.
I can add a BUG_ON though just to make it explicit and be safe in case those
checks or the array size ever change.


Sven


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v4 3/3] iommu: dart: Add DART iommu driver

2021-06-27 Thread Sven Peter via iommu
Apple's new SoCs use iommus for almost all peripherals. These Device
Address Resolution Tables must be setup before these peripherals can
act as DMA masters.

Signed-off-by: Sven Peter 
---
 MAINTAINERS  |1 +
 drivers/iommu/Kconfig|   15 +
 drivers/iommu/Makefile   |1 +
 drivers/iommu/apple-dart-iommu.c | 1058 ++
 4 files changed, 1075 insertions(+)
 create mode 100644 drivers/iommu/apple-dart-iommu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 29e5541c8f21..c1ffaa56b5f9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1245,6 +1245,7 @@ M:    Sven Peter 
 L: iommu@lists.linux-foundation.org
 S: Maintained
 F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+F: drivers/iommu/apple-dart-iommu.c
 
 APPLE SMC DRIVER
 M: Henrik Rydberg 
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 1f111b399bca..87882c628b46 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -249,6 +249,21 @@ config SPAPR_TCE_IOMMU
  Enables bits of IOMMU API required by VFIO. The iommu_ops
  is not implemented as it is not necessary for VFIO.
 
+config IOMMU_APPLE_DART
+   tristate "Apple DART IOMMU Support"
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   select IOMMU_API
+   select IOMMU_IO_PGTABLE
+   select IOMMU_IO_PGTABLE_LPAE
+   default ARCH_APPLE
+   help
+ Support for Apple DART (Device Address Resolution Table) IOMMUs
+ found in Apple ARM SoCs like the M1.
+ This IOMMU is required for most peripherals using DMA to access
+ the main memory.
+
+ Say Y here if you are using an Apple SoC with a DART IOMMU.
+
 # ARM IOMMU support
 config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c0fb0ba88143..8c813f0ebc54 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
 obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
 obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o io-pgfault.o
 obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
+obj-$(CONFIG_IOMMU_APPLE_DART) += apple-dart-iommu.o
diff --git a/drivers/iommu/apple-dart-iommu.c b/drivers/iommu/apple-dart-iommu.c
new file mode 100644
index ..637ba6e7cef9
--- /dev/null
+++ b/drivers/iommu/apple-dart-iommu.c
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple DART (Device Address Resolution Table) IOMMU driver
+ *
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ *
+ * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
+ *  Copyright (C) 2013 ARM Limited
+ *  Copyright (C) 2015 ARM Limited
+ * and on exynos-iommu.c
+ *  Copyright (c) 2011,2016 Samsung Electronics Co., Ltd.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DART_MAX_STREAMS 16
+#define DART_MAX_TTBR 4
+
+#define DART_STREAM_ALL 0x
+
+#define DART_PARAMS1 0x00
+#define DART_PARAMS_PAGE_SHIFT GENMASK(27, 24)
+
+#define DART_PARAMS2 0x04
+#define DART_PARAMS_BYPASS_SUPPORT BIT(0)
+
+#define DART_STREAM_COMMAND 0x20
+#define DART_STREAM_COMMAND_BUSY BIT(2)
+#define DART_STREAM_COMMAND_INVALIDATE BIT(20)
+
+#define DART_STREAM_SELECT 0x34
+
+#define DART_ERROR 0x40
+#define DART_ERROR_STREAM GENMASK(27, 24)
+#define DART_ERROR_CODE GENMASK(23, 0)
+#define DART_ERROR_FLAG BIT(31)
+#define DART_ERROR_READ_FAULT BIT(4)
+#define DART_ERROR_WRITE_FAULT BIT(3)
+#define DART_ERROR_NO_PTE BIT(2)
+#define DART_ERROR_NO_PMD BIT(1)
+#define DART_ERROR_NO_TTBR BIT(0)
+
+#define DART_CONFIG 0x60
+#define DART_CONFIG_LOCK BIT(15)
+
+#define DART_STREAM_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_STREAM_REMAP 0x80
+
+#define DART_ERROR_ADDR_HI 0x54
+#define DART_ERROR_ADDR_LO 0x50
+
+#define DART_TCR(sid) (0x100 + 4 * (sid))
+#define DART_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_TCR_BYPASS0_ENABLE BIT(8)
+#define DART_TCR_BYPASS1_ENABLE BIT(12)
+
+#define DART_TTBR(sid, idx) (0x200 + 16 * (sid) + 4 * (idx))
+#define DART_TTBR_VALID BIT(31)
+#define DART_TTBR_SHIFT 12
+
+/*
+ * Private structure associated with each DART device.
+ *
+ * @dev: device struct
+ * @regs: mapped MMIO region
+ * @irq: interrupt number, can be shared with other DARTs
+ * @clks: clocks associated with this DART
+ * @num_clks: number of @clks
+ * @lock: lock for @used_sids and hardware operations involving this dart
+ * @used_sids: bitmap of streams attached to a domain
+ * @pgsize: pagesize supported by this DART
+ * @supports_bypass: indicates if this DART supports bypass mode
+ * @force_bypass: force bypass mode due to pagesize mismatch?
+ * @sw_bypass_cpu_start: offset into cpu address space in software byp

[PATCH v4 2/3] dt-bindings: iommu: add DART iommu bindings

2021-06-27 Thread Sven Peter via iommu
DART (Device Address Resolution Table) is the iommu found on Apple
ARM SoCs such as the M1.

Reviewed-by: Rob Herring 
Signed-off-by: Sven Peter 
---
 .../devicetree/bindings/iommu/apple,dart.yaml | 81 +++
 MAINTAINERS   |  6 ++
 2 files changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml

diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
new file mode 100644
index ..94aa9e9afa59
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/apple,dart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple DART IOMMU
+
+maintainers:
+  - Sven Peter 
+
+description: |+
+  Apple SoCs may contain an implementation of their Device Address
+  Resolution Table which provides a mandatory layer of address
+  translations for various masters.
+
+  Each DART instance is capable of handling up to 16 different streams
+  with individual pagetables and page-level read/write protection flags.
+
+  This DART IOMMU also raises interrupts in response to various
+  fault conditions.
+
+properties:
+  compatible:
+const: apple,t8103-dart
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+description:
+  Reference to the gate clock phandle if required for this IOMMU.
+  Optional since not all IOMMUs are attached to a clock gate.
+
+  '#iommu-cells':
+const: 1
+description:
+  Has to be one. The single cell describes the stream id emitted by
+  a master to the IOMMU.
+
+required:
+  - compatible
+  - reg
+  - '#iommu-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |+
+dart1: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master1 {
+  iommus = <&dart1 0>;
+};
+
+  - |+
+dart2a: iommu@82f0 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f0 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+dart2b: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master2 {
+  iommus = <&dart2a 0>, <&dart2b 1>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 8c5ee008301a..29e5541c8f21 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1240,6 +1240,12 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c
 
+APPLE DART IOMMU DRIVER
+M: Sven Peter 
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+
 APPLE SMC DRIVER
 M: Henrik Rydberg 
 L: linux-hw...@vger.kernel.org
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v4 0/3] Apple M1 DART IOMMU driver

2021-06-27 Thread Sven Peter via iommu
perture by Mark Kettenis and the
   follow-up discussion and investigation with Mark Kettenis, Arnd Bergmann,
   Robin Murphy and Rob Herring. This change also simplified the code
   in io-pgtable.c and made some of the improvements suggested during review
   not apply anymore.
 - added support for bypassed and isolated domain modes.
 - reject IOMMU_MMIO and IOMMU_NOEXEC since it's unknown how to set these up
   for now or if the hardware even supports these flags.
 - renamed some registers to be less confusing (mainly s/DOMAIN/STREAM/ to
   prevent confusion with linux's iommu domain concept).


[1] 
https://lore.kernel.org/linux-iommu/20210320151903.60759-1-s...@svenpeter.dev/
[2] 
https://lore.kernel.org/linux-iommu/20210328074009.95932-1-s...@svenpeter.dev/
[3] 
https://lore.kernel.org/linux-iommu/20210603085003.50465-1-s...@svenpeter.dev/
[4] https://github.com/AsahiLinux/docs/wiki/Developer-Quickstart
[5] 
https://github.com/AsahiLinux/linux/commit/7d4ebb0b22e9bfec849e2af86ddeb46ec29d7feb
[6] https://github.com/AsahiLinux/linux/tree/dart/dev
[7] 
https://github.com/AsahiLinux/m1n1/commit/9529ec2b4fd6550f9cfd66d9f2448b90804699a1

Sven Peter (3):
  iommu: io-pgtable: add DART pagetable format
  dt-bindings: iommu: add DART iommu bindings
  iommu: dart: Add DART iommu driver

 .../devicetree/bindings/iommu/apple,dart.yaml |   81 ++
 MAINTAINERS   |7 +
 drivers/iommu/Kconfig |   15 +
 drivers/iommu/Makefile|1 +
 drivers/iommu/apple-dart-iommu.c  | 1058 +
 drivers/iommu/io-pgtable-arm.c|   62 +
 drivers/iommu/io-pgtable.c|1 +
 include/linux/io-pgtable.h|7 +
 8 files changed, 1232 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml
 create mode 100644 drivers/iommu/apple-dart-iommu.c

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v4 1/3] iommu: io-pgtable: add DART pagetable format

2021-06-27 Thread Sven Peter via iommu
Apple's DART iommu uses a pagetable format that shares some
similarities with the ones already implemented by io-pgtable.c.
Add a new format variant to support the required differences
so that we don't have to duplicate the pagetable handling code.

Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 62 ++
 drivers/iommu/io-pgtable.c |  1 +
 include/linux/io-pgtable.h |  7 
 3 files changed, 70 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 87def58e79b5..1dd5c45b4b5b 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -127,6 +127,9 @@
 #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
 #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
 
+#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
+#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -381,6 +384,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
arm_lpae_io_pgtable *data,
 {
arm_lpae_iopte pte;
 
+   if (data->iop.fmt == ARM_APPLE_DART) {
+   pte = 0;
+   if (!(prot & IOMMU_WRITE))
+   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
+   if (!(prot & IOMMU_READ))
+   pte |= APPLE_DART_PTE_PROT_NO_READ;
+   return pte;
+   }
+
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
pte = ARM_LPAE_PTE_nG;
@@ -1043,6 +1055,51 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
return NULL;
 }
 
+static struct io_pgtable *
+apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+   struct arm_lpae_io_pgtable *data;
+   int i;
+
+   if (cfg->oas > 36)
+   return NULL;
+
+   data = arm_lpae_alloc_pgtable(cfg);
+   if (!data)
+   return NULL;
+
+   /*
+* Apple's DART always requires three levels with the first level being
+* stored in four MMIO registers. We always concatenate the first and
+* second level so that we only have to setup the MMIO registers once.
+* This results in an effective two level pagetable.
+*/
+   if (data->start_level < 1)
+   return NULL;
+   if (data->start_level == 1 && data->pgd_bits > 2)
+   return NULL;
+   if (data->start_level > 1)
+   data->pgd_bits = 0;
+   data->start_level = 2;
+   cfg->apple_dart_cfg.n_ttbrs = 1 << data->pgd_bits;
+   data->pgd_bits += data->bits_per_level;
+
+   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
+  cfg);
+   if (!data->pgd)
+   goto out_free_data;
+
+   for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i)
+   cfg->apple_dart_cfg.ttbr[i] =
+   virt_to_phys(data->pgd + i * ARM_LPAE_GRANULE(data));
+
+   return &data->iop;
+
+out_free_data:
+   kfree(data);
+   return NULL;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
.alloc  = arm_64_lpae_alloc_pgtable_s1,
.free   = arm_lpae_free_pgtable,
@@ -1068,6 +1125,11 @@ struct io_pgtable_init_fns 
io_pgtable_arm_mali_lpae_init_fns = {
.free   = arm_lpae_free_pgtable,
 };
 
+struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns = {
+   .alloc  = apple_dart_alloc_pgtable,
+   .free   = arm_lpae_free_pgtable,
+};
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
 
 static struct io_pgtable_cfg *cfg_cookie __initdata;
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6e9917ce980f..fd8e6bd6caf9 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
+   [ARM_APPLE_DART] = &io_pgtable_apple_dart_init_fns,
 #endif
 #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index 4d40dfa75b55..a4bfac7f85f7 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -16,6 +16,7 @@ enum io_pgtable_fmt {
ARM_V7S,
ARM_MALI_LPAE,
AMD_IOMMU_V1,
+   ARM_APPLE_DART,
IO_PGTABLE_NUM_FMTS,
 };
 
@@ -136,6 +137,11 @@ struct io_pgtable_cfg {
u64 transtab;
u64 memattr;
} arm_mali_lpae_cfg;
+
+   struct {
+   u64 ttbr[4];
+   u32 n

Re: [PATCH v3 2/3] dt-bindings: iommu: add DART iommu bindings

2021-06-12 Thread Sven Peter via iommu
Hi,

On Thu, Jun 10, 2021, at 18:52, Rob Herring wrote:
> On Thu, Jun 03, 2021 at 10:50:02AM +0200, Sven Peter wrote:
> > +
> > +examples:
> > +  - |+
> > +dart1: iommu@82f8 {
> > +  compatible = "apple,t8103-dart";
> > +  reg = <0x82f8 0x4000>;
> > +  interrupts = <1 781 4>;
> > +  #iommu-cells = <1>;
> > +};
> > +
> > +master1 {
> > +  iommus = <&{/dart1} 0>;
> 
> /dart1 is a path, but 'dart1' is a label. You need '&dart1' (or 
> '&{/iommu@82f8}' but that doesn't really work here because the 
> examples get prefixed with /example-n/...)
> 
> With that fixed,
> 
> Reviewed-by: Rob Herring 

Makes sense, thanks for the review!
Fixed for the next version.


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 3/3] iommu: dart: Add DART iommu driver

2021-06-05 Thread Sven Peter via iommu
Hi Rouven,

On Sat, Jun 5, 2021, at 13:50, Rouven Czerwinski wrote:
> Hi Sven,
> 
> just a small comment, see inline.
> 
> On Thu, 2021-06-03 at 10:50 +0200, Sven Peter wrote:
> > +
> > +/* must be called with held dart_domain->lock */
> 
> You can remove this comment, include lockdep.h and…
> 
> > +static int apple_dart_finalize_domain(struct iommu_domain *domain)
> > +{
> > +   struct apple_dart_domain *dart_domain = to_dart_domain(domain);
> > +   struct apple_dart *dart = dart_domain->dart;
> > +   struct io_pgtable_cfg pgtbl_cfg;
> > +
> 
>   lockdep_assert_held(&dart_domain->lock);
> 
> A lockdep enabled kernel will warn if this function is called without
> the lock held, otherwise this gets optimized out. Same for the similar
> comments below.
> 

That looks very useful, thanks! Will use it for v4.

I only found assert_spin_locked originally but didn't want to have that
performance overhead for code that (I hope :-)) correctly uses these functions
with a held lock right now.


Thanks,


Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH v3 3/3] iommu: dart: Add DART iommu driver

2021-06-03 Thread Sven Peter via iommu
Apple's new SoCs use iommus for almost all peripherals. These Device
Address Resolution Tables must be setup before these peripherals can
act as DMA masters.

Signed-off-by: Sven Peter 
---
 MAINTAINERS  |   1 +
 drivers/iommu/Kconfig|  15 +
 drivers/iommu/Makefile   |   1 +
 drivers/iommu/apple-dart-iommu.c | 966 +++
 4 files changed, 983 insertions(+)
 create mode 100644 drivers/iommu/apple-dart-iommu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4373d63f9ccf..cb9200ad05fe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1245,6 +1245,7 @@ M:    Sven Peter 
 L: iommu@lists.linux-foundation.org
 S: Maintained
 F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+F: drivers/iommu/apple-dart-iommu.c
 
 APPLE SMC DRIVER
 M: Henrik Rydberg 
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 1f111b399bca..87882c628b46 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -249,6 +249,21 @@ config SPAPR_TCE_IOMMU
  Enables bits of IOMMU API required by VFIO. The iommu_ops
  is not implemented as it is not necessary for VFIO.
 
+config IOMMU_APPLE_DART
+   tristate "Apple DART IOMMU Support"
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   select IOMMU_API
+   select IOMMU_IO_PGTABLE
+   select IOMMU_IO_PGTABLE_LPAE
+   default ARCH_APPLE
+   help
+ Support for Apple DART (Device Address Resolution Table) IOMMUs
+ found in Apple ARM SoCs like the M1.
+ This IOMMU is required for most peripherals using DMA to access
+ the main memory.
+
+ Say Y here if you are using an Apple SoC with a DART IOMMU.
+
 # ARM IOMMU support
 config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c0fb0ba88143..8c813f0ebc54 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
 obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
 obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o io-pgfault.o
 obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
+obj-$(CONFIG_IOMMU_APPLE_DART) += apple-dart-iommu.o
diff --git a/drivers/iommu/apple-dart-iommu.c b/drivers/iommu/apple-dart-iommu.c
new file mode 100644
index ..2777852498de
--- /dev/null
+++ b/drivers/iommu/apple-dart-iommu.c
@@ -0,0 +1,966 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple DART (Device Address Resolution Table) IOMMU driver
+ *
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ *
+ * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
+ *  Copyright (C) 2013 ARM Limited
+ *  Copyright (C) 2015 ARM Limited
+ * and on exynos-iommu.c
+ *  Copyright (c) 2011,2016 Samsung Electronics Co., Ltd.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DART_MAX_STREAMS 16
+#define DART_MAX_TTBR 4
+
+#define DART_STREAM_ALL 0x
+
+#define DART_PARAMS1 0x00
+#define DART_PARAMS_PAGE_SHIFT GENMASK(27, 24)
+
+#define DART_PARAMS2 0x04
+#define DART_PARAMS_BYPASS_SUPPORT BIT(0)
+
+#define DART_STREAM_COMMAND 0x20
+#define DART_STREAM_COMMAND_BUSY BIT(2)
+#define DART_STREAM_COMMAND_INVALIDATE BIT(20)
+
+#define DART_STREAM_SELECT 0x34
+
+#define DART_ERROR 0x40
+#define DART_ERROR_STREAM GENMASK(27, 24)
+#define DART_ERROR_CODE GENMASK(23, 0)
+#define DART_ERROR_FLAG BIT(31)
+#define DART_ERROR_READ_FAULT BIT(4)
+#define DART_ERROR_WRITE_FAULT BIT(3)
+#define DART_ERROR_NO_PTE BIT(2)
+#define DART_ERROR_NO_PMD BIT(1)
+#define DART_ERROR_NO_TTBR BIT(0)
+
+#define DART_CONFIG 0x60
+#define DART_CONFIG_LOCK BIT(15)
+
+#define DART_STREAM_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_STREAM_REMAP 0x80
+
+#define DART_ERROR_ADDR_HI 0x54
+#define DART_ERROR_ADDR_LO 0x50
+
+#define DART_TCR(sid) (0x100 + 4 * (sid))
+#define DART_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_TCR_BYPASS0_ENABLE BIT(8)
+#define DART_TCR_BYPASS1_ENABLE BIT(12)
+
+#define DART_TTBR(sid, idx) (0x200 + 16 * (sid) + 4 * (idx))
+#define DART_TTBR_VALID BIT(31)
+#define DART_TTBR_SHIFT 12
+
+/*
+ * Private structure associated with each DART device.
+ *
+ * @dev: device struct
+ * @regs: mapped MMIO region
+ * @irq: interrupt number, can be shared with other DARTs
+ * @clks: clocks associated with this DART
+ * @num_clks: number of @clks
+ * @lock: lock for @used_sids and hardware operations involving this dart
+ * @used_sids: bitmap of streams attached to a domain
+ * @pgsize: pagesize supported by this DART
+ * @supports_bypass: indicates if this DART supports bypass mode
+ * @force_bypass: force bypass mode due to pagesize mismatch?
+ * @iommu: iommu core device
+ */
+struct apple_dart {
+   struct device *dev;
+
+   void

[PATCH v3 1/3] iommu: io-pgtable: add DART pagetable format

2021-06-03 Thread Sven Peter via iommu
Apple's DART iommu uses a pagetable format that shares some
similarities with the ones already implemented by io-pgtable.c.
Add a new format variant to support the required differences
so that we don't have to duplicate the pagetable handling code.

Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 62 ++
 drivers/iommu/io-pgtable.c |  1 +
 include/linux/io-pgtable.h |  7 
 3 files changed, 70 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 87def58e79b5..1dd5c45b4b5b 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -127,6 +127,9 @@
 #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
 #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
 
+#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
+#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -381,6 +384,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
arm_lpae_io_pgtable *data,
 {
arm_lpae_iopte pte;
 
+   if (data->iop.fmt == ARM_APPLE_DART) {
+   pte = 0;
+   if (!(prot & IOMMU_WRITE))
+   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
+   if (!(prot & IOMMU_READ))
+   pte |= APPLE_DART_PTE_PROT_NO_READ;
+   return pte;
+   }
+
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
pte = ARM_LPAE_PTE_nG;
@@ -1043,6 +1055,51 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
return NULL;
 }
 
+static struct io_pgtable *
+apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+   struct arm_lpae_io_pgtable *data;
+   int i;
+
+   if (cfg->oas > 36)
+   return NULL;
+
+   data = arm_lpae_alloc_pgtable(cfg);
+   if (!data)
+   return NULL;
+
+   /*
+* Apple's DART always requires three levels with the first level being
+* stored in four MMIO registers. We always concatenate the first and
+* second level so that we only have to setup the MMIO registers once.
+* This results in an effective two level pagetable.
+*/
+   if (data->start_level < 1)
+   return NULL;
+   if (data->start_level == 1 && data->pgd_bits > 2)
+   return NULL;
+   if (data->start_level > 1)
+   data->pgd_bits = 0;
+   data->start_level = 2;
+   cfg->apple_dart_cfg.n_ttbrs = 1 << data->pgd_bits;
+   data->pgd_bits += data->bits_per_level;
+
+   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
+  cfg);
+   if (!data->pgd)
+   goto out_free_data;
+
+   for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i)
+   cfg->apple_dart_cfg.ttbr[i] =
+   virt_to_phys(data->pgd + i * ARM_LPAE_GRANULE(data));
+
+   return &data->iop;
+
+out_free_data:
+   kfree(data);
+   return NULL;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
.alloc  = arm_64_lpae_alloc_pgtable_s1,
.free   = arm_lpae_free_pgtable,
@@ -1068,6 +1125,11 @@ struct io_pgtable_init_fns 
io_pgtable_arm_mali_lpae_init_fns = {
.free   = arm_lpae_free_pgtable,
 };
 
+struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns = {
+   .alloc  = apple_dart_alloc_pgtable,
+   .free   = arm_lpae_free_pgtable,
+};
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
 
 static struct io_pgtable_cfg *cfg_cookie __initdata;
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6e9917ce980f..fd8e6bd6caf9 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
+   [ARM_APPLE_DART] = &io_pgtable_apple_dart_init_fns,
 #endif
 #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index 4d40dfa75b55..a4bfac7f85f7 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -16,6 +16,7 @@ enum io_pgtable_fmt {
ARM_V7S,
ARM_MALI_LPAE,
AMD_IOMMU_V1,
+   ARM_APPLE_DART,
IO_PGTABLE_NUM_FMTS,
 };
 
@@ -136,6 +137,11 @@ struct io_pgtable_cfg {
u64 transtab;
u64 memattr;
} arm_mali_lpae_cfg;
+
+   struct {
+   u64 ttbr[4];
+   u32 n

[PATCH v3 2/3] dt-bindings: iommu: add DART iommu bindings

2021-06-03 Thread Sven Peter via iommu
DART (Device Address Resolution Table) is the iommu found on Apple
ARM SoCs such as the M1.

Signed-off-by: Sven Peter 
---
 .../devicetree/bindings/iommu/apple,dart.yaml | 81 +++
 MAINTAINERS   |  6 ++
 2 files changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml

diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
new file mode 100644
index ..db21ca07d121
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/apple,dart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple DART IOMMU
+
+maintainers:
+  - Sven Peter 
+
+description: |+
+  Apple SoCs may contain an implementation of their Device Address
+  Resolution Table which provides a mandatory layer of address
+  translations for various masters.
+
+  Each DART instance is capable of handling up to 16 different streams
+  with individual pagetables and page-level read/write protection flags.
+
+  This DART IOMMU also raises interrupts in response to various
+  fault conditions.
+
+properties:
+  compatible:
+const: apple,t8103-dart
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+description:
+  Reference to the gate clock phandle if required for this IOMMU.
+  Optional since not all IOMMUs are attached to a clock gate.
+
+  '#iommu-cells':
+const: 1
+description:
+  Has to be one. The single cell describes the stream id emitted by
+  a master to the IOMMU.
+
+required:
+  - compatible
+  - reg
+  - '#iommu-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |+
+dart1: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master1 {
+  iommus = <&{/dart1} 0>;
+};
+
+  - |+
+dart2a: iommu@82f0 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f0 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+dart2b: iommu@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master2 {
+  iommus = <&{/dart2a} 0>, <&{/dart2b} 1>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 673cadd5107a..4373d63f9ccf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1240,6 +1240,12 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c
 
+APPLE DART IOMMU DRIVER
+M: Sven Peter 
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
+
 APPLE SMC DRIVER
 M: Henrik Rydberg 
 L: linux-hw...@vger.kernel.org
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 0/3] Apple M1 DART IOMMU driver

2021-06-03 Thread Sven Peter via iommu
Hi,

This is v3 of my Apple M1 DART IOMMU driver series as a follow up to the 
original
two versions [1][2].

Short summary: this series adds support for the iommu found in Apple's new M1
SoC which is required to use DMA on most peripherals. So far this code has been
tested with dwc3 in host and device mode and PCIe on a M1 Mac Mini.

This IOMMU comes with a hard-wired pagesize of 16K. This makes booting a
kernel with 4K page challenging.

For dwc3 this is no issue: As long as the iommu is set to bypass mode
dwc3 works just fine. Translated mode isn't supported right now.

The most controversial part on which I'd like to get feedback are the
PCIe DARTs. These DARTs do not support hardware bypass mode and also limit
the iova space to 32 bit. To still allow booting on kernels with a 4K
pagesize I have introduced the following hack:

I program a static pagetable that maps the entire 32bit iova space to the
first 4GB of RAM starting at 0x8__. Combined with an appropriate
dma-ranges property in the pcie node this allows to create a fake
bypass mode and successfully enables PCIe.
Right now the RAM offset is hardcoded in the DART driver and this will
likely have to change if this workaround is acceptable for now. I could
either introduce a separate property for the iommu node or try to grab
the offset from the dma-ranges once the first device is attached.


Changes for v3:
 - fixed name of the iommu node in the device tree binding example
   pointed out by Arnd Bergmann
 - remove hardware specific checks from io-pgtable.c  as pointed out by
   Will Deacon
 - introduced a fake bypass mode by programming static linear pagetables
   if the DART does not support regular bypass mode as proposed by Alex
   Graf
 - added checks to enforce bypass mode if there is a pagesize mismatch
   between the DART HW and the CPU.
 - fixed usage of GFP_KERNEL during a held spinlock found by Julia Lawall
 - rebased on v5.13-rc3

Changes for v2:
 - fixed devicetree binding linting issues pointed out by Rob Herring and
   reworked that file.
 - made DART-specific code in io-pgtable.c unconditional and removed flag from
   Kconfig as proposed by Robin Murphy.
 - allowed multiple DART nodes in the "iommus" property as proposed by
   Rob Herring and Robin Murphy. this resulted in significant changes
   to apple-iommu-dart.c.
 - the domain aperture is now forced to 32bit if translation is enabled after
   the original suggestion to limit the aperture by Mark Kettenis and the
   follow-up discussion and investigation with Mark Kettenis, Arnd Bergmann,
   Robin Murphy and Rob Herring. This change also simplified the code
   in io-pgtable.c and made some of the improvements suggested during review
   not apply anymore.
 - added support for bypassed and isolated domain modes.
 - reject IOMMU_MMIO and IOMMU_NOEXEC since it's unknown how to set these up
   for now or if the hardware even supports these flags.
 - renamed some registers to be less confusing (mainly s/DOMAIN/STREAM/ to
   prevent confusion with linux's iommu domain concept).


[1] v1: 
https://lore.kernel.org/linux-iommu/20210320151903.60759-1-s...@svenpeter.dev/
[2] v2: 
https://lore.kernel.org/linux-iommu/20210328074009.95932-1-s...@svenpeter.dev/

Sven Peter (3):
  iommu: io-pgtable: add DART pagetable format
  dt-bindings: iommu: add DART iommu bindings
  iommu: dart: Add DART iommu driver

 .../devicetree/bindings/iommu/apple,dart.yaml |  81 ++
 MAINTAINERS   |   7 +
 drivers/iommu/Kconfig |  15 +
 drivers/iommu/Makefile|   1 +
 drivers/iommu/apple-dart-iommu.c  | 966 ++
 drivers/iommu/io-pgtable-arm.c|  62 ++
 drivers/iommu/io-pgtable.c|   1 +
 include/linux/io-pgtable.h|   7 +
 8 files changed, 1140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml
 create mode 100644 drivers/iommu/apple-dart-iommu.c

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3] iommu/dma: Fix IOVA reserve dma ranges

2021-06-02 Thread Sven Peter via iommu
Hi,

I just ran into the exact same issue while working on the M1 DART IOMMU driver
and it was fixed by this commit. Thanks!

Would be great if this could be picked up.

Tested-by: Sven Peter 


Best,


Sven


On Mon, Sep 14, 2020, at 09:23, Srinath Mannam via iommu wrote:
> Fix IOVA reserve failure in the case when address of first memory region
> listed in dma-ranges is equal to 0x0.
> 
> Fixes: aadad097cd46f ("iommu/dma: Reserve IOVA for PCIe inaccessible 
> DMA address")
> Signed-off-by: Srinath Mannam 
> ---
> Changes from v2:
>Modify error message with useful information based on Bjorn's 
> comments.
> 
> Changes from v1:
>Removed unnecessary changes based on Robin's review comments.
> 
>  drivers/iommu/dma-iommu.c | 6 --
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 5141d49a046b..5b9791f35c5e 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -217,9 +217,11 @@ static int iova_reserve_pci_windows(struct pci_dev *dev,
>   lo = iova_pfn(iovad, start);
>   hi = iova_pfn(iovad, end);
>   reserve_iova(iovad, lo, hi);
> - } else {
> + } else if (end < start) {
>   /* dma_ranges list should be sorted */
> - dev_err(&dev->dev, "Failed to reserve IOVA\n");
> + dev_err(&dev->dev,
> + "Failed to reserve IOVA [%#010llx-%#010llx]\n",
> + start, end);
>   return -EINVAL;
>   }
>  
> -- 
> 2.17.1
> 
> ___
> iommu mailing list
> iommu@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 
> 
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH] iommu: dart: fix call_kern.cocci warnings

2021-04-09 Thread Sven Peter via iommu
On Sun, Apr 4, 2021, at 17:26, Julia Lawall wrote:
> From: kernel test robot 
> 
> Function apple_dart_attach_stream called on line 519 inside
> lock on line 509 but uses GFP_KERNEL

Thanks! Fixed for v3.


Best,


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 1/3] iommu: io-pgtable: add DART pagetable format

2021-04-09 Thread Sven Peter via iommu



On Wed, Apr 7, 2021, at 12:44, Will Deacon wrote:
> On Sun, Mar 28, 2021 at 09:40:07AM +0200, Sven Peter wrote:
[...]
> >  
> > +static struct io_pgtable *
> > +apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> > +{
> > +   struct arm_lpae_io_pgtable *data;
> > +
> > +   if (cfg->ias > 36)
> > +   return NULL;
> > +   if (cfg->oas > 36)
> > +   return NULL;
> > +
> > +   if (!cfg->coherent_walk)
> > +   return NULL;
> 
> This all feels like IOMMU-specific limitations leaking into the page-table
> code here; it doesn't feel so unlikely that future implementations of this
> IP might have greater addressing capabilities, for example, and so I don't
> see why the page-table code needs to police this.

That's true, this really doesn't belong here.
I'll fix it for the next version and make sure to keep iommu-specific
limitations inside the driver itself.


> 
> > +   cfg->pgsize_bitmap &= SZ_16K;
> > +   if (!cfg->pgsize_bitmap)
> > +   return NULL;
> 
> This is worrying (and again, I don't think this belongs here). How is this
> thing supposed to work if the CPU is using 4k pages?

This SoC is just full of fun surprises!
I didn't even think about that case since I've always been using 16k pages so 
far.

I've checked again and wasn't able to find any way to configure the pagesize
of the IOMMU. There seem to be variants of this IP in older iPhones which
support a 4k pagesize but to the best of my knowledge this is hard wired
and not configurable in software.

When booting with 4k pages I hit the BUG_ON in iova.c that ensures that the
iommu pagesize has to be <= the cpu page size.

I see two options here and I'm not sure I like either of them:

1) Just don't support 4k CPU pages together with IOMMU translations and only
   allow full bypass mode there.
   This would however mean that PCIe (i.e. ethernet, usb ports on the Mac
   mini) and possibly Thunderbolt support would not be possible since these
   devices don't seem to like iommu bypass mode at all.

2) I've had a brief discussion on IRC with Arnd about this [1] and he pointed
   out that the dma_map_sg API doesn't make any guarantees about the returned
   iovas and that it might be possible to make this work at least for devices
   that go through the normal DMA API.

   I've then replaced the page size check with a WARN_ON in iova.c just to see
   what happens. At least normal devices that go through the DMA API seem to
   work with my configuration. iommu_dma_alloc took the iommu_dma_alloc_remap
   path which was called with the cpu page size but then used
   domain->pgsize_bitmap to increase that to 16k. So this kinda works out, but
   there are other functions in dma-iommu.c that I believe rely on the fact that
   the iommu can map single cpu pages. This feels very fragile right now and
   would probably require some rather invasive changes.

   Any driver that tries to use the iommu API directly could be trouble
   as well if they make similar assumptions.

   Is this something you would even want to support in the iommu subsytem
   and is it even possible to do this in a sane way?


Best,


Sven


[1] https://freenode.irclog.whitequark.org/asahi/2021-04-07#29609786;
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 3/3] iommu: dart: Add DART iommu driver

2021-04-09 Thread Sven Peter via iommu



On Wed, Apr 7, 2021, at 12:42, Will Deacon wrote:
> On Sun, Mar 28, 2021 at 09:40:09AM +0200, Sven Peter wrote:
> > Apple's new SoCs use iommus for almost all peripherals. These Device
> > Address Resolution Tables must be setup before these peripherals can
> > act as DMA masters.
> > 
> > Signed-off-by: Sven Peter 
> > ---
> >  MAINTAINERS  |   1 +
> >  drivers/iommu/Kconfig|  14 +
> >  drivers/iommu/Makefile   |   1 +
> >  drivers/iommu/apple-dart-iommu.c | 858 +++
> >  4 files changed, 874 insertions(+)
> >  create mode 100644 drivers/iommu/apple-dart-iommu.c
> 
> [...]
> 
> > +/* must be called with held domain->lock */
> > +static int apple_dart_attach_stream(struct apple_dart_domain *domain,
> > +   struct apple_dart *dart, u32 sid)
> > +{
> > +   unsigned long flags;
> > +   struct apple_dart_stream *stream;
> > +   struct io_pgtable_cfg *pgtbl_cfg;
> > +   int ret;
> > +
> > +   list_for_each_entry(stream, &domain->streams, stream_head) {
> > +   if (stream->dart == dart && stream->sid == sid) {
> > +   stream->num_devices++;
> > +   return 0;
> > +   }
> > +   }
> > +
> > +   spin_lock_irqsave(&dart->lock, flags);
> > +
> > +   if (WARN_ON(dart->used_sids & BIT(sid))) {
> > +   ret = -EINVAL;
> > +   goto error;
> > +   }
> > +
> > +   stream = kzalloc(sizeof(*stream), GFP_KERNEL);
> > +   if (!stream) {
> > +   ret = -ENOMEM;
> > +   goto error;
> > +   }
> 
> Just in case you missed it, a cocci bot noticed that you're using GFP_KERNEL
> to allocate while holding a spinlock here:
> 
> https://lore.kernel.org/r/alpine.DEB.2.22.394.2104041724340.2958@hadrien
> 

Thanks for the reminder!
I haven't replied yet because that one was found later when the bot picked up
a (slightly earlier) version that Marc was using to bring up pcie I believe.
I'll fix it for the next version.


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 2/3] dt-bindings: iommu: add DART iommu bindings

2021-03-28 Thread Sven Peter via iommu



On Sun, Mar 28, 2021, at 10:16, Arnd Bergmann wrote:
> On Sun, Mar 28, 2021 at 9:40 AM Sven Peter  wrote:
> 
> I noticed only one detail here:
> 
> > +  - |+
> > +dart2a: dart2a@82f0 {
> > +  compatible = "apple,t8103-dart";
> > +  reg = <0x82f0 0x4000>;
> > +  interrupts = <1 781 4>;
> > +  #iommu-cells = <1>;
> > +};
> 
> The name of the iommu should be iommu@82f0, not dart2a@82f0.
> 
>Arnd
>

Thanks, fixed for v3. I've also just noticed that I forgot to update
the filename in MAINTAINERS after I renamed it from apple,t8103-dart.yaml
which I've fixed as well.


Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 3/3] iommu: dart: Add DART iommu driver

2021-03-28 Thread Sven Peter via iommu
Apple's new SoCs use iommus for almost all peripherals. These Device
Address Resolution Tables must be setup before these peripherals can
act as DMA masters.

Signed-off-by: Sven Peter 
---
 MAINTAINERS  |   1 +
 drivers/iommu/Kconfig|  14 +
 drivers/iommu/Makefile   |   1 +
 drivers/iommu/apple-dart-iommu.c | 858 +++
 4 files changed, 874 insertions(+)
 create mode 100644 drivers/iommu/apple-dart-iommu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f5397328fa1f..70747b8ac0ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1241,6 +1241,7 @@ M:    Sven Peter 
 L: iommu@lists.linux-foundation.org
 S: Maintained
 F: Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
+F: drivers/iommu/apple-dart-iommu.c
 
 APPLE SMC DRIVER
 M: Henrik Rydberg 
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 192ef8f61310..a1b239147dbc 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -249,6 +249,20 @@ config SPAPR_TCE_IOMMU
  Enables bits of IOMMU API required by VFIO. The iommu_ops
  is not implemented as it is not necessary for VFIO.
 
+config IOMMU_APPLE_DART
+   tristate "Apple DART IOMMU Support"
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   select IOMMU_API
+   select IOMMU_IO_PGTABLE
+   select IOMMU_IO_PGTABLE_LPAE
+   help
+ Support for Apple DART (Device Address Resolution Table) IOMMUs
+ found in Apple ARM SoCs like the M1.
+ This IOMMU is required for most peripherals using DMA to access
+ the main memory.
+
+ Say Y here if you are using an Apple SoC with a DART IOMMU.
+
 # ARM IOMMU support
 config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 61bd30cd8369..5f21f0dfec6a 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
 obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
 obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
 obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o
+obj-$(CONFIG_IOMMU_APPLE_DART) += apple-dart-iommu.o
diff --git a/drivers/iommu/apple-dart-iommu.c b/drivers/iommu/apple-dart-iommu.c
new file mode 100644
index ..05fb8ca44843
--- /dev/null
+++ b/drivers/iommu/apple-dart-iommu.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple DART (Device Address Resolution Table) IOMMU driver
+ *
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ *
+ * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
+ *  Copyright (C) 2013 ARM Limited
+ *  Copyright (C) 2015 ARM Limited
+ * and on exynos-iommu.c
+ *  Copyright (c) 2011,2016 Samsung Electronics Co., Ltd.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DART_MAX_STREAMS 16
+#define DART_MAX_TTBR 4
+
+#define DART_STREAM_ALL 0x
+
+#define DART_CONFIG 0x60
+#define DART_CONFIG_LOCK BIT(15)
+
+#define DART_ERROR 0x40
+#define DART_ERROR_STREAM_SHIFT 24
+#define DART_ERROR_STREAM_MASK 0xf
+#define DART_ERROR_CODE_MASK 0xff
+#define DART_ERROR_FLAG BIT(31)
+#define DART_ERROR_READ_FAULT BIT(4)
+#define DART_ERROR_WRITE_FAULT BIT(3)
+#define DART_ERROR_NO_PTE BIT(2)
+#define DART_ERROR_NO_PMD BIT(1)
+#define DART_ERROR_NO_TTBR BIT(0)
+
+#define DART_STREAM_SELECT 0x34
+
+#define DART_STREAM_COMMAND 0x20
+#define DART_STREAM_COMMAND_BUSY BIT(2)
+#define DART_STREAM_COMMAND_INVALIDATE BIT(20)
+
+#define DART_STREAM_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_STREAM_REMAP 0x80
+
+#define DART_ERROR_ADDR_HI 0x54
+#define DART_ERROR_ADDR_LO 0x50
+
+#define DART_TCR(sid) (0x100 + 4 * (sid))
+#define DART_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_TCR_BYPASS0_ENABLE BIT(8)
+#define DART_TCR_BYPASS1_ENABLE BIT(12)
+
+#define DART_TTBR(sid, idx) (0x200 + 16 * (sid) + 4 * (idx))
+#define DART_TTBR_VALID BIT(31)
+#define DART_TTBR_SHIFT 12
+
+/*
+ * Private structure associated with each DART device.
+ *
+ * @dev: device struct
+ * @regs: mapped MMIO region
+ * @irq: interrupt number, can be shared with other DARTs
+ * @clks: clocks associated with this DART
+ * @num_clks: number of @clks
+ * @lock: lock for @used_sids and hardware operations involving this dart
+ * @used_sids: bitmap of streams attached to a domain
+ * @iommu: iommu core device
+ */
+struct apple_dart {
+   struct device *dev;
+
+   void __iomem *regs;
+
+   int irq;
+   struct clk_bulk_data *clks;
+   int num_clks;
+
+   spinlock_t lock;
+
+   u32 used_sids;
+
+   struct iommu_device iommu;
+};
+
+/*
+ * This structure is used to identify a single stream attached to a domain.
+ * It's used as a list inside that domain to be able to attach multiple
+ * streams to a single domain. Since mu

[PATCH v2 2/3] dt-bindings: iommu: add DART iommu bindings

2021-03-28 Thread Sven Peter via iommu
DART (Device Address Resolution Table) is the iommu found on Apple
ARM SoCs such as the M1.

Signed-off-by: Sven Peter 
---
 .../devicetree/bindings/iommu/apple,dart.yaml | 81 +++
 MAINTAINERS   |  6 ++
 2 files changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml

diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
new file mode 100644
index ..c0b43d90c157
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/apple,dart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple DART IOMMU
+
+maintainers:
+  - Sven Peter 
+
+description: |+
+  Apple SoCs may contain an implementation of their Device Address
+  Resolution Table which provides a mandatory layer of address
+  translations for various masters.
+
+  Each DART instance is capable of handling up to 16 different streams
+  with individual pagetables and page-level read/write protection flags.
+
+  This DART IOMMU also raises interrupts in response to various
+  fault conditions.
+
+properties:
+  compatible:
+const: apple,t8103-dart
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+description:
+  Reference to the gate clock phandle if required for this IOMMU.
+  Optional since not all IOMMUs are attached to a clock gate.
+
+  '#iommu-cells':
+const: 1
+description:
+  Has to be one. The single cell describes the stream id emitted by
+  a master to the IOMMU.
+
+required:
+  - compatible
+  - reg
+  - '#iommu-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |+
+dart1: dart1@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master1 {
+  iommus = <&{/dart1} 0>;
+};
+
+  - |+
+dart2a: dart2a@82f0 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f0 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+dart2b: dart2@82f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x82f8 0x4000>;
+  interrupts = <1 781 4>;
+  #iommu-cells = <1>;
+};
+
+master2 {
+  iommus = <&{/dart2a} 0>, <&{/dart2b} 1>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 9ac46317840b..f5397328fa1f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1236,6 +1236,12 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c
 
+APPLE DART IOMMU DRIVER
+M: Sven Peter 
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
+
 APPLE SMC DRIVER
 M: Henrik Rydberg 
 L: linux-hw...@vger.kernel.org
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 0/3] Apple M1 DART IOMMU driver

2021-03-28 Thread Sven Peter via iommu
Hi,

Here's v2 of my Apple M1 DART IOMMU driver series as a follow up to the original
version [1].

Short summary: this series adds support for the iommu found in Apple's new M1
SoC which is required to use DMA on most peripherals. So far this code has been
tested with dwc3 in host and device mode on a M1 Mac Mini on top of the latest
version of Hector's bringup series [2,3] together with my m1n1 bootloader
branch to bring up USB [4]. It will also apply (but not be very useful) on
top of iommu/next and v5.12-rc3.

Thanks everyone for the suggestions and discussions so far. I believe they
have already significantly improved the state of this driver and our
understanding of the DART iommu!

The part I'm most unsure about is the way I keep track of the multiple
iommu nodes attached to a device. I would love to especially get some
feedback there.


Changes for v2:
 - fixed devicetree binding linting issues pointed out by Rob Herring and
   reworked that file.
 - made DART-specific code in io-pgtable.c unconditional and removed flag from
   Kconfig as proposed by Robin Murphy.
 - allowed multiple DART nodes in the "iommus" property as proposed by
   Rob Herring and Robin Murphy. this resulted in significant changes
   to apple-iommu-dart.c.
 - the domain aperture is now forced to 32bit if translation is enabled after
   the original suggestion to limit the aperture by Mark Kettenis and the
   follow-up discussion and investigation with Mark Kettenis, Arnd Bergmann,
   Robin Murphy and Rob Herring. This change also simplified the code
   in io-pgtable.c and made some of the improvements suggested during review
   not apply anymore.
 - added support for bypassed and isolated domain modes.
 - reject IOMMU_MMIO and IOMMU_NOEXEC since it's unknown how to set these up
   for now or if the hardware even supports these flags.
 - renamed some registers to be less confusing (mainly s/DOMAIN/STREAM/ to
   prevent confusion with linux's iommu domain concept).

I have also fixed my email provider so this time the series should actually
be a single thread and not contain any HTML by accident anymore...

Best,


Sven


[1] 
https://lore.kernel.org/linux-iommu/20210320151903.60759-1-s...@svenpeter.dev/
[2] https://lore.kernel.org/linux-arch/20210304213902.83903-1-mar...@marcan.st/
[3] https://github.com/AsahiLinux/linux/tree/upstream-bringup-v4
[4] https://github.com/svenpeter42/m1n1/tree/usb-dwc3-serial-wip

Sven Peter (3):
  iommu: io-pgtable: add DART pagetable format
  dt-bindings: iommu: add DART iommu bindings
  iommu: dart: Add DART iommu driver

 .../devicetree/bindings/iommu/apple,dart.yaml |  81 ++
 MAINTAINERS   |   7 +
 drivers/iommu/Kconfig |  14 +
 drivers/iommu/Makefile|   1 +
 drivers/iommu/apple-dart-iommu.c  | 858 ++
 drivers/iommu/io-pgtable-arm.c|  59 ++
 drivers/iommu/io-pgtable.c|   1 +
 include/linux/io-pgtable.h|   6 +
 8 files changed, 1027 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iommu/apple,dart.yaml
 create mode 100644 drivers/iommu/apple-dart-iommu.c

-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 1/3] iommu: io-pgtable: add DART pagetable format

2021-03-28 Thread Sven Peter via iommu
Apple's DART iommu uses a pagetable format that shares some
similarities with the ones already implemented by io-pgtable.c.
Add a new format variant to support the required differences
so that we don't have to duplicate the pagetable handling code.

Signed-off-by: Sven Peter 
---
 drivers/iommu/io-pgtable-arm.c | 59 ++
 drivers/iommu/io-pgtable.c |  1 +
 include/linux/io-pgtable.h |  6 
 3 files changed, 66 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 87def58e79b5..2f63443fd115 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -127,6 +127,9 @@
 #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
 #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
 
+#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
+#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
@@ -381,6 +384,15 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
arm_lpae_io_pgtable *data,
 {
arm_lpae_iopte pte;
 
+   if (data->iop.fmt == ARM_APPLE_DART) {
+   pte = 0;
+   if (!(prot & IOMMU_WRITE))
+   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
+   if (!(prot & IOMMU_READ))
+   pte |= APPLE_DART_PTE_PROT_NO_READ;
+   return pte;
+   }
+
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
pte = ARM_LPAE_PTE_nG;
@@ -1043,6 +1055,48 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
return NULL;
 }
 
+static struct io_pgtable *
+apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+   struct arm_lpae_io_pgtable *data;
+
+   if (cfg->ias > 36)
+   return NULL;
+   if (cfg->oas > 36)
+   return NULL;
+
+   if (!cfg->coherent_walk)
+   return NULL;
+
+   cfg->pgsize_bitmap &= SZ_16K;
+   if (!cfg->pgsize_bitmap)
+   return NULL;
+
+   if (cfg->quirks)
+   return NULL;
+
+   data = arm_lpae_alloc_pgtable(cfg);
+   if (!data)
+   return NULL;
+
+   data->start_level = 2;
+   data->pgd_bits = 11;
+   data->bits_per_level = 11;
+
+   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
+  cfg);
+   if (!data->pgd)
+   goto out_free_data;
+
+   cfg->apple_dart_cfg.ttbr = virt_to_phys(data->pgd);
+
+   return &data->iop;
+
+out_free_data:
+   kfree(data);
+   return NULL;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
.alloc  = arm_64_lpae_alloc_pgtable_s1,
.free   = arm_lpae_free_pgtable,
@@ -1068,6 +1122,11 @@ struct io_pgtable_init_fns 
io_pgtable_arm_mali_lpae_init_fns = {
.free   = arm_lpae_free_pgtable,
 };
 
+struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns = {
+   .alloc  = apple_dart_alloc_pgtable,
+   .free   = arm_lpae_free_pgtable,
+};
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
 
 static struct io_pgtable_cfg *cfg_cookie __initdata;
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6e9917ce980f..6ec75f3e9c3b 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -27,6 +27,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
 #ifdef CONFIG_AMD_IOMMU
[AMD_IOMMU_V1] = &io_pgtable_amd_iommu_v1_init_fns,
 #endif
+   [ARM_APPLE_DART] = &io_pgtable_apple_dart_init_fns,
 };
 
 struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index a4c9ca2c31f1..b06925eb20d3 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -16,6 +16,7 @@ enum io_pgtable_fmt {
ARM_V7S,
ARM_MALI_LPAE,
AMD_IOMMU_V1,
+   ARM_APPLE_DART,
IO_PGTABLE_NUM_FMTS,
 };
 
@@ -136,6 +137,10 @@ struct io_pgtable_cfg {
u64 transtab;
u64 memattr;
} arm_mali_lpae_cfg;
+
+   struct {
+   u64 ttbr;
+   } apple_dart_cfg;
};
 };
 
@@ -250,5 +255,6 @@ extern struct io_pgtable_init_fns 
io_pgtable_arm_64_lpae_s2_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
+extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
 
 #endif /* __IO_PGTABLE_H */
-- 
2.25.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-27 Thread Sven Peter via iommu
Hi Robin,


On Thu, Mar 25, 2021, at 12:50, Robin Murphy wrote:
> On 2021-03-25 07:53, Sven Peter wrote:
> > The iommu binding documentation [1] mentions that
> > 
> >  The device tree node of the IOMMU device's parent bus must contain a 
> > valid
> >  "dma-ranges" property that describes how the physical address space of 
> > the
> >  IOMMU maps to memory. An empty "dma-ranges" property means that there 
> > is a
> >  1:1 mapping from IOMMU to memory.
> > 
> > which, if I understand this correctly, means that the 'dma-ranges' for the
> > parent bus of the iommu should be empty since the DART hardware can see the
> > full physical address space with a 1:1 mapping.
> > 
> > 
> > The documentation also mentions that
> > 
> >   When an "iommus" property is specified in a device tree node, the 
> > IOMMU
> >   will be used for address translation. If a "dma-ranges" property 
> > exists
> >   in the device's parent node it will be ignored.
> > 
> > which means that specifying a 'dma-ranges' in the parent bus of any devices
> > that use the iommu will just be ignored.
> 
> I think that's just wrong and wants updating (or at least clarifying). 
> The high-level view now is that we use "dma-ranges" to describe 
> limitations imposed by a bridge or interconnect segment, and that can 
> certainly happen upstream of an IOMMU. As it happens, I've just recently 
> sent a patch for precisely that case[1].
> 
> I guess what it might have been trying to say is that "dma-ranges" 
> *does* become irrelevant in terms of constraining what physical memory 
> is usable for DMA, but that shouldn't imply that its meaning doesn't 
> just shift to a different purpose.

Should I add a patch to clarify this paragraph for v2 or submit a separate
one-off patch? I'm not entirely sure about the process here but I could add
a Suggested-by: to the commit if that's fine with you.



Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-27 Thread Sven Peter via iommu



On Fri, Mar 26, 2021, at 20:59, Arnd Bergmann wrote:
> On Fri, Mar 26, 2021 at 6:51 PM Sven Peter  wrote:
> >   dart-sio: 0021c000 fbde4000 (at least their Secure Enclave/TPM 
> > co-processor)
> 
> Same here:
> dart-sio {
>vm-base = <0x0>;
>vm-size = <0xfc00>;
>pio-vm-base = <0xfd00>;
>   pio-vm-size = <0x200>;
>   pio-granularity = <0x100>;
>}
> 
> There are clearly two distinct ranges that split up the 4GB space again,
> with a small hole of 16MB (==pio-granularity) at the end of each range.
> 
> The "pio" name might indicate that this is a range of addresses that
> can be programmed to point at I/O registers in another device, rather
> than pointing to RAM.
> 
>Arnd
>

Very interesting observation!

Mark and I have discussed this a little bit further on IRC. Mark also 
successfully
used the PCIe DARTs with a DMA window outside of the one specified by 
vm-base/vm-size
in the ADT.

I believe that the (pio-)vm-base/size properties merely specify the ranges their
allocator uses and do not describe actual hardware limitations. Mark also 
suggested
that they might reserve memory at the beginning to find bugs similar to how one
might not allow to map memory at 0x0.

I have also done a few more experiments and figured out that if I put the IOMMU
into bypass mode (which doesn't seem to work for all IOMMUs/master combinations
which is why I'll leave it out of this series for now until I figure out more
details) I *can* use the full address space. I think the limitation
is therefore imposed by the translation hardware inside the IOMMU and not by
the bus/the interconnect.

If that's correct I think the right place to enforce this is to just limit
the aperture inside the DART driver to a 32bit address space whenever address
translation is enabled.


Thanks,

Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-26 Thread Sven Peter via iommu



On Fri, Mar 26, 2021, at 18:34, Robin Murphy wrote:
> On 2021-03-26 17:26, Mark Kettenis wrote:
> > 
> > Anyway, from my viewpoint having the information about the IOVA
> > address space sit on the devices makes little sense.  This information
> > is needed by the DART driver, and there is no direct cnnection from
> > the DART to the individual devices in the devicetree.  The "iommus"
> > property makes a connection in the opposite direction.
> 
> What still seems unclear is whether these addressing limitations are a 
> property of the DART input interface, the device output interface, or 
> the interconnect between them. Although the observable end result 
> appears more or less the same either way, they are conceptually 
> different things which we have different abstractions to deal with.
> 
> Robin.
>

I'm not really sure if there is any way for us to figure out where these
limitation comes from though.

I've done some more experiments and looked at all DART nodes in Apple's Device
Tree though. It seems that most (if not all) masters only connect 32 address
lines even though the iommu can handle a much larger address space. I'll 
therefore
remove the code to handle the full space for v2 since it's essentially dead
code that can't be tested anyway.


There are some exceptions though:

There are the PCIe DARTs which have a different limitation which could be
encoded as 'dma-ranges' in the pci bus node:

   name base  size
 dart-apcie1: 0010  3fe0
 dart-apcie2: 0010  3fe0
 dart-apcie0: 0010  3fe0
dart-apciec0: 4000  7fffc000
dart-apciec1: 8000  7fffc000

Then there are also these display controller DARTs. If we wanted to use 
dma-ranges
we could just put them in a single sub bus:

  name base  size
  dart-disp0:  fc00
dart-dcp:  fc00
   dart-dispext0:  fc00
 dart-dcpext:  fc00


And finally we have these strange ones which might eventually each require
another awkward sub-bus if we want to stick to the dma-ranges property.

name base  size
  dart-aop: 0003  ("always-on processor")
  dart-pmp:  bff0 (no idea yet)
  dart-sio: 0021c000 fbde4000 (at least their Secure Enclave/TPM co-processor)
  dart-ane:  e000 ("Neural Engine", their ML accelerator)


For all we know these limitations could even arise for different reasons.
(the secure enclave one looks like it might be imposed by the code running
on there).


Not really sure to proceed from here. I'll give the dma-ranges options a try
for v2 and see how that one works out but that's not going to help us understand
*why* these limitations exist. 
At least I won't have to change much code if we agree on a different 
abstraction :)

The important ones for now are probably the USB and the PCIe ones. We'll need 
the
display ones after that and can probably ignore the strange ones for quite a 
while.




Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-26 Thread Sven Peter via iommu



On Fri, Mar 26, 2021, at 17:38, Arnd Bergmann wrote:
> On Fri, Mar 26, 2021 at 5:10 PM Sven Peter  wrote:
> > On Fri, Mar 26, 2021, at 16:59, Mark Kettenis wrote:
> > > Some of the DARTs provide a bypass facility.  That code make using the
> > > standard "dma-ranges" property tricky.  That property would need to
> > > contain the bypass address range.  But that would mean that if the
> > > DART driver needs to look at that property to figure out the address
> > > range that supports translation it will need to be able to distinguish
> > > between the translatable address range and the bypass address range.
> >
> > Do we understand if and why we even need to bypass certain streams?
> 
> My guess is that this is a performance optimization.

Makes sense.

> 
> There are generally three reasons to want an iommu in the first place:
>  - Pass a device down to a guest or user process without giving
>access to all of memory
>  - Avoid problems with limitations in the device, typically when it
> only supports
>32-bit bus addressing, but the installed memory is larger than 4GB
>  - Protect kernel memory from broken drivers
> 
> If you care about none of the above, but you do care about data transfer
> speed, you are better off just leaving the IOMMU in bypass mode.
> I don't think we have to support it if the IOMMU works reliably, but it's
> something that users might want.

Right now the IOMMU works very reliably while bypass mode seems to be tricky
at best. I think I partly know how to enable it but it looks like either not
every DART or DART/master combination even supports it or that there is
some additional configuration required to make it work reliably.

I had it working with the USB DART at one point but I needed to enable it in
all 16 streams of the IOMMU even though the pagetables only need to be setup
in one stream as indicated by the ADT.
I couldn't get it to work at all for the framebuffer IOMMU.

I think it's fine to skip it for now until it's either actually required due
to some hardware quirk or once we have users requesting support. Apple uses
almost all IOMMUs without bypass mode if that ADT is to be believed though.


Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-26 Thread Sven Peter via iommu



On Fri, Mar 26, 2021, at 16:59, Mark Kettenis wrote:
> Some of the DARTs provide a bypass facility.  That code make using the
> standard "dma-ranges" property tricky.  That property would need to
> contain the bypass address range.  But that would mean that if the
> DART driver needs to look at that property to figure out the address
> range that supports translation it will need to be able to distinguish
> between the translatable address range and the bypass address range.

Do we understand if and why we even need to bypass certain streams?
And do you have an example for a node in the ADT that contains this bypass 
range?

I've only seen nodes with "bypass" and "bypass-adress" but that could just be
some software abstraction Apple uses which doesn't map well to Linux or other 
OSes
and might not even be required here.


Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-25 Thread Sven Peter via iommu



On Thu, Mar 25, 2021, at 12:50, Robin Murphy wrote:
> On 2021-03-25 07:53, Sven Peter wrote:
> > 
> > 
> > On Tue, Mar 23, 2021, at 21:53, Rob Herring wrote:
> >> On Sun, Mar 21, 2021 at 05:00:50PM +0100, Mark Kettenis wrote:
> >>>
> >>> As I mentioned before, not all DARTs support the full 32-bit aperture.
> >>> In particular the PCIe DARTs support a smaller address-space.  It is
> >>> not clear whether this is a restriction of the PCIe host controller or
> >>> the DART, but the Apple Device Tree has "vm-base" and "vm-size"
> >>> properties that encode the base address and size of the aperture.
> >>> These single-cell properties which is probably why for the USB DARTs
> >>> only "vm-base" is given; since "vm-base" is 0, a 32-bit number
> >>> wouldn't be able to encode the full aperture size.  We could make them
> >>> 64-bit numbers in the Linux device tree though and always be explicit
> >>> about the size.  Older Sun SPARC machines used a single "virtual-dma"
> >>> property to encode the aperture.  We could do someting similar.  You
> >>> would use this property to initialize domain->geometry.aperture_start
> >>> and domain->geometry.aperture_end in diff 3/3 of this series.
> >>
> >> 'dma-ranges' is what should be used here.
> >>
> > 
> > The iommu binding documentation [1] mentions that
> > 
> >  The device tree node of the IOMMU device's parent bus must contain a 
> > valid
> >  "dma-ranges" property that describes how the physical address space of 
> > the
> >  IOMMU maps to memory. An empty "dma-ranges" property means that there 
> > is a
> >  1:1 mapping from IOMMU to memory.
> > 
> > which, if I understand this correctly, means that the 'dma-ranges' for the
> > parent bus of the iommu should be empty since the DART hardware can see the
> > full physical address space with a 1:1 mapping.
> > 
> > 
> > The documentation also mentions that
> > 
> >   When an "iommus" property is specified in a device tree node, the 
> > IOMMU
> >   will be used for address translation. If a "dma-ranges" property 
> > exists
> >   in the device's parent node it will be ignored.
> > 
> > which means that specifying a 'dma-ranges' in the parent bus of any devices
> > that use the iommu will just be ignored.
> 
> I think that's just wrong and wants updating (or at least clarifying). 
> The high-level view now is that we use "dma-ranges" to describe 
> limitations imposed by a bridge or interconnect segment, and that can 
> certainly happen upstream of an IOMMU. As it happens, I've just recently 
> sent a patch for precisely that case[1].
> 
> I guess what it might have been trying to say is that "dma-ranges" 
> *does* become irrelevant in terms of constraining what physical memory 
> is usable for DMA, but that shouldn't imply that its meaning doesn't 
> just shift to a different purpose.
> 

Okay, now it makes sense then!

> > As a concrete example, the PCIe DART IOMMU only allows translations from 
> > iovas
> > within 0x0010...0x3ff0 to the entire physical address space (though
> > realistically it will only map to 16GB RAM starting at 0x8 on the 
> > M1).
> > 
> > I'm probably just confused or maybe the documentation is outdated but I 
> > don't
> > see how I could specify "this device can only use DMA addresses from
> > 0x0010...0x3ff0 but can map these via the iommu to any physical
> > address" using 'dma-ranges'.
> > 
> > Could you maybe point me to the right direction or give me a small example?
> > That would help a lot!
> 
> PCI is easy, since it's already standard practice to use "dma-ranges" to 
> describe host bridge inbound windows. Even if the restriction is really 
> out in the host-side interconnect rather than in the bridge itself, to 
> all intents and purposes it's indistinguishable so can still be 
> described the same way.
> 
> The case of a standalone device having fewer address bits wired up than 
> both its output and the corresponding IOMMU input might expect is a 
> little more awkward, since that often *does* require adding an extra 
> level of "bus" to explicitly represent that interconnect link in the DT 
> model, e.g. [2].
> 

Nice, thanks! That's exactly what I was looking for :)


Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 1/3] iommu: io-pgtable: add DART pagetable format

2021-03-25 Thread Sven Peter via iommu
Hi Robin,

Thanks for the review!

On Wed, Mar 24, 2021, at 17:37, Robin Murphy wrote:
> On 2021-03-20 15:19, Sven Peter wrote:
> > Apple's DART iommu uses a pagetable format that's very similar to the ones
> > already implemented by io-pgtable.c.
> > Add a new format variant to support the required differences.
> 
> TBH there look to be more differences than similarities, but I guess we 
> already opened that door with the Mali format, and nobody likes writing 
> pagetable code :)

Fair enough. There are some similarities but especially the actual
PTE format is completely different. And yes, I very much prefer to use
well-tested and written pagetable code rather than coming up with
my own if possible :-)


> 
> > Signed-off-by: Sven Peter 
> > ---
> >   drivers/iommu/Kconfig  | 13 +++
> >   drivers/iommu/io-pgtable-arm.c | 70 ++
> >   drivers/iommu/io-pgtable.c |  3 ++
> >   include/linux/io-pgtable.h |  6 +++
> >   4 files changed, 92 insertions(+)
> > 
> > diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> > index 192ef8f61310..3c95c8524abe 100644
> > --- a/drivers/iommu/Kconfig
> > +++ b/drivers/iommu/Kconfig
> > @@ -39,6 +39,19 @@ config IOMMU_IO_PGTABLE_LPAE
> >   sizes at both stage-1 and stage-2, as well as address spaces
> >   up to 48-bits in size.
> > 
> > +config IOMMU_IO_PGTABLE_APPLE_DART
> 
> Does this really need to be configurable? I don't think there's an 
> appreciable code saving to be had, and it's not like we do it for any of 
> the other sub-formats.
> 

Probably not, I'll just make it unconditional for v2.

> > +   bool "Apple DART Descriptor Format"
> > +   select IOMMU_IO_PGTABLE
> > +   select IOMMU_IO_PGTABLE_LPAE
> > +   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
> > +   help
> > + Enable support for the Apple DART iommu pagetable format.
> > + This format is a variant of the ARMv7/v8 Long Descriptor
> > + Format specific to Apple's iommu found in their SoCs.
> > +
> > + Say Y here if you have a Apple SoC like the M1 which
> > + contains DART iommus.
> > +
> >   config IOMMU_IO_PGTABLE_LPAE_SELFTEST
> > bool "LPAE selftests"
> > depends on IOMMU_IO_PGTABLE_LPAE
> > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> > index 87def58e79b5..18674469313d 100644
> > --- a/drivers/iommu/io-pgtable-arm.c
> > +++ b/drivers/iommu/io-pgtable-arm.c
> > @@ -127,6 +127,10 @@
> >   #define ARM_MALI_LPAE_MEMATTR_IMP_DEF 0x88ULL
> >   #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
> > 
> > +/* APPLE_DART_PTE_PROT_NO_WRITE actually maps to ARM_LPAE_PTE_AP_RDONLY  */
> > +#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
> Given that there's apparently zero similarity with any of the other 
> attributes/permissions, this seems more like a coincidence that probably 
> doesn't need to be called out.

Agreed, removed for v2.

> 
> > +#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
> > +
> 
> Do you have XN permission? How about memory type attributes?

I haven't been able to find either of them yet.

There is no (public) documentation for this hardware and this was all
figured out by essentially looking at the pagetables the (unknown)
first-level bootloader left around for us by the time we get to run code
and by flipping bits in HW registers or pagetables and observing what happens.

I only have the framebuffer and USB running right now and neither of
them are able to run code so I can't really find the NX bit if it exists.
I'm not sure if there are any peripherals that can even execute code
from pages mapped through a DART. *Maybe* the GPU but it'll still take
a while until we can tackle that one.

I'll see if I can find something that controls memory attributes though.

The only other way would be to actually reverse engineer Apple code but
that's something I've been deliberately avoiding and I'd really like to
keep it that way.


> 
> >   /* IOPTE accessors */
> >   #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
> > 
> > @@ -381,6 +385,17 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
> > arm_lpae_io_pgtable *data,
> >   {
> > arm_lpae_iopte pte;
> > 
> > +#ifdef CONFIG_IOMMU_IO_PGTABLE_APPLE_DART
> 
> As a general tip, prefer IS_ENABLED() to inline #ifdefs.

Thanks, will keep that in mind for the future! This #ifdef here will disappear
in v2 once I remove this from Kconfig.

> 
> > +   if (data->iop.fmt == ARM_AP

Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-25 Thread Sven Peter via iommu
Hi Robin,


On Wed, Mar 24, 2021, at 16:29, Robin Murphy wrote:
> On 2021-03-20 15:19, Sven Peter wrote:
> > 
> > I have just noticed today though that at least the USB DWC3 controller in 
> > host
> > mode uses *two* darts at the same time. I'm not sure yet which parts seem to
> > require which DART instance.
> > 
> > This means that we might need to support devices attached to two iommus
> > simultaneously and just create the same iova mappings. Currently this only
> > seems to be required for USB according to Apple's Device Tree.
> > 
> > I see two options for this and would like to get feedback before
> > I implement either one:
> > 
> >  1) Change #iommu-cells = <1>; to #iommu-cells = <2>; and use the first 
> > cell
> > to identify the DART and the second one to identify the master.
> > The DART DT node would then also take two register ranges that would
> > correspond to the two DARTs. Both instances use the same IRQ and the
> > same clocks according to Apple's device tree and my experiments.
> > This would keep a single device node and the DART driver would then
> > simply map iovas in both DARTs if required.
> 
> This is broadly similar to the approach used by rockchip-iommu and the 
> special arm-smmu-nvidia implementation, where there are multiple 
> instances which require programming identically, that are abstracted 
> behind a single "device". Your case is a little different since you're 
> not programming both *entirely* identically, although maybe that's a 
> possibility if each respective ID isn't used by anything else on the 
> "other" DART?

That would be possible. The only difference is that I need to
program ID 0 of the first DART and ID 1 of the second one. Both
of these IDs are only connected to the same USB controller.


> 
> Overall I tend to view this approach as a bit of a hack because it's not 
> really describing the hardware truthfully - just because two distinct 
> functional blocks have their IRQ lines wired together doesn't suddenly 
> make them a single monolithic block with multiple interfaces - and tends 
> to be done for the sake of making the driver implementation easier in 
> terms of the Linux IOMMU API (which, note, hasn't evolved all that far 
> from its PCI-centric origins and isn't exactly great for arbitrary SoC 
> topologies).

Yes, the easier driver implementation was my reason to favour this option.

> 
> >  2) Keep #iommu-cells as-is but support
> >  iommus = <&usb_dart1a 1>, <&usb_dart1b 0>;
> > instead.
> > This would then require two devices nodes for the two DART 
> > instances and
> > some housekeeping in the DART driver to support mapping iovas in 
> > both
> > DARTs.
> > I believe omap-iommu.c supports this setup but I will have to read
> > more code to understand the details there and figure out how to 
> > implement
> > this in a sane way.
> 
> This approach is arguably the most honest, and more robust in terms of 
> making fewer assumptions, and is used by at least exynos-iommu and 
> omap-iommu. In Linux it currently takes a little bit more housekeeping 
> to keep track of linked instances within the driver since the IOMMU API 
> holds the notion that any given client device is associated with "an 
> IOMMU", but that's always free to change at any time, unlike the design 
> of a DT binding.

Sounds good. I'll read those drivers and give it a try for v2.


Thanks,


Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-25 Thread Sven Peter via iommu



On Tue, Mar 23, 2021, at 21:53, Rob Herring wrote:
> On Sun, Mar 21, 2021 at 05:00:50PM +0100, Mark Kettenis wrote:
> > > Date: Sat, 20 Mar 2021 15:19:33 +
> > > From: Sven Peter 
> > > I have just noticed today though that at least the USB DWC3 controller in 
> > > host
> > > mode uses *two* darts at the same time. I'm not sure yet which parts seem 
> > > to
> > > require which DART instance.
> > > 
> > > This means that we might need to support devices attached to two iommus
> > > simultaneously and just create the same iova mappings. Currently this only
> > > seems to be required for USB according to Apple's Device Tree.
> > > 
> > > I see two options for this and would like to get feedback before
> > > I implement either one:
> > > 
> > > 1) Change #iommu-cells = <1>; to #iommu-cells = <2>; and use the 
> > > first cell
> > >to identify the DART and the second one to identify the master.
> > >The DART DT node would then also take two register ranges that 
> > > would
> > >correspond to the two DARTs. Both instances use the same IRQ and 
> > > the
> > >same clocks according to Apple's device tree and my experiments.
> > >This would keep a single device node and the DART driver would then
> > >simply map iovas in both DARTs if required.
> > > 
> > > 2) Keep #iommu-cells as-is but support
> > > iommus = <&usb_dart1a 1>, <&usb_dart1b 0>;
> > >instead.
> > >This would then require two devices nodes for the two DART 
> > > instances and
> > >some housekeeping in the DART driver to support mapping iovas in 
> > > both
> > >DARTs.
> > >I believe omap-iommu.c supports this setup but I will have to read
> > >more code to understand the details there and figure out how to 
> > > implement
> > >this in a sane way.
> > > 
> > > I currently prefer the first option but I don't understand enough details 
> > > of
> > > the iommu system to actually make an informed decision.
> 
> Please don't mix what does the h/w look like and what's easy to 
> implement in Linux's IOMMU subsytem. It's pretty clear (at least 
> from the description here) that option 2 reflects the h/w. 
> 

Good point, I'll keep that in mind and give option 2 a try.

> > 
> > As I mentioned before, not all DARTs support the full 32-bit aperture.
> > In particular the PCIe DARTs support a smaller address-space.  It is
> > not clear whether this is a restriction of the PCIe host controller or
> > the DART, but the Apple Device Tree has "vm-base" and "vm-size"
> > properties that encode the base address and size of the aperture.
> > These single-cell properties which is probably why for the USB DARTs
> > only "vm-base" is given; since "vm-base" is 0, a 32-bit number
> > wouldn't be able to encode the full aperture size.  We could make them
> > 64-bit numbers in the Linux device tree though and always be explicit
> > about the size.  Older Sun SPARC machines used a single "virtual-dma"
> > property to encode the aperture.  We could do someting similar.  You
> > would use this property to initialize domain->geometry.aperture_start
> > and domain->geometry.aperture_end in diff 3/3 of this series.
> 
> 'dma-ranges' is what should be used here.
> 

The iommu binding documentation [1] mentions that

The device tree node of the IOMMU device's parent bus must contain a valid
"dma-ranges" property that describes how the physical address space of the
IOMMU maps to memory. An empty "dma-ranges" property means that there is a 
1:1 mapping from IOMMU to memory.

which, if I understand this correctly, means that the 'dma-ranges' for the
parent bus of the iommu should be empty since the DART hardware can see the
full physical address space with a 1:1 mapping.


The documentation also mentions that

 When an "iommus" property is specified in a device tree node, the IOMMU
 will be used for address translation. If a "dma-ranges" property exists
 in the device's parent node it will be ignored.

which means that specifying a 'dma-ranges' in the parent bus of any devices
that use the iommu will just be ignored.

As a concrete example, the PCIe DART IOMMU only allows translations from iovas
within 0x0010...0x3ff0 to the entire physical address space (though
realistically it will only map to 16GB RAM starting at 0x8 on the M1).

I'm probably just confused or maybe the documentation is outdated but I don't
see how I could specify "this device can only use DMA addresses from
0x0010...0x3ff0 but can map these via the iommu to any physical
address" using 'dma-ranges'.

Could you maybe point me to the right direction or give me a small example?
That would help a lot!



Thanks,

Sven


[1] Documentation/devicetree/bindings/iommu/iommu.txt
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-23 Thread Sven Peter via iommu
Hi Mark,

On Tue, Mar 23, 2021, at 21:00, Mark Kettenis wrote:
> The problem with both #1 and #2 is that you end up with two references
> to (effectively) different iommu's in the dwc3 device node.  I don't
> see how that is compatible with the idea of using a single translation
> table for both sub-DARTs.

I don't have a strong opinion about this fwiw.
Option #1 and #2 seem to have precedence in the already existing
iommu bindings though [1,2,3].
I just want to make sure we've thought through all options and understand
their advantages/disadvantages before we take a decision.


I've been reading some more Linux iommu code and here's how I understand
it now / how I could implement at least #1 with a single shared pagetable.
This might also be possible for option #2, but I'll need to think that through
in more detail.

An iommu domain is a collection of devices that share a virtual address space.
During domain allocation I can just allocate a single DART pagetable and
not have anything point to it for now.

A stream identifies the smallest unit the iommu hardware can differentiate.
For the DART we have 16 of these with a single TCR + the four TTBR register
for each stream.

A device is assigned to individual streams using the "iommus" property. When
a device is attached to a domain we now simply setup the TTBR registers to
point to the iommu domain pagetable. It doesn't matter here if it's a single
stream or multiple ones or even multiple devices sharing a single stream as
long as they're attached to the same domain.

All operations (map, unmap, etc.) now simply first modify the domain
pagetable and then issue the TLB maintenance operations for attached streams.


> 
> If you no longer think that is desirable, you'll still have the
> problem that you'll need to modify the dwc3 driver code such that it
> uses the right IOMMU to do its DMA address translation.  Given what
> you write above that sounds really ugly and confusing.  I would
> certainly want to avoid doing that in OpenBSD.

Yeah, I absolutely don't want to hack on the dwc3 code at all.
That will end up being *very* ugly.



Best,

Sven


[1] Documentation/devicetree/bindings/iommu/iommu.txt "Multiple-master IOMMU"
[2] Documentation/devicetree/bindings/qcom,iommu.txt last example
[3] Documentation/devicetree/bindings/arm,smmu.yaml first example
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-22 Thread Sven Peter via iommu


Hi Mark,

On Sun, Mar 21, 2021, at 19:35, Mark Kettenis wrote:
>
> Guess we do need to understand a little bit better how the USB DART
> actually works.  My hypothesis (based on our discussion on #asahi) is
> that the XHCI host controller and the peripheral controller of the
> DWC3 block use different DMA "streams" that are handled by the
> different sub-DARTs.

I've done some more experiments and the situation is unfortunately more
complicated: Most DMA transfers are translated with the first DART.
But sometimes (and I have not been able to figure out the exact conditions)
transfers instead have to go through the second DART. 
This happens e.g. with one of my USB keyboards after a stop EP command
is issued: Suddenly the xhci_ep_ctx struct must be translated through the
second DART.

What this likely means is that we'll need to point both DARTs
to the same pagetables and just issue the TLB maintenance operations
as a group.

> 
> The Corellium folks use a DART + sub-DART model in their driver and a
> single node in the device tree that represents both.  That might sense
> since the error registers and interrupts are shared.  Maybe it would
> make sense to select the appropriate sub-DART based on the DMA stream
> ID?

dwc3 specifically seems to require stream id #1 from the DART
at <0x5 0x02f0> and stream id #0 from the DART at <0x5 0x02f8>.
Both of these only share a IRQ line but are otherwise completely independent.
Each has their own error registers, etc. and we need some way to
specify these two DARTs + the appropriate stream ID.

Essentially we have three options to represent this now:

1) Add both DARTs as separate regs, use #iommu-cells = <2> and have the
   first cell select the DART and the second one the stream ID.
   We could allow #iommu-cells = <1> in case only one reg is specified
   for the PCIe DART:

   usb_dart1@502f0 {
 compatible = "apple,t8103-dart";
 reg = <0x5 0x02f0 0x0 0x4000>, <0x5 0x02f8 0x0 0x4000>;
 #iommu-cells = <2>;
 ...
   };

   usb1 {
 iommus = <&usb_dart1 0 1>, <&usb_dart1 1 0>;
 ...
   };

   I prefer this option because we fully describe the DART in a single
   device node here. It also feels natural to group them like this because
   they need to share some properties (like dma-window and the interrupt)
   anyway. 

2) Create two DART nodes which share the same IRQ line and attach them
   both to the master node:

   usb_dart1a@502f0 {
 compatible = "apple,t8103-dart";
 reg = <0x5 0x02f0 0x0 0x4000>;
 #iommu-cells = <1>;
 ...
   };
   usb_dart1b@502f8 {
 compatible = "apple,t8103-dart";
 reg = <0x5 0x02f8 0x0 0x4000>;
 #iommu-cells = <1>;
 ...
   };

   usb1 {
 iommus = <&usb_dart1a 1>, <&usb_dart1b 0>;
 ...
   };

   I dislike this one because attaching those two DARTs to a single device
   seems rather unusual. We'd also have to duplicate the dma-window setting,
   make sure it's the same for both DARTs and there are probably even more
   complications I can't think of right now. It seems like this would also
   make the device tree very verbose and the implementation itself more
   complicated.

3) Introduce another property and let the DART driver take care of
   mirroring the pagetables. I believe this would be similar to
   the sid-remap property:

   usb_dart1@502f0 {
 compatible = "apple,t8103-dart";
 reg = <0x5 0x02f0 0x0 0x4000>, <0x5 0x02f8 0x0 0x4000>;
 #iommu-cells = <1>;
 sid-remap = <0 1>;
   };
   usb1 {
 iommus = <&usb_dart1 0>;
   };

   I slightly dislike this one because we now specify which stream id 
   to use in two places: Once in the device node and another time in the
   new property in the DART node. I also don't think the binding is much
   simpler than the first one.


> > where #dma-address-cells and #dma-size-cells default to
> > #address-cells and #size-cells respectively if I understand
> > the code correctly. That way we could also just always use
> > a 64bit address and size in the DT, e.g.
> > 
> >   pcie_dart {
> >   [ ... ]
> >   dma-window = <0 0x10 0 0x3fe0>;
> >   [ ... ]
> >   };
> 
> That sounds like a serious contender to me!  Hopefully one of the
> Linux kernel developers can give this some sort of blessing.
> 
> I think it would make sense for us to just rely on the #address-cells
> and #size-cells defaults for the M1 device tree.
>

Agreed.


Best,

Sven
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 2/3] dt-bindings: iommu: add DART iommu bindings

2021-03-22 Thread Sven Peter via iommu
Hi Rob,

On Mon, Mar 22, 2021, at 01:15, Rob Herring wrote:
> 
> This check can fail if there are any dependencies. The base for a patch
> series is generally the most recent rc1.
> 
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
> 
> pip3 install dtschema --upgrade
> 
> Please check and re-submit.

Sorry about that! It looks like I didn't have yamllint installed.
I have fixed the issues and will re-submit.


Thanks,

Sven

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-21 Thread Sven Peter via iommu

Hi Mark,

> On 21. Mar 2021, at 17:00, Mark Kettenis  wrote:
> 
> I don't think the first option is going to work for PCIe.  PCIe
> devices will have to use "iommu-map" properties to map PCI devices to
> the right iommu, and the currently implementation seems to assume that
> #iommu-cells = <1>.  The devictree binding[1] doesn't explicitly state
> that it relies on #iommu-cells = <1>, but it isn't clear how the
> rid-base to iommu-base mapping mechanism would work when that isn't
> the case.
> 
> Now the PCIe DARTs are simpler and seem to have only one "instance"
> per DART.  So if we keep #iommu-cells = <1> for those, you'd still be
> fine using the first approach.

Good point, I guess that only leaves option two for now then.
Having some DARTs use cells = <1> and others <2> sounds confusing to me.


> 
> As I mentioned before, not all DARTs support the full 32-bit aperture.
> In particular the PCIe DARTs support a smaller address-space.  It is
> not clear whether this is a restriction of the PCIe host controller or
> the DART, but the Apple Device Tree has "vm-base" and "vm-size"
> properties that encode the base address and size of the aperture.
> These single-cell properties which is probably why for the USB DARTs
> only "vm-base" is given; since "vm-base" is 0, a 32-bit number
> wouldn't be able to encode the full aperture size.  We could make them
> 64-bit numbers in the Linux device tree though and always be explicit
> about the size.  Older Sun SPARC machines used a single "virtual-dma"
> property to encode the aperture.  We could do someting similar.  You
> would use this property to initialize domain->geometry.aperture_start
> and domain->geometry.aperture_end in diff 3/3 of this series.
> 
> I think it would make sense to include this in this series, as this
> would make adding support for PCIe very easy, and PCIe gives you
> aupport for network (both wired and wireless) and the type-A USB ports
> on the mini.



Agreed, I'd ideally like to converge on a device tree binding
that won't have to change in the near future.

I've tried to use an address space larger than 32bit and that seems to
work for parts of the dwc3 controller but breaks for the xhci parts because
the upper lines don't seem to be connected there (e.g. if xhci tries to
use <0x1 0x> I get a fault for <0 0x>).

Looking at other iommu drivers I have found the following two similar
bindings:

qcom uses a ranges property with a 64bit address and 32 bit size [1]

  apps_iommu: iommu@1e2 {
  ...
  ranges = <0 0x1e2 0x4>;
  ...
  };

and tegra seems to support a dma-window property with 32bit address
and size [2]

  smmu {
  [...]
  dma-window = <0 0x4000>;/* IOVA start & length */
  [...]
  };

I believe there already is of_get_dma_window to handle parsing this
in the common iommu code [3] but I can't find any place using it.
It's a little bit more complex that we need since it allows to specify the
number of cells for both the address and the size but it should allow us to
express all possible configurations:

  usb_dart {
  [ ... ]
  #dma-address-cells = <1>;
  #dma-size-cells = <2>;
  dma-window = <0 0x1 0x0>;
  [ ... ]
  };
  pcie_dart {
  [ ... ]
  #dma-address-cells = <1>;
  #dma-size-cells = <1>;
  dma-window = <0x10 0x3fe0>;
  [ ... ]
  };

where #dma-address-cells and #dma-size-cells default to
#address-cells and #size-cells respectively if I understand
the code correctly. That way we could also just always use
a 64bit address and size in the DT, e.g.

  pcie_dart {
  [ ... ]
  dma-window = <0 0x10 0 0x3fe0>;
  [ ... ]
  };


Best,

Sven


[1] Documentation/devicetree/bindings/iommu/qcom,iommu.txt
[2] Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt
[3] drivers/iommu/of_iommu.c
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Re: [PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-21 Thread Sven Peter via iommu



Hi Mark,

Sorry for the spam if you get this message twice. This is pretty embarrassing
but I've just switched mail providers after ProtonMail messed up yesterday and
it looks like my new one defaulted to sending HTML messages even though I only
typed plaintext. This shouldn't have happened in the first place but it
certainly shouldn't happen again now :-(

> On 21. Mar 2021, at 17:00, Mark Kettenis  wrote:
> 
> I don't think the first option is going to work for PCIe.  PCIe
> devices will have to use "iommu-map" properties to map PCI devices to
> the right iommu, and the currently implementation seems to assume that
> #iommu-cells = <1>.  The devictree binding[1] doesn't explicitly state
> that it relies on #iommu-cells = <1>, but it isn't clear how the
> rid-base to iommu-base mapping mechanism would work when that isn't
> the case.
> 
> Now the PCIe DARTs are simpler and seem to have only one "instance"
> per DART.  So if we keep #iommu-cells = <1> for those, you'd still be
> fine using the first approach.

Good point, I guess that only leaves option two for now then.
Having some DARTs use cells = <1> and others <2> sounds confusing to me.


> 
> As I mentioned before, not all DARTs support the full 32-bit aperture.
> In particular the PCIe DARTs support a smaller address-space.  It is
> not clear whether this is a restriction of the PCIe host controller or
> the DART, but the Apple Device Tree has "vm-base" and "vm-size"
> properties that encode the base address and size of the aperture.
> These single-cell properties which is probably why for the USB DARTs
> only "vm-base" is given; since "vm-base" is 0, a 32-bit number
> wouldn't be able to encode the full aperture size.  We could make them
> 64-bit numbers in the Linux device tree though and always be explicit
> about the size.  Older Sun SPARC machines used a single "virtual-dma"
> property to encode the aperture.  We could do someting similar.  You
> would use this property to initialize domain->geometry.aperture_start
> and domain->geometry.aperture_end in diff 3/3 of this series.
> 
> I think it would make sense to include this in this series, as this
> would make adding support for PCIe very easy, and PCIe gives you
> aupport for network (both wired and wireless) and the type-A USB ports
> on the mini.



Agreed, I'd ideally like to converge on a device tree binding
that won't have to change in the near future.

I've tried to use an address space larger than 32bit and that seems to
work for parts of the dwc3 controller but breaks for the xhci parts because
the upper lines don't seem to be connected there (e.g. if xhci tries to
use <0x1 0x> I get a fault for <0 0x>).

Looking at other iommu drivers I have found the following two similar
bindings:

qcom uses a ranges property with a 64bit address and 32 bit size [1]

  apps_iommu: iommu@1e2 {
  ...
  ranges = <0 0x1e2 0x4>;
  ...
  };

and tegra seems to support a dma-window property with 32bit address
and size [2]

  smmu {
  [...]
  dma-window = <0 0x4000>;/* IOVA start & length */
  [...]
  };

I believe there already is of_get_dma_window to handle parsing this
in the common iommu code [3] but I can't find any place using it.
It's a little bit more complex that we need since it allows to specify the
number of cells for both the address and the size but it should allow us to
express all possible configurations:

  usb_dart {
  [ ... ]
  #dma-address-cells = <1>;
  #dma-size-cells = <2>;
  dma-window = <0 0x1 0x0>;
  [ ... ]
  };
  pcie_dart {
  [ ... ]
  #dma-address-cells = <1>;
  #dma-size-cells = <1>;
  dma-window = <0x10 0x3fe0>;
  [ ... ]
  };

where #dma-address-cells and #dma-size-cells default to
#address-cells and #size-cells respectively if I understand
the code correctly. That way we could also just always use
a 64bit address and size in the DT, e.g.

  pcie_dart {
  [ ... ]
  dma-window = <0 0x10 0 0x3fe0>;
  [ ... ]
  };


Best,

Sven


[1] Documentation/devicetree/bindings/iommu/qcom,iommu.txt
[2] Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt
[3] drivers/iommu/of_iommu.c
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 0/3] Apple M1 DART IOMMU driver

2021-03-20 Thread Sven Peter via iommu
Hi,

After Hector's initial work [1] to bring up Linux on Apple's M1 it's time to
bring up more devices. Most peripherals connected to the SoC are behind a iommu
which Apple calls "Device Address Resolution Table", or DART for short [2].
Unfortunately, it only shares the name with PowerPC's DART.
Configuring this iommu is mandatory if these peripherals require DMA access.

This patchset implements initial support for this iommu. The hardware itself
uses a pagetable format that's very similar to the one already implement in
io-pgtable.c. There are some minor modifications, namely some details of the
PTE format and that there are always three pagetable levels, which I've
implement as a new format variant.

I have mainly tested this with the USB controller in device mode which is
compatible with Linux's dwc3 driver. Some custom PHY initialization (which is
not yet ready or fully understood) is required though to bring up the ports,
see e.g. my patches to our m1n1 bootloader [3,4]. If you want to test the same
setup you will probably need that branch for now and add the nodes from
the DT binding specification example to your device tree.

Even though each DART instances could support up to 16 devices usually only
a single device is actually connected. Different devices generally just use
an entirely separate DART instance with a seperate MMIO range, IRQ, etc.

I have just noticed today though that at least the USB DWC3 controller in host
mode uses *two* darts at the same time. I'm not sure yet which parts seem to
require which DART instance.

This means that we might need to support devices attached to two iommus
simultaneously and just create the same iova mappings. Currently this only
seems to be required for USB according to Apple's Device Tree.

I see two options for this and would like to get feedback before
I implement either one:

1) Change #iommu-cells = <1>; to #iommu-cells = <2>; and use the first cell
   to identify the DART and the second one to identify the master.
   The DART DT node would then also take two register ranges that would
   correspond to the two DARTs. Both instances use the same IRQ and the
   same clocks according to Apple's device tree and my experiments.
   This would keep a single device node and the DART driver would then
   simply map iovas in both DARTs if required.

2) Keep #iommu-cells as-is but support
iommus = <&usb_dart1a 1>, <&usb_dart1b 0>;
   instead.
   This would then require two devices nodes for the two DART instances and
   some housekeeping in the DART driver to support mapping iovas in both
   DARTs.
   I believe omap-iommu.c supports this setup but I will have to read
   more code to understand the details there and figure out how to implement
   this in a sane way.

I currently prefer the first option but I don't understand enough details of
the iommu system to actually make an informed decision.
I'm obviously also open to more options :-)


Best regards,


Sven

[1] https://lore.kernel.org/linux-arch/20210304213902.83903-1-mar...@marcan.st/
[2] 
https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/DataMgmt/DataMgmt.html
[3] 
https://github.com/svenpeter42/m1n1/commit/1e2661abf5ea2c820297b3ff591235c408d19a34
[4] https://github.com/svenpeter42/m1n1/tree/usb-uartproxy-console-wip



___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 3/3] iommu: dart: Add DART iommu driver

2021-03-20 Thread Sven Peter via iommu
Apple's new SoCs use iommus for almost all peripherals. These Device
Address Resolution Tables must be setup before these peripherals can
act as DMA masters.

Signed-off-by: Sven Peter 
---
 MAINTAINERS  |   1 +
 drivers/iommu/Kconfig|  13 +
 drivers/iommu/Makefile   |   1 +
 drivers/iommu/apple-dart-iommu.c | 653 +++
 4 files changed, 668 insertions(+)
 create mode 100644 drivers/iommu/apple-dart-iommu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1f9a4f2de88b..7dcfce53dd04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1241,6 +1241,7 @@ M:    Sven Peter 
 L: iommu@lists.linux-foundation.org
 S: Maintained
 F: Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
+F: drivers/iommu/apple-dart-iommu.c

 APPLE SMC DRIVER
 M: Henrik Rydberg 
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 3c95c8524abe..810bcb3ed414 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -262,6 +262,19 @@ config SPAPR_TCE_IOMMU
  Enables bits of IOMMU API required by VFIO. The iommu_ops
  is not implemented as it is not necessary for VFIO.

+config IOMMU_APPLE_DART
+   tristate "Apple DART IOMMU Support"
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   select IOMMU_API
+   select IOMMU_IO_PGTABLE_APPLE_DART
+   help
+ Support for Apple DART (Device Address Resolution Table) IOMMUs
+ found in Apple ARM SoCs like the M1.
+ This IOMMU is required for most peripherals using DMA to access
+ the main memory.
+
+ Say Y here if you are using an Apple SoC with a DART IOMMU.
+
 # ARM IOMMU support
 config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 61bd30cd8369..5f21f0dfec6a 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
 obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
 obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
 obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o
+obj-$(CONFIG_IOMMU_APPLE_DART) += apple-dart-iommu.o
diff --git a/drivers/iommu/apple-dart-iommu.c b/drivers/iommu/apple-dart-iommu.c
new file mode 100644
index ..a642dbc22281
--- /dev/null
+++ b/drivers/iommu/apple-dart-iommu.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple DART (Device Address Resolution Table) IOMMU driver
+ *
+ * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
+ *  Copyright (C) 2013 ARM Limited
+ *  Copyright (C) 2015 ARM Limited
+ *
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DART_MAX_DEVICES 16
+#define DART_MAX_DOMAINS 16
+#define DART_MAX_TTBR 4
+
+#define DART_DOMAINS_ALL 0x
+
+#define DART_CONFIG 0x60
+#define DART_CONFIG_LOCK BIT(15)
+
+#define DART_ERROR 0x40
+#define DART_ERROR_DOMAIN_SHIFT 24
+#define DART_ERROR_DOMAIN_MASK 0xf
+#define DART_ERROR_CODE_MASK 0xff
+#define DART_ERROR_FLAG BIT(31)
+#define DART_ERROR_READ_FAULT BIT(4)
+#define DART_ERROR_WRITE_FAULT BIT(3)
+#define DART_ERROR_NO_PTE BIT(2)
+#define DART_ERROR_NO_PMD BIT(1)
+#define DART_ERROR_NO_PGD BIT(0)
+
+#define DART_DOMAIN_SELECT 0x34
+
+#define DART_DOMAIN_COMMAND 0x20
+#define DART_DOMAIN_COMMAND_BUSY BIT(2)
+#define DART_DOMAIN_COMMAND_INVALIDATE BIT(20)
+
+#define DART_DOMAIN_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_DEVICE2DOMAIN_MAP 0x80
+
+#define DART_ERROR_ADDR_HI 0x54
+#define DART_ERROR_ADDR_LO 0x50
+
+#define DART_TCR(domain) (0x100 + 4 * (domain))
+#define DART_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_TCR_BYPASS_ENABLE BIT(8)
+
+#define DART_TTBR(domain, idx) (0x200 + 16 * (domain) + 4 * (idx))
+#define DART_TTBR_VALID BIT(31)
+#define DART_TTBR_SHIFT 12
+
+struct apple_dart_domain;
+
+struct apple_dart {
+   struct device *dev;
+   void __iomem *regs;
+
+   int irq;
+   struct clk_bulk_data *clks;
+   int num_clks;
+
+   struct iommu_device iommu;
+
+   struct apple_dart_domain *domains[DART_MAX_DOMAINS];
+
+   spinlock_t command_lock;
+   struct mutex domain_mutex;
+};
+
+struct apple_dart_domain {
+   struct apple_dart *dart;
+
+   int domain_idx;
+
+   struct io_pgtable_ops *pgtbl_ops;
+
+   struct iommu_domain domain;
+};
+
+struct apple_dart_master {
+   struct apple_dart *dart;
+   u32 sid;
+};
+
+static struct platform_driver apple_dart_driver;
+static const struct iommu_ops apple_dart_iommu_ops;
+
+static struct apple_dart_domain *to_dart_domain(struct iommu_domain *dom)
+{
+   return container_of(dom, struct apple_dart_domain, domain);
+}
+
+static void apple_dart_hw_enable_translation(struct apple_dart *dart,
+

[PATCH 1/3] iommu: io-pgtable: add DART pagetable format

2021-03-20 Thread Sven Peter via iommu
Apple's DART iommu uses a pagetable format that's very similar to the ones
already implemented by io-pgtable.c.
Add a new format variant to support the required differences.

Signed-off-by: Sven Peter 
---
 drivers/iommu/Kconfig  | 13 +++
 drivers/iommu/io-pgtable-arm.c | 70 ++
 drivers/iommu/io-pgtable.c |  3 ++
 include/linux/io-pgtable.h |  6 +++
 4 files changed, 92 insertions(+)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 192ef8f61310..3c95c8524abe 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -39,6 +39,19 @@ config IOMMU_IO_PGTABLE_LPAE
  sizes at both stage-1 and stage-2, as well as address spaces
  up to 48-bits in size.

+config IOMMU_IO_PGTABLE_APPLE_DART
+   bool "Apple DART Descriptor Format"
+   select IOMMU_IO_PGTABLE
+   select IOMMU_IO_PGTABLE_LPAE
+   depends on ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+   help
+ Enable support for the Apple DART iommu pagetable format.
+ This format is a variant of the ARMv7/v8 Long Descriptor
+ Format specific to Apple's iommu found in their SoCs.
+
+ Say Y here if you have a Apple SoC like the M1 which
+ contains DART iommus.
+
 config IOMMU_IO_PGTABLE_LPAE_SELFTEST
bool "LPAE selftests"
depends on IOMMU_IO_PGTABLE_LPAE
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 87def58e79b5..18674469313d 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -127,6 +127,10 @@
 #define ARM_MALI_LPAE_MEMATTR_IMP_DEF  0x88ULL
 #define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL

+/* APPLE_DART_PTE_PROT_NO_WRITE actually maps to ARM_LPAE_PTE_AP_RDONLY  */
+#define APPLE_DART_PTE_PROT_NO_WRITE (1<<7)
+#define APPLE_DART_PTE_PROT_NO_READ (1<<8)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))

@@ -381,6 +385,17 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct 
arm_lpae_io_pgtable *data,
 {
arm_lpae_iopte pte;

+#ifdef CONFIG_IOMMU_IO_PGTABLE_APPLE_DART
+   if (data->iop.fmt == ARM_APPLE_DART) {
+   pte = 0;
+   if (!(prot & IOMMU_WRITE))
+   pte |= APPLE_DART_PTE_PROT_NO_WRITE;
+   if (!(prot & IOMMU_READ))
+   pte |= APPLE_DART_PTE_PROT_NO_READ;
+   return pte;
+   }
+#endif
+
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
pte = ARM_LPAE_PTE_nG;
@@ -1043,6 +1058,54 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, 
void *cookie)
return NULL;
 }

+#ifdef CONFIG_IOMMU_IO_PGTABLE_APPLE_DART
+static struct io_pgtable *
+apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+   struct arm_lpae_io_pgtable *data;
+
+   if (cfg->ias > 38)
+   return NULL;
+   if (cfg->oas > 36)
+   return NULL;
+
+   if (!cfg->coherent_walk)
+   return NULL;
+
+   cfg->pgsize_bitmap &= SZ_16K;
+   if (!cfg->pgsize_bitmap)
+   return NULL;
+
+   data = arm_lpae_alloc_pgtable(cfg);
+   if (!data)
+   return NULL;
+
+   /*
+* the hardware only supports this specific three level pagetable 
layout with
+* the first level being encoded into four hardware registers
+*/
+   data->start_level = ARM_LPAE_MAX_LEVELS - 2;
+   data->pgd_bits = 13;
+   data->bits_per_level = 11;
+
+   data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
+  cfg);
+   if (!data->pgd)
+   goto out_free_data;
+
+   cfg->apple_dart_cfg.pgd[0] = virt_to_phys(data->pgd);
+   cfg->apple_dart_cfg.pgd[1] = virt_to_phys(data->pgd + 0x4000);
+   cfg->apple_dart_cfg.pgd[2] = virt_to_phys(data->pgd + 0x8000);
+   cfg->apple_dart_cfg.pgd[3] = virt_to_phys(data->pgd + 0xc000);
+
+   return &data->iop;
+
+out_free_data:
+   kfree(data);
+   return NULL;
+}
+#endif
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
.alloc  = arm_64_lpae_alloc_pgtable_s1,
.free   = arm_lpae_free_pgtable,
@@ -1068,6 +1131,13 @@ struct io_pgtable_init_fns 
io_pgtable_arm_mali_lpae_init_fns = {
.free   = arm_lpae_free_pgtable,
 };

+#ifdef CONFIG_IOMMU_IO_PGTABLE_APPLE_DART
+struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns = {
+   .alloc  = apple_dart_alloc_pgtable,
+   .free   = arm_lpae_free_pgtable,
+};
+#endif
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST

 static struct io_pgtable_cfg *cfg_cookie __initdata;
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6e9917ce980f..d86590b0673a 100644
--- a/driver

[PATCH 2/3] dt-bindings: iommu: add DART iommu bindings

2021-03-20 Thread Sven Peter via iommu
DART (Device Address Resolution Table) is the iommu found on Apple
ARM SoCs such as the M1.

Signed-off-by: Sven Peter 
---
 .../bindings/iommu/apple,t8103-dart.yaml  | 82 +++
 MAINTAINERS   |  6 ++
 2 files changed, 88 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml

diff --git a/Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml 
b/Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
new file mode 100644
index ..2ec2d905a2ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/apple,t8103-dart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple DART IOMMU Implementation
+
+maintainers:
+  - Sven Peter 
+
+description: |+
+  Apple SoCs may contain an implementation of their Device Address
+  Resolution Table which provides a mandatory layer of address
+  translations for various masters external to the CPU.
+
+  Each DART instance is capable of handling up to 16 masters
+  mapped to up tp 16 different virtual address spaces with
+  page-level read/write access control flags.
+
+  This DART IOMMU also raises interrupts in response to various
+  fault conditions.
+
+properties:
+  compatible:
+const: apple,t8103-dart
+
+  reg:
+const: 1
+
+  interrupts:
+const: 1
+
+  clocks:
+maxItems: 1
+
+  '#iommu-cells':
+const: 1
+description:
+  The number of the master connected to this DART.
+
+required:
+  - compatible
+  - reg
+  - '#iommu-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |+
+dart1: dart1@382f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x3 0x82f8 0x0 0x4000>;
+  interrupt-parent = <&aic>;
+  interrupts = ;
+  #iommu-cells = <1>;
+};
+
+master1 {
+  iommus = <&dart1 0>;
+};
+
+  - |+
+usb_dart0: usb_dart0@382f8 {
+  compatible = "apple,t8103-dart";
+  reg = <0x3 0x82f8 0x0 0x4000>;
+  interrupt-parent = <&aic>;
+  interrupts = ;
+  #iommu-cells = <1>;
+};
+
+usbdrd_dwc3_0: dwc3@38228 {
+  compatible = "snps,dwc3";
+  reg = <0x3 0x8228 0x0 0x10>;
+  interrupt-parent = <&aic>;
+  interrupts = ;
+  iommus = <&usb_dart0 1>;
+  #address-cells = <1>;
+  #size-cells = <0>;
+  dr_mode = "peripheral";
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index d5e4d93a536a..1f9a4f2de88b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1236,6 +1236,12 @@ L:   linux-in...@vger.kernel.org
 S: Odd fixes
 F: drivers/input/mouse/bcm5974.c

+APPLE DART IOMMU DRIVER
+M: Sven Peter 
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/apple,t8103-dart.yaml
+
 APPLE SMC DRIVER
 M: Henrik Rydberg 
 L: linux-hw...@vger.kernel.org
--
2.25.1


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu