]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/gpu/drm/tegra/drm.c
Backmerge tag 'v4.12-rc7' into drm-next
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / tegra / drm.c
index 81f86a67c10d28416a3fbe3d69694b8c7dfc34ee..518f4b69ea5335983ac3dc6d570a4b77c10d46eb 100644 (file)
@@ -26,6 +26,7 @@
 #define DRIVER_PATCHLEVEL 0
 
 #define CARVEOUT_SZ SZ_64M
+#define CDMA_GATHER_FETCHES_MAX_NB 16383
 
 struct tegra_drm_file {
        struct idr contexts;
@@ -348,6 +349,36 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
        return 0;
 }
 
+static int host1x_waitchk_copy_from_user(struct host1x_waitchk *dest,
+                                        struct drm_tegra_waitchk __user *src,
+                                        struct drm_file *file)
+{
+       u32 cmdbuf;
+       int err;
+
+       err = get_user(cmdbuf, &src->handle);
+       if (err < 0)
+               return err;
+
+       err = get_user(dest->offset, &src->offset);
+       if (err < 0)
+               return err;
+
+       err = get_user(dest->syncpt_id, &src->syncpt);
+       if (err < 0)
+               return err;
+
+       err = get_user(dest->thresh, &src->thresh);
+       if (err < 0)
+               return err;
+
+       dest->bo = host1x_bo_lookup(file, cmdbuf);
+       if (!dest->bo)
+               return -ENOENT;
+
+       return 0;
+}
+
 int tegra_drm_submit(struct tegra_drm_context *context,
                     struct drm_tegra_submit *args, struct drm_device *drm,
                     struct drm_file *file)
@@ -362,6 +393,8 @@ int tegra_drm_submit(struct tegra_drm_context *context,
        struct drm_tegra_waitchk __user *waitchks =
                (void __user *)(uintptr_t)args->waitchks;
        struct drm_tegra_syncpt syncpt;
+       struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
+       struct host1x_syncpt *sp;
        struct host1x_job *job;
        int err;
 
@@ -369,6 +402,10 @@ int tegra_drm_submit(struct tegra_drm_context *context,
        if (args->num_syncpts != 1)
                return -EINVAL;
 
+       /* We don't yet support waitchks */
+       if (args->num_waitchks != 0)
+               return -EINVAL;
+
        job = host1x_job_alloc(context->channel, args->num_cmdbufs,
                               args->num_relocs, args->num_waitchks);
        if (!job)
@@ -383,18 +420,42 @@ int tegra_drm_submit(struct tegra_drm_context *context,
        while (num_cmdbufs) {
                struct drm_tegra_cmdbuf cmdbuf;
                struct host1x_bo *bo;
+               struct tegra_bo *obj;
+               u64 offset;
 
                if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) {
                        err = -EFAULT;
                        goto fail;
                }
 
+               /*
+                * The maximum number of CDMA gather fetches is 16383, a higher
+                * value means the words count is malformed.
+                */
+               if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+
                bo = host1x_bo_lookup(file, cmdbuf.handle);
                if (!bo) {
                        err = -ENOENT;
                        goto fail;
                }
 
+               offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32);
+               obj = host1x_to_tegra_bo(bo);
+
+               /*
+                * Gather buffer base address must be 4-bytes aligned,
+                * unaligned offset is malformed and cause commands stream
+                * corruption on the buffer address relocation.
+                */
+               if (offset & 3 || offset >= obj->gem.size) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+
                host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
                num_cmdbufs--;
                cmdbufs++;
@@ -402,17 +463,59 @@ int tegra_drm_submit(struct tegra_drm_context *context,
 
        /* copy and resolve relocations from submit */
        while (num_relocs--) {
+               struct host1x_reloc *reloc;
+               struct tegra_bo *obj;
+
                err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
                                                  &relocs[num_relocs], drm,
                                                  file);
                if (err < 0)
                        goto fail;
+
+               reloc = &job->relocarray[num_relocs];
+               obj = host1x_to_tegra_bo(reloc->cmdbuf.bo);
+
+               /*
+                * The unaligned cmdbuf offset will cause an unaligned write
+                * during of the relocations patching, corrupting the commands
+                * stream.
+                */
+               if (reloc->cmdbuf.offset & 3 ||
+                   reloc->cmdbuf.offset >= obj->gem.size) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+
+               obj = host1x_to_tegra_bo(reloc->target.bo);
+
+               if (reloc->target.offset >= obj->gem.size) {
+                       err = -EINVAL;
+                       goto fail;
+               }
        }
 
-       if (copy_from_user(job->waitchk, waitchks,
-                          sizeof(*waitchks) * num_waitchks)) {
-               err = -EFAULT;
-               goto fail;
+       /* copy and resolve waitchks from submit */
+       while (num_waitchks--) {
+               struct host1x_waitchk *wait = &job->waitchk[num_waitchks];
+               struct tegra_bo *obj;
+
+               err = host1x_waitchk_copy_from_user(wait,
+                                                   &waitchks[num_waitchks],
+                                                   file);
+               if (err < 0)
+                       goto fail;
+
+               obj = host1x_to_tegra_bo(wait->bo);
+
+               /*
+                * The unaligned offset will cause an unaligned write during
+                * of the waitchks patching, corrupting the commands stream.
+                */
+               if (wait->offset & 3 ||
+                   wait->offset >= obj->gem.size) {
+                       err = -EINVAL;
+                       goto fail;
+               }
        }
 
        if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts,
@@ -421,7 +524,15 @@ int tegra_drm_submit(struct tegra_drm_context *context,
                goto fail;
        }
 
+       /* check whether syncpoint ID is valid */
+       sp = host1x_syncpt_get(host1x, syncpt.id);
+       if (!sp) {
+               err = -ENOENT;
+               goto fail;
+       }
+
        job->is_addr_reg = context->client->ops->is_addr_reg;
+       job->is_valid_class = context->client->ops->is_valid_class;
        job->syncpt_incrs = syncpt.incrs;
        job->syncpt_id = syncpt.id;
        job->timeout = 10000;
@@ -880,7 +991,7 @@ static int tegra_drm_context_cleanup(int id, void *p, void *data)
        return 0;
 }
 
-static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
 {
        struct tegra_drm_file *fpriv = file->driver_priv;
 
@@ -948,7 +1059,7 @@ static struct drm_driver tegra_drm_driver = {
        .load = tegra_drm_load,
        .unload = tegra_drm_unload,
        .open = tegra_drm_open,
-       .preclose = tegra_drm_preclose,
+       .postclose = tegra_drm_postclose,
        .lastclose = tegra_drm_lastclose,
 
 #if defined(CONFIG_DEBUG_FS)