iommu/arm-smmu: current updates for 4.1
Subsystems (2)
cover iommu/arm-smmu: current updates for 4.1
Hi all,
I only have a small handful of arm-smmu patches on the radar for 4.1, so here they are in case anybody has any comments.
Cheers,
Will
--->8
Robin Murphy (1):
iommu/arm-smmu: set a more appropriate DMA mask
Will Deacon (3):
iommu/arm-smmu: ensure CBA2R is initialised before CBAR on SMMUv2 iommu/arm-smmu: handle multi-alias IOMMU groups for PCI devices iommu/io-pgtable-arm: avoid speculative walks through TTBR1
drivers/iommu/arm-smmu.c | 125 ++++++++++++++++++++++++++--------------- drivers/iommu/io-pgtable-arm.c | 5 ++ 2 files changed, 85 insertions(+), 45 deletions(-)
Signature · 1 line
2.1.4
PATCH 1/4 iommu/arm-smmu: ensure CBA2R is initialised before CBAR on SMMUv2
The VMID16 (8.1) extension to SMMUv2 added a 16-bit VMID16 field to the CBA2R registers. Unfortunately, if software writes this field as zero after setting an 8-bit VMID in a stage-2 CBAR, then the VMID may also be overwritten with zero on some early implementations (the architecture was later updated to fix this issue).
This patch ensures that we initialise CBA2R before CBAR, therefore ensuring that the VMID is set correctly.
--- drivers/iommu/arm-smmu.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
drivers/iommu/arm-smmu.c +14 -10
@@ -730,6 +730,20 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, stage1 = cfg->cbar !=CBAR_TYPE_S2_TRANS ; cb_base =ARM_SMMU_CB_BASE (smmu) +ARM_SMMU_CB (smmu, cfg->cbndx); +if (smmu->version >ARM_SMMU_V1 ) { +/* +* CBA2R. +* *Must* be initialised before CBAR thanks to VMID16 +* architectural oversight affected some implementations. +*/ +#ifdef CONFIG_64BIT + reg =CBA2R_RW64_64BIT ; +#else + reg =CBA2R_RW64_32BIT ; +#endif +writel_relaxed (reg, gr1_base +ARM_SMMU_GR1_CBA2R (cfg->cbndx)); + } +/* CBAR */ reg = cfg->cbar;if (smmu->version ==ARM_SMMU_V1 )
@@ -747,16 +761,6 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, }writel_relaxed (reg, gr1_base +ARM_SMMU_GR1_CBAR (cfg->cbndx)); -if (smmu->version >ARM_SMMU_V1 ) { -/* CBA2R */ -#ifdef CONFIG_64BIT - reg =CBA2R_RW64_64BIT ; -#else - reg =CBA2R_RW64_32BIT ; -#endif -writel_relaxed (reg, gr1_base +ARM_SMMU_GR1_CBA2R (cfg->cbndx)); - } -/* TTBRs */ if (stage1) { reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0 ];
Signature · 1 line
2.1.4
PATCH 2/4 iommu/arm-smmu: set a more appropriate DMA mask
In reply to Will DeaconFrom: Robin Murphy <[email protected]>
Since we use dma_map_page() as an architecture-independent means of making page table updates visible to non-coherent SMMUs, we need to have a suitable DMA mask set to discourage the DMA mapping layer from creating bounce buffers and flushing those instead, if said page tables happen to lie outside the default 32-bit mask.
--- drivers/iommu/arm-smmu.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/iommu/arm-smmu.c +9 -0
@@ -1634,6 +1634,15 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) size =arm_smmu_id_size_to_bits ((id >>ID2_OAS_SHIFT ) &ID2_OAS_MASK ); smmu->pa_size = size; +/* +* What the page table walker can address actually depends on which +* descriptor format is in use, but since a) we don't know that yet, +* and b) it can vary per context bank, this will have to do... +*/ +if (dma_set_mask_and_coherent (smmu->dev,DMA_BIT_MASK (size))) +dev_warn (smmu->dev, +"failed to set DMA mask for table walker\n" ); +if (smmu->version ==ARM_SMMU_V1 ) { smmu->va_size = smmu->ipa_size; size =SZ_4K |SZ_2M |SZ_1G ;
Signature · 1 line
2.1.4
PATCH 3/4 iommu/arm-smmu: handle multi-alias IOMMU groups for PCI devices
In reply to Will DeaconIOMMU groups for PCI devices can correspond to multiple DMA aliases due to things like ACS and PCI quirks.
This patch extends the ARM SMMU ->add_device callback so that we consider all of the DMA aliases for a PCI IOMMU group, rather than creating a separate group for each Requester ID.
--- drivers/iommu/arm-smmu.c | 92 ++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 35 deletions(-)
drivers/iommu/arm-smmu.c +57 -35
@@ -1330,61 +1330,83 @@ static void __arm_smmu_release_pci_iommudata(void *data)kfree (data); } -static int arm_smmu_add_device (struct device *dev) +static int arm_smmu_add_pci_device (struct pci_dev *pdev) { -struct arm_smmu_device *smmu; -struct arm_smmu_master_cfg *cfg; +int i, ret; +u16 sid;struct iommu_group *group; -void (*releasefn)(void *) =NULL ; -int ret; - - smmu =find_smmu_for_device (dev); -if (!smmu) -return -ENODEV ; +struct arm_smmu_master_cfg *cfg; - group =iommu_group_alloc (); -if (IS_ERR (group)) { -dev_err (dev,"Failed to allocate IOMMU group\n" ); + group =iommu_group_get_for_dev (&pdev->dev); +if (IS_ERR (group))return PTR_ERR (group); - } - -if (dev_is_pci (dev)) { -struct pci_dev *pdev =to_pci_dev (dev); + cfg =iommu_group_get_iommudata (group); +if (!cfg) { cfg =kzalloc (sizeof (*cfg),GFP_KERNEL );if (!cfg) { ret = -ENOMEM ; goto out_put_group; } - cfg->num_streamids =1 ; -/* -* Assume Stream ID == Requester ID for now. -* We need a way to describe the ID mappings in FDT. -*/ -pci_for_each_dma_alias (pdev, __arm_smmu_get_pci_sid, - &cfg->streamids[0 ]); - releasefn = __arm_smmu_release_pci_iommudata; - }else { -struct arm_smmu_master *master; - - master =find_smmu_master (smmu, dev->of_node); -if (!master) { - ret = -ENODEV ; - goto out_put_group; - } +iommu_group_set_iommudata (group, cfg, + __arm_smmu_release_pci_iommudata); + } - cfg = &master->cfg; +if (cfg->num_streamids >=MAX_MASTER_STREAMIDS ) { + ret = -ENOSPC ; + goto out_put_group; } -iommu_group_set_iommudata (group, cfg, releasefn); - ret =iommu_group_add_device (group, dev); +/* +* Assume Stream ID == Requester ID for now. +* We need a way to describe the ID mappings in FDT. +*/ +pci_for_each_dma_alias (pdev, __arm_smmu_get_pci_sid, &sid); +for (i =0 ; i < cfg->num_streamids; ++i) +if (cfg->streamids[i] == sid) +break ; + +/* Avoid duplicate SIDs, as this can lead to SMR conflicts */ +if (i == cfg->num_streamids) + cfg->streamids[cfg->num_streamids++] = sid; +return 0 ; out_put_group:iommu_group_put (group);return ret; } +static int arm_smmu_add_platform_device (struct device *dev) +{ +struct iommu_group *group; +struct arm_smmu_master *master; +struct arm_smmu_device *smmu =find_smmu_for_device (dev); + +if (!smmu) +return -ENODEV ; + + master =find_smmu_master (smmu, dev->of_node); +if (!master) +return -ENODEV ; + +/* No automatic group creation for platform devices */ + group =iommu_group_alloc (); +if (IS_ERR (group)) +return PTR_ERR (group); + +iommu_group_set_iommudata (group, &master->cfg,NULL ); +return iommu_group_add_device (group, dev); +} + +static int arm_smmu_add_device (struct device *dev) +{ +if (dev_is_pci (dev)) +return arm_smmu_add_pci_device (to_pci_dev (dev)); + +return arm_smmu_add_platform_device (dev); +} +static void arm_smmu_remove_device (struct device *dev) {iommu_group_remove_device (dev);
Signature · 1 line
2.1.4
PATCH 4/4 iommu/io-pgtable-arm: avoid speculative walks through TTBR1
In reply to Will DeaconAlthough we set TCR.T1SZ to 0, the input address range covered by TTBR1 is actually calculated using T0SZ in this case on the ARM SMMU. This could theoretically lead to speculative table walks through physical address zero, leading to all sorts of fun and games if we have MMIO regions down there.
This patch avoids the issue by setting EPD1 to disable walks through the unused TTBR1 register.
--- drivers/iommu/io-pgtable-arm.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/iommu/io-pgtable-arm.c +5 -0
@@ -116,6 +116,8 @@#define ARM_32_LPAE_TCR_EAE (1 << 31)#define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) +#define ARM_LPAE_TCR_EPD1 (1 << 23) +#define ARM_LPAE_TCR_TG0_4K (0 << 14)#define ARM_LPAE_TCR_TG0_64K (1 << 14)#define ARM_LPAE_TCR_TG0_16K (2 << 14)
@@ -621,6 +623,9 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) } reg |= (64ULL - cfg->ias) <<ARM_LPAE_TCR_T0SZ_SHIFT ; + +/* Disable speculative walks through TTBR1 */ + reg |=ARM_LPAE_TCR_EPD1 ; cfg->arm_lpae_s1_cfg.tcr = reg;/* MAIRs */
Signature · 1 line
2.1.4