From 9345fe000f7de69d46e05edeac502284b8fd138f Mon Sep 17 00:00:00 2001 From: Anandu Krishnan E Date: Wed, 1 Jul 2026 10:12:21 +0530 Subject: [PATCH] FROMLIST: misc: fastrpc: map ADSP remote heap into remoteproc IOMMU domain When the remoteproc has an IOMMU (kernel running at EL2 without a separate hypervisor), memory carveouts must be explicitly mapped into the remoteproc's IOMMU domain so the DSP can access them. Without this mapping the DSP triggers an SMMU translation fault when accessing the remote heap carveout used for audio PD static process creation. Add has_iommu to fastrpc_channel_ctx, set from the "iommus" property of the remoteproc DT node. When set, map the ADSP remote heap carveout into the remoteproc's IOMMU domain using an identity mapping (IOVA == PA) via iommu_map(), and skip qcom_scm_assign_mem() which is only needed when a separate hypervisor manages inter-VM memory access control. Introduce fastrpc_remote_heap_map() and fastrpc_remote_heap_unmap() helpers to encapsulate the IOMMU domain lookup and map/unmap. Link: https://lore.kernel.org/all/20260618-audio_fix_clean_v3-v1-1-ec1ee66fe455@oss.qualcomm.com/ Signed-off-by: Anandu Krishnan E --- drivers/misc/fastrpc.c | 142 +++++++++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 28 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index d110b69454eef..ae3b810032753 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -310,6 +311,8 @@ struct fastrpc_channel_ctx { struct list_head invoke_interrupted_mmaps; bool secure; bool unsigned_support; + /* set when remoteproc has an IOMMU; use iommu_map instead of hyp_assign */ + bool has_iommu; bool poll_mode_supported; u64 dma_mask; const struct fastrpc_soc_data *soc_data; @@ -2537,10 +2540,65 @@ static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unuse {}, }; +static int fastrpc_remote_heap_map(struct device *rdev, + struct device_node *rproc_node, + struct fastrpc_buf *heap) +{ + struct platform_device *rproc_pdev; + struct iommu_domain *domain; + int ret; + + rproc_pdev = of_find_device_by_node(rproc_node); + if (!rproc_pdev) { + dev_err(rdev, "failed to find remoteproc platform device\n"); + return -ENODEV; + } + + domain = iommu_get_domain_for_dev(&rproc_pdev->dev); + if (!domain) { + put_device(&rproc_pdev->dev); + dev_err(rdev, "no IOMMU domain for remoteproc\n"); + return -ENODEV; + } + + ret = iommu_map(domain, heap->dma_addr, heap->dma_addr, heap->size, + IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); + if (ret) + dev_err(rdev, "failed to map remote heap phys=0x%llx size=0x%llx err=%d\n", + heap->dma_addr, heap->size, ret); + + put_device(&rproc_pdev->dev); + return ret; +} + +static void fastrpc_remote_heap_unmap(struct rpmsg_device *rpdev, + struct fastrpc_buf *heap) +{ + struct device_node *rproc_node; + struct platform_device *rproc_pdev; + struct iommu_domain *domain; + + rproc_node = of_get_parent(of_get_parent(rpdev->dev.of_node)); + if (!rproc_node) + return; + + rproc_pdev = of_find_device_by_node(rproc_node); + of_node_put(rproc_node); + if (!rproc_pdev) + return; + + domain = iommu_get_domain_for_dev(&rproc_pdev->dev); + if (domain) + iommu_unmap(domain, heap->dma_addr, heap->size); + + put_device(&rproc_pdev->dev); +} + static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *rdev = &rpdev->dev; struct fastrpc_channel_ctx *data; + struct device_node *rproc_node; int i, err, domain_id = -1, vmcount; const char *domain; bool secure_dsp; @@ -2584,9 +2642,12 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } } + rproc_node = of_get_parent(of_get_parent(rdev->of_node)); + if (rproc_node) + data->has_iommu = of_property_present(rproc_node, "iommus"); + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) { struct resource res; - u64 src_perms; err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res); if (!err) { @@ -2595,21 +2656,41 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) kzalloc_obj(*data->remote_heap, GFP_KERNEL); if (!data->remote_heap) { err = -ENOMEM; - goto err_free_data; + goto err_put_node; } data->remote_heap->dma_addr = res.start; data->remote_heap->size = resource_size(&res); + + if (data->has_iommu) { + err = fastrpc_remote_heap_map(rdev, + rproc_node, + data->remote_heap); + if (err) { + kfree(data->remote_heap); + data->remote_heap = NULL; + goto err_put_node; + } + } } - src_perms = BIT(QCOM_SCM_VMID_HLOS); - err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, - data->vmperms, data->vmcount); - if (err) - goto err_free_data; + if (!data->has_iommu) { + u64 src_perms = BIT(QCOM_SCM_VMID_HLOS); + + err = qcom_scm_assign_mem(res.start, + resource_size(&res), + &src_perms, + data->vmperms, + data->vmcount); + if (err) + goto err_put_node; + } } } + of_node_put(rproc_node); + rproc_node = NULL; + secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); data->secure = secure_dsp; data->soc_data = soc_data; @@ -2667,8 +2748,10 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) if (data->secure_fdevice) misc_deregister(&data->secure_fdevice->miscdev); +err_put_node: + of_node_put(rproc_node); + err_free_data: - kfree(data->remote_heap); kfree(data); return err; } @@ -2709,28 +2792,31 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node) list_del(&buf->node); - if (cctx->remote_heap && cctx->vmcount) { - u64 src_perms = 0; - struct qcom_scm_vmperm dst_perms; - - for (u32 i = 0; i < cctx->vmcount; i++) - src_perms |= BIT(cctx->vmperms[i].vmid); - - dst_perms.vmid = QCOM_SCM_VMID_HLOS; - dst_perms.perm = QCOM_SCM_PERM_RWX; - - err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr, - cctx->remote_heap->size, &src_perms, - &dst_perms, 1); - if (err) - dev_err(&rpdev->dev, - "Failed to assign memory back to HLOS: dma_addr %pad size %#llx err %d\n", - &cctx->remote_heap->dma_addr, cctx->remote_heap->size, err); + if (cctx->remote_heap) { + if (cctx->has_iommu) { + fastrpc_remote_heap_unmap(rpdev, cctx->remote_heap); + kfree(cctx->remote_heap); + cctx->remote_heap = NULL; + } else if (cctx->vmcount) { + u64 src_perms = 0; + struct qcom_scm_vmperm dst_perms; + + for (u32 i = 0; i < cctx->vmcount; i++) + src_perms |= BIT(cctx->vmperms[i].vmid); + + dst_perms.vmid = QCOM_SCM_VMID_HLOS; + dst_perms.perm = QCOM_SCM_PERM_RWX; + + err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr, + cctx->remote_heap->size, + &src_perms, &dst_perms, 1); + if (!err) { + kfree(cctx->remote_heap); + cctx->remote_heap = NULL; + } + } } - kfree(cctx->remote_heap); - cctx->remote_heap = NULL; - of_platform_depopulate(&rpdev->dev); fastrpc_channel_ctx_put(cctx);