summaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-06-10 10:25:00 +0200
committerThierry Reding <treding@nvidia.com>2014-08-04 10:07:36 +0200
commit961e3beae3b29ae9463631415342244cdaf1cd47 (patch)
tree3b961e00f8cde9f11e88cdcab604d927d7757446 /drivers/gpu
parentbd4f236024a8027b2d5ee9de1f823fbf07c39cef (diff)
downloadlinux-stable-961e3beae3b29ae9463631415342244cdaf1cd47.tar.gz
linux-stable-961e3beae3b29ae9463631415342244cdaf1cd47.tar.bz2
linux-stable-961e3beae3b29ae9463631415342244cdaf1cd47.zip
drm/tegra: Make job submission 64-bit safe
Job submission currently relies on the fact that struct drm_tegra_reloc and struct host1x_reloc are the same size and uses a simple call to the copy_from_user() function to copy them to kernel space. This causes the handle to be stored in the buffer object field, which then needs a cast to a 32 bit integer to resolve it to a proper buffer object pointer and store it back in the buffer object field. On 64-bit architectures that will no longer work, since pointers are 64 bits wide whereas handles will remain 32 bits. This causes the sizes of both structures to because different and copying will no longer work. Fix this by adding a new function, host1x_reloc_get_user(), that copies the structures field by field. While at it, use substructures for the command and target buffers in struct host1x_reloc for better readability. Also use unsized types to make it more obvious that this isn't part of userspace ABI. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/tegra/drm.c62
-rw-r--r--drivers/gpu/host1x/job.c22
2 files changed, 55 insertions, 29 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 5cba5e736130..59736bb810cd 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -132,6 +132,45 @@ host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
return &bo->base;
}
+static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
+ struct drm_tegra_reloc __user *src,
+ struct drm_device *drm,
+ struct drm_file *file)
+{
+ u32 cmdbuf, target;
+ int err;
+
+ err = get_user(cmdbuf, &src->cmdbuf.handle);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
+ if (err < 0)
+ return err;
+
+ err = get_user(target, &src->target.handle);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->target.offset, &src->cmdbuf.offset);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->shift, &src->shift);
+ if (err < 0)
+ return err;
+
+ dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
+ if (!dest->cmdbuf.bo)
+ return -ENOENT;
+
+ dest->target.bo = host1x_bo_lookup(drm, file, target);
+ if (!dest->target.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)
@@ -184,26 +223,13 @@ int tegra_drm_submit(struct tegra_drm_context *context,
cmdbufs++;
}
- if (copy_from_user(job->relocarray, relocs,
- sizeof(*relocs) * num_relocs)) {
- err = -EFAULT;
- goto fail;
- }
-
+ /* copy and resolve relocations from submit */
while (num_relocs--) {
- struct host1x_reloc *reloc = &job->relocarray[num_relocs];
- struct host1x_bo *cmdbuf, *target;
-
- cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
- target = host1x_bo_lookup(drm, file, (u32)reloc->target);
-
- reloc->cmdbuf = cmdbuf;
- reloc->target = target;
-
- if (!reloc->target || !reloc->cmdbuf) {
- err = -ENOENT;
+ err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
+ &relocs[num_relocs], drm,
+ file);
+ if (err < 0)
goto fail;
- }
}
if (copy_from_user(job->waitchk, waitchks,
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 112f27e51bc7..63bd63f3c7df 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -185,16 +185,16 @@ static unsigned int pin_job(struct host1x_job *job)
struct sg_table *sgt;
dma_addr_t phys_addr;
- reloc->target = host1x_bo_get(reloc->target);
- if (!reloc->target)
+ reloc->target.bo = host1x_bo_get(reloc->target.bo);
+ if (!reloc->target.bo)
goto unpin;
- phys_addr = host1x_bo_pin(reloc->target, &sgt);
+ phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
if (!phys_addr)
goto unpin;
job->addr_phys[job->num_unpins] = phys_addr;
- job->unpins[job->num_unpins].bo = reloc->target;
+ job->unpins[job->num_unpins].bo = reloc->target.bo;
job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++;
}
@@ -235,21 +235,21 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocarray[i];
u32 reloc_addr = (job->reloc_addr_phys[i] +
- reloc->target_offset) >> reloc->shift;
+ reloc->target.offset) >> reloc->shift;
u32 *target;
/* skip all other gathers */
- if (cmdbuf != reloc->cmdbuf)
+ if (cmdbuf != reloc->cmdbuf.bo)
continue;
- if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) {
+ if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
if (cmdbuf_page_addr)
host1x_bo_kunmap(cmdbuf, last_page,
cmdbuf_page_addr);
cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
- reloc->cmdbuf_offset >> PAGE_SHIFT);
- last_page = reloc->cmdbuf_offset >> PAGE_SHIFT;
+ reloc->cmdbuf.offset >> PAGE_SHIFT);
+ last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
if (unlikely(!cmdbuf_page_addr)) {
pr_err("Could not map cmdbuf for relocation\n");
@@ -257,7 +257,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
}
}
- target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK);
+ target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
*target = reloc_addr;
}
@@ -272,7 +272,7 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
{
offset *= sizeof(u32);
- if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset)
+ if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
return false;
return true;