Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 1 | /* |
2 | * Copyright © 2014 Broadcom |
||
3 | * |
||
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
5 | * copy of this software and associated documentation files (the "Software"), |
||
6 | * to deal in the Software without restriction, including without limitation |
||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
8 | * and/or sell copies of the Software, and to permit persons to whom the |
||
9 | * Software is furnished to do so, subject to the following conditions: |
||
10 | * |
||
11 | * The above copyright notice and this permission notice (including the next |
||
12 | * paragraph) shall be included in all copies or substantial portions of the |
||
13 | * Software. |
||
14 | * |
||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||
21 | * IN THE SOFTWARE. |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * Command list validator for VC4. |
||
26 | * |
||
27 | * The VC4 has no IOMMU between it and system memory. So, a user with |
||
28 | * access to execute command lists could escalate privilege by |
||
29 | * overwriting system memory (drawing to it as a framebuffer) or |
||
30 | * reading system memory it shouldn't (reading it as a texture, or |
||
31 | * uniform data, or vertex data). |
||
32 | * |
||
33 | * This validates command lists to ensure that all accesses are within |
||
34 | * the bounds of the GEM objects referenced. It explicitly whitelists |
||
35 | * packets, and looks at the offsets in any address fields to make |
||
36 | * sure they're constrained within the BOs they reference. |
||
37 | * |
||
38 | * Note that because of the validation that's happening anyway, this |
||
39 | * is where GEM relocation processing happens. |
||
40 | */ |
||
41 | |||
42 | #include "vc4_drv.h" |
||
43 | #include "vc4_packet.h" |
||
44 | |||
45 | #define VALIDATE_ARGS \ |
||
46 | struct vc4_exec_info *exec, \ |
||
47 | void *validated, \ |
||
48 | void *untrusted |
||
49 | |||
50 | |||
51 | /** Return the width in pixels of a 64-byte microtile. */ |
||
52 | static uint32_t |
||
53 | utile_width(int cpp) |
||
54 | { |
||
55 | switch (cpp) { |
||
56 | case 1: |
||
57 | case 2: |
||
58 | return 8; |
||
59 | case 4: |
||
60 | return 4; |
||
61 | case 8: |
||
62 | return 2; |
||
63 | default: |
||
64 | DRM_ERROR("unknown cpp: %d\n", cpp); |
||
65 | return 1; |
||
66 | } |
||
67 | } |
||
68 | |||
69 | /** Return the height in pixels of a 64-byte microtile. */ |
||
70 | static uint32_t |
||
71 | utile_height(int cpp) |
||
72 | { |
||
73 | switch (cpp) { |
||
74 | case 1: |
||
75 | return 8; |
||
76 | case 2: |
||
77 | case 4: |
||
78 | case 8: |
||
79 | return 4; |
||
80 | default: |
||
81 | DRM_ERROR("unknown cpp: %d\n", cpp); |
||
82 | return 1; |
||
83 | } |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * The texture unit decides what tiling format a particular miplevel is using |
||
88 | * this function, so we lay out our miptrees accordingly. |
||
89 | */ |
||
90 | static bool |
||
91 | size_is_lt(uint32_t width, uint32_t height, int cpp) |
||
92 | { |
||
93 | return (width <= 4 * utile_width(cpp) || |
||
94 | height <= 4 * utile_height(cpp)); |
||
95 | } |
||
96 | |||
97 | static bool |
||
98 | vc4_use_bo(struct vc4_exec_info *exec, |
||
99 | uint32_t hindex, |
||
100 | enum vc4_bo_mode mode, |
||
101 | struct drm_gem_cma_object **obj) |
||
102 | { |
||
103 | *obj = NULL; |
||
104 | |||
105 | if (hindex >= exec->bo_count) { |
||
106 | DRM_ERROR("BO index %d greater than BO count %d\n", |
||
107 | hindex, exec->bo_count); |
||
108 | return false; |
||
109 | } |
||
110 | |||
111 | if (exec->bo[hindex].mode != mode) { |
||
112 | if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { |
||
113 | exec->bo[hindex].mode = mode; |
||
114 | } else { |
||
115 | DRM_ERROR("BO index %d reused with mode %d vs %d\n", |
||
116 | hindex, exec->bo[hindex].mode, mode); |
||
117 | return false; |
||
118 | } |
||
119 | } |
||
120 | |||
121 | *obj = exec->bo[hindex].bo; |
||
122 | return true; |
||
123 | } |
||
124 | |||
125 | static bool |
||
126 | vc4_use_handle(struct vc4_exec_info *exec, |
||
127 | uint32_t gem_handles_packet_index, |
||
128 | enum vc4_bo_mode mode, |
||
129 | struct drm_gem_cma_object **obj) |
||
130 | { |
||
131 | return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], |
||
132 | mode, obj); |
||
133 | } |
||
134 | |||
135 | static uint32_t |
||
136 | gl_shader_rec_size(uint32_t pointer_bits) |
||
137 | { |
||
138 | uint32_t attribute_count = pointer_bits & 7; |
||
139 | bool extended = pointer_bits & 8; |
||
140 | |||
141 | if (attribute_count == 0) |
||
142 | attribute_count = 8; |
||
143 | |||
144 | if (extended) |
||
145 | return 100 + attribute_count * 4; |
||
146 | else |
||
147 | return 36 + attribute_count * 8; |
||
148 | } |
||
149 | |||
150 | static bool |
||
151 | check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, |
||
152 | uint32_t offset, uint8_t tiling_format, |
||
153 | uint32_t width, uint32_t height, uint8_t cpp) |
||
154 | { |
||
155 | uint32_t aligned_width, aligned_height, stride, size; |
||
156 | uint32_t utile_w = utile_width(cpp); |
||
157 | uint32_t utile_h = utile_height(cpp); |
||
158 | |||
159 | /* The values are limited by the packet/texture parameter bitfields, |
||
160 | * so we don't need to worry as much about integer overflow. |
||
161 | */ |
||
162 | BUG_ON(width > 65535); |
||
163 | BUG_ON(height > 65535); |
||
164 | |||
165 | switch (tiling_format) { |
||
166 | case VC4_TILING_FORMAT_LINEAR: |
||
167 | aligned_width = roundup(width, utile_w); |
||
168 | aligned_height = height; |
||
169 | break; |
||
170 | case VC4_TILING_FORMAT_T: |
||
171 | aligned_width = roundup(width, utile_w * 8); |
||
172 | aligned_height = roundup(height, utile_h * 8); |
||
173 | break; |
||
174 | case VC4_TILING_FORMAT_LT: |
||
175 | aligned_width = roundup(width, utile_w); |
||
176 | aligned_height = roundup(height, utile_h); |
||
177 | break; |
||
178 | default: |
||
179 | DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); |
||
180 | return false; |
||
181 | } |
||
182 | |||
183 | stride = aligned_width * cpp; |
||
184 | |||
185 | if (INT_MAX / stride < aligned_height) { |
||
186 | DRM_ERROR("Overflow in fbo size (%dx%d -> %dx%d)\n", |
||
187 | width, height, |
||
188 | aligned_width, aligned_height); |
||
189 | return false; |
||
190 | } |
||
191 | size = stride * aligned_height; |
||
192 | |||
193 | if (size + offset < size || |
||
194 | size + offset > fbo->base.size) { |
||
195 | DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", |
||
196 | width, height, |
||
197 | aligned_width, aligned_height, |
||
198 | size, offset, fbo->base.size); |
||
199 | return false; |
||
200 | } |
||
201 | |||
202 | return true; |
||
203 | } |
||
204 | |||
205 | static int |
||
206 | validate_flush_all(VALIDATE_ARGS) |
||
207 | { |
||
208 | if (exec->found_increment_semaphore_packet) { |
||
209 | DRM_ERROR("VC4_PACKET_FLUSH_ALL after " |
||
210 | "VC4_PACKET_INCREMENT_SEMAPHORE\n"); |
||
211 | return -EINVAL; |
||
212 | } |
||
213 | |||
214 | return 0; |
||
215 | } |
||
216 | |||
217 | static int |
||
218 | validate_start_tile_binning(VALIDATE_ARGS) |
||
219 | { |
||
220 | if (exec->found_start_tile_binning_packet) { |
||
221 | DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); |
||
222 | return -EINVAL; |
||
223 | } |
||
224 | exec->found_start_tile_binning_packet = true; |
||
225 | |||
226 | if (!exec->found_tile_binning_mode_config_packet) { |
||
227 | DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); |
||
228 | return -EINVAL; |
||
229 | } |
||
230 | |||
231 | return 0; |
||
232 | } |
||
233 | |||
234 | static int |
||
235 | validate_increment_semaphore(VALIDATE_ARGS) |
||
236 | { |
||
237 | if (exec->found_increment_semaphore_packet) { |
||
238 | DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); |
||
239 | return -EINVAL; |
||
240 | } |
||
241 | exec->found_increment_semaphore_packet = true; |
||
242 | |||
243 | /* Once we've found the semaphore increment, there should be one FLUSH |
||
244 | * then the end of the command list. The FLUSH actually triggers the |
||
245 | * increment, so we only need to make sure there |
||
246 | */ |
||
247 | |||
248 | return 0; |
||
249 | } |
||
250 | |||
251 | static int |
||
252 | validate_wait_on_semaphore(VALIDATE_ARGS) |
||
253 | { |
||
254 | if (exec->found_wait_on_semaphore_packet) { |
||
255 | DRM_ERROR("Duplicate VC4_PACKET_WAIT_ON_SEMAPHORE\n"); |
||
256 | return -EINVAL; |
||
257 | } |
||
258 | exec->found_wait_on_semaphore_packet = true; |
||
259 | |||
260 | if (!exec->found_increment_semaphore_packet) { |
||
261 | DRM_ERROR("VC4_PACKET_WAIT_ON_SEMAPHORE without " |
||
262 | "VC4_PACKET_INCREMENT_SEMAPHORE\n"); |
||
263 | return -EINVAL; |
||
264 | } |
||
265 | |||
266 | return 0; |
||
267 | } |
||
268 | |||
269 | static int |
||
270 | validate_branch_to_sublist(VALIDATE_ARGS) |
||
271 | { |
||
272 | struct drm_gem_cma_object *target; |
||
273 | uint32_t offset; |
||
274 | |||
275 | if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &target)) |
||
276 | return -EINVAL; |
||
277 | |||
278 | if (target != exec->tile_alloc_bo) { |
||
279 | DRM_ERROR("Jumping to BOs other than tile alloc unsupported\n"); |
||
280 | return -EINVAL; |
||
281 | } |
||
282 | |||
283 | if (!exec->found_wait_on_semaphore_packet) { |
||
284 | DRM_ERROR("Jumping to tile alloc before binning finished.\n"); |
||
285 | return -EINVAL; |
||
286 | } |
||
287 | |||
288 | offset = *(uint32_t *)(untrusted + 0); |
||
289 | if (offset % exec->tile_alloc_init_block_size || |
||
290 | offset / exec->tile_alloc_init_block_size >= |
||
291 | exec->bin_tiles_x * exec->bin_tiles_y) { |
||
292 | DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial " |
||
293 | "tile allocation space.\n"); |
||
294 | return -EINVAL; |
||
295 | } |
||
296 | |||
297 | *(uint32_t *)(validated + 0) = target->paddr + offset; |
||
298 | |||
299 | return 0; |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * validate_loadstore_tile_buffer_general() - Validation for |
||
304 | * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL and |
||
305 | * VC4_PACKET_STORE_TILE_BUFFER_GENERAL. |
||
306 | * |
||
307 | * The two packets are nearly the same, except for the TLB-clearing management |
||
308 | * bits not being present for loads. Additionally, while stores are executed |
||
309 | * immediately (using the current tile coordinates), loads are queued to be |
||
310 | * executed when the tile coordinates packet occurs. |
||
311 | * |
||
312 | * Note that coordinates packets are validated to be within the declared |
||
313 | * bin_x/y, which themselves are verified to match the rendering-configuration |
||
314 | * FB width and height (which the hardware uses to clip loads and stores). |
||
315 | */ |
||
316 | static int |
||
317 | validate_loadstore_tile_buffer_general(VALIDATE_ARGS) |
||
318 | { |
||
319 | uint32_t packet_b0 = *(uint8_t *)(untrusted + 0); |
||
320 | uint32_t packet_b1 = *(uint8_t *)(untrusted + 1); |
||
321 | struct drm_gem_cma_object *fbo; |
||
322 | uint32_t buffer_type = packet_b0 & 0xf; |
||
323 | uint32_t untrusted_address, offset, cpp; |
||
324 | |||
325 | switch (buffer_type) { |
||
326 | case VC4_LOADSTORE_TILE_BUFFER_NONE: |
||
327 | return 0; |
||
328 | case VC4_LOADSTORE_TILE_BUFFER_COLOR: |
||
329 | if ((packet_b1 & VC4_LOADSTORE_TILE_BUFFER_MASK) == |
||
330 | VC4_LOADSTORE_TILE_BUFFER_RGBA8888) { |
||
331 | cpp = 4; |
||
332 | } else { |
||
333 | cpp = 2; |
||
334 | } |
||
335 | break; |
||
336 | |||
337 | case VC4_LOADSTORE_TILE_BUFFER_Z: |
||
338 | case VC4_LOADSTORE_TILE_BUFFER_ZS: |
||
339 | cpp = 4; |
||
340 | break; |
||
341 | |||
342 | default: |
||
343 | DRM_ERROR("Load/store type %d unsupported\n", buffer_type); |
||
344 | return -EINVAL; |
||
345 | } |
||
346 | |||
347 | if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) |
||
348 | return -EINVAL; |
||
349 | |||
350 | untrusted_address = *(uint32_t *)(untrusted + 2); |
||
351 | offset = untrusted_address & ~0xf; |
||
352 | |||
353 | if (!check_tex_size(exec, fbo, offset, |
||
354 | ((packet_b0 & |
||
355 | VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK) >> |
||
356 | VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT), |
||
357 | exec->fb_width, exec->fb_height, cpp)) { |
||
358 | return -EINVAL; |
||
359 | } |
||
360 | |||
361 | *(uint32_t *)(validated + 2) = (offset + fbo->paddr + |
||
362 | (untrusted_address & 0xf)); |
||
363 | |||
364 | return 0; |
||
365 | } |
||
366 | |||
367 | static int |
||
368 | validate_indexed_prim_list(VALIDATE_ARGS) |
||
369 | { |
||
370 | struct drm_gem_cma_object *ib; |
||
371 | uint32_t length = *(uint32_t *)(untrusted + 1); |
||
372 | uint32_t offset = *(uint32_t *)(untrusted + 5); |
||
373 | uint32_t max_index = *(uint32_t *)(untrusted + 9); |
||
374 | uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; |
||
375 | struct vc4_shader_state *shader_state; |
||
376 | |||
377 | if (exec->found_increment_semaphore_packet) { |
||
378 | DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); |
||
379 | return -EINVAL; |
||
380 | } |
||
381 | |||
382 | /* Check overflow condition */ |
||
383 | if (exec->shader_state_count == 0) { |
||
384 | DRM_ERROR("shader state must precede primitives\n"); |
||
385 | return -EINVAL; |
||
386 | } |
||
387 | shader_state = &exec->shader_state[exec->shader_state_count - 1]; |
||
388 | |||
389 | if (max_index > shader_state->max_index) |
||
390 | shader_state->max_index = max_index; |
||
391 | |||
392 | if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) |
||
393 | return -EINVAL; |
||
394 | |||
395 | if (offset > ib->base.size || |
||
396 | (ib->base.size - offset) / index_size < length) { |
||
397 | DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", |
||
398 | offset, length, index_size, ib->base.size); |
||
399 | return -EINVAL; |
||
400 | } |
||
401 | |||
402 | *(uint32_t *)(validated + 5) = ib->paddr + offset; |
||
403 | |||
404 | return 0; |
||
405 | } |
||
406 | |||
407 | static int |
||
408 | validate_gl_array_primitive(VALIDATE_ARGS) |
||
409 | { |
||
410 | uint32_t length = *(uint32_t *)(untrusted + 1); |
||
411 | uint32_t base_index = *(uint32_t *)(untrusted + 5); |
||
412 | uint32_t max_index; |
||
413 | struct vc4_shader_state *shader_state; |
||
414 | |||
415 | if (exec->found_increment_semaphore_packet) { |
||
416 | DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); |
||
417 | return -EINVAL; |
||
418 | } |
||
419 | |||
420 | /* Check overflow condition */ |
||
421 | if (exec->shader_state_count == 0) { |
||
422 | DRM_ERROR("shader state must precede primitives\n"); |
||
423 | return -EINVAL; |
||
424 | } |
||
425 | shader_state = &exec->shader_state[exec->shader_state_count - 1]; |
||
426 | |||
427 | if (length + base_index < length) { |
||
428 | DRM_ERROR("primitive vertex count overflow\n"); |
||
429 | return -EINVAL; |
||
430 | } |
||
431 | max_index = length + base_index - 1; |
||
432 | |||
433 | if (max_index > shader_state->max_index) |
||
434 | shader_state->max_index = max_index; |
||
435 | |||
436 | return 0; |
||
437 | } |
||
438 | |||
439 | static int |
||
440 | validate_gl_shader_state(VALIDATE_ARGS) |
||
441 | { |
||
442 | uint32_t i = exec->shader_state_count++; |
||
443 | |||
444 | if (i >= exec->shader_state_size) { |
||
445 | DRM_ERROR("More requests for shader states than declared\n"); |
||
446 | return -EINVAL; |
||
447 | } |
||
448 | |||
449 | exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; |
||
450 | exec->shader_state[i].addr = *(uint32_t *)untrusted; |
||
451 | exec->shader_state[i].max_index = 0; |
||
452 | |||
453 | if (exec->shader_state[i].addr & ~0xf) { |
||
454 | DRM_ERROR("high bits set in GL shader rec reference\n"); |
||
455 | return -EINVAL; |
||
456 | } |
||
457 | |||
458 | *(uint32_t *)validated = (exec->shader_rec_p + |
||
459 | exec->shader_state[i].addr); |
||
460 | |||
461 | exec->shader_rec_p += |
||
462 | roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); |
||
463 | |||
464 | return 0; |
||
465 | } |
||
466 | |||
467 | static int |
||
468 | validate_nv_shader_state(VALIDATE_ARGS) |
||
469 | { |
||
470 | uint32_t i = exec->shader_state_count++; |
||
471 | |||
472 | if (i >= exec->shader_state_size) { |
||
473 | DRM_ERROR("More requests for shader states than declared\n"); |
||
474 | return -EINVAL; |
||
475 | } |
||
476 | |||
477 | exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; |
||
478 | exec->shader_state[i].addr = *(uint32_t *)untrusted; |
||
479 | |||
480 | if (exec->shader_state[i].addr & 15) { |
||
481 | DRM_ERROR("NV shader state address 0x%08x misaligned\n", |
||
482 | exec->shader_state[i].addr); |
||
483 | return -EINVAL; |
||
484 | } |
||
485 | |||
486 | *(uint32_t *)validated = (exec->shader_state[i].addr + |
||
487 | exec->shader_rec_p); |
||
488 | |||
489 | return 0; |
||
490 | } |
||
491 | |||
492 | static int |
||
493 | validate_tile_binning_config(VALIDATE_ARGS) |
||
494 | { |
||
495 | struct drm_gem_cma_object *tile_allocation; |
||
496 | struct drm_gem_cma_object *tile_state_data_array; |
||
497 | uint8_t flags; |
||
498 | uint32_t tile_allocation_size; |
||
499 | |||
500 | if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &tile_allocation) || |
||
501 | !vc4_use_handle(exec, 1, VC4_MODE_TSDA, &tile_state_data_array)) |
||
502 | return -EINVAL; |
||
503 | |||
504 | if (exec->found_tile_binning_mode_config_packet) { |
||
505 | DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); |
||
506 | return -EINVAL; |
||
507 | } |
||
508 | exec->found_tile_binning_mode_config_packet = true; |
||
509 | |||
510 | exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); |
||
511 | exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); |
||
512 | flags = *(uint8_t *)(untrusted + 14); |
||
513 | |||
514 | if (exec->bin_tiles_x == 0 || |
||
515 | exec->bin_tiles_y == 0) { |
||
516 | DRM_ERROR("Tile binning config of %dx%d too small\n", |
||
517 | exec->bin_tiles_x, exec->bin_tiles_y); |
||
518 | return -EINVAL; |
||
519 | } |
||
520 | |||
521 | /* Our validation relies on the user not getting to set up their own |
||
522 | * tile state/tile allocation BO contents. |
||
523 | */ |
||
524 | if (!(flags & VC4_BIN_CONFIG_AUTO_INIT_TSDA)) { |
||
525 | DRM_ERROR("binning config missing " |
||
526 | "VC4_BIN_CONFIG_AUTO_INIT_TSDA\n"); |
||
527 | return -EINVAL; |
||
528 | } |
||
529 | |||
530 | if (flags & (VC4_BIN_CONFIG_DB_NON_MS | |
||
531 | VC4_BIN_CONFIG_TILE_BUFFER_64BIT | |
||
532 | VC4_BIN_CONFIG_MS_MODE_4X)) { |
||
533 | DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); |
||
534 | return -EINVAL; |
||
535 | } |
||
536 | |||
537 | if (*(uint32_t *)(untrusted + 0) != 0) { |
||
538 | DRM_ERROR("tile allocation offset != 0 unsupported\n"); |
||
539 | return -EINVAL; |
||
540 | } |
||
541 | tile_allocation_size = *(uint32_t *)(untrusted + 4); |
||
542 | if (tile_allocation_size > tile_allocation->base.size) { |
||
543 | DRM_ERROR("tile allocation size %d > BO size %d\n", |
||
544 | tile_allocation_size, tile_allocation->base.size); |
||
545 | return -EINVAL; |
||
546 | } |
||
547 | *(uint32_t *)validated = tile_allocation->paddr; |
||
548 | exec->tile_alloc_bo = tile_allocation; |
||
549 | |||
550 | exec->tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3)); |
||
551 | if (exec->bin_tiles_x * exec->bin_tiles_y * |
||
552 | exec->tile_alloc_init_block_size > tile_allocation_size) { |
||
553 | DRM_ERROR("tile init exceeds tile alloc size (%d vs %d)\n", |
||
554 | exec->bin_tiles_x * exec->bin_tiles_y * |
||
555 | exec->tile_alloc_init_block_size, |
||
556 | tile_allocation_size); |
||
557 | return -EINVAL; |
||
558 | } |
||
559 | if (*(uint32_t *)(untrusted + 8) != 0) { |
||
560 | DRM_ERROR("TSDA offset != 0 unsupported\n"); |
||
561 | return -EINVAL; |
||
562 | } |
||
563 | if (exec->bin_tiles_x * exec->bin_tiles_y * 48 > |
||
564 | tile_state_data_array->base.size) { |
||
565 | DRM_ERROR("TSDA of %db too small for %dx%d bin config\n", |
||
566 | tile_state_data_array->base.size, |
||
567 | exec->bin_tiles_x, exec->bin_tiles_y); |
||
568 | } |
||
569 | *(uint32_t *)(validated + 8) = tile_state_data_array->paddr; |
||
570 | |||
571 | return 0; |
||
572 | } |
||
573 | |||
574 | static int |
||
575 | validate_tile_rendering_mode_config(VALIDATE_ARGS) |
||
576 | { |
||
577 | struct drm_gem_cma_object *fbo; |
||
578 | uint32_t flags, offset, cpp; |
||
579 | |||
580 | if (exec->found_tile_rendering_mode_config_packet) { |
||
581 | DRM_ERROR("Duplicate VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); |
||
582 | return -EINVAL; |
||
583 | } |
||
584 | exec->found_tile_rendering_mode_config_packet = true; |
||
585 | |||
586 | if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) |
||
587 | return -EINVAL; |
||
588 | |||
589 | exec->fb_width = *(uint16_t *)(untrusted + 4); |
||
590 | exec->fb_height = *(uint16_t *)(untrusted + 6); |
||
591 | |||
592 | flags = *(uint16_t *)(untrusted + 8); |
||
593 | if ((flags & VC4_RENDER_CONFIG_FORMAT_MASK) == |
||
594 | VC4_RENDER_CONFIG_FORMAT_RGBA8888) { |
||
595 | cpp = 4; |
||
596 | } else { |
||
597 | cpp = 2; |
||
598 | } |
||
599 | |||
600 | offset = *(uint32_t *)untrusted; |
||
601 | if (!check_tex_size(exec, fbo, offset, |
||
602 | ((flags & |
||
603 | VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK) >> |
||
604 | VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT), |
||
605 | exec->fb_width, exec->fb_height, cpp)) { |
||
606 | return -EINVAL; |
||
607 | } |
||
608 | |||
609 | *(uint32_t *)validated = fbo->paddr + offset; |
||
610 | |||
611 | return 0; |
||
612 | } |
||
613 | |||
614 | static int |
||
615 | validate_tile_coordinates(VALIDATE_ARGS) |
||
616 | { |
||
617 | uint8_t tile_x = *(uint8_t *)(untrusted + 0); |
||
618 | uint8_t tile_y = *(uint8_t *)(untrusted + 1); |
||
619 | |||
620 | if (tile_x * 64 >= exec->fb_width || tile_y * 64 >= exec->fb_height) { |
||
621 | DRM_ERROR("Tile coordinates %d,%d > render config %dx%d\n", |
||
622 | tile_x, tile_y, exec->fb_width, exec->fb_height); |
||
623 | return -EINVAL; |
||
624 | } |
||
625 | |||
626 | return 0; |
||
627 | } |
||
628 | |||
629 | static int |
||
630 | validate_gem_handles(VALIDATE_ARGS) |
||
631 | { |
||
632 | memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); |
||
633 | return 0; |
||
634 | } |
||
635 | |||
636 | static const struct cmd_info { |
||
637 | bool bin; |
||
638 | bool render; |
||
639 | uint16_t len; |
||
640 | const char *name; |
||
641 | int (*func)(struct vc4_exec_info *exec, void *validated, |
||
642 | void *untrusted); |
||
643 | } cmd_info[] = { |
||
644 | [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL }, |
||
645 | [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL }, |
||
646 | [VC4_PACKET_FLUSH] = { 1, 1, 1, "flush", NULL }, |
||
647 | [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", validate_flush_all }, |
||
648 | [VC4_PACKET_START_TILE_BINNING] = { 1, 0, 1, "start tile binning", validate_start_tile_binning }, |
||
649 | [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", validate_increment_semaphore }, |
||
650 | [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 0, 1, 1, "wait on semaphore", validate_wait_on_semaphore }, |
||
651 | /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but |
||
652 | * we only use it from the render CL in order to jump into the tile |
||
653 | * allocation BO. |
||
654 | */ |
||
655 | [VC4_PACKET_BRANCH_TO_SUB_LIST] = { 0, 1, 5, "branch to sublist", validate_branch_to_sublist }, |
||
656 | [VC4_PACKET_STORE_MS_TILE_BUFFER] = { 0, 1, 1, "store MS resolved tile color buffer", NULL }, |
||
657 | [VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, |
||
658 | |||
659 | [VC4_PACKET_STORE_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Store Tile Buffer General", validate_loadstore_tile_buffer_general }, |
||
660 | [VC4_PACKET_LOAD_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Load Tile Buffer General", validate_loadstore_tile_buffer_general }, |
||
661 | |||
662 | [VC4_PACKET_GL_INDEXED_PRIMITIVE] = { 1, 1, 14, "Indexed Primitive List", validate_indexed_prim_list }, |
||
663 | |||
664 | [VC4_PACKET_GL_ARRAY_PRIMITIVE] = { 1, 1, 10, "Vertex Array Primitives", validate_gl_array_primitive }, |
||
665 | |||
666 | /* This is only used by clipped primitives (packets 48 and 49), which |
||
667 | * we don't support parsing yet. |
||
668 | */ |
||
669 | [VC4_PACKET_PRIMITIVE_LIST_FORMAT] = { 1, 1, 2, "primitive list format", NULL }, |
||
670 | |||
671 | [VC4_PACKET_GL_SHADER_STATE] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state }, |
||
672 | [VC4_PACKET_NV_SHADER_STATE] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state }, |
||
673 | |||
674 | [VC4_PACKET_CONFIGURATION_BITS] = { 1, 1, 4, "configuration bits", NULL }, |
||
675 | [VC4_PACKET_FLAT_SHADE_FLAGS] = { 1, 1, 5, "flat shade flags", NULL }, |
||
676 | [VC4_PACKET_POINT_SIZE] = { 1, 1, 5, "point size", NULL }, |
||
677 | [VC4_PACKET_LINE_WIDTH] = { 1, 1, 5, "line width", NULL }, |
||
678 | [VC4_PACKET_RHT_X_BOUNDARY] = { 1, 1, 3, "RHT X boundary", NULL }, |
||
679 | [VC4_PACKET_DEPTH_OFFSET] = { 1, 1, 5, "Depth Offset", NULL }, |
||
680 | [VC4_PACKET_CLIP_WINDOW] = { 1, 1, 9, "Clip Window", NULL }, |
||
681 | [VC4_PACKET_VIEWPORT_OFFSET] = { 1, 1, 5, "Viewport Offset", NULL }, |
||
682 | [VC4_PACKET_CLIPPER_XY_SCALING] = { 1, 1, 9, "Clipper XY Scaling", NULL }, |
||
683 | /* Note: The docs say this was also 105, but it was 106 in the |
||
684 | * initial userland code drop. |
||
685 | */ |
||
686 | [VC4_PACKET_CLIPPER_Z_SCALING] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL }, |
||
687 | |||
688 | [VC4_PACKET_TILE_BINNING_MODE_CONFIG] = { 1, 0, 16, "tile binning configuration", validate_tile_binning_config }, |
||
689 | |||
690 | [VC4_PACKET_TILE_RENDERING_MODE_CONFIG] = { 0, 1, 11, "tile rendering mode configuration", validate_tile_rendering_mode_config}, |
||
691 | |||
692 | [VC4_PACKET_CLEAR_COLORS] = { 0, 1, 14, "Clear Colors", NULL }, |
||
693 | |||
694 | [VC4_PACKET_TILE_COORDINATES] = { 0, 1, 3, "Tile Coordinates", validate_tile_coordinates }, |
||
695 | |||
696 | [VC4_PACKET_GEM_HANDLES] = { 1, 1, 9, "GEM handles", validate_gem_handles }, |
||
697 | }; |
||
698 | |||
699 | int |
||
700 | vc4_validate_cl(struct drm_device *dev, |
||
701 | void *validated, |
||
702 | void *unvalidated, |
||
703 | uint32_t len, |
||
704 | bool is_bin, |
||
705 | bool has_bin, |
||
706 | struct vc4_exec_info *exec) |
||
707 | { |
||
708 | uint32_t dst_offset = 0; |
||
709 | uint32_t src_offset = 0; |
||
710 | |||
711 | while (src_offset < len) { |
||
712 | void *dst_pkt = validated + dst_offset; |
||
713 | void *src_pkt = unvalidated + src_offset; |
||
714 | u8 cmd = *(uint8_t *)src_pkt; |
||
715 | const struct cmd_info *info; |
||
716 | |||
717 | if (cmd > ARRAY_SIZE(cmd_info)) { |
||
718 | DRM_ERROR("0x%08x: packet %d out of bounds\n", |
||
719 | src_offset, cmd); |
||
720 | return -EINVAL; |
||
721 | } |
||
722 | |||
723 | info = &cmd_info[cmd]; |
||
724 | if (!info->name) { |
||
725 | DRM_ERROR("0x%08x: packet %d invalid\n", |
||
726 | src_offset, cmd); |
||
727 | return -EINVAL; |
||
728 | } |
||
729 | |||
730 | #if 0 |
||
731 | DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", |
||
732 | src_offset, cmd, info->name, info->len); |
||
733 | #endif |
||
734 | |||
735 | if ((is_bin && !info->bin) || |
||
736 | (!is_bin && !info->render)) { |
||
737 | DRM_ERROR("0x%08x: packet %d (%s) invalid for %s\n", |
||
738 | src_offset, cmd, info->name, |
||
739 | is_bin ? "binner" : "render"); |
||
740 | return -EINVAL; |
||
741 | } |
||
742 | |||
743 | if (src_offset + info->len > len) { |
||
744 | DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " |
||
745 | "exceeds bounds (0x%08x)\n", |
||
746 | src_offset, cmd, info->name, info->len, |
||
747 | src_offset + len); |
||
748 | return -EINVAL; |
||
749 | } |
||
750 | |||
751 | if (cmd != VC4_PACKET_GEM_HANDLES) |
||
752 | memcpy(dst_pkt, src_pkt, info->len); |
||
753 | |||
754 | if (info->func && info->func(exec, |
||
755 | dst_pkt + 1, |
||
756 | src_pkt + 1)) { |
||
757 | DRM_ERROR("0x%08x: packet %d (%s) failed to " |
||
758 | "validate\n", |
||
759 | src_offset, cmd, info->name); |
||
760 | return -EINVAL; |
||
761 | } |
||
762 | |||
763 | src_offset += info->len; |
||
764 | /* GEM handle loading doesn't produce HW packets. */ |
||
765 | if (cmd != VC4_PACKET_GEM_HANDLES) |
||
766 | dst_offset += info->len; |
||
767 | |||
768 | /* When the CL hits halt, it'll stop reading anything else. */ |
||
769 | if (cmd == VC4_PACKET_HALT) |
||
770 | break; |
||
771 | } |
||
772 | |||
773 | if (is_bin) { |
||
774 | exec->ct0ea = exec->ct0ca + dst_offset; |
||
775 | |||
776 | if (has_bin && !exec->found_start_tile_binning_packet) { |
||
777 | DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); |
||
778 | return -EINVAL; |
||
779 | } |
||
780 | } else { |
||
781 | if (!exec->found_tile_rendering_mode_config_packet) { |
||
782 | DRM_ERROR("Render CL missing VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); |
||
783 | return -EINVAL; |
||
784 | } |
||
785 | |||
786 | /* Make sure that they actually consumed the semaphore |
||
787 | * increment from the bin CL. Otherwise a later submit would |
||
788 | * have render execute immediately. |
||
789 | */ |
||
790 | if (exec->found_wait_on_semaphore_packet != has_bin) { |
||
791 | DRM_ERROR("Render CL %s VC4_PACKET_WAIT_ON_SEMAPHORE\n", |
||
792 | exec->found_wait_on_semaphore_packet ? |
||
793 | "has" : "missing"); |
||
794 | return -EINVAL; |
||
795 | } |
||
796 | exec->ct1ea = exec->ct1ca + dst_offset; |
||
797 | } |
||
798 | |||
799 | return 0; |
||
800 | } |
||
801 | |||
802 | static bool |
||
803 | reloc_tex(struct vc4_exec_info *exec, |
||
804 | void *uniform_data_u, |
||
805 | struct vc4_texture_sample_info *sample, |
||
806 | uint32_t texture_handle_index) |
||
807 | |||
808 | { |
||
809 | struct drm_gem_cma_object *tex; |
||
810 | uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); |
||
811 | uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); |
||
812 | uint32_t p2 = (sample->p_offset[2] != ~0 ? |
||
813 | *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); |
||
814 | uint32_t p3 = (sample->p_offset[3] != ~0 ? |
||
815 | *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); |
||
816 | uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; |
||
817 | uint32_t offset = p0 & ~0xfff; |
||
818 | uint32_t miplevels = (p0 & 15); |
||
819 | uint32_t width = (p1 >> 8) & 2047; |
||
820 | uint32_t height = (p1 >> 20) & 2047; |
||
821 | uint32_t cpp, tiling_format, utile_w, utile_h; |
||
822 | uint32_t i; |
||
823 | uint32_t cube_map_stride = 0; |
||
824 | enum vc4_texture_data_type type; |
||
825 | |||
826 | if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) |
||
827 | return false; |
||
828 | |||
829 | if (sample->is_direct) { |
||
830 | uint32_t remaining_size = tex->base.size - p0; |
||
831 | if (p0 > tex->base.size - 4) { |
||
832 | DRM_ERROR("UBO offset greater than UBO size\n"); |
||
833 | return false; |
||
834 | } |
||
835 | if (p1 > remaining_size - 4) { |
||
836 | DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); |
||
837 | return false; |
||
838 | } |
||
839 | *validated_p0 = tex->paddr + p0; |
||
840 | return true; |
||
841 | } |
||
842 | |||
843 | if (width == 0) |
||
844 | width = 2048; |
||
845 | if (height == 0) |
||
846 | height = 2048; |
||
847 | |||
848 | if (p0 & (1 << 9)) { |
||
849 | if ((p2 & (3 << 30)) == (1 << 30)) |
||
850 | cube_map_stride = p2 & 0x3ffff000; |
||
851 | if ((p3 & (3 << 30)) == (1 << 30)) { |
||
852 | if (cube_map_stride) { |
||
853 | DRM_ERROR("Cube map stride set twice\n"); |
||
854 | return false; |
||
855 | } |
||
856 | |||
857 | cube_map_stride = p3 & 0x3ffff000; |
||
858 | } |
||
859 | if (!cube_map_stride) { |
||
860 | DRM_ERROR("Cube map stride not set\n"); |
||
861 | return false; |
||
862 | } |
||
863 | } |
||
864 | |||
865 | type = ((p0 >> 4) & 15) | ((p1 >> 31) << 4); |
||
866 | |||
867 | switch (type) { |
||
868 | case VC4_TEXTURE_TYPE_RGBA8888: |
||
869 | case VC4_TEXTURE_TYPE_RGBX8888: |
||
870 | case VC4_TEXTURE_TYPE_RGBA32R: |
||
871 | cpp = 4; |
||
872 | break; |
||
873 | case VC4_TEXTURE_TYPE_RGBA4444: |
||
874 | case VC4_TEXTURE_TYPE_RGBA5551: |
||
875 | case VC4_TEXTURE_TYPE_RGB565: |
||
876 | case VC4_TEXTURE_TYPE_LUMALPHA: |
||
877 | case VC4_TEXTURE_TYPE_S16F: |
||
878 | case VC4_TEXTURE_TYPE_S16: |
||
879 | cpp = 2; |
||
880 | break; |
||
881 | case VC4_TEXTURE_TYPE_LUMINANCE: |
||
882 | case VC4_TEXTURE_TYPE_ALPHA: |
||
883 | case VC4_TEXTURE_TYPE_S8: |
||
884 | cpp = 1; |
||
885 | break; |
||
886 | case VC4_TEXTURE_TYPE_ETC1: |
||
887 | case VC4_TEXTURE_TYPE_BW1: |
||
888 | case VC4_TEXTURE_TYPE_A4: |
||
889 | case VC4_TEXTURE_TYPE_A1: |
||
890 | case VC4_TEXTURE_TYPE_RGBA64: |
||
891 | case VC4_TEXTURE_TYPE_YUV422R: |
||
892 | default: |
||
893 | DRM_ERROR("Texture format %d unsupported\n", type); |
||
894 | return false; |
||
895 | } |
||
896 | utile_w = utile_width(cpp); |
||
897 | utile_h = utile_height(cpp); |
||
898 | |||
899 | if (type == VC4_TEXTURE_TYPE_RGBA32R) { |
||
900 | tiling_format = VC4_TILING_FORMAT_LINEAR; |
||
901 | } else { |
||
902 | if (size_is_lt(width, height, cpp)) |
||
903 | tiling_format = VC4_TILING_FORMAT_LT; |
||
904 | else |
||
905 | tiling_format = VC4_TILING_FORMAT_T; |
||
906 | } |
||
907 | |||
908 | if (!check_tex_size(exec, tex, offset + cube_map_stride * 5, |
||
909 | tiling_format, width, height, cpp)) { |
||
910 | return false; |
||
911 | } |
||
912 | |||
913 | /* The mipmap levels are stored before the base of the texture. Make |
||
914 | * sure there is actually space in the BO. |
||
915 | */ |
||
916 | for (i = 1; i <= miplevels; i++) { |
||
917 | uint32_t level_width = max(width >> i, 1u); |
||
918 | uint32_t level_height = max(height >> i, 1u); |
||
919 | uint32_t aligned_width, aligned_height; |
||
920 | uint32_t level_size; |
||
921 | |||
922 | /* Once the levels get small enough, they drop from T to LT. */ |
||
923 | if (tiling_format == VC4_TILING_FORMAT_T && |
||
924 | size_is_lt(level_width, level_height, cpp)) { |
||
925 | tiling_format = VC4_TILING_FORMAT_LT; |
||
926 | } |
||
927 | |||
928 | switch (tiling_format) { |
||
929 | case VC4_TILING_FORMAT_T: |
||
930 | aligned_width = roundup(level_width, utile_w * 8); |
||
931 | aligned_height = roundup(level_height, utile_h * 8); |
||
932 | break; |
||
933 | case VC4_TILING_FORMAT_LT: |
||
934 | aligned_width = roundup(level_width, utile_w); |
||
935 | aligned_height = roundup(level_height, utile_h); |
||
936 | break; |
||
937 | default: |
||
938 | aligned_width = roundup(level_width, utile_w); |
||
939 | aligned_height = level_height; |
||
940 | break; |
||
941 | } |
||
942 | |||
943 | level_size = aligned_width * cpp * aligned_height; |
||
944 | |||
945 | if (offset < level_size) { |
||
946 | DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " |
||
947 | "overflowed buffer bounds (offset %d)\n", |
||
948 | i, level_width, level_height, |
||
949 | aligned_width, aligned_height, |
||
950 | level_size, offset); |
||
951 | return false; |
||
952 | } |
||
953 | |||
954 | offset -= level_size; |
||
955 | } |
||
956 | |||
957 | *validated_p0 = tex->paddr + p0; |
||
958 | |||
959 | return true; |
||
960 | } |
||
961 | |||
962 | static int |
||
963 | validate_shader_rec(struct drm_device *dev, |
||
964 | struct vc4_exec_info *exec, |
||
965 | struct vc4_shader_state *state) |
||
966 | { |
||
967 | uint32_t *src_handles; |
||
968 | void *pkt_u, *pkt_v; |
||
969 | enum shader_rec_reloc_type { |
||
970 | RELOC_CODE, |
||
971 | RELOC_VBO, |
||
972 | }; |
||
973 | struct shader_rec_reloc { |
||
974 | enum shader_rec_reloc_type type; |
||
975 | uint32_t offset; |
||
976 | }; |
||
977 | static const struct shader_rec_reloc gl_relocs[] = { |
||
978 | { RELOC_CODE, 4 }, /* fs */ |
||
979 | { RELOC_CODE, 16 }, /* vs */ |
||
980 | { RELOC_CODE, 28 }, /* cs */ |
||
981 | }; |
||
982 | static const struct shader_rec_reloc nv_relocs[] = { |
||
983 | { RELOC_CODE, 4 }, /* fs */ |
||
984 | { RELOC_VBO, 12 } |
||
985 | }; |
||
986 | const struct shader_rec_reloc *relocs; |
||
987 | struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; |
||
988 | uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; |
||
989 | int i; |
||
990 | struct vc4_validated_shader_info *validated_shader = NULL; |
||
991 | |||
992 | if (state->packet == VC4_PACKET_NV_SHADER_STATE) { |
||
993 | relocs = nv_relocs; |
||
994 | nr_fixed_relocs = ARRAY_SIZE(nv_relocs); |
||
995 | |||
996 | packet_size = 16; |
||
997 | } else { |
||
998 | relocs = gl_relocs; |
||
999 | nr_fixed_relocs = ARRAY_SIZE(gl_relocs); |
||
1000 | |||
1001 | nr_attributes = state->addr & 0x7; |
||
1002 | if (nr_attributes == 0) |
||
1003 | nr_attributes = 8; |
||
1004 | packet_size = gl_shader_rec_size(state->addr); |
||
1005 | } |
||
1006 | nr_relocs = nr_fixed_relocs + nr_attributes; |
||
1007 | |||
1008 | if (nr_relocs * 4 > exec->shader_rec_size) { |
||
1009 | DRM_ERROR("overflowed shader recs reading %d handles " |
||
1010 | "from %d bytes left\n", |
||
1011 | nr_relocs, exec->shader_rec_size); |
||
1012 | return -EINVAL; |
||
1013 | } |
||
1014 | src_handles = exec->shader_rec_u; |
||
1015 | exec->shader_rec_u += nr_relocs * 4; |
||
1016 | exec->shader_rec_size -= nr_relocs * 4; |
||
1017 | |||
1018 | if (packet_size > exec->shader_rec_size) { |
||
1019 | DRM_ERROR("overflowed shader recs copying %db packet " |
||
1020 | "from %d bytes left\n", |
||
1021 | packet_size, exec->shader_rec_size); |
||
1022 | return -EINVAL; |
||
1023 | } |
||
1024 | pkt_u = exec->shader_rec_u; |
||
1025 | pkt_v = exec->shader_rec_v; |
||
1026 | memcpy(pkt_v, pkt_u, packet_size); |
||
1027 | exec->shader_rec_u += packet_size; |
||
1028 | /* Shader recs have to be aligned to 16 bytes (due to the attribute |
||
1029 | * flags being in the low bytes), so round the next validated shader |
||
1030 | * rec address up. This should be safe, since we've got so many |
||
1031 | * relocations in a shader rec packet. |
||
1032 | */ |
||
1033 | BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); |
||
1034 | exec->shader_rec_v += roundup(packet_size, 16); |
||
1035 | exec->shader_rec_size -= packet_size; |
||
1036 | |||
1037 | for (i = 0; i < nr_relocs; i++) { |
||
1038 | enum vc4_bo_mode mode; |
||
1039 | |||
1040 | if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) |
||
1041 | mode = VC4_MODE_SHADER; |
||
1042 | else |
||
1043 | mode = VC4_MODE_RENDER; |
||
1044 | |||
1045 | if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { |
||
1046 | return false; |
||
1047 | } |
||
1048 | } |
||
1049 | |||
1050 | for (i = 0; i < nr_fixed_relocs; i++) { |
||
1051 | uint32_t o = relocs[i].offset; |
||
1052 | uint32_t src_offset = *(uint32_t *)(pkt_u + o); |
||
1053 | uint32_t *texture_handles_u; |
||
1054 | void *uniform_data_u; |
||
1055 | uint32_t tex; |
||
1056 | |||
1057 | *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; |
||
1058 | |||
1059 | switch (relocs[i].type) { |
||
1060 | case RELOC_CODE: |
||
1061 | if (src_offset != 0) { |
||
1062 | DRM_ERROR("Shaders must be at offset 0 of " |
||
1063 | "the BO.\n"); |
||
1064 | goto fail; |
||
1065 | } |
||
1066 | |||
1067 | kfree(validated_shader); |
||
1068 | validated_shader = vc4_validate_shader(bo[i]); |
||
1069 | if (!validated_shader) |
||
1070 | goto fail; |
||
1071 | |||
1072 | if (validated_shader->uniforms_src_size > |
||
1073 | exec->uniforms_size) { |
||
1074 | DRM_ERROR("Uniforms src buffer overflow\n"); |
||
1075 | goto fail; |
||
1076 | } |
||
1077 | |||
1078 | texture_handles_u = exec->uniforms_u; |
||
1079 | uniform_data_u = (texture_handles_u + |
||
1080 | validated_shader->num_texture_samples); |
||
1081 | |||
1082 | memcpy(exec->uniforms_v, uniform_data_u, |
||
1083 | validated_shader->uniforms_size); |
||
1084 | |||
1085 | for (tex = 0; |
||
1086 | tex < validated_shader->num_texture_samples; |
||
1087 | tex++) { |
||
1088 | if (!reloc_tex(exec, |
||
1089 | uniform_data_u, |
||
1090 | &validated_shader->texture_samples[tex], |
||
1091 | texture_handles_u[tex])) { |
||
1092 | goto fail; |
||
1093 | } |
||
1094 | } |
||
1095 | |||
1096 | *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; |
||
1097 | |||
1098 | exec->uniforms_u += validated_shader->uniforms_src_size; |
||
1099 | exec->uniforms_v += validated_shader->uniforms_size; |
||
1100 | exec->uniforms_p += validated_shader->uniforms_size; |
||
1101 | |||
1102 | break; |
||
1103 | |||
1104 | case RELOC_VBO: |
||
1105 | break; |
||
1106 | } |
||
1107 | } |
||
1108 | |||
1109 | for (i = 0; i < nr_attributes; i++) { |
||
1110 | struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; |
||
1111 | uint32_t o = 36 + i * 8; |
||
1112 | uint32_t offset = *(uint32_t *)(pkt_u + o + 0); |
||
1113 | uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; |
||
1114 | uint32_t stride = *(uint8_t *)(pkt_u + o + 5); |
||
1115 | uint32_t max_index; |
||
1116 | |||
1117 | if (state->addr & 0x8) |
||
1118 | stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; |
||
1119 | |||
1120 | if (vbo->base.size < offset || |
||
1121 | vbo->base.size - offset < attr_size) { |
||
1122 | DRM_ERROR("BO offset overflow (%d + %d > %d)\n", |
||
1123 | offset, attr_size, vbo->base.size); |
||
1124 | return -EINVAL; |
||
1125 | } |
||
1126 | |||
1127 | if (stride != 0) { |
||
1128 | max_index = ((vbo->base.size - offset - attr_size) / |
||
1129 | stride); |
||
1130 | if (state->max_index > max_index) { |
||
1131 | DRM_ERROR("primitives use index %d out of supplied %d\n", |
||
1132 | state->max_index, max_index); |
||
1133 | return -EINVAL; |
||
1134 | } |
||
1135 | } |
||
1136 | |||
1137 | *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; |
||
1138 | } |
||
1139 | |||
1140 | kfree(validated_shader); |
||
1141 | |||
1142 | return 0; |
||
1143 | |||
1144 | fail: |
||
1145 | kfree(validated_shader); |
||
1146 | return -EINVAL; |
||
1147 | } |
||
1148 | |||
1149 | int |
||
1150 | vc4_validate_shader_recs(struct drm_device *dev, |
||
1151 | struct vc4_exec_info *exec) |
||
1152 | { |
||
1153 | uint32_t i; |
||
1154 | int ret = 0; |
||
1155 | |||
1156 | for (i = 0; i < exec->shader_state_count; i++) { |
||
1157 | ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); |
||
1158 | if (ret) |
||
1159 | return ret; |
||
1160 | } |
||
1161 | |||
1162 | return ret; |
||
1163 | }>>>>>>>>>=>><>><>><>><>><>><>>><>>>>>=>=> |