aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-06-10 04:25:00 -0400
committerThierry Reding <treding@nvidia.com>2014-08-04 04:07:36 -0400
commit961e3beae3b29ae9463631415342244cdaf1cd47 (patch)
tree3b961e00f8cde9f11e88cdcab604d927d7757446
parentbd4f236024a8027b2d5ee9de1f823fbf07c39cef (diff)
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>
-rw-r--r--drivers/gpu/drm/tegra/drm.c62
-rw-r--r--drivers/gpu/host1x/job.c22
-rw-r--r--include/linux/host1x.h15
3 files changed, 64 insertions, 35 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)
132 return &bo->base; 132 return &bo->base;
133} 133}
134 134
135static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
136 struct drm_tegra_reloc __user *src,
137 struct drm_device *drm,
138 struct drm_file *file)
139{
140 u32 cmdbuf, target;
141 int err;
142
143 err = get_user(cmdbuf, &src->cmdbuf.handle);
144 if (err < 0)
145 return err;
146
147 err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
148 if (err < 0)
149 return err;
150
151 err = get_user(target, &src->target.handle);
152 if (err < 0)
153 return err;
154
155 err = get_user(dest->target.offset, &src->cmdbuf.offset);
156 if (err < 0)
157 return err;
158
159 err = get_user(dest->shift, &src->shift);
160 if (err < 0)
161 return err;
162
163 dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
164 if (!dest->cmdbuf.bo)
165 return -ENOENT;
166
167 dest->target.bo = host1x_bo_lookup(drm, file, target);
168 if (!dest->target.bo)
169 return -ENOENT;
170
171 return 0;
172}
173
135int tegra_drm_submit(struct tegra_drm_context *context, 174int tegra_drm_submit(struct tegra_drm_context *context,
136 struct drm_tegra_submit *args, struct drm_device *drm, 175 struct drm_tegra_submit *args, struct drm_device *drm,
137 struct drm_file *file) 176 struct drm_file *file)
@@ -184,26 +223,13 @@ int tegra_drm_submit(struct tegra_drm_context *context,
184 cmdbufs++; 223 cmdbufs++;
185 } 224 }
186 225
187 if (copy_from_user(job->relocarray, relocs, 226 /* copy and resolve relocations from submit */
188 sizeof(*relocs) * num_relocs)) {
189 err = -EFAULT;
190 goto fail;
191 }
192
193 while (num_relocs--) { 227 while (num_relocs--) {
194 struct host1x_reloc *reloc = &job->relocarray[num_relocs]; 228 err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
195 struct host1x_bo *cmdbuf, *target; 229 &relocs[num_relocs], drm,
196 230 file);
197 cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf); 231 if (err < 0)
198 target = host1x_bo_lookup(drm, file, (u32)reloc->target);
199
200 reloc->cmdbuf = cmdbuf;
201 reloc->target = target;
202
203 if (!reloc->target || !reloc->cmdbuf) {
204 err = -ENOENT;
205 goto fail; 232 goto fail;
206 }
207 } 233 }
208 234
209 if (copy_from_user(job->waitchk, waitchks, 235 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)
185 struct sg_table *sgt; 185 struct sg_table *sgt;
186 dma_addr_t phys_addr; 186 dma_addr_t phys_addr;
187 187
188 reloc->target = host1x_bo_get(reloc->target); 188 reloc->target.bo = host1x_bo_get(reloc->target.bo);
189 if (!reloc->target) 189 if (!reloc->target.bo)
190 goto unpin; 190 goto unpin;
191 191
192 phys_addr = host1x_bo_pin(reloc->target, &sgt); 192 phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
193 if (!phys_addr) 193 if (!phys_addr)
194 goto unpin; 194 goto unpin;
195 195
196 job->addr_phys[job->num_unpins] = phys_addr; 196 job->addr_phys[job->num_unpins] = phys_addr;
197 job->unpins[job->num_unpins].bo = reloc->target; 197 job->unpins[job->num_unpins].bo = reloc->target.bo;
198 job->unpins[job->num_unpins].sgt = sgt; 198 job->unpins[job->num_unpins].sgt = sgt;
199 job->num_unpins++; 199 job->num_unpins++;
200 } 200 }
@@ -235,21 +235,21 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
235 for (i = 0; i < job->num_relocs; i++) { 235 for (i = 0; i < job->num_relocs; i++) {
236 struct host1x_reloc *reloc = &job->relocarray[i]; 236 struct host1x_reloc *reloc = &job->relocarray[i];
237 u32 reloc_addr = (job->reloc_addr_phys[i] + 237 u32 reloc_addr = (job->reloc_addr_phys[i] +
238 reloc->target_offset) >> reloc->shift; 238 reloc->target.offset) >> reloc->shift;
239 u32 *target; 239 u32 *target;
240 240
241 /* skip all other gathers */ 241 /* skip all other gathers */
242 if (cmdbuf != reloc->cmdbuf) 242 if (cmdbuf != reloc->cmdbuf.bo)
243 continue; 243 continue;
244 244
245 if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { 245 if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
246 if (cmdbuf_page_addr) 246 if (cmdbuf_page_addr)
247 host1x_bo_kunmap(cmdbuf, last_page, 247 host1x_bo_kunmap(cmdbuf, last_page,
248 cmdbuf_page_addr); 248 cmdbuf_page_addr);
249 249
250 cmdbuf_page_addr = host1x_bo_kmap(cmdbuf, 250 cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
251 reloc->cmdbuf_offset >> PAGE_SHIFT); 251 reloc->cmdbuf.offset >> PAGE_SHIFT);
252 last_page = reloc->cmdbuf_offset >> PAGE_SHIFT; 252 last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
253 253
254 if (unlikely(!cmdbuf_page_addr)) { 254 if (unlikely(!cmdbuf_page_addr)) {
255 pr_err("Could not map cmdbuf for relocation\n"); 255 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)
257 } 257 }
258 } 258 }
259 259
260 target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK); 260 target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
261 *target = reloc_addr; 261 *target = reloc_addr;
262 } 262 }
263 263
@@ -272,7 +272,7 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
272{ 272{
273 offset *= sizeof(u32); 273 offset *= sizeof(u32);
274 274
275 if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset) 275 if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
276 return false; 276 return false;
277 277
278 return true; 278 return true;
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index d2b52999e771..bb9840fd1e18 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -164,12 +164,15 @@ int host1x_job_submit(struct host1x_job *job);
164 */ 164 */
165 165
166struct host1x_reloc { 166struct host1x_reloc {
167 struct host1x_bo *cmdbuf; 167 struct {
168 u32 cmdbuf_offset; 168 struct host1x_bo *bo;
169 struct host1x_bo *target; 169 unsigned long offset;
170 u32 target_offset; 170 } cmdbuf;
171 u32 shift; 171 struct {
172 u32 pad; 172 struct host1x_bo *bo;
173 unsigned long offset;
174 } target;
175 unsigned long shift;
173}; 176};
174 177
175struct host1x_job { 178struct host1x_job {