summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/tegra/drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/tegra/drm.c')
-rw-r--r--drivers/gpu/drm/tegra/drm.c123
1 files changed, 117 insertions, 6 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 81f86a67c10d..518f4b69ea53 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -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)