From: Alexandre MINETTE <[email protected]> MSM IOMMU stores context descriptors on each IOMMU instance. Looking up the descriptor through dev_iommu_priv_get() is not sufficient because a device can reference multiple IOMMU provider nodes.
Look up the master from the target IOMMU ctx_list instead, and use the same helper when probing and attaching devices. This avoids dereferencing a NULL master when an IOMMU already has context entries for another device, and keeps separate context descriptors for separate IOMMU instances. On APQ8064 this crashes during IOMMU probe while qcom_iommu_of_xlate() adds stream IDs for a device referencing multiple IOMMU provider nodes. The failure comes from insert_iommu_master() dereferencing a NULL master after dev_iommu_priv_get() returns no context descriptor for the current IOMMU instance: Unable to handle kernel NULL pointer dereference at virtual address 00000088 PC is at qcom_iommu_of_xlate+0x84/0x174 Call trace: qcom_iommu_of_xlate from of_iommu_configure+0x140/0x234 of_iommu_configure from of_dma_configure_id+0xec/0x3b0 of_dma_configure_id from platform_dma_configure+0xb0/0xcc platform_dma_configure from __iommu_probe_device+0x270/0x450 __iommu_probe_device from probe_iommu_group+0x24/0x48 probe_iommu_group from bus_for_each_dev+0x7c/0xcc bus_for_each_dev from iommu_device_register+0xcc/0x220 iommu_device_register from msm_iommu_probe+0x47c/0x578 This is required to boot APQ8064 boards using multiple IOMMU contexts. Signed-off-by: Alexandre MINETTE <[email protected]> --- drivers/iommu/msm_iommu.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 0ad5ff431d5b..9c03878d0d2c 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -357,17 +357,25 @@ static int msm_iommu_domain_config(struct msm_priv *priv) return 0; } +static struct msm_iommu_ctx_dev *find_iommu_master(struct msm_iommu_dev *iommu, + struct device *dev) +{ + struct msm_iommu_ctx_dev *master; + + list_for_each_entry(master, &iommu->ctx_list, list) + if (master->of_node == dev->of_node) + return master; + + return NULL; +} + /* Must be called under msm_iommu_lock */ static struct msm_iommu_dev *find_iommu_for_dev(struct device *dev) { struct msm_iommu_dev *iommu, *ret = NULL; - struct msm_iommu_ctx_dev *master; list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { - master = list_first_entry(&iommu->ctx_list, - struct msm_iommu_ctx_dev, - list); - if (master->of_node == dev->of_node) { + if (find_iommu_master(iommu, dev)) { ret = iommu; break; } @@ -405,10 +413,7 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev, spin_lock_irqsave(&msm_iommu_lock, flags); list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { - master = list_first_entry(&iommu->ctx_list, - struct msm_iommu_ctx_dev, - list); - if (master->of_node == dev->of_node) { + if (find_iommu_master(iommu, dev)) { ret = __enable_clocks(iommu); if (ret) goto fail; @@ -601,10 +606,10 @@ static int insert_iommu_master(struct device *dev, struct msm_iommu_dev **iommu, const struct of_phandle_args *spec) { - struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev); + struct msm_iommu_ctx_dev *master = find_iommu_master(*iommu, dev); int sid; - if (list_empty(&(*iommu)->ctx_list)) { + if (!master) { master = kzalloc_obj(*master, GFP_ATOMIC); if (!master) { dev_err(dev, "Failed to allocate iommu_master\n"); @@ -612,7 +617,6 @@ static int insert_iommu_master(struct device *dev, } master->of_node = dev->of_node; list_add(&master->list, &(*iommu)->ctx_list); - dev_iommu_priv_set(dev, master); } for (sid = 0; sid < master->num_mids; sid++) -- 2.43.0

