diff options
Diffstat (limited to 'drivers/misc/fastrpc.c')
| -rw-r--r-- | drivers/misc/fastrpc.c | 340 | 
1 files changed, 269 insertions, 71 deletions
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 7ff0b63c25e3..5310606113fe 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -6,6 +6,7 @@  #include <linux/device.h>  #include <linux/dma-buf.h>  #include <linux/dma-mapping.h> +#include <linux/dma-resv.h>  #include <linux/idr.h>  #include <linux/list.h>  #include <linux/miscdevice.h> @@ -19,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) @@ -36,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) @@ -71,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 @@ -83,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) @@ -260,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 { @@ -303,19 +321,26 @@ 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);  				return;  			}  		} -		dma_buf_unmap_attachment(map->attach, map->table, -					 DMA_BIDIRECTIONAL); +		dma_buf_unmap_attachment_unlocked(map->attach, map->table, +						  DMA_BIDIRECTIONAL);  		dma_buf_detach(map->buf, map->attach);  		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);  } @@ -325,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;  } @@ -368,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; @@ -396,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; @@ -682,6 +733,8 @@ static int fastrpc_mmap(struct dma_buf *dmabuf,  	struct fastrpc_buf *buf = dmabuf->priv;  	size_t size = vma->vm_end - vma->vm_start; +	dma_resv_assert_held(dmabuf->resv); +  	return dma_mmap_coherent(buf->dev, vma, buf->virt,  				 FASTRPC_PHYS(buf->phys), size);  } @@ -703,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); @@ -711,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); @@ -726,7 +781,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,  		goto attach_err;  	} -	map->table = dma_buf_map_attachment(map->attach, DMA_BIDIRECTIONAL); +	map->table = dma_buf_map_attachment_unlocked(map->attach, DMA_BIDIRECTIONAL);  	if (IS_ERR(map->table)) {  		err = PTR_ERR(map->table);  		goto map_err; @@ -737,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", @@ -767,7 +819,7 @@ map_err:  attach_err:  	dma_buf_put(map->buf);  get_err: -	kfree(map); +	fastrpc_map_put(map);  	return err;  } @@ -1026,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);  	} @@ -1070,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) @@ -1132,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); @@ -1156,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)  { @@ -1265,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); @@ -1346,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); @@ -1602,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; @@ -1651,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) @@ -1665,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; @@ -1675,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;  	} @@ -1722,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", @@ -1739,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); @@ -1886,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; @@ -2065,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) @@ -2117,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; @@ -2143,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; @@ -2157,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;  |