summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/tegra/drm.c60
-rw-r--r--drivers/gpu/host1x/job.h7
-rw-r--r--include/linux/host1x.h7
3 files changed, 63 insertions, 11 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 51e20e015053..0928f2bb4203 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -349,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)
@@ -370,6 +400,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)
@@ -458,10 +492,28 @@ int tegra_drm_submit(struct tegra_drm_context *context,
}
}
- 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,
diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h
index 878239c476d2..0debd93a1849 100644
--- a/drivers/gpu/host1x/job.h
+++ b/drivers/gpu/host1x/job.h
@@ -34,13 +34,6 @@ struct host1x_cmdbuf {
u32 pad;
};
-struct host1x_waitchk {
- struct host1x_bo *bo;
- u32 offset;
- u32 syncpt_id;
- u32 thresh;
-};
-
struct host1x_job_unpin_data {
struct host1x_bo *bo;
struct sg_table *sgt;
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 840a8ad627b2..ba0b245da732 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -193,6 +193,13 @@ struct host1x_reloc {
unsigned long shift;
};
+struct host1x_waitchk {
+ struct host1x_bo *bo;
+ u32 offset;
+ u32 syncpt_id;
+ u32 thresh;
+};
+
struct host1x_job {
/* When refcount goes to zero, job can be freed */
struct kref ref;