diff options
Diffstat (limited to 'drivers/video/tegra/host/bus_client.c')
-rw-r--r-- | drivers/video/tegra/host/bus_client.c | 101 |
1 files changed, 82 insertions, 19 deletions
diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 2bf11ed426c0..eabb09f1a683 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -173,6 +173,9 @@ struct nvhost_channel_userctx { u32 priority; int clientid; bool timeout_debug_dump; + + /* lock to protect this structure from concurrent ioctl usage */ + struct mutex ioctl_lock; }; static int nvhost_channelrelease(struct inode *inode, struct file *filp) @@ -276,6 +279,7 @@ static int __nvhost_channelopen(struct inode *inode, pdata = dev_get_drvdata(ch->dev->dev.parent); priv->timeout = pdata->nvhost_timeout_default; priv->timeout_debug_dump = true; + mutex_init(&priv->ioctl_lock); if (!tegra_platform_is_silicon()) priv->timeout = 0; mutex_unlock(&channel_lock); @@ -394,12 +398,17 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, u32 __user *waitbases = (u32 *)(uintptr_t)args->waitbases; u32 __user *fences = (u32 *)(uintptr_t)args->fences; u32 __user *class_ids = (u32 *)(uintptr_t)args->class_ids; + struct nvhost_device_data *pdata = platform_get_drvdata(ctx->ch->dev); struct nvhost_master *host = nvhost_get_host(ctx->ch->dev); u32 *local_waitbases = NULL, *local_class_ids = NULL; int err, i, hwctx_syncpt_idx = -1; - if (num_syncpt_incrs > host->info.nb_pts) + if ((num_syncpt_incrs < 1) || (num_syncpt_incrs > + host->info.nb_pts)) + return -EINVAL; + + if (num_cmdbufs < 0 || num_syncpt_incrs < 0) return -EINVAL; job = nvhost_job_alloc(ctx->ch, @@ -449,6 +458,13 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, if (err) cmdbuf_ext.pre_fence = -1; + if (class_id && + class_id != pdata->class && + class_id != NV_HOST1X_CLASS_ID) { + err = -EINVAL; + goto fail; + } + nvhost_job_add_gather(job, cmdbuf.mem, cmdbuf.words, cmdbuf.offset, class_id, cmdbuf_ext.pre_fence); @@ -504,6 +520,8 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, for (i = 0; i < num_syncpt_incrs; ++i) { u32 waitbase; struct nvhost_syncpt_incr sp; + bool found = false; + int j; /* Copy */ err = copy_from_user(&sp, syncpt_incrs + i, sizeof(sp)); @@ -511,7 +529,19 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, goto fail; /* Validate */ - if (sp.syncpt_id >= host->info.nb_pts) { + if (sp.syncpt_id == 0) { + err = -EINVAL; + goto fail; + } + + for (j = 0; j < NVHOST_MODULE_MAX_SYNCPTS; ++j) { + if (pdata->syncpts[j] == sp.syncpt_id) { + found = true; + break; + } + } + + if (!found) { err = -EINVAL; goto fail; } @@ -580,7 +610,15 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, * syncpoint is used. */ if (args->flags & BIT(NVHOST_SUBMIT_FLAG_SYNC_FENCE_FD)) { - struct nvhost_ctrl_sync_fence_info pts[num_syncpt_incrs]; + struct nvhost_ctrl_sync_fence_info *pts; + + pts = kzalloc(num_syncpt_incrs * + sizeof(struct nvhost_ctrl_sync_fence_info), + GFP_KERNEL); + if (!pts) { + err = -ENOMEM; + goto fail; + } for (i = 0; i < num_syncpt_incrs; i++) { pts[i].id = job->sp[i].id; @@ -589,6 +627,7 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, err = nvhost_sync_create_fence_fd(ctx->ch->dev, pts, num_syncpt_incrs, "fence", &args->fence); + kfree(pts); if (err) goto fail; } else if (num_syncpt_incrs == 1) @@ -739,8 +778,12 @@ static long nvhost_channelctl(struct file *filp, return -EFAULT; } + /* serialize calls from this fd */ + mutex_lock(&priv->ioctl_lock); + if (!priv->ch->dev) { pr_warn("Channel already unmapped\n"); + mutex_unlock(&priv->ioctl_lock); return -EFAULT; } @@ -772,7 +815,6 @@ static long nvhost_channelctl(struct file *filp, put_unused_fd(fd); break; } - fd_install(fd, file); err = __nvhost_channelopen(NULL, priv->ch, file); if (err) { @@ -782,6 +824,7 @@ static long nvhost_channelctl(struct file *filp, } ((struct nvhost_channel_open_args *)buf)->channel_fd = fd; + fd_install(fd, file); break; } case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS: @@ -798,8 +841,12 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; - if (arg->param >= NVHOST_MODULE_MAX_SYNCPTS) - return -EINVAL; + + if (arg->param >= NVHOST_MODULE_MAX_SYNCPTS) { + err = -EINVAL; + break; + } + /* if we already have required syncpt then return it ... */ if (pdata->syncpts[arg->param]) { arg->value = pdata->syncpts[arg->param]; @@ -808,8 +855,10 @@ static long nvhost_channelctl(struct file *filp, /* ... otherwise get a new syncpt dynamically */ arg->value = nvhost_get_syncpt_host_managed(pdata->pdev, arg->param); - if (!arg->value) - return -EAGAIN; + if (!arg->value) { + err = -EAGAIN; + break; + } /* ... and store it for further references */ pdata->syncpts[arg->param] = arg->value; break; @@ -827,8 +876,10 @@ static long nvhost_channelctl(struct file *filp, if (args_name) { if (strncpy_from_user(name, args_name, - sizeof(name)) < 0) - return -EFAULT; + sizeof(name)) < 0) { + err = -EFAULT; + break; + } name[sizeof(name) - 1] = '\0'; } else { name[0] = '\0'; @@ -842,8 +893,10 @@ static long nvhost_channelctl(struct file *filp, snprintf(set_name, sizeof(set_name), "%s_%s", dev_name(&pdata->pdev->dev), name); args->value = nvhost_get_syncpt_client_managed(set_name); - if (!args->value) - return -EAGAIN; + if (!args->value) { + err = -EAGAIN; + break; + } /* ... and store it for further references */ pdata->client_managed_syncpt = args->value; break; @@ -856,8 +909,10 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); if (!args->value) break; - if (args->value != pdata->client_managed_syncpt) - return -EINVAL; + if (args->value != pdata->client_managed_syncpt) { + err = -EINVAL; + break; + } nvhost_free_syncpt(args->value); pdata->client_managed_syncpt = 0; break; @@ -878,8 +933,10 @@ static long nvhost_channelctl(struct file *filp, struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; if (arg->param >= NVHOST_MODULE_MAX_WAITBASES - || !pdata->waitbases[arg->param]) - return -EINVAL; + || !pdata->waitbases[arg->param]) { + err = -EINVAL; + break; + } arg->value = pdata->waitbases[arg->param]; break; } @@ -898,9 +955,13 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; - if (arg->param >= NVHOST_MODULE_MAX_MODMUTEXES - || !pdata->modulemutexes[arg->param]) - return -EINVAL; + + if (arg->param >= NVHOST_MODULE_MAX_MODMUTEXES || + !pdata->modulemutexes[arg->param]) { + err = -EINVAL; + break; + } + arg->value = pdata->modulemutexes[arg->param]; break; } @@ -1019,6 +1080,8 @@ static long nvhost_channelctl(struct file *filp, break; } + mutex_unlock(&priv->ioctl_lock); + if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); |