Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 114 additions & 28 deletions drivers/misc/fastrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
#include <linux/iommu.h>
#include <linux/of_reserved_mem.h>
#include <linux/bits.h>
#include <linux/bitops.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down