summaryrefslogtreecommitdiff
path: root/drivers/misc/fastrpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/fastrpc.c')
-rw-r--r--drivers/misc/fastrpc.c331
1 files changed, 263 insertions, 68 deletions
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 0f467a71b069..5310606113fe 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
+#include <linux/of_reserved_mem.h>
#define ADSP_DOMAIN_ID (0)
#define MDSP_DOMAIN_ID (1)
@@ -37,8 +38,20 @@
#define FASTRPC_DSP_UTILITIES_HANDLE 2
#define FASTRPC_CTXID_MASK (0xFF0)
#define INIT_FILELEN_MAX (2 * 1024 * 1024)
+#define INIT_FILE_NAMELEN_MAX (128)
#define FASTRPC_DEVICE_NAME "fastrpc"
+
+/* Add memory to static PD pool, protection thru XPU */
+#define ADSP_MMAP_HEAP_ADDR 4
+/* MAP static DMA buffer on DSP User PD */
+#define ADSP_MMAP_DMA_BUFFER 6
+/* Add memory to static PD pool protection thru hypervisor */
+#define ADSP_MMAP_REMOTE_HEAP_ADDR 8
+/* Add memory to userPD pool, for user heap */
#define ADSP_MMAP_ADD_PAGES 0x1000
+/* Add memory to userPD pool, for LLC heap */
+#define ADSP_MMAP_ADD_PAGES_LLC 0x3000,
+
#define DSP_UNSUPPORTED_API (0x80000414)
/* MAX NUMBER of DSP ATTRIBUTES SUPPORTED */
#define FASTRPC_MAX_DSP_ATTRIBUTES (256)
@@ -72,6 +85,7 @@
FASTRPC_BUILD_SCALARS(0, method, in, out, 0, 0)
#define FASTRPC_CREATE_PROCESS_NARGS 6
+#define FASTRPC_CREATE_STATIC_PROCESS_NARGS 3
/* Remote Method id table */
#define FASTRPC_RMID_INIT_ATTACH 0
#define FASTRPC_RMID_INIT_RELEASE 1
@@ -84,7 +98,7 @@
#define FASTRPC_RMID_INIT_MEM_UNMAP 11
/* Protection Domain(PD) ids */
-#define AUDIO_PD (0) /* also GUEST_OS PD? */
+#define ROOT_PD (0)
#define USER_PD (1)
#define SENSORS_PD (2)
@@ -261,8 +275,11 @@ struct fastrpc_channel_ctx {
u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
struct fastrpc_device *secure_fdevice;
struct fastrpc_device *fdevice;
+ struct fastrpc_buf *remote_heap;
+ struct list_head invoke_interrupted_mmaps;
bool secure;
bool unsigned_support;
+ u64 dma_mask;
};
struct fastrpc_device {
@@ -304,7 +321,7 @@ static void fastrpc_free_map(struct kref *ref)
perm.vmid = QCOM_SCM_VMID_HLOS;
perm.perm = QCOM_SCM_PERM_RWX;
err = qcom_scm_assign_mem(map->phys, map->size,
- &(map->fl->cctx->vmperms[0].vmid), &perm, 1);
+ &map->fl->cctx->perms, &perm, 1);
if (err) {
dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
map->phys, map->size, err);
@@ -317,6 +334,13 @@ static void fastrpc_free_map(struct kref *ref)
dma_buf_put(map->buf);
}
+ if (map->fl) {
+ spin_lock(&map->fl->lock);
+ list_del(&map->node);
+ spin_unlock(&map->fl->lock);
+ map->fl = NULL;
+ }
+
kfree(map);
}
@@ -326,38 +350,41 @@ static void fastrpc_map_put(struct fastrpc_map *map)
kref_put(&map->refcount, fastrpc_free_map);
}
-static void fastrpc_map_get(struct fastrpc_map *map)
+static int fastrpc_map_get(struct fastrpc_map *map)
{
- if (map)
- kref_get(&map->refcount);
+ if (!map)
+ return -ENOENT;
+
+ return kref_get_unless_zero(&map->refcount) ? 0 : -ENOENT;
}
static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
- struct fastrpc_map **ppmap)
+ struct fastrpc_map **ppmap, bool take_ref)
{
+ struct fastrpc_session_ctx *sess = fl->sctx;
struct fastrpc_map *map = NULL;
+ int ret = -ENOENT;
- mutex_lock(&fl->mutex);
+ spin_lock(&fl->lock);
list_for_each_entry(map, &fl->maps, node) {
- if (map->fd == fd) {
- *ppmap = map;
- mutex_unlock(&fl->mutex);
- return 0;
- }
- }
- mutex_unlock(&fl->mutex);
-
- return -ENOENT;
-}
+ if (map->fd != fd)
+ continue;
-static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
- struct fastrpc_map **ppmap)
-{
- int ret = fastrpc_map_lookup(fl, fd, ppmap);
+ if (take_ref) {
+ ret = fastrpc_map_get(map);
+ if (ret) {
+ dev_dbg(sess->dev, "%s: Failed to get map fd=%d ret=%d\n",
+ __func__, fd, ret);
+ break;
+ }
+ }
- if (!ret)
- fastrpc_map_get(*ppmap);
+ *ppmap = map;
+ ret = 0;
+ break;
+ }
+ spin_unlock(&fl->lock);
return ret;
}
@@ -369,7 +396,7 @@ static void fastrpc_buf_free(struct fastrpc_buf *buf)
kfree(buf);
}
-static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
+static int __fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
u64 size, struct fastrpc_buf **obuf)
{
struct fastrpc_buf *buf;
@@ -397,14 +424,37 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
return -ENOMEM;
}
+ *obuf = buf;
+
+ return 0;
+}
+
+static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
+ u64 size, struct fastrpc_buf **obuf)
+{
+ int ret;
+ struct fastrpc_buf *buf;
+
+ ret = __fastrpc_buf_alloc(fl, dev, size, obuf);
+ if (ret)
+ return ret;
+
+ buf = *obuf;
+
if (fl->sctx && fl->sctx->sid)
buf->phys += ((u64)fl->sctx->sid << 32);
- *obuf = buf;
-
return 0;
}
+static int fastrpc_remote_heap_alloc(struct fastrpc_user *fl, struct device *dev,
+ u64 size, struct fastrpc_buf **obuf)
+{
+ struct device *rdev = &fl->cctx->rpdev->dev;
+
+ return __fastrpc_buf_alloc(fl, rdev, size, obuf);
+}
+
static void fastrpc_channel_ctx_free(struct kref *ref)
{
struct fastrpc_channel_ctx *cctx;
@@ -706,7 +756,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
struct fastrpc_map *map = NULL;
int err = 0;
- if (!fastrpc_map_find(fl, fd, ppmap))
+ if (!fastrpc_map_lookup(fl, fd, ppmap, true))
return 0;
map = kzalloc(sizeof(*map), GFP_KERNEL);
@@ -714,6 +764,8 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
return -ENOMEM;
INIT_LIST_HEAD(&map->node);
+ kref_init(&map->refcount);
+
map->fl = fl;
map->fd = fd;
map->buf = dma_buf_get(fd);
@@ -740,17 +792,14 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
map->size = len;
map->va = sg_virt(map->table->sgl);
map->len = len;
- kref_init(&map->refcount);
if (attr & FASTRPC_ATTR_SECUREMAP) {
/*
* If subsystem VMIDs are defined in DTSI, then do
* hyp_assign from HLOS to those VM(s)
*/
- unsigned int perms = BIT(QCOM_SCM_VMID_HLOS);
-
map->attr = attr;
- err = qcom_scm_assign_mem(map->phys, (u64)map->size, &perms,
+ err = qcom_scm_assign_mem(map->phys, (u64)map->size, &fl->cctx->perms,
fl->cctx->vmperms, fl->cctx->vmcount);
if (err) {
dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d",
@@ -770,7 +819,7 @@ map_err:
attach_err:
dma_buf_put(map->buf);
get_err:
- kfree(map);
+ fastrpc_map_put(map);
return err;
}
@@ -1029,7 +1078,7 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
for (i = 0; i < FASTRPC_MAX_FDLIST; i++) {
if (!fdlist[i])
break;
- if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap))
+ if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false))
fastrpc_map_put(mmap);
}
@@ -1073,6 +1122,8 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
struct fastrpc_invoke_args *args)
{
struct fastrpc_invoke_ctx *ctx = NULL;
+ struct fastrpc_buf *buf, *b;
+
int err = 0;
if (!fl->sctx)
@@ -1135,6 +1186,14 @@ bail:
spin_unlock(&fl->lock);
fastrpc_context_put(ctx);
}
+
+ if (err == -ERESTARTSYS) {
+ list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
+ list_del(&buf->node);
+ list_add_tail(&buf->node, &fl->cctx->invoke_interrupted_mmaps);
+ }
+ }
+
if (err)
dev_dbg(fl->sctx->dev, "Error: Invoke Failed %d\n", err);
@@ -1159,6 +1218,119 @@ static bool is_session_rejected(struct fastrpc_user *fl, bool unsigned_pd_reques
return false;
}
+static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
+ char __user *argp)
+{
+ struct fastrpc_init_create_static init;
+ struct fastrpc_invoke_args *args;
+ struct fastrpc_phy_page pages[1];
+ char *name;
+ int err;
+ struct {
+ int pgid;
+ u32 namelen;
+ u32 pageslen;
+ } inbuf;
+ u32 sc;
+
+ args = kcalloc(FASTRPC_CREATE_STATIC_PROCESS_NARGS, sizeof(*args), GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ if (copy_from_user(&init, argp, sizeof(init))) {
+ err = -EFAULT;
+ goto err;
+ }
+
+ if (init.namelen > INIT_FILE_NAMELEN_MAX) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ name = kzalloc(init.namelen, GFP_KERNEL);
+ if (!name) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ if (copy_from_user(name, (void __user *)(uintptr_t)init.name, init.namelen)) {
+ err = -EFAULT;
+ goto err_name;
+ }
+
+ if (!fl->cctx->remote_heap) {
+ err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
+ &fl->cctx->remote_heap);
+ if (err)
+ goto err_name;
+
+ /* Map if we have any heap VMIDs associated with this ADSP Static Process. */
+ if (fl->cctx->vmcount) {
+ err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
+ (u64)fl->cctx->remote_heap->size,
+ &fl->cctx->perms,
+ fl->cctx->vmperms, fl->cctx->vmcount);
+ if (err) {
+ dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d",
+ fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err);
+ goto err_map;
+ }
+ }
+ }
+
+ inbuf.pgid = fl->tgid;
+ inbuf.namelen = init.namelen;
+ inbuf.pageslen = 0;
+ fl->pd = USER_PD;
+
+ args[0].ptr = (u64)(uintptr_t)&inbuf;
+ args[0].length = sizeof(inbuf);
+ args[0].fd = -1;
+
+ args[1].ptr = (u64)(uintptr_t)name;
+ args[1].length = inbuf.namelen;
+ args[1].fd = -1;
+
+ pages[0].addr = fl->cctx->remote_heap->phys;
+ pages[0].size = fl->cctx->remote_heap->size;
+
+ args[2].ptr = (u64)(uintptr_t) pages;
+ args[2].length = sizeof(*pages);
+ args[2].fd = -1;
+
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE_STATIC, 3, 0);
+
+ err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE,
+ sc, args);
+ if (err)
+ goto err_invoke;
+
+ kfree(args);
+
+ return 0;
+err_invoke:
+ if (fl->cctx->vmcount) {
+ struct qcom_scm_vmperm perm;
+
+ perm.vmid = QCOM_SCM_VMID_HLOS;
+ perm.perm = QCOM_SCM_PERM_RWX;
+ err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
+ (u64)fl->cctx->remote_heap->size,
+ &fl->cctx->perms, &perm, 1);
+ if (err)
+ dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
+ fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err);
+ }
+err_map:
+ fastrpc_buf_free(fl->cctx->remote_heap);
+err_name:
+ kfree(name);
+err:
+ kfree(args);
+
+ return err;
+}
+
static int fastrpc_init_create_process(struct fastrpc_user *fl,
char __user *argp)
{
@@ -1268,12 +1440,7 @@ err_invoke:
fl->init_mem = NULL;
fastrpc_buf_free(imem);
err_alloc:
- if (map) {
- spin_lock(&fl->lock);
- list_del(&map->node);
- spin_unlock(&fl->lock);
- fastrpc_map_put(map);
- }
+ fastrpc_map_put(map);
err:
kfree(args);
@@ -1349,10 +1516,8 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
fastrpc_context_put(ctx);
}
- list_for_each_entry_safe(map, m, &fl->maps, node) {
- list_del(&map->node);
+ list_for_each_entry_safe(map, m, &fl->maps, node)
fastrpc_map_put(map);
- }
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
@@ -1605,30 +1770,14 @@ static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp)
return 0;
}
-static int fastrpc_req_munmap_impl(struct fastrpc_user *fl,
- struct fastrpc_req_munmap *req)
+static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf *buf)
{
struct fastrpc_invoke_args args[1] = { [0] = { 0 } };
- struct fastrpc_buf *buf = NULL, *iter, *b;
struct fastrpc_munmap_req_msg req_msg;
struct device *dev = fl->sctx->dev;
int err;
u32 sc;
- spin_lock(&fl->lock);
- list_for_each_entry_safe(iter, b, &fl->mmaps, node) {
- if ((iter->raddr == req->vaddrout) && (iter->size == req->size)) {
- buf = iter;
- break;
- }
- }
- spin_unlock(&fl->lock);
-
- if (!buf) {
- dev_err(dev, "mmap not in list\n");
- return -EINVAL;
- }
-
req_msg.pgid = fl->tgid;
req_msg.size = buf->size;
req_msg.vaddr = buf->raddr;
@@ -1654,12 +1803,29 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl,
static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
{
+ struct fastrpc_buf *buf = NULL, *iter, *b;
struct fastrpc_req_munmap req;
+ struct device *dev = fl->sctx->dev;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
- return fastrpc_req_munmap_impl(fl, &req);
+ spin_lock(&fl->lock);
+ list_for_each_entry_safe(iter, b, &fl->mmaps, node) {
+ if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) {
+ buf = iter;
+ break;
+ }
+ }
+ spin_unlock(&fl->lock);
+
+ if (!buf) {
+ dev_err(dev, "mmap\t\tpt 0x%09llx [len 0x%08llx] not in list\n",
+ req.vaddrout, req.size);
+ return -EINVAL;
+ }
+
+ return fastrpc_req_munmap_impl(fl, buf);
}
static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
@@ -1668,7 +1834,6 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
struct fastrpc_buf *buf = NULL;
struct fastrpc_mmap_req_msg req_msg;
struct fastrpc_mmap_rsp_msg rsp_msg;
- struct fastrpc_req_munmap req_unmap;
struct fastrpc_phy_page pages;
struct fastrpc_req_mmap req;
struct device *dev = fl->sctx->dev;
@@ -1678,8 +1843,9 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
- if (req.flags != ADSP_MMAP_ADD_PAGES) {
+ if (req.flags != ADSP_MMAP_ADD_PAGES && req.flags != ADSP_MMAP_REMOTE_HEAP_ADDR) {
dev_err(dev, "flag not supported 0x%x\n", req.flags);
+
return -EINVAL;
}
@@ -1725,16 +1891,28 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
/* let the client know the address to use */
req.vaddrout = rsp_msg.vaddr;
+ /* Add memory to static PD pool, protection thru hypervisor */
+ if (req.flags != ADSP_MMAP_REMOTE_HEAP_ADDR && fl->cctx->vmcount) {
+ struct qcom_scm_vmperm perm;
+
+ perm.vmid = QCOM_SCM_VMID_HLOS;
+ perm.perm = QCOM_SCM_PERM_RWX;
+ err = qcom_scm_assign_mem(buf->phys, buf->size,
+ &fl->cctx->perms, &perm, 1);
+ if (err) {
+ dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
+ buf->phys, buf->size, err);
+ goto err_assign;
+ }
+ }
+
spin_lock(&fl->lock);
list_add_tail(&buf->node, &fl->mmaps);
spin_unlock(&fl->lock);
if (copy_to_user((void __user *)argp, &req, sizeof(req))) {
- /* unmap the memory and release the buffer */
- req_unmap.vaddrout = buf->raddr;
- req_unmap.size = buf->size;
- fastrpc_req_munmap_impl(fl, &req_unmap);
- return -EFAULT;
+ err = -EFAULT;
+ goto err_assign;
}
dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n",
@@ -1742,6 +1920,8 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
return 0;
+err_assign:
+ fastrpc_req_munmap_impl(fl, buf);
err_invoke:
fastrpc_buf_free(buf);
@@ -1889,11 +2069,14 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
err = fastrpc_invoke(fl, argp);
break;
case FASTRPC_IOCTL_INIT_ATTACH:
- err = fastrpc_init_attach(fl, AUDIO_PD);
+ err = fastrpc_init_attach(fl, ROOT_PD);
break;
case FASTRPC_IOCTL_INIT_ATTACH_SNS:
err = fastrpc_init_attach(fl, SENSORS_PD);
break;
+ case FASTRPC_IOCTL_INIT_CREATE_STATIC:
+ err = fastrpc_init_create_static_process(fl, argp);
+ break;
case FASTRPC_IOCTL_INIT_CREATE:
err = fastrpc_init_create_process(fl, argp);
break;
@@ -2068,6 +2251,9 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
return -EINVAL;
}
+ if (of_reserved_mem_device_init_by_idx(rdev, rdev->of_node, 0))
+ dev_info(rdev, "no reserved DMA memory for FASTRPC\n");
+
vmcount = of_property_read_variable_u32_array(rdev->of_node,
"qcom,vmids", &vmids[0], 0, FASTRPC_MAX_VMIDS);
if (vmcount < 0)
@@ -2120,8 +2306,10 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
kref_init(&data->refcount);
dev_set_drvdata(&rpdev->dev, data);
+ rdev->dma_mask = &data->dma_mask;
dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
INIT_LIST_HEAD(&data->users);
+ INIT_LIST_HEAD(&data->invoke_interrupted_mmaps);
spin_lock_init(&data->lock);
idr_init(&data->ctx_idr);
data->domain_id = domain_id;
@@ -2146,6 +2334,7 @@ static void fastrpc_notify_users(struct fastrpc_user *user)
static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
{
struct fastrpc_channel_ctx *cctx = dev_get_drvdata(&rpdev->dev);
+ struct fastrpc_buf *buf, *b;
struct fastrpc_user *user;
unsigned long flags;
@@ -2160,6 +2349,12 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
if (cctx->secure_fdevice)
misc_deregister(&cctx->secure_fdevice->miscdev);
+ list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
+ list_del(&buf->node);
+
+ if (cctx->remote_heap)
+ fastrpc_buf_free(cctx->remote_heap);
+
of_platform_depopulate(&rpdev->dev);
cctx->rpdev = NULL;