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



Reply via email to