Add support for ARM SMMU architectures 1 and 2
Subsystems (7)
cover Add support for ARM SMMU architectures 1 and 2
Hi all,
This patch series adds support for ARM's SMMU architectures 1 and 2 to Linux. It has been tested with models of MMU-400 (AArch32) and MMU-500 (AArch32 and AArch64) coupled with four PL330s doing memory-to-memory DMA.
The first six patches are preparatory and fix problems that I encountered whilst trying to use both the ARM IOMMU API and the PL330 driver. The remaining patches add the device-tree binding, previously discussed at:
http://lists.infradead.org/pipermail/linux-arm-kernel/2013-April/160663.html
http://lists.infradead.org/pipermail/linux-arm-kernel/2013-April/162945.html
as well as the driver implementation and an update to the MAINTAINERS file.
The driver is written only with single-stage (possibly stage 2), LPAE, non-secure translation in mind. Nested translation may of course be added later via KVM, but that will require some changes to the core IOMMU API in Linux. Both arm and arm64 are supported, as well as chained SMMUs, although we only install our translation in the SMMU furthest from the device. We're also currently limited in our IPA size, due to input size restrictions at stage 2. This can be resolved later by munging about in the page table allocation code, where we currently re-use parts of the CPU page table helpers.
Read and write protection is supported as far as the pte formats allow (i.e. no write-only at stage-1) and memory attributes are either normal, non-cacheable (default) or normal, cacheable, write-back, write-allocate. TLB broadcasting is not used for either ASIDs (since we don't support user tables for DMA in Linux) or VMIDs (which are instead used to tag address spaces in order to limit the scope of TLB invalidation operations).
Both 4k and 64k (AArch64 only) pages are supported, with the contiguous hint bit being used in the pte entries for mappings that allow it.
Support for stream-indexing and non-coherent table walking is provided, but untested.
All comments welcome,
Will
Will Deacon (9):
dma: pl330: rip out broken, redundant ID probing
dma: pl330: use dma_addr_t for describing bus addresses
ARM: dma-mapping: convert DMA direction into IOMMU protection
attributes
ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
arm64: pgtable: use pte_index instead of __pte_index
arm64: device: add iommu pointer to device archdata
documentation: iommu: add description of ARM System MMU binding
iommu: add support for ARM Ltd. System MMU architecture
MAINTAINERS: add entry for ARM system MMU driver
.../devicetree/bindings/iommu/arm,smmu.txt | 70 + MAINTAINERS | 6 + arch/arm/mm/dma-mapping.c | 20 +- arch/arm64/include/asm/device.h | 3 + arch/arm64/include/asm/pgtable.h | 4 +- drivers/dma/pl330.c | 29 +- drivers/iommu/Kconfig | 13 + drivers/iommu/Makefile | 1 + drivers/iommu/arm-smmu.c | 1965 ++++++++++++++++++++ 9 files changed, 2081 insertions(+), 30 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/arm,smmu.txt create mode 100644 drivers/iommu/arm-smmu.c
Signature · 1 line
1.8.2.2
PATCH 1/9 dma: pl330: rip out broken, redundant ID probing
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
--- drivers/dma/pl330.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-)
drivers/dma/pl330.c +3 -24
@@ -157,7 +157,6 @@ enum pl330_reqtype {#define PERIPH_REV_R0P0 0#define PERIPH_REV_R1P0 1#define PERIPH_REV_R1P1 2 -#define PCELL_ID 0xff0#define CR0_PERIPH_REQ_SET (1 << 0)#define CR0_BOOT_EN_SET (1 << 1)
@@ -193,8 +192,6 @@ enum pl330_reqtype {#define INTEG_CFG 0x0#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) -#define PCELL_ID_VAL 0xb105f00d -#define PL330_STATE_STOPPED (1 << 0)#define PL330_STATE_EXECUTING (1 << 1)#define PL330_STATE_WFE (1 << 2)
@@ -292,7 +289,6 @@ static unsigned cmd_line;/* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config {u32 periph_id; -u32 pcell_id;#define DMAC_MODE_NS (1 << 0)unsigned int mode;unsigned int data_bus_width:10 ;/* In number of bits */
@@ -650,19 +646,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd)return (pl330->pinfo->pcfg.mode &DMAC_MODE_NS ) ? true : false; } -static inline u32 get_id (struct pl330_info *pi,u32 off) -{ -void __iomem *regs = pi->base; -u32 id =0 ; - - id |= (readb (regs + off +0x0 ) <<0 ); - id |= (readb (regs + off +0x4 ) <<8 ); - id |= (readb (regs + off +0x8 ) <<16 ); - id |= (readb (regs + off +0xc ) <<24 ); - -return id; -} -static inline u32 get_revision (u32 periph_id) {return (periph_id >>PERIPH_REV_SHIFT ) &PERIPH_REV_MASK ;
@@ -1986,9 +1969,6 @@ static void read_dmac_config(struct pl330_info *pi) pi->pcfg.num_events = val; pi->pcfg.irq_ns =readl (regs +CR3 ); - - pi->pcfg.periph_id =get_id (pi,PERIPH_ID ); - pi->pcfg.pcell_id =get_id (pi,PCELL_ID ); }static inline void _reset_thread (struct pl330_thread *thrd)
@@ -2098,10 +2078,8 @@ static int pl330_add(struct pl330_info *pi) regs = pi->base;/* Check if we can handle this DMAC */ -if ((get_id (pi,PERIPH_ID ) &0xfffff ) !=PERIPH_ID_VAL - ||get_id (pi,PCELL_ID ) !=PCELL_ID_VAL ) { -dev_err (pi->dev,"PERIPH_ID 0x%x, PCELL_ID 0x%x !\n" , -get_id (pi,PERIPH_ID ),get_id (pi,PCELL_ID )); +if ((pi->pcfg.periph_id &0xfffff ) !=PERIPH_ID_VAL ) { +dev_err (pi->dev,"PERIPH_ID 0x%x !\n" , pi->pcfg.periph_id);return -EINVAL ; }
@@ -2922,6 +2900,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)if (ret)return ret; + pi->pcfg.periph_id = adev->periphid; ret =pl330_add (pi);if (ret) goto probe_err1;
Signature · 1 line
1.8.2.2
A message that is not in the archive (sent off-list or not yet received): 1370889285-22799-2-git-send-email-will.deacon-5wv7dgnIgG8@public.gmane.org
Re: [PATCH 1/9] dma: pl330: rip out broken, redundant ID probing
Quoted 10 lines · Will Deacon
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
Acked-by Jassi Brar <[email protected]>
PATCH 1/9 dma: pl330: rip out broken, redundant ID probing
In reply to Will DeaconQuoted 9 lines · Will Deacon
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
PATCH 1/9 dma: pl330: rip out broken, redundant ID probing
Quoted 11 lines · Jassi Brar
Quoted 9 lines · Will Deacon
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
Acked-by Jassi Brar <[email protected]>
Thanks for the acks Jassi!
Will
PATCH 1/9 dma: pl330: rip out broken, redundant ID probing
In reply to Will DeaconQuoted 6 lines · Will Deacon
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Quoted 6 lines
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
Cc Jassi Brar <[email protected]>Cc Vinod Koul <[email protected]>Signed-off-by Will Deacon <[email protected]>
Right, device drivers shouldn't be cosing their own primecell ID parser.
Quoted 92 lines
--- drivers/dma/pl330.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-)drivers/dma/pl330.c +3
-24@@ -157,7 +157,6 @@ enum pl330_reqtype {#define PERIPH_REV_R0P0 0#define PERIPH_REV_R1P0 1#define PERIPH_REV_R1P1 2 -#define PCELL_ID 0xff0#define CR0_PERIPH_REQ_SET (1 << 0)#define CR0_BOOT_EN_SET (1 << 1)@@ -193,8 +192,6 @@ enum pl330_reqtype {#define INTEG_CFG 0x0#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) -#define PCELL_ID_VAL 0xb105f00d -#define PL330_STATE_STOPPED (1 << 0)#define PL330_STATE_EXECUTING (1 << 1)#define PL330_STATE_WFE (1 << 2)@@ -292,7 +289,6 @@ static unsigned cmd_line;/* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config {u32 periph_id; -u32 pcell_id;#define DMAC_MODE_NS (1 << 0)unsigned int mode;unsigned int data_bus_width:10 ;/* In number of bits */ @@ -650,19 +646,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd)return (pl330->pinfo->pcfg.mode &DMAC_MODE_NS ) ? true : false; } -static inline u32 get_id (struct pl330_info *pi,u32 off) -{ -void __iomem *regs = pi->base; -u32 id =0 ; - - id |= (readb (regs + off +0x0 ) <<0 ); - id |= (readb (regs + off +0x4 ) <<8 ); - id |= (readb (regs + off +0x8 ) <<16 ); - id |= (readb (regs + off +0xc ) <<24 ); - -return id; -} -static inline u32 get_revision (u32 periph_id) {return (periph_id >>PERIPH_REV_SHIFT ) &PERIPH_REV_MASK ;@@ -1986,9 +1969,6 @@ static void read_dmac_config(struct pl330_info *pi) pi->pcfg.num_events = val; pi->pcfg.irq_ns =readl (regs +CR3 ); - - pi->pcfg.periph_id =get_id (pi,PERIPH_ID ); - pi->pcfg.pcell_id =get_id (pi,PCELL_ID ); }static inline void _reset_thread (struct pl330_thread *thrd)@@ -2098,10 +2078,8 @@ static int pl330_add(struct pl330_info *pi) regs = pi->base;/* Check if we can handle this DMAC */ -if ((get_id (pi,PERIPH_ID ) &0xfffff ) !=PERIPH_ID_VAL - ||get_id (pi,PCELL_ID ) !=PCELL_ID_VAL ) { -dev_err (pi->dev,"PERIPH_ID 0x%x, PCELL_ID 0x%x !\n" , -get_id (pi,PERIPH_ID ),get_id (pi,PCELL_ID )); +if ((pi->pcfg.periph_id &0xfffff ) !=PERIPH_ID_VAL ) { +dev_err (pi->dev,"PERIPH_ID 0x%x !\n" , pi->pcfg.periph_id);return -EINVAL ; }@@ -2922,6 +2900,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)if (ret)return ret; + pi->pcfg.periph_id = adev->periphid; ret =pl330_add (pi);if (ret) goto probe_err1;Signature · 7 lines
1.8.2.2 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 2 lines
Grant Likely, B.Sc, P.Eng. Secret Lab Technologies, Ltd.
Vinod Koul
AppliedPATCH 1/9 dma: pl330: rip out broken, redundant ID probing
In reply to Will DeaconQuoted 6 lines · Will Deacon
The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions).
Quoted 6 lines
Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us.
Cc Jassi Brar <[email protected]>Cc Vinod Koul <[email protected]>Signed-off-by Will Deacon <[email protected]>
Applied, thanks
--
~Vinod
Quoted 87 lines
--- drivers/dma/pl330.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-)drivers/dma/pl330.c +3
-24@@ -157,7 +157,6 @@ enum pl330_reqtype {#define PERIPH_REV_R0P0 0#define PERIPH_REV_R1P0 1#define PERIPH_REV_R1P1 2 -#define PCELL_ID 0xff0#define CR0_PERIPH_REQ_SET (1 << 0)#define CR0_BOOT_EN_SET (1 << 1)@@ -193,8 +192,6 @@ enum pl330_reqtype {#define INTEG_CFG 0x0#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) -#define PCELL_ID_VAL 0xb105f00d -#define PL330_STATE_STOPPED (1 << 0)#define PL330_STATE_EXECUTING (1 << 1)#define PL330_STATE_WFE (1 << 2)@@ -292,7 +289,6 @@ static unsigned cmd_line;/* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config {u32 periph_id; -u32 pcell_id;#define DMAC_MODE_NS (1 << 0)unsigned int mode;unsigned int data_bus_width:10 ;/* In number of bits */ @@ -650,19 +646,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd)return (pl330->pinfo->pcfg.mode &DMAC_MODE_NS ) ? true : false; } -static inline u32 get_id (struct pl330_info *pi,u32 off) -{ -void __iomem *regs = pi->base; -u32 id =0 ; - - id |= (readb (regs + off +0x0 ) <<0 ); - id |= (readb (regs + off +0x4 ) <<8 ); - id |= (readb (regs + off +0x8 ) <<16 ); - id |= (readb (regs + off +0xc ) <<24 ); - -return id; -} -static inline u32 get_revision (u32 periph_id) {return (periph_id >>PERIPH_REV_SHIFT ) &PERIPH_REV_MASK ;@@ -1986,9 +1969,6 @@ static void read_dmac_config(struct pl330_info *pi) pi->pcfg.num_events = val; pi->pcfg.irq_ns =readl (regs +CR3 ); - - pi->pcfg.periph_id =get_id (pi,PERIPH_ID ); - pi->pcfg.pcell_id =get_id (pi,PCELL_ID ); }static inline void _reset_thread (struct pl330_thread *thrd)@@ -2098,10 +2078,8 @@ static int pl330_add(struct pl330_info *pi) regs = pi->base;/* Check if we can handle this DMAC */ -if ((get_id (pi,PERIPH_ID ) &0xfffff ) !=PERIPH_ID_VAL - ||get_id (pi,PCELL_ID ) !=PCELL_ID_VAL ) { -dev_err (pi->dev,"PERIPH_ID 0x%x, PCELL_ID 0x%x !\n" , -get_id (pi,PERIPH_ID ),get_id (pi,PCELL_ID )); +if ((pi->pcfg.periph_id &0xfffff ) !=PERIPH_ID_VAL ) { +dev_err (pi->dev,"PERIPH_ID 0x%x !\n" , pi->pcfg.periph_id);return -EINVAL ; }@@ -2922,6 +2900,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)if (ret)return ret; + pi->pcfg.periph_id = adev->periphid; ret =pl330_add (pi);if (ret) goto probe_err1;Signature · 1 line
1.8.2.2
PATCH 2/9 dma: pl330: use dma_addr_t for describing bus addresses
In reply to Will DeaconThe microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses.
This patch uses dma_addr_t to represent the address instead.
--- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/pl330.c +1 -1
@@ -501,7 +501,7 @@ struct pl330_dmac {/* Maximum possible events/irqs */ int events[32 ];/* BUS address of MicroCode buffer */ -u32 mcode_bus; +dma_addr_t mcode_bus;/* CPU address of MicroCode buffer */ void *mcode_cpu;/* List of all Channel threads */
Signature · 1 line
1.8.2.2
A message that is not in the archive (sent off-list or not yet received): 1370889285-22799-3-git-send-email-will.deacon-5wv7dgnIgG8@public.gmane.org
Re: [PATCH 2/9] dma: pl330: use dma_addr_t for describing bus addresses
Quoted 33 lines · Will Deacon
The microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses.
This patch uses
dma_addr_tto represent the address instead.Acked-by Jassi Brar <[email protected]>Cc Jassi Brar <[email protected]>Cc Vinod Koul <[email protected]>Signed-off-by Will Deacon <[email protected]>--- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/dma/pl330.c +1
-2@@ -501,7 +501,7 @@ struct pl330_dmac {/* Maximum possible events/irqs */ int events[32 ];/* BUS address of MicroCode buffer */ -u32 mcode_bus; +dma_addr_t mcode_bus;/* CPU address of MicroCode buffer */ void *mcode_cpu;/* List of all Channel threads */ --1.8.2.2
Signature · 3 lines
Linaro.org │ Open source software for ARM SoCs | Follow Linaro http://facebook.com/pages/Linaro/155974581091106 - http://twitter.com/#!/linaroorg - http://linaro.org/linaro-blog
Vinod Koul
AppliedPATCH 2/9 dma: pl330: use dma_addr_t for describing bus addresses
Quoted 6 lines · Jassi Brar
Quoted 4 lines · Will Deacon
The microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses.
Quoted 8 lines
Quoted 8 lines
This patch uses
dma_addr_tto represent the address instead.Acked-by Jassi Brar <[email protected]>Cc Jassi Brar <[email protected]>Cc Vinod Koul <[email protected]>Signed-off-by Will Deacon <[email protected]>
Applied, thanks
--
~Vinod
Quoted 27 lines
Quoted 21 lines
--- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/dma/pl330.c +1
-2@@ -501,7 +501,7 @@ struct pl330_dmac {/* Maximum possible events/irqs */ int events[32 ];/* BUS address of MicroCode buffer */ -u32 mcode_bus; +dma_addr_t mcode_bus;/* CPU address of MicroCode buffer */ void *mcode_cpu;/* List of all Channel threads */ --1.8.2.2
Signature · 3 lines
Linaro.org ? Open source software for ARM SoCs | Follow Linaro http://facebook.com/pages/Linaro/155974581091106 - http://twitter.com/#!/linaroorg - http://linaro.org/linaro-blog
PATCH 2/9 dma: pl330: use dma_addr_t for describing bus addresses
In reply to Will DeaconQuoted 6 lines · Will Deacon
The microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses.
This patch uses
dma_addr_tto represent the address instead.
PATCH 2/9 dma: pl330: use dma_addr_t for describing bus addresses
In reply to Will DeaconQuoted 9 lines · Will Deacon
The microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses.
This patch uses
dma_addr_tto represent the address instead.Cc Jassi Brar <[email protected]>Cc Vinod Koul <[email protected]>Signed-off-by Will Deacon <[email protected]>
Quoted 24 lines
--- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/dma/pl330.c +1
-1@@ -501,7 +501,7 @@ struct pl330_dmac {/* Maximum possible events/irqs */ int events[32 ];/* BUS address of MicroCode buffer */ -u32 mcode_bus; +dma_addr_t mcode_bus;/* CPU address of MicroCode buffer */ void *mcode_cpu;/* List of all Channel threads */ Signature · 6 lines
1.8.2.2 _______________________________________________ devicetree-discuss mailing list devicetree-discuss at lists.ozlabs.org https://lists.ozlabs.org/listinfo/devicetree-discuss
Signature · 2 lines
Grant Likely, B.Sc, P.Eng. Secret Lab Technologies, Ltd.
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
In reply to Will DeaconIOMMU mappings take a prot parameter, identifying the protection bits to enforce on the newly created mapping (READ or WRITE). The ARM dma-mapping framework currently just passes 0 as the prot argument, resulting in faulting mappings.
This patch infers the protection attributes based on the direction of the DMA transfer.
--- arch/arm/mm/dma-mapping.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
arch/arm/mm/dma-mapping.c +16 -2
@@ -1636,13 +1636,27 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p {struct dma_iommu_mapping *mapping = dev->archdata.mapping;dma_addr_t dma_addr; -int ret, len =PAGE_ALIGN (size + offset); +int ret, prot, len =PAGE_ALIGN (size + offset); dma_addr =__alloc_iova (mapping, len);if (dma_addr ==DMA_ERROR_CODE )return dma_addr; - ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len,0 ); +switch (dir) { +case DMA_BIDIRECTIONAL : + prot =IOMMU_READ |IOMMU_WRITE ; +break ; +case DMA_TO_DEVICE : + prot =IOMMU_READ ; +break ; +case DMA_FROM_DEVICE : + prot =IOMMU_WRITE ; +break ; +default : + prot =0 ; + } + + ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len, prot);if (ret <0 ) goto fail;
Signature · 1 line
1.8.2.2
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Hello,
Quoted 10 lines · Will Deacon
IOMMU mappings take a prot parameter, identifying the protection bits to enforce on the newly created mapping (READ or WRITE). The ARM dma-mapping framework currently just passes 0 as the prot argument, resulting in faulting mappings.
This patch infers the protection attributes based on the direction of the DMA transfer.
Cc Marek Szyprowski <[email protected]>Signed-off-by Will Deacon <[email protected]>
Thanks for fixing this issue. Could I take this patch to my dma-mapping tree with other dma-mapping changes I've collected recently or do you want my ack and push it via other tree?
Quoted 28 lines
--- arch/arm/mm/dma-mapping.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)arch/arm/mm/dma-mapping.c +9
-2@@ -1636,13 +1636,27 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p {struct dma_iommu_mapping *mapping = dev->archdata.mapping;dma_addr_t dma_addr; -int ret, len =PAGE_ALIGN (size + offset); +int ret, prot, len =PAGE_ALIGN (size + offset); dma_addr =__alloc_iova (mapping, len);if (dma_addr ==DMA_ERROR_CODE )return dma_addr; - ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len,0 ); +switch (dir) { +case DMA_BIDIRECTIONAL : + prot =IOMMU_READ |IOMMU_WRITE ; +break ; +case DMA_TO_DEVICE : + prot =IOMMU_READ ; +break ; +case DMA_FROM_DEVICE :
arch/arm/mm/dma-mapping.c static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p · line 1662
Quoted 10 lines
arch/arm/mm/dma-mapping.c +7
-0+ prot =IOMMU_WRITE ; +break ; +default : + prot =0 ; + } + + ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len, prot);if (ret <0 ) goto fail;
Best regards
Signature · 2 lines
Marek Szyprowski Samsung R&D Institute Poland
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Quoted 1 line · Marek Szyprowski
Hello,
Hi Marek,
Quoted 9 lines · Will Deacon
Quoted 8 lines
IOMMU mappings take a prot parameter, identifying the protection bits to enforce on the newly created mapping (READ or WRITE). The ARM dma-mapping framework currently just passes 0 as the prot argument, resulting in faulting mappings.
This patch infers the protection attributes based on the direction of the DMA transfer.
Quoted 6 lines
Quoted 2 lines
Cc Marek Szyprowski <[email protected]>Signed-off-by Will Deacon <[email protected]>Thanks for fixing this issue. Could I take this patch to my dma-mapping tree with other dma-mapping changes I've collected recently or do you want my ack and push it via other tree?
Please, feel free to take it via the dma-mapping tree! That's probably the best route for it and there's nothing that really depends on it anyway.
Cheers,
Will
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Hello,
Quoted 17 lines · Will Deacon
Quoted 1 line · Marek Szyprowski
Hello,
Hi Marek,
Quoted 12 lines · Will Deacon
Quoted 10 lines
IOMMU mappings take a prot parameter, identifying the protection bits to enforce on the newly created mapping (READ or WRITE). The ARM dma-mapping framework currently just passes 0 as the prot argument, resulting in faulting mappings.
This patch infers the protection attributes based on the direction of the DMA transfer.
Cc Marek Szyprowski <[email protected]>Signed-off-by Will Deacon <[email protected]>
Quoted 6 lines
Quoted 3 lines
Thanks for fixing this issue. Could I take this patch to my dma-mapping tree with other dma-mapping changes I've collected recently or do you want my ack and push it via other tree?
Please, feel free to take it via the dma-mapping tree! That's probably the best route for it and there's nothing that really depends on it anyway.
Ok, thank I will put it on my tree. Thanks for your patches!
Best regards
Signature · 2 lines
Marek Szyprowski Samsung R&D Institute Poland
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
In reply to Will DeaconHi Will,
On Mon, 10 Jun 2013 20:34:39 +0200
Will Deacon <[email protected]> wrote:
...
Quoted 17 lines
arch/arm/mm/dma-mapping.c +6
-2@@ -1636,13 +1636,27 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p {struct dma_iommu_mapping *mapping = dev->archdata.mapping;dma_addr_t dma_addr; -int ret, len =PAGE_ALIGN (size + offset); +int ret, prot, len =PAGE_ALIGN (size + offset); dma_addr =__alloc_iova (mapping, len);if (dma_addr ==DMA_ERROR_CODE )return dma_addr; - ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len,0 ); +switch (dir) { +case DMA_BIDIRECTIONAL : + prot =IOMMU_READ |IOMMU_WRITE ; +break ; +case DMA_TO_DEVICE :
arch/arm/mm/dma-mapping.c static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p · line 1659
Quoted 10 lines
arch/arm/mm/dma-mapping.c +10
-0+ prot =IOMMU_READ ; +break ; +case DMA_FROM_DEVICE : + prot =IOMMU_WRITE ; +break ; +default : + prot =0 ; + } + + ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len, prot);
Do we need similar changes for map_sg case as well? They still passes '0' as prot.
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Quoted 1 line · Hiroshi Doyu
Hi Will,
Hi Hiroshi,
Quoted 24 lines
On Mon, 10 Jun 2013 20:34:39 +0200
Will Deacon <[email protected]> wrote:
...Quoted 21 lines
arch/arm/mm/dma-mapping.c +10
-2@@ -1636,13 +1636,27 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p {struct dma_iommu_mapping *mapping = dev->archdata.mapping;dma_addr_t dma_addr; -int ret, len =PAGE_ALIGN (size + offset); +int ret, prot, len =PAGE_ALIGN (size + offset); dma_addr =__alloc_iova (mapping, len);if (dma_addr ==DMA_ERROR_CODE )return dma_addr; - ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len,0 ); +switch (dir) { +case DMA_BIDIRECTIONAL : + prot =IOMMU_READ |IOMMU_WRITE ; +break ; +case DMA_TO_DEVICE : + prot =IOMMU_READ ; +break ; +case DMA_FROM_DEVICE : + prot =IOMMU_WRITE ;
Quoted 8 lines
arch/arm/mm/dma-mapping.c static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p · line 1659
Quoted 6 lines
arch/arm/mm/dma-mapping.c +6
-0+break ; +default : + prot =0 ; + } + + ret =iommu_map (mapping->domain, dma_addr,page_to_phys (page), len, prot);Do we need similar changes for
map_sgcase as well? They still passes '0' as prot.
Yes, we could use the same trick there (probably worth moving the logic into a helper function for translating dma_data_direction into IOMMU_* values).
There are also iommu_map calls when allocating DMA buffers, but I think 0 is the right thing to pass there (i.e. no permission until pages have been explicitly mapped). Although, to be honest, I don't see why we need to map the buffer at all when we allocate it.
Will
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Will Deacon <[email protected]> wrote @ Tue, 25 Jun 2013 13:37:14 +0200:
...
Quoted 9 lines
Quoted 1 line
Do we need similar changes for
map_sgcase as well? They still passes '0' as prot.Yes, we could use the same trick there (probably worth moving the logic into a helper function for translating
dma_data_directioninto IOMMU_* values).There are also
iommu_mapcalls when allocating DMA buffers, but I think 0 is the right thing to pass there (i.e. no permission until pages have been explicitly mapped). Although, to be honest, I don't see why we need to map the buffer at all when we allocate it.
Yes, I thought too. I have a patch for that as below. If you like, I'll rebase and send for merge with the one which changes dma-mapping.c.
Quoted 1 line
From
699e6bd4fef86383d197775486b47bcbdc594f4aMon Sep 17 00:00:00 2001
From: Hiroshi Doyu <[email protected]>
Date: Tue, 25 Jun 2013 13:43:29 +0300
Subject: [PATCH 1/2] iommu/core: convert DMA direction into IOMMU protection
attributes
Introduce a new function to convert DMA direction into IOMMU protection attributes.
--- include/linux/iommu.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
include/linux/iommu.h +15 -0
@@ -21,11 +21,26 @@#include <linux/errno.h> #include <linux/types.h> +#include <linux/dma-direction.h> #define IOMMU_READ (1)#define IOMMU_WRITE (2)#define IOMMU_CACHE (4)/* DMA cache coherency */ +static inline int to_iommu_prot (enum dma_data_direction dir) +{ +switch (dir) { +case DMA_BIDIRECTIONAL : +return IOMMU_READ |IOMMU_WRITE ; +case DMA_TO_DEVICE : +return IOMMU_READ ; +case DMA_FROM_DEVICE : +return IOMMU_WRITE ; +default : +return 0 ; + } +} +struct iommu_ops ;struct iommu_group ;struct bus_type ;
Signature · 1 line
1.8.1.5
PATCH 3/9 ARM: dma-mapping: convert DMA direction into IOMMU protection attributes
Quoted 7 lines · Hiroshi Doyu
Will Deacon <[email protected]> wrote @ Tue, 25 Jun 2013 13:37:14 +0200:
...Quoted 5 lines
Quoted 1 line
Do we need similar changes for
map_sgcase as well? They still passes '0' as prot.Yes, we could use the same trick there (probably worth moving the logic into a helper function for translating
dma_data_directioninto IOMMU_* values).
Quoted 8 lines
Quoted 4 lines
There are also
iommu_mapcalls when allocating DMA buffers, but I think 0 is the right thing to pass there (i.e. no permission until pages have been explicitly mapped). Although, to be honest, I don't see why we need to map the buffer at all when we allocate it.Yes, I thought too. I have a patch for that as below. If you like, I'll rebase and send for merge with the one which changes
dma-mapping.c.
Yes, please send the series and I'll take a look. Marek's already picked up my original patch, so it's better if you can base against a stable branch from him.
Will
PATCH 4/9 ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
In reply to Will DeaconThe current code only clobbers a local variable, so the device is left with a stale mapping pointer.
--- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mm/dma-mapping.c +1 -1
@@ -1921,7 +1921,7 @@ void arm_iommu_detach_device(struct device *dev)iommu_detach_device (mapping->domain, dev);kref_put (&mapping->kref, release_iommu_mapping); - mapping =NULL ; + dev->archdata.mapping =NULL ;set_dma_ops (dev,NULL );pr_debug ("Detached IOMMU controller from %s device.\n" ,dev_name (dev));
Signature · 1 line
1.8.2.2
PATCH 4/9 ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
Hi Will,
Will Deacon <[email protected]> wrote @ Mon, 10 Jun 2013 20:34:40 +0200:
Quoted 2 lines
The current code only clobbers a local variable, so the device is left with a stale mapping pointer.
True. This's my bad. Thanks.
PATCH 4/9 ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
Quoted 8 lines · Hiroshi Doyu
Hi Will,
Will Deacon <[email protected]> wrote @ Mon, 10 Jun 2013 20:34:40 +0200:
Quoted 2 lines
The current code only clobbers a local variable, so the device is left with a stale mapping pointer.
True. This's my bad. Thanks.
That's alright, it's easy to fix. Mind if I add your ack please?
Will
PATCH 4/9 ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
Quoted 11 lines · On Tue, 11 Jun 2013 10:50:15 +0200 Will Deacon <[email protected]> wrote:
Quoted 8 lines · Hiroshi Doyu
Hi Will,
Will Deacon <[email protected]> wrote @ Mon, 10 Jun 2013 20:34:40 +0200:
Quoted 2 lines
The current code only clobbers a local variable, so the device is left with a stale mapping pointer.
True. This's my bad. Thanks.
That's alright, it's easy to fix. Mind if I add your ack please?
Feel free to add: Acked-by: Hiroshi Doyu <[email protected]>
PATCH 4/9 ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach
Hello,
Quoted 8 lines · Hiroshi Doyu
Quoted 5 lines · On Tue, 11 Jun 2013 10:50:15 +0200 Will Deacon <[email protected]> wrote:
Quoted 4 lines · Hiroshi Doyu
Hi Will,
Will Deacon <[email protected]> wrote @ Mon, 10 Jun 2013 20:34:40 +0200:
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
The current code only clobbers a local variable, so the device is left with a stale mapping pointer.
True. This's my bad. Thanks.
That's alright, it's easy to fix. Mind if I add your ack please?
Feel free to add: Acked-by: Hiroshi Doyu <[email protected]>
Thanks for spotting the bug. I've taked the patch to my dma-mapping tree.
Best regards
Signature · 2 lines
Marek Szyprowski Samsung R&D Institute Poland
PATCH 5/9 arm64: pgtable: use pte_index instead of __pte_index
In reply to Will Deaconpte_index is a useful helper outside of arch/arm64, for things like the ARM SMMU driver, so rename __pte_index to pte_index to be consistent with both arch/arm/ and also the definitions of pmd_index and pgd_index.
--- arch/arm64/include/asm/pgtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/include/asm/pgtable.h +2 -2
@@ -119,7 +119,7 @@ extern struct page *empty_zero_page;#define pte_none (pte) (!pte_val(pte))#define pte_clear (mm,addr,ptep) set_pte(ptep, __pte(0))#define pte_page (pte) (pfn_to_page(pte_pfn(pte))) -#define pte_offset_kernel (dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr)) +#define pte_offset_kernel (dir,addr) (pmd_page_vaddr(*(dir)) + pte_index(addr))#define pte_offset_map (dir,addr) pte_offset_kernel((dir), (addr))#define pte_offset_map_nested (dir,addr) pte_offset_kernel((dir), (addr))
@@ -263,7 +263,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)#endif /* Find an entry in the third-level page table.. */ -#define __pte_index (addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_index (addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))static inline pte_t pte_modify (pte_t pte,pgprot_t newprot) {
Signature · 1 line
1.8.2.2
PATCH 6/9 arm64: device: add iommu pointer to device archdata
In reply to Will DeaconWhen using an IOMMU for device mappings, it is necessary to keep a pointer between the device and the IOMMU to which it is attached in order to obtain the correct IOMMU when attaching the device to a domain.
This patch adds an iommu pointer to the dev_archdata structure, in a similar manner to other architectures (ARM, PowerPC, x86, ...).
--- arch/arm64/include/asm/device.h | 3 +++ 1 file changed, 3 insertions(+)
arch/arm64/include/asm/device.h +3 -0
@@ -18,6 +18,9 @@struct dev_archdata {struct dma_map_ops *dma_ops; +#ifdef CONFIG_IOMMU_API +void *iommu;/* private IOMMU data */ +#endif struct dma_iommu_mapping *mapping; };
Signature · 1 line
1.8.2.2
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
In reply to Will DeaconThis patch adds a description of the device tree binding for the ARM System MMU architecture.
--- .../devicetree/bindings/iommu/arm,smmu.txt | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/arm,smmu.txt
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +70 -0
@@ -0,0 +1,70 @@ +* ARM System MMU Architecture Implementation + +ARM SoCs may contain an implementation of the ARM System Memory +Management Unit Architecture, which can be used to provide 1 or 2 stages +of address translation to bus masters external to the CPU. + +The SMMU may also raise interrupts in response to various fault +conditions. + +** System MMU required properties: + +- compatible : Should be one of: + + "arm,smmu-v1" + "arm,smmu-v2" + "arm,mmu-400" + "arm,mmu-500" + + depending on the particular implementation and/or the + version of the architecture implemented. + +- reg : Base address and size of the SMMU. + +- #global-interrupts : The number of global interrupts exposed by the + device. + +- interrupts : Interrupt list, with the first #global-irqs entries + corresponding to the global interrupts and any + following entries corresponding to context interrupts, + specified in order of their indexing by the SMMU. + + For SMMUv2 implementations, there must be exactly one + interrupt per context bank. In the case of a single, + combined interrupt, it must be listed multiple times. + +- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs associated with it. + +** System MMU optional properties: + +- smmu-parent : When multiple SMMUs are chained together, this + property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU. + +Example: + + smmu { + compatible = "arm,smmu-v1"; + reg = <0xba5e0000 0x10000>; + #global-interrupts = <2>; + interrupts = <0 32 4>, + <0 33 4>, + <0 34 4>, /* This is the first context interrupt */ + <0 35 4>, + <0 36 4>, + <0 37 4>; + + /* + * Two DMA controllers, the first with two StreamIDs (0xd01d + * and 0xd01e) and the second with only one (0xd11c). + */ + mmu-masters = <&dma0 0xd01d 0xd01e>, + <&dma1 0xd11c>; + };
Signature · 1 line
1.8.2.2
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 7 lines · Will Deacon
This patch adds a description of the device tree binding for the ARM System MMU architecture.
Cc Rob Herring <[email protected]>Cc Andreas Herrmann <[email protected]>Cc Joerg Roedel <[email protected]>Signed-off-by Will Deacon <[email protected]>
Quoted 88 lines
--- .../devicetree/bindings/iommu/arm,smmu.txt | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/arm,smmu.txtDocumentation/devicetree/bindings/iommu/arm,smmu.txt new file +70
-0@@ -0,0 +1,70 @@ +* ARM System MMU Architecture Implementation + +ARM SoCs may contain an implementation of the ARM System Memory +Management Unit Architecture, which can be used to provide 1 or 2 stages +of address translation to bus masters external to the CPU. + +The SMMU may also raise interrupts in response to various fault +conditions. + +** System MMU required properties: + +- compatible : Should be one of: + + "arm,smmu-v1" + "arm,smmu-v2" + "arm,mmu-400" + "arm,mmu-500" + + depending on the particular implementation and/or the + version of the architecture implemented. + +- reg : Base address and size of the SMMU. + +- #global-interrupts : The number of global interrupts exposed by the + device. + +- interrupts : Interrupt list, with the first #global-irqs entries + corresponding to the global interrupts and any + following entries corresponding to context interrupts, + specified in order of their indexing by the SMMU. + + For SMMUv2 implementations, there must be exactly one + interrupt per context bank. In the case of a single, + combined interrupt, it must be listed multiple times. + +- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs associated with it. + +** System MMU optional properties: + +- smmu-parent : When multiple SMMUs are chained together, this + property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU. + +Example: + + smmu { + compatible = "arm,smmu-v1"; + reg = <0xba5e0000 0x10000>; + #global-interrupts = <2>; + interrupts = <0 32 4>, + <0 33 4>, + <0 34 4>, /* This is the first context interrupt */ + <0 35 4>, + <0 36 4>, + <0 37 4>; + + /* + * Two DMA controllers, the first with two StreamIDs (0xd01d + * and 0xd01e) and the second with only one (0xd11c). + */ + mmu-masters = <&dma0 0xd01d 0xd01e>, + <&dma1 0xd11c>; + };Signature · 6 lines
1.8.2.2 _______________________________________________ devicetree-discuss mailing list devicetree-discuss at lists.ozlabs.org https://lists.ozlabs.org/listinfo/devicetree-discuss
Signature · 2 lines
Grant Likely, B.Sc, P.Eng. Secret Lab Technologies, Ltd.
Joerg Roedel
maintainerPATCH 7/9 documentation: iommu: add description of ARM System MMU binding
In reply to Will DeaconQuoted 2 lines · Will Deacon
This patch adds a description of the device tree binding for the ARM System MMU architecture.
Interesting, will this be a common driver to replace existing ARM IOMMU drivers or is it a common driver for ARM IOMMUs found in future chips?
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 49
Quoted 5 lines
+- smmu-parent : When multiple SMMUs are chained together, this
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +4
-0+ property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU.
What happens when SMMUs are chained? Will the second SMMU seeing the DMA just pass it through or is it translated again?
Thanks,
Joerg
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Hi Joerg,
Quoted 6 lines · Joerg Roedel
Quoted 2 lines · Will Deacon
This patch adds a description of the device tree binding for the ARM System MMU architecture.
Interesting, will this be a common driver to replace existing ARM IOMMU drivers or is it a common driver for ARM IOMMUs found in future chips?
This is a common driver that will support any IOMMUs compatible with v1 or v2 of the ARM SMMU architecture. Currently, that includes SMMUs known informatively as MMU-400, MMU-401 and MMU-500. I had a look at the other IOMMU drivers in the kernel and they seem to be driving incompatible IOMMUs, so I don't see how this driver can replace those.
However, we'll hopefully see ARM SMMU-compatible devices turning up soon (I know of many SoCs in development that are looking at them).
Quoted 8 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 49
Quoted 5 lines
+- smmu-parent : When multiple SMMUs are chained together, this
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +4
-0+ property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU.What happens when SMMUs are chained? Will the second SMMU seeing the DMA just pass it through or is it translated again?
Chaining is really horrible and exists as a hack to support virtualisation using two separate SMMUs, where neither of them can support nested translation.
The current driver just programs the translation in the SMMU nearest the device, then sets the other SMMU into `bypass' mode (it was simple enough to generalise the chain to contain an arbitrary number of SMMUs, so the driver can actually deal with any number of the things). In theory (and if we extended the IOMMU API to distinguish between guest and host mappings -- something which I plan to look at in the future), KVM could install mappings in the second SMMU, but I think we should draw a line in the sand and mandate support for nested translation for KVM.
The only thing to take care of is that the SMMUs in the chain don't silently truncate addresses.
Will
Joerg Roedel
maintainerPATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Hi Will,
Quoted 8 lines · Will Deacon
This is a common driver that will support any IOMMUs compatible with v1 or v2 of the ARM SMMU architecture. Currently, that includes SMMUs known informatively as MMU-400, MMU-401 and MMU-500. I had a look at the other IOMMU drivers in the kernel and they seem to be driving incompatible IOMMUs, so I don't see how this driver can replace those.
However, we'll hopefully see ARM SMMU-compatible devices turning up soon (I know of many SoCs in development that are looking at them).
That sounds great. So in the future there will be a single driver for most future SOCs. This is a real improvement over the current situation.
Quoted 1 line
From NVidia is another driver with 'smmu' in its name, is that one also
incompatible?
Quoted 3 lines
Chaining is really horrible and exists as a hack to support virtualisation using two separate SMMUs, where neither of them can support nested translation.
I see, thanks for the explanation.
Joerg
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 7 lines · Joerg Roedel
Quoted 6 lines · Will Deacon
This is a common driver that will support any IOMMUs compatible with v1 or v2 of the ARM SMMU architecture. Currently, that includes SMMUs known informatively as MMU-400, MMU-401 and MMU-500. I had a look at the other IOMMU drivers in the kernel and they seem to be driving incompatible IOMMUs, so I don't see how this driver can replace those.
Quoted 8 lines
Quoted 2 lines
However, we'll hopefully see ARM SMMU-compatible devices turning up soon (I know of many SoCs in development that are looking at them).
That sounds great. So in the future there will be a single driver for most future SOCs. This is a real improvement over the current situation.
From NVidia is another driver with 'smmu' in its name, is that one also incompatible?
Yup. I even checked with Stephen Warren before I started my driver, just in case :)
Will
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
In reply to Will DeaconQuoted 36 lines · Will Deacon
This patch adds a description of the device tree binding for the ARM System MMU architecture.
Cc Rob Herring <[email protected]>Cc Andreas Herrmann <[email protected]>Cc Joerg Roedel <[email protected]>Signed-off-by Will Deacon <[email protected]>--- .../devicetree/bindings/iommu/arm,smmu.txt | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/arm,smmu.txtDocumentation/devicetree/bindings/iommu/arm,smmu.txt new file +18
-0@@ -0,0 +1,70 @@ +* ARM System MMU Architecture Implementation + +ARM SoCs may contain an implementation of the ARM System Memory +Management Unit Architecture, which can be used to provide 1 or 2 stages +of address translation to bus masters external to the CPU. + +The SMMU may also raise interrupts in response to various fault +conditions. + +** System MMU required properties: + +- compatible : Should be one of: + + "arm,smmu-v1" + "arm,smmu-v2" + "arm,mmu-400" + "arm,mmu-500" +
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +10
-0+ depending on the particular implementation and/or the + version of the architecture implemented. + +- reg : Base address and size of the SMMU. + +- #global-interrupts : The number of global interrupts exposed by the + device. + +- interrupts : Interrupt list, with the first #global-irqs entries + corresponding to the global interrupts
It seems like we don't have enough information here. It's not enough
for the OS to know that there are 2, 4, etc global interrupts, no? It needs
to know which hardware interrupt each corresponds to. That kind of
stuff is normally defined in the device binding.
What is it that determines the number of global interrupts?
Quoted 4 lines
and any
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +3
-0+ following entries corresponding to context interrupts, + specified in order of their indexing by the SMMU. +
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 41
Quoted 10 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +10
-0+ For SMMUv2 implementations, there must be exactly one + interrupt per context bank. In the case of a single, + combined interrupt, it must be listed multiple times. + +- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs associated with it.
So to find a the SMMU for a given device, I walk up the bus hierarchy until I find an SMMU?
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 49
Quoted 7 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +7
-0+** System MMU optional properties: + +- smmu-parent : When multiple SMMUs are chained together, this + property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU.
Why is an explicit phandle link needed here when you don't need a
smmu-parent phandle in each mmu-master? Won't walking the bus
hierarchy identify the parent SMMU if things are chained?
Thanks,
Stuart Yoder
Freescale Semiconductor
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Hi Stuart,
Quoted 41 lines · Stuart Yoder
Quoted 24 lines · Will Deacon
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +18
-0@@ -0,0 +1,70 @@ +* ARM System MMU Architecture Implementation + +ARM SoCs may contain an implementation of the ARM System Memory +Management Unit Architecture, which can be used to provide 1 or 2 stages +of address translation to bus masters external to the CPU. + +The SMMU may also raise interrupts in response to various fault +conditions. + +** System MMU required properties: + +- compatible : Should be one of: + + "arm,smmu-v1" + "arm,smmu-v2" + "arm,mmu-400" + "arm,mmu-500" +Documentation/devicetree/bindings/iommu/arm,smmu.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +10
-0+ depending on the particular implementation and/or the + version of the architecture implemented. + +- reg : Base address and size of the SMMU. + +- #global-interrupts : The number of global interrupts exposed by the + device. + +- interrupts : Interrupt list, with the first #global-irqs entries + corresponding to the global interruptsIt seems like we don't have enough information here. It's not enough
for the OS to know that there are 2, 4, etc global interrupts, no? It needs
to know which hardware interrupt each corresponds to. That kind of
stuff is normally defined in the device binding.
Quoted 1 line
What is it that determines the number of global interrupts?
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
Quoted 9 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 41
Quoted 6 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +6
-0+- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs associated with it.So to find a the SMMU for a given device, I walk up the bus hierarchy until I find an SMMU?
Again, the code is better than any explanation I can give here, but we basically construct a tree of masters for each SMMU in the system based on the mmu-masters property, which we can later search.
Quoted 11 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 49
Quoted 7 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +7
-0+** System MMU optional properties: + +- smmu-parent : When multiple SMMUs are chained together, this + property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU.Why is an explicit phandle link needed here when you don't need a
smmu-parent phandle in each mmu-master? Won't walking the bus
hierarchy identify the parent SMMU if things are chained?
What bus hierarchy? If I have two SMMU device nodes, how do I infer any topological information without an explicit linkage?
Will
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 44 lines · Will Deacon
Hi Stuart,
Quoted 41 lines · Stuart Yoder
Quoted 24 lines · Will Deacon
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +18
-0@@ -0,0 +1,70 @@ +* ARM System MMU Architecture Implementation + +ARM SoCs may contain an implementation of the ARM System Memory +Management Unit Architecture, which can be used to provide 1 or 2 stages +of address translation to bus masters external to the CPU. + +The SMMU may also raise interrupts in response to various fault +conditions. + +** System MMU required properties: + +- compatible : Should be one of: + + "arm,smmu-v1" + "arm,smmu-v2" + "arm,mmu-400" + "arm,mmu-500" +Documentation/devicetree/bindings/iommu/arm,smmu.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +10
-0+ depending on the particular implementation and/or the + version of the architecture implemented. + +- reg : Base address and size of the SMMU. + +- #global-interrupts : The number of global interrupts exposed by the + device. + +- interrupts : Interrupt list, with the first #global-irqs entries + corresponding to the global interruptsIt seems like we don't have enough information here. It's not enough
for the OS to know that there are 2, 4, etc global interrupts, no? It needs
to know which hardware interrupt each corresponds to. That kind of
stuff is normally defined in the device binding.
Quoted 5 lines
Quoted 1 line
What is it that determines the number of global interrupts?
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
I understand that the number of interrupts and actual interrupt numbers are described here, but was referring to the _meaning_ of the interrupt
numbers. A binding for a device with 2 interrupts, a TX and RX would
normally identify which interrupt specifier is for TX and which is for RX.
Based on your code, the 2 global interrupts seem to be the secure and non-secure fault interrupts...which your driver does not differentiate. However, the device tree is describing hardware and you can't assume that all drivers don't care which is which.
So, I would suggest that the binding identify which interrupt specifier is secure and which is non-secure.
If there are other interrupts added in the future like the performance interrupt the definition could be expanded to add that.
But a question.... why do you need the #global-interrupts property
at all? Is the number of "global" interrupts really implementation dependent?
Does't a specific compatible string imply the number of interrupts that there actually are?
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +6
-0+- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs associated with it.
Quoted 6 lines
Quoted 2 lines
So to find a the SMMU for a given device, I walk up the bus hierarchy until I find an SMMU?
Again, the code is better than any explanation I can give here, but we basically construct a tree of masters for each SMMU in the system based on the mmu-masters property, which we can later search.
I see.
Quoted 12 lines
Quoted 11 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt line 49
Quoted 7 lines
Documentation/devicetree/bindings/iommu/arm,smmu.txt new file +7
-0+** System MMU optional properties: + +- smmu-parent : When multiple SMMUs are chained together, this + property can be used to provide a phandle to the + parent SMMU (that is the next SMMU on the path going + from the mmu-masters towards memory) node for this + SMMU.Why is an explicit phandle link needed here when you don't need a
smmu-parent phandle in each mmu-master? Won't walking the bus
hierarchy identify the parent SMMU if things are chained?
Quoted 2 lines
What bus hierarchy? If I have two SMMU device nodes, how do I infer any topological information without an explicit linkage?
I really don't know anything about SMMU chaining, but am inferring what
this might look like. Does the "child" SMMU look just like another
mmu-master to the parent? If so, why not just use the mmu-masters
property to establish the relationship.
I just found it a bit strange that the relationship between I/O mmu-masters is defined from parent SMMU node pointing to the device, and the relationship between a 'child' SMMU and a parent is established
in the other direction. Not a big deal.
Stuart
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 10 lines · Stuart Yoder
Quoted 3 lines · Will Deacon
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
I understand that the number of interrupts and actual interrupt numbers are described here, but was referring to the _meaning_ of the interrupt
numbers. A binding for a device with 2 interrupts, a TX and RX would
normally identify which interrupt specifier is for TX and which is for RX.
Quoted 4 lines
Based on your code, the 2 global interrupts seem to be the secure and non-secure fault interrupts...which your driver does not differentiate. However, the device tree is describing hardware and you can't assume that all drivers don't care which is which.
Currently, the driver only works when Linux is running as non-secure, which is becoming more and more common since it is required to be able to make use of hyp mode.
There are actually two global interrupts for SMMUv1 and SMMUv2, which correspond to configuration faults and `other' global faults.
Quoted 2 lines
If there are other interrupts added in the future like the performance interrupt the definition could be expanded to add that.
PMU interrupts would like have a separate property, since the PMU would be driven by a largely independent piece of code.
Quoted 4 lines
But a question.... why do you need the #global-interrupts property
at all? Is the number of "global" interrupts really implementation dependent?
Does't a specific compatible string imply the number of interrupts that there actually are?
Integrators have a nasty habit of ORing interrupts together or not wiring them at all and I don't want to be caught out by that.
Quoted 7 lines
Quoted 2 lines
What bus hierarchy? If I have two SMMU device nodes, how do I infer any topological information without an explicit linkage?
I really don't know anything about SMMU chaining, but am inferring what
this might look like. Does the "child" SMMU look just like another
mmu-master to the parent? If so, why not just use the mmu-masters
property to establish the relationship.
No, the child SMMU does not look like a master. It can remaster upstream StreamIDs, for example.
Will
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 16 lines · Will Deacon
Quoted 10 lines · Stuart Yoder
Quoted 3 lines · Will Deacon
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
I understand that the number of interrupts and actual interrupt numbers are described here, but was referring to the _meaning_ of the interrupt
numbers. A binding for a device with 2 interrupts, a TX and RX would
normally identify which interrupt specifier is for TX and which is for RX.Quoted 4 lines
Based on your code, the 2 global interrupts seem to be the secure and non-secure fault interrupts...which your driver does not differentiate. However, the device tree is describing hardware and you can't assume that all drivers don't care which is which.
Quoted 6 lines
Currently, the driver only works when Linux is running as non-secure, which is becoming more and more common since it is required to be able to make use of hyp mode.
There are actually two global interrupts for SMMUv1 and SMMUv2, which correspond to configuration faults and `other' global faults.
So, why don't we define which interrupt is which in this binding? ...e.g. "The first
interrupt specifier is for the configuration access fault interrupt, the second interrupt specifier is for other global faults."
How else is software supposed to know which interrupt corresponds to what event.
Thanks,
Stuart
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 21 lines · Stuart Yoder
Quoted 20 lines · Will Deacon
Quoted 10 lines · Stuart Yoder
Quoted 3 lines · Will Deacon
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
I understand that the number of interrupts and actual interrupt numbers are described here, but was referring to the _meaning_ of the interrupt
numbers. A binding for a device with 2 interrupts, a TX and RX would
normally identify which interrupt specifier is for TX and which is for RX.Quoted 4 lines
Based on your code, the 2 global interrupts seem to be the secure and non-secure fault interrupts...which your driver does not differentiate. However, the device tree is describing hardware and you can't assume that all drivers don't care which is which.
Currently, the driver only works when Linux is running as non-secure, which is becoming more and more common since it is required to be able to make use of hyp mode.
Quoted 7 lines
Quoted 2 lines
There are actually two global interrupts for SMMUv1 and SMMUv2, which correspond to configuration faults and `other' global faults.
So, why don't we define which interrupt is which in this binding? ...e.g. "The first
interrupt specifier is for the configuration access fault interrupt, the second interrupt specifier is for other global faults."
Well, first of all, they're both global interrupts and the handler will have to go and access exactly the same registers to deal with the fault, so you don't really gain anything.
However, the main reason is that you need to be able to handle:
(1) Only one of the interrupts is routed to the interrupt controller
(2) The interrupts are ORd into a single line
Quoted 2 lines
How else is software supposed to know which interrupt corresponds to what event.
It really doesn't care. It can't, otherwise we break in the above situations.
Will
PATCH 7/9 documentation: iommu: add description of ARM System MMU binding
Quoted 25 lines · Will Deacon
Quoted 24 lines · Stuart Yoder
Quoted 22 lines · Will Deacon
Quoted 10 lines · Stuart Yoder
Quoted 3 lines · Will Deacon
I'd suggest looking at the driver I posted to get a gist of how the parsing code works, but suffice to say that we describe both the number of interrupts and the actual interrupt numbers here.
I understand that the number of interrupts and actual interrupt numbers are described here, but was referring to the _meaning_ of the interrupt
numbers. A binding for a device with 2 interrupts, a TX and RX would
normally identify which interrupt specifier is for TX and which is for RX.Quoted 4 lines
Based on your code, the 2 global interrupts seem to be the secure and non-secure fault interrupts...which your driver does not differentiate. However, the device tree is describing hardware and you can't assume that all drivers don't care which is which.
Currently, the driver only works when Linux is running as non-secure, which is becoming more and more common since it is required to be able to make use of hyp mode.
There are actually two global interrupts for SMMUv1 and SMMUv2, which correspond to configuration faults and `other' global faults.
Quoted 8 lines
Quoted 4 lines
So, why don't we define which interrupt is which in this binding? ...e.g. "The first
interrupt specifier is for the configuration access fault interrupt, the second interrupt specifier is for other global faults."Well, first of all, they're both global interrupts and the handler will have to go and access exactly the same registers to deal with the fault, so you don't really gain anything.
It should be up to the OS to handle 2 interrupts the same, not something
Quoted 4 lines
However, the main reason is that you need to be able to handle:
(1) Only one of the interrupts is routed to the interrupt controller
(2) The interrupts are ORd into a single line
That's a particular SoC implementation, no? It shouldn't dictate the SMMU binding.
If they are ORed then the interrupt controller can't distinguish them and both interrupt specifiers should have the _same_ interrupt number:
+ #global-interrupts = <2>;
+ interrupts = <0 32 4>,
+ <0 32 4>,
(...or perhaps only define 1 global interrupt-- interrupt 32)
If an implementation chose to not OR them, then they will have different interrupt numbers and need to be distinguished:
+ #global-interrupts = <2>;
+ interrupts = <0 32 4>,
+ <0 33 4>,
...and in that case the binding needs to differentiate which is which.
Stuart
PATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
In reply to Will DeaconThis patch adds support for SMMUs implementing the ARM System MMU architecture versions 1 or 2. Both arm and arm64 are supported, although the v7s descriptor format is not used.
--- drivers/iommu/Kconfig | 13 + drivers/iommu/Makefile | 1 + drivers/iommu/arm-smmu.c | 1965 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1979 insertions(+) create mode 100644 drivers/iommu/arm-smmu.c
drivers/iommu/Kconfig +13 -0
@@ -261,4 +261,17 @@ config SHMOBILE_IOMMU_L1SIZE default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB +config ARM_SMMU + bool "ARM Ltd. System MMU (SMMU) Support" + depends on ARM64 || (ARM_LPAE && OF) + select IOMMU_API + select ARM_DMA_USE_IOMMU if ARM + help + Support for implementations of the ARM System MMU architecture + versions 1 and 2. The driver supports both v7l and v8l table + formats with 4k and 64k page sizes. + + Say Y here if your SoC includes an IOMMU device implementing + the ARM SMMU architecture. + endif # IOMMU_SUPPORT
drivers/iommu/Makefile +1 -0
@@ -3,6 +3,7 @@ obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o +obj-$(CONFIG_ARM_SMMU) += arm-smmu.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
drivers/iommu/arm-smmu.c new file +1965 -0
1,965 lines not on this page · raw message
Signature · 1 line
1.8.2.2
Joerg Roedel
maintainerPATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Hi Will,
Quoted 9 lines · Will Deacon
This patch adds support for SMMUs implementing the ARM System MMU architecture versions 1 or 2. Both arm and arm64 are supported, although the v7s descriptor format is not used.
Cc Rob Herring <[email protected]>Cc Andreas Herrmann <[email protected]>Cc Olav Haugan <[email protected]>Cc Joerg Roedel <[email protected]>Signed-off-by Will Deacon <[email protected]>
A few general questions:
How have you tested this code? Has it been run on real hardware? What were the results?
The code looks good and clean in general, minus a few places mentioned below were I have questions and/or suggestions:
drivers/iommu/arm-smmu.c line 485
Quoted 10 lines
drivers/iommu/arm-smmu.c new file +10
-0+static struct arm_smmu_device *find_parent_smmu (struct arm_smmu_device *smmu) +{ +struct arm_smmu_device *parent, *tmp; + +if (!smmu->parent_of_node) +return NULL ; + +list_for_each_entry_safe (parent, tmp, &arm_smmu_devices, list) +if (parent->dev->of_node == smmu->parent_of_node) +return parent;
Why do you need the _safe variant here? You are not changing the list in this loop so you should be fine with list_for_each_entry().
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+ +dev_warn (smmu->dev, +"Failed to find SMMU parent despite parent in DT\n" ); +return NULL ; +}
drivers/iommu/arm-smmu.c line 539
Quoted 10 lines
drivers/iommu/arm-smmu.c new file +9
-0+/* Wait for any pending TLB invalidations to complete */ +static void arm_smmu_tlb_sync (struct arm_smmu_device *smmu) +{ +void __iomem *gr0_base =ARM_SMMU_GR0 (smmu); + +writel_relaxed (0 , gr0_base + ARM_SMMU_GR0_sTLBGSYNC); +while (readl_relaxed (gr0_base + ARM_SMMU_GR0_sTLBGSTATUS) + & sTLBGSTATUS_GSACTIVE) +cpu_relax ();
Other IOMMU drivers have a timeout for this loop and report an error when the state does not change. I think this makes sense here too so that the kernel will not just stop spinning in that loop if something goes wrong but prints an error instead.
drivers/iommu/arm-smmu.c line 1162
Quoted 13 lines
drivers/iommu/arm-smmu.c new file +13
-0+} +static void arm_smmu_flush_pgtable (struct arm_smmu_device *smmu,void *addr, +size_t size) +{ +unsigned long offset = (unsigned long )addr & ~PAGE_MASK ; + +/* +* If the SMMU can't walk tables in the CPU caches, treat them +* like non-coherent DMA... +*/ +if (!(smmu->features &ARM_SMMU_FEAT_COHERENT_WALK )) +dma_map_page (smmu->dev,virt_to_page (addr), offset, size, +DMA_TO_DEVICE );
Why can you call into DMA-API here? A DMA-API implementation may call back into this IOMMU driver, no? So this looks a little bit like a layering violation.
Quoted 9 lines
drivers/iommu/arm-smmu.c new file +8
-0+} +static int arm_smmu_map (struct iommu_domain *domain,unsigned long iova, +phys_addr_t paddr,size_t size,int flags) +{ +struct arm_smmu_domain *smmu_domain = domain->priv; +struct arm_smmu_device *smmu = smmu_domain->leaf_smmu; + +if (!smmu_domain || !smmu)
drivers/iommu/arm-smmu.c line 1404
Quoted 10 lines
drivers/iommu/arm-smmu.c new file +10
-0+return -ENODEV ; + +/* +* Check for silent address truncation up the SMMU chain. +*/ +do { +phys_addr_t output_mask = (1ULL << smmu->s2_output_size) -1 ; +if ((phys_addr_t )iova & ~output_mask) +return -ERANGE ; + }while ((smmu =find_parent_smmu (smmu)));
This looks a bit too expensive to have in the map path. How about saving something like an effective_output_mask (or output_size) which contains the logical OR of every mask up the path? This would make this check a lot cheaper.
Quoted 4 lines
drivers/iommu/arm-smmu.c new file +4
-0+ +return arm_smmu_create_mapping (smmu_domain, iova, paddr, size, flags); +} +
drivers/iommu/arm-smmu.c line 1418
Quoted 10 lines
drivers/iommu/arm-smmu.c new file +10
-0+static size_t arm_smmu_unmap (struct iommu_domain *domain,unsigned long iova, +size_t size) +{ +int ret; +struct arm_smmu_domain *smmu_domain = domain->priv; +struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg; +struct arm_smmu_device *smmu = root_cfg->smmu; +void __iomem *gr0_base =ARM_SMMU_GR0 (smmu); + + ret =arm_smmu_create_mapping (smmu_domain, iova,0 , size,0 );
Since this function does also unmapping, how about renaming it to arm_smmu_handle_mapping(). The 'create' part in there is misleading.
drivers/iommu/arm-smmu.c line 1485
Quoted 11 lines
drivers/iommu/arm-smmu.c new file +11
-0+writel_relaxed (root_cfg->vmid, gr0_base +ARM_SMMU_GR0_TLBIVMID ); +arm_smmu_tlb_sync (smmu); +return ret ? ret : size; +} +static int arm_smmu_add_device (struct device *dev) +{ +struct arm_smmu_device *child, *parent, *smmu; +struct arm_smmu_device *tmp[2 ]; +struct arm_smmu_master *master =NULL ; + +list_for_each_entry_safe (parent, tmp[0 ], &arm_smmu_devices, list) {
Again, why do you use the _safe variant, you do not seem to change the lists traversed here.
Quoted 31 lines
drivers/iommu/arm-smmu.c new file +31
-0+ smmu = parent; + +/* Try to find a child of the current SMMU. */ +list_for_each_entry_safe (child, tmp[1 ], &arm_smmu_devices, list) { +if (child->parent_of_node == parent->dev->of_node) { +/* Does the child sit above our master? */ + master =find_smmu_master (child, dev->of_node); +if (master) { + smmu =NULL ; +break ; + } + } + } + +/* We found some children, so keep searching. */ +if (!smmu) { + master =NULL ; +continue ; + } + + master =find_smmu_master (smmu, dev->of_node); +if (master) +break ; + } + +if (!master) +return -ENODEV ; + + dev->archdata.iommu = smmu; +return 0 ; +}
PATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Quoted 1 line · Joerg Roedel
Hi Will,
Hi Joerg,
First, thanks for looking at this patch. I really appreciate it.
Quoted 11 lines · Will Deacon
Quoted 9 lines
This patch adds support for SMMUs implementing the ARM System MMU architecture versions 1 or 2. Both arm and arm64 are supported, although the v7s descriptor format is not used.
Cc Rob Herring <[email protected]>Cc Andreas Herrmann <[email protected]>Cc Olav Haugan <[email protected]>Cc Joerg Roedel <[email protected]>Signed-off-by Will Deacon <[email protected]>
Quoted 4 lines
A few general questions:
How have you tested this code? Has it been run on real hardware? What were the results?
I've only tested it with software models, however they are fairly sophisticated and provide *lots* of tunables, so you can test different configurations which don't actually exist in hardware yet. You also get a tonne of trace output, so you can inspect things like page table walking and TLB filling to make sure the driver is setting things up as you planned.
I have also tested on MMU-400 (AArch32) and MMU-500 (both AArch32 and AArch64, with 4k and 64k pages in the latter case) models. The setup I used was 4 PL330 DMA controllers driven by dmatest.ko, which I hacked to call into the ARM dma-mapping API to create a separate domain for each controller.
The results were that the memory-to-memory DMA didn't show any corruption. I also managed to tickle access faults by messing around with the permissions, then remap the buffers and resume the transfers.
Quoted 2 lines
The code looks good and clean in general, minus a few places mentioned below were I have questions and/or suggestions:
Cheers, responses inline.
Quoted 5 lines
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+static struct arm_smmu_device *find_parent_smmu (struct arm_smmu_device *smmu) +{ +struct arm_smmu_device *parent, *tmp; + +if (!smmu->parent_of_node)
Quoted 8 lines
drivers/iommu/arm-smmu.c line 485
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+return NULL ; + +list_for_each_entry_safe (parent, tmp, &arm_smmu_devices, list) +if (parent->dev->of_node == smmu->parent_of_node) +return parent;Why do you need the
_safevariant here? You are not changing the list in this loop so you should be fine withlist_for_each_entry().
For a system with multiple SMMUs (regardless of chaining), couldn't this code run in parallel with probing of another SMMU (which has to add to the arm_smmu_devices list)? The same applies for device removal, which could perhaps be driven from some power-managment code.
Quoted 6 lines
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+ +dev_warn (smmu->dev, +"Failed to find SMMU parent despite parent in DT\n" ); +return NULL ; +}
Quoted 14 lines
drivers/iommu/arm-smmu.c line 539
Quoted 9 lines
drivers/iommu/arm-smmu.c new file +9
-0+/* Wait for any pending TLB invalidations to complete */ +static void arm_smmu_tlb_sync (struct arm_smmu_device *smmu) +{ +void __iomem *gr0_base =ARM_SMMU_GR0 (smmu); + +writel_relaxed (0 , gr0_base + ARM_SMMU_GR0_sTLBGSYNC); +while (readl_relaxed (gr0_base + ARM_SMMU_GR0_sTLBGSTATUS) + & sTLBGSTATUS_GSACTIVE) +cpu_relax ();Other IOMMU drivers have a timeout for this loop and report an error when the state does not change. I think this makes sense here too so that the kernel will not just stop spinning in that loop if something goes wrong but prints an error instead.
Good idea, I'll add that.
Quoted 9 lines
Quoted 9 lines
drivers/iommu/arm-smmu.c new file +9
-0+} +static void arm_smmu_flush_pgtable (struct arm_smmu_device *smmu,void *addr, +size_t size) +{ +unsigned long offset = (unsigned long )addr & ~PAGE_MASK ; + +/* +* If the SMMU can't walk tables in the CPU caches, treat them +* like non-coherent DMA...
Quoted 8 lines
drivers/iommu/arm-smmu.c line 1162
Quoted 4 lines
drivers/iommu/arm-smmu.c new file +4
-0+*/ +if (!(smmu->features &ARM_SMMU_FEAT_COHERENT_WALK )) +dma_map_page (smmu->dev,virt_to_page (addr), offset, size, +DMA_TO_DEVICE );Why can you call into DMA-API here? A DMA-API implementation may call back into this IOMMU driver, no? So this looks a little bit like a layering violation.
So this is subtle, and the comment could probably do with more explanation.
The problem is that the SMMU hardware page table walker might not be able to snoop the CPU caches. This means that after writing page table entries on the CPU, we have to flush them *all* the way out to main memory, so the SMMU can pick them up. There are only two other places I can think of where we have to do something like this in Linux:
(1) In the bowels of CPU suspend, where we may need to clean all of our caches prior to shutdown. This is all hidden away in low-level arch code.
(2) When dealing with non-coherent DMA, since we have to transfer buffer ownership between the CPU and the device. There is an API for this.
so, in fact, non-coherent DMA is a perfect fit for what we're trying to achieve with the SMMU page tables! I admit it looks odd, but it can't possibly go recursive because the master in question is a page-table walker, rather than anything backed by a struct device.
Quoted 16 lines
Quoted 16 lines
drivers/iommu/arm-smmu.c new file +15
-0+} +static int arm_smmu_map (struct iommu_domain *domain,unsigned long iova, +phys_addr_t paddr,size_t size,int flags) +{ +struct arm_smmu_domain *smmu_domain = domain->priv; +struct arm_smmu_device *smmu = smmu_domain->leaf_smmu; + +if (!smmu_domain || !smmu) +return -ENODEV ; + +/* +* Check for silent address truncation up the SMMU chain. +*/ + do { + phys_addr_t output_mask = (1ULL << smmu->s2_output_size) - 1;
Quoted 8 lines
drivers/iommu/arm-smmu.c line 1404
Quoted 3 lines
drivers/iommu/arm-smmu.c new file +3
-0+if ((phys_addr_t )iova & ~output_mask) +return -ERANGE ; + }while ((smmu =find_parent_smmu (smmu)));This looks a bit too expensive to have in the map path. How about saving something like an
effective_output_mask(oroutput_size) which contains the logical OR of every mask up the path? This would make this check a lot cheaper.
As mentioned in the DT binding thread, it's rare that this loop would execute more than once, and largely inconceivable that it would execute more than twice, so I don't know how much we need to worry about the cost.
Quoted 9 lines
Quoted 9 lines
drivers/iommu/arm-smmu.c new file +9
-0+ +return arm_smmu_create_mapping (smmu_domain, iova, paddr, size, flags); +} + +static size_t arm_smmu_unmap (struct iommu_domain *domain,unsigned long iova, +size_t size) +{ +int ret; +struct arm_smmu_domain *smmu_domain = domain->priv;
Quoted 8 lines
drivers/iommu/arm-smmu.c line 1418
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg; +struct arm_smmu_device *smmu = root_cfg->smmu; +void __iomem *gr0_base =ARM_SMMU_GR0 (smmu); + + ret =arm_smmu_create_mapping (smmu_domain, iova,0 , size,0 );Since this function does also unmapping, how about renaming it to
arm_smmu_handle_mapping(). The 'create' part in there is misleading.
Yep, that's a hangover from when I originally had separate functions. Will fix,
Quoted 6 lines
Quoted 6 lines
drivers/iommu/arm-smmu.c new file +6
-0+writel_relaxed (root_cfg->vmid, gr0_base +ARM_SMMU_GR0_TLBIVMID ); +arm_smmu_tlb_sync (smmu); +return ret ? ret : size; +} +static int arm_smmu_add_device (struct device *dev) +{
Quoted 8 lines
drivers/iommu/arm-smmu.c line 1485
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+struct arm_smmu_device *child, *parent, *smmu; +struct arm_smmu_device *tmp[2 ]; +struct arm_smmu_master *master =NULL ; + +list_for_each_entry_safe (parent, tmp[0 ], &arm_smmu_devices, list) {Again, why do you use the
_safevariant, you do not seem to change the lists traversed here.
Same reasoning as above. Happy to remove it if it's not actually required...
Cheers again,
Will
Joerg Roedel
maintainerPATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Quoted 3 lines · Will Deacon
The results were that the memory-to-memory DMA didn't show any corruption. I also managed to tickle access faults by messing around with the permissions, then remap the buffers and resume the transfers.
That sounds pretty conclusive. So when real hardware shows up it should work reasonably well.
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+static struct arm_smmu_device *find_parent_smmu (struct arm_smmu_device *smmu) +{ +struct arm_smmu_device *parent, *tmp; + +if (!smmu->parent_of_node)Quoted 8 lines
drivers/iommu/arm-smmu.c line 485
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+return NULL ; + +list_for_each_entry_safe (parent, tmp, &arm_smmu_devices, list) +if (parent->dev->of_node == smmu->parent_of_node) +return parent;Why do you need the
_safevariant here? You are not changing the list in this loop so you should be fine withlist_for_each_entry().
Quoted 4 lines
For a system with multiple SMMUs (regardless of chaining), couldn't this code run in parallel with probing of another SMMU (which has to add to the
arm_smmu_deviceslist)? The same applies for device removal, which could perhaps be driven from some power-managment code.
Well, the '_safe' does not mean it is safe from concurrent list manipulations. If you want to protect from that you still need a lock. The '_safe' variant only allows to remove the current element from the list while traversing it.
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm-smmu.c line 1404
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+do { +phys_addr_t output_mask = (1ULL << smmu->s2_output_size) -1 ; +if ((phys_addr_t )iova & ~output_mask) +return -ERANGE ; + }while ((smmu =find_parent_smmu (smmu)));This looks a bit too expensive to have in the map path. How about saving something like an
effective_output_mask(oroutput_size) which contains the logical OR of every mask up the path? This would make this check a lot cheaper.
Quoted 3 lines
As mentioned in the DT binding thread, it's rare that this loop would execute more than once, and largely inconceivable that it would execute more than twice, so I don't know how much we need to worry about the cost.
But still, this code is a challenge for the branch-predictor, plus the additional function calls to find_parent_smmu(). I still think it is worth to optimize this away. The map function is supposed to be a fast-path function.
Joerg
PATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Quoted 7 lines · Joerg Roedel
Quoted 3 lines · Will Deacon
The results were that the memory-to-memory DMA didn't show any corruption. I also managed to tickle access faults by messing around with the permissions, then remap the buffers and resume the transfers.
That sounds pretty conclusive. So when real hardware shows up it should work reasonably well.
Fingers crossed. Of course, as I get to run on real RTL I'll address any issues that might crop up.
Quoted 19 lines
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+static struct arm_smmu_device *find_parent_smmu (struct arm_smmu_device *smmu) +{ +struct arm_smmu_device *parent, *tmp; + +if (!smmu->parent_of_node)Quoted 8 lines
drivers/iommu/arm-smmu.c line 485
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+return NULL ; + +list_for_each_entry_safe (parent, tmp, &arm_smmu_devices, list) +if (parent->dev->of_node == smmu->parent_of_node) +return parent;Why do you need the
_safevariant here? You are not changing the list in this loop so you should be fine withlist_for_each_entry().Quoted 4 lines
For a system with multiple SMMUs (regardless of chaining), couldn't this code run in parallel with probing of another SMMU (which has to add to the
arm_smmu_deviceslist)? The same applies for device removal, which could perhaps be driven from some power-managment code.
Quoted 4 lines
Well, the '
_safe' does not mean it is safe from concurrent list manipulations. If you want to protect from that you still need a lock. The '_safe' variant only allows to remove the current element from the list while traversing it.
Damn, I was hoping to avoid locking on the map path. In fact, this is a good argument to go with your suggestion below (otherwise I'd need to use reader/writer locks which seem to be frowned on).
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm-smmu.c line 1404
Quoted 5 lines
drivers/iommu/arm-smmu.c new file +5
-0+do { +phys_addr_t output_mask = (1ULL << smmu->s2_output_size) -1 ; +if ((phys_addr_t )iova & ~output_mask) +return -ERANGE ; + }while ((smmu =find_parent_smmu (smmu)));This looks a bit too expensive to have in the map path. How about saving something like an
effective_output_mask(oroutput_size) which contains the logical OR of every mask up the path? This would make this check a lot cheaper.
Quoted 8 lines
Quoted 3 lines
As mentioned in the DT binding thread, it's rare that this loop would execute more than once, and largely inconceivable that it would execute more than twice, so I don't know how much we need to worry about the cost.
But still, this code is a challenge for the branch-predictor, plus the additional function calls to
find_parent_smmu(). I still think it is worth to optimize this away. The map function is supposed to be a fast-path function.
Yes, the locking I'd need to introduce in find_parent_smmu seals the deal. I'll have a go at constructing the mask statically for each SMMU (although I think I want to use logical AND rather than OR).
Cheers,
Will
Joerg Roedel
maintainerPATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Quoted 3 lines · Will Deacon
Damn, I was hoping to avoid locking on the map path. In fact, this is a good argument to go with your suggestion below (otherwise I'd need to use reader/writer locks which seem to be frowned on).
You should look into using rcu-lists instead. You still need a lock, but only when you actually manipulating the lists. For traversing them it is sufficient to take the rcu_read_lock() which has very low overhead.
Joerg
PATCH 8/9 iommu: add support for ARM Ltd. System MMU architecture
Quoted 8 lines · Joerg Roedel
Quoted 3 lines · Will Deacon
Damn, I was hoping to avoid locking on the map path. In fact, this is a good argument to go with your suggestion below (otherwise I'd need to use reader/writer locks which seem to be frowned on).
You should look into using rcu-lists instead. You still need a lock, but only when you actually manipulating the lists. For traversing them it is sufficient to take the
rcu_read_lock()which has very low overhead.
Indeed, I'd got RCU-protected lists mixed up with the _safe iterators. However, if I construct a compound output_mask as you suggest, all the locking is on the slow path (add/attach of devices) so it's no longer an issue.
I'll post a v2 next week.
Will
PATCH 9/9 MAINTAINERS: add entry for ARM system MMU driver
In reply to Will DeaconAdd myself as maintainer for the ARM system MMU driver.
--- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
MAINTAINERS +6 -0
@@ -1310,6 +1310,12 @@ T: git git://git.xilinx.com/linux-xlnx.git S: Supported F: arch/arm/mach-zynq/ +ARM SMMU DRIVER +M: Will Deacon <[email protected]> +L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/iommu/arm-smmu.c + ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas <[email protected]> M: Will Deacon <[email protected]>
Signature · 1 line
1.8.2.2
PATCH 9/9 MAINTAINERS: add entry for ARM system MMU driver
Quoted 3 lines · Will Deacon
Add myself as maintainer for the ARM system MMU driver.
Signed-off-by Will Deacon <[email protected]>
Quoted 29 lines
--- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)MAINTAINERS +6
-0@@ -1310,6 +1310,12 @@ T: git git://git.xilinx.com/linux-xlnx.git S: Supported F: arch/arm/mach-zynq/ +ARM SMMU DRIVER +M: Will Deacon <[email protected]> +L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/iommu/arm-smmu.c + ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas <[email protected]> M: Will Deacon <[email protected]>Signature · 7 lines
1.8.2.2 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 2 lines
Grant Likely, B.Sc, P.Eng. Secret Lab Technologies, Ltd.