vc4: Add a bunch of validation of render mode configuration.
[mesa.git] / src / gallium / drivers / vc4 / vc4_simulator_validate.c
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_simulator_validate.h"
43 #include "vc4_packet.h"
44
45 #define VALIDATE_ARGS \
46 struct exec_info *exec, \
47 void *validated, \
48 void *untrusted
49
50 static bool
51 vc4_use_bo(struct exec_info *exec,
52 uint32_t hindex,
53 enum vc4_bo_mode mode,
54 struct drm_gem_cma_object **obj)
55 {
56 *obj = NULL;
57
58 if (hindex >= exec->bo_count) {
59 DRM_ERROR("BO index %d greater than BO count %d\n",
60 hindex, exec->bo_count);
61 return false;
62 }
63
64 if (exec->bo[hindex].mode != mode) {
65 if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) {
66 exec->bo[hindex].mode = mode;
67 } else {
68 DRM_ERROR("BO index %d reused with mode %d vs %d\n",
69 hindex, exec->bo[hindex].mode, mode);
70 return false;
71 }
72 }
73
74 *obj = exec->bo[hindex].bo;
75 return true;
76 }
77
78 static bool
79 vc4_use_handle(struct exec_info *exec,
80 uint32_t gem_handles_packet_index,
81 enum vc4_bo_mode mode,
82 struct drm_gem_cma_object **obj)
83 {
84 return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index],
85 mode, obj);
86 }
87
88 static uint32_t
89 gl_shader_rec_size(uint32_t pointer_bits)
90 {
91 uint32_t attribute_count = pointer_bits & 7;
92 bool extended = pointer_bits & 8;
93
94 if (attribute_count == 0)
95 attribute_count = 8;
96
97 return 36 + attribute_count * (extended ? 12 : 8);
98 }
99
100 static bool
101 check_fbo_size(struct exec_info *exec, struct drm_gem_cma_object *fbo,
102 uint32_t offset, uint8_t tiling_format, uint8_t cpp)
103 {
104 uint32_t width_align, height_align;
105 uint32_t aligned_row_len, aligned_h, size;
106
107 switch (tiling_format) {
108 case VC4_TILING_FORMAT_LINEAR:
109 width_align = 16;
110 height_align = 1;
111 break;
112 case VC4_TILING_FORMAT_T:
113 width_align = 128;
114 height_align = 32;
115 break;
116 case VC4_TILING_FORMAT_LT:
117 width_align = 16;
118 height_align = 4;
119 break;
120 default:
121 DRM_ERROR("buffer tiling %d unsupported\n", tiling_format);
122 return false;
123 }
124
125 /* The values are limited by the packet bitfields, so we don't need to
126 * worry as much about integer overflow.
127 */
128 BUG_ON(exec->fb_width > 65535);
129 BUG_ON(exec->fb_height > 65535);
130
131 aligned_row_len = roundup(exec->fb_width * cpp, width_align);
132 aligned_h = roundup(exec->fb_height, height_align);
133
134 if (INT_MAX / aligned_row_len < aligned_h) {
135 DRM_ERROR("Overflow in fbo size (%d * %d)\n",
136 aligned_row_len, aligned_h);
137 return false;
138 }
139 size = aligned_row_len * aligned_h;
140
141 if (size + offset < size ||
142 size + offset > fbo->base.size) {
143 DRM_ERROR("Overflow in %dx%d fbo size (%d + %d > %d)\n",
144 exec->fb_width, exec->fb_height, size, offset,
145 fbo->base.size);
146 return false;
147 }
148
149 return true;
150 }
151
152 static int
153 validate_start_tile_binning(VALIDATE_ARGS)
154 {
155 if (exec->found_start_tile_binning_packet) {
156 DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n");
157 return -EINVAL;
158 }
159 exec->found_start_tile_binning_packet = true;
160
161 if (!exec->found_tile_binning_mode_config_packet) {
162 DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
163 return -EINVAL;
164 }
165
166 return 0;
167 }
168
169 static int
170 validate_branch_to_sublist(VALIDATE_ARGS)
171 {
172 struct drm_gem_cma_object *target;
173 uint32_t offset;
174
175 if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &target))
176 return -EINVAL;
177
178 if (target != exec->tile_alloc_bo) {
179 DRM_ERROR("Jumping to BOs other than tile alloc unsupported\n");
180 return -EINVAL;
181 }
182
183 offset = *(uint32_t *)(untrusted + 0);
184 if (offset % exec->tile_alloc_init_block_size ||
185 offset / exec->tile_alloc_init_block_size >
186 exec->bin_tiles_x * exec->bin_tiles_y) {
187 DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial "
188 "tile allocation space.\n");
189 return -EINVAL;
190 }
191
192 *(uint32_t *)(validated + 0) = target->paddr + offset;
193
194 return 0;
195 }
196
197 /**
198 * validate_loadstore_tile_buffer_general() - Validation for
199 * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL and
200 * VC4_PACKET_STORE_TILE_BUFFER_GENERAL.
201 *
202 * The two packets are nearly the same, except for the TLB-clearing management
203 * bits not being present for loads. Additionally, while stores are executed
204 * immediately (using the current tile coordinates), loads are queued to be
205 * executed when the tile coordinates packet occurs.
206 *
207 * Note that coordinates packets are validated to be within the declared
208 * bin_x/y, which themselves are verified to match the rendering-configuration
209 * FB width and height (which the hardware uses to clip loads and stores).
210 */
211 static int
212 validate_loadstore_tile_buffer_general(VALIDATE_ARGS)
213 {
214 uint32_t packet_b0 = *(uint8_t *)(untrusted + 0);
215 uint32_t packet_b1 = *(uint8_t *)(untrusted + 1);
216 struct drm_gem_cma_object *fbo;
217 uint32_t buffer_type = packet_b0 & 0xf;
218 uint32_t offset, cpp;
219
220 switch (buffer_type) {
221 case VC4_LOADSTORE_TILE_BUFFER_NONE:
222 return 0;
223 case VC4_LOADSTORE_TILE_BUFFER_COLOR:
224 if ((packet_b1 & VC4_LOADSTORE_TILE_BUFFER_MASK) ==
225 VC4_LOADSTORE_TILE_BUFFER_RGBA8888) {
226 cpp = 4;
227 } else {
228 cpp = 2;
229 }
230 break;
231
232 case VC4_LOADSTORE_TILE_BUFFER_Z:
233 case VC4_LOADSTORE_TILE_BUFFER_ZS:
234 cpp = 4;
235 break;
236
237 default:
238 DRM_ERROR("Load/store type %d unsupported\n", buffer_type);
239 return -EINVAL;
240 }
241
242 if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo))
243 return -EINVAL;
244
245 offset = *(uint32_t *)(untrusted + 2);
246
247 if (!check_fbo_size(exec, fbo, offset,
248 ((packet_b0 &
249 VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK) >>
250 VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT),
251 cpp)) {
252 return -EINVAL;
253 }
254
255 *(uint32_t *)(validated + 2) = offset + fbo->paddr;
256
257 return 0;
258 }
259
260 static int
261 validate_indexed_prim_list(VALIDATE_ARGS)
262 {
263 struct drm_gem_cma_object *ib;
264 uint32_t max_index = *(uint32_t *)(untrusted + 9);
265 uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1;
266 uint32_t ib_access_end = (max_index + 1) * index_size;
267
268 /* Check overflow condition */
269 if (max_index == ~0) {
270 DRM_ERROR("unlimited max index\n");
271 return -EINVAL;
272 }
273
274 if (ib_access_end < max_index) {
275 DRM_ERROR("IB access overflow\n");
276 return -EINVAL;
277 }
278
279
280 if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib))
281 return -EINVAL;
282 if (ib_access_end > ib->base.size) {
283 DRM_ERROR("IB access out of bounds (%d/%d)\n",
284 ib_access_end, ib->base.size);
285 return -EINVAL;
286 }
287
288 *(uint32_t *)(validated + 5) =
289 *(uint32_t *)(untrusted + 5) + ib->paddr;
290
291 return 0;
292 }
293
294 static int
295 validate_gl_shader_state(VALIDATE_ARGS)
296 {
297 uint32_t i = exec->shader_state_count++;
298
299 if (i >= exec->shader_state_size) { /* XXX? */
300 DRM_ERROR("More requests for shader states than declared\n");
301 return -EINVAL;
302 }
303
304 exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE;
305 exec->shader_state[i].addr = *(uint32_t *)untrusted;
306
307 if (exec->shader_state[i].addr & ~0xf) {
308 DRM_ERROR("high bits set in GL shader rec reference\n");
309 return -EINVAL;
310 }
311
312 *(uint32_t *)validated = (exec->shader_rec_p +
313 exec->shader_state[i].addr);
314
315 exec->shader_rec_p +=
316 roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16);
317
318 return 0;
319 }
320
321 static int
322 validate_nv_shader_state(VALIDATE_ARGS)
323 {
324 uint32_t i = exec->shader_state_count++;
325
326 if (i >= exec->shader_state_size) {
327 DRM_ERROR("More requests for shader states than declared\n");
328 return -EINVAL;
329 }
330
331 exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE;
332 exec->shader_state[i].addr = *(uint32_t *)untrusted;
333
334 if (exec->shader_state[i].addr & 15) {
335 DRM_ERROR("NV shader state address 0x%08x misaligned\n",
336 exec->shader_state[i].addr);
337 return -EINVAL;
338 }
339
340 *(uint32_t *)validated = (exec->shader_state[i].addr +
341 exec->shader_rec_p);
342
343 return 0;
344 }
345
346 static int
347 validate_tile_binning_config(VALIDATE_ARGS)
348 {
349 struct drm_gem_cma_object *tile_allocation;
350 struct drm_gem_cma_object *tile_state_data_array;
351 uint8_t flags;
352 uint32_t tile_allocation_size;
353
354 if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &tile_allocation) ||
355 !vc4_use_handle(exec, 1, VC4_MODE_TSDA, &tile_state_data_array))
356 return -EINVAL;
357
358 if (exec->found_tile_binning_mode_config_packet) {
359 DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
360 return -EINVAL;
361 }
362 exec->found_tile_binning_mode_config_packet = true;
363
364 exec->bin_tiles_x = *(uint8_t *)(untrusted + 12);
365 exec->bin_tiles_y = *(uint8_t *)(untrusted + 13);
366 flags = *(uint8_t *)(untrusted + 14);
367
368 if (exec->bin_tiles_x == 0 ||
369 exec->bin_tiles_y == 0) {
370 DRM_ERROR("Tile binning config of %dx%d too small\n",
371 exec->bin_tiles_x, exec->bin_tiles_y);
372 return -EINVAL;
373 }
374
375 /* Our validation relies on the user not getting to set up their own
376 * tile state/tile allocation BO contents.
377 */
378 if (!(flags & VC4_BIN_CONFIG_AUTO_INIT_TSDA)) {
379 DRM_ERROR("binning config missing "
380 "VC4_BIN_CONFIG_AUTO_INIT_TSDA\n");
381 return -EINVAL;
382 }
383
384 if (flags & (VC4_BIN_CONFIG_DB_NON_MS |
385 VC4_BIN_CONFIG_TILE_BUFFER_64BIT |
386 VC4_BIN_CONFIG_MS_MODE_4X)) {
387 DRM_ERROR("unsupported bining config flags 0x%02x\n", flags);
388 return -EINVAL;
389 }
390
391 if (*(uint32_t *)(untrusted + 0) != 0) {
392 DRM_ERROR("tile allocation offset != 0 unsupported\n");
393 return -EINVAL;
394 }
395 tile_allocation_size = *(uint32_t *)(untrusted + 4);
396 if (tile_allocation_size > tile_allocation->base.size) {
397 DRM_ERROR("tile allocation size %d > BO size %d",
398 tile_allocation_size, tile_allocation->base.size);
399 return -EINVAL;
400 }
401 *(uint32_t *)validated = tile_allocation->paddr;
402 exec->tile_alloc_bo = tile_allocation;
403
404 exec->tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3));
405 if (exec->bin_tiles_x * exec->bin_tiles_y *
406 exec->tile_alloc_init_block_size > tile_allocation_size) {
407 DRM_ERROR("tile init exceeds tile alloc size (%d vs %d)\n",
408 exec->bin_tiles_x * exec->bin_tiles_y *
409 exec->tile_alloc_init_block_size,
410 tile_allocation_size);
411 return -EINVAL;
412 }
413 if (*(uint32_t *)(untrusted + 8) != 0) {
414 DRM_ERROR("TSDA offset != 0 unsupported\n");
415 return -EINVAL;
416 }
417 if (exec->bin_tiles_x * exec->bin_tiles_y * 48 >
418 tile_state_data_array->base.size) {
419 DRM_ERROR("TSDA of %db too small for %dx%d bin config\n",
420 tile_state_data_array->base.size,
421 exec->bin_tiles_x, exec->bin_tiles_y);
422 }
423 *(uint32_t *)(validated + 8) = tile_state_data_array->paddr;
424
425 return 0;
426 }
427
428 static int
429 validate_tile_rendering_mode_config(VALIDATE_ARGS)
430 {
431 struct drm_gem_cma_object *fbo;
432 uint32_t flags, offset, cpp;
433
434 if (exec->found_tile_rendering_mode_config_packet) {
435 DRM_ERROR("Duplicate VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n");
436 return -EINVAL;
437 }
438 exec->found_tile_rendering_mode_config_packet = true;
439
440 if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo))
441 return -EINVAL;
442
443 exec->fb_width = *(uint16_t *)(untrusted + 4);
444 exec->fb_height = *(uint16_t *)(untrusted + 6);
445
446 /* Make sure that the fb width/height matches the binning config -- we
447 * rely on being able to interchange these for various assertions.
448 * (Within a tile, loads and stores will be clipped to the
449 * width/height, but we allow load/storing to any binned tile).
450 */
451 if (exec->fb_width <= (exec->bin_tiles_x - 1) * 64 ||
452 exec->fb_width > exec->bin_tiles_x * 64 ||
453 exec->fb_height <= (exec->bin_tiles_y - 1) * 64 ||
454 exec->fb_height > exec->bin_tiles_y * 64) {
455 DRM_ERROR("bin config %dx%d doesn't match FB %dx%d\n",
456 exec->bin_tiles_x, exec->bin_tiles_y,
457 exec->fb_width, exec->fb_height);
458 return -EINVAL;
459 }
460
461 flags = *(uint16_t *)(untrusted + 8);
462 if ((flags & VC4_RENDER_CONFIG_FORMAT_MASK) ==
463 VC4_RENDER_CONFIG_FORMAT_RGBA8888) {
464 cpp = 4;
465 } else {
466 cpp = 2;
467 }
468
469 offset = *(uint32_t *)untrusted;
470 if (!check_fbo_size(exec, fbo, offset,
471 ((flags &
472 VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK) >>
473 VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT),
474 cpp)) {
475 return -EINVAL;
476 }
477
478 *(uint32_t *)validated = fbo->paddr + offset;
479
480 return 0;
481 }
482
483 static int
484 validate_tile_coordinates(VALIDATE_ARGS)
485 {
486 uint8_t tile_x = *(uint8_t *)(untrusted + 0);
487 uint8_t tile_y = *(uint8_t *)(untrusted + 1);
488
489 if (tile_x >= exec->bin_tiles_x ||
490 tile_y >= exec->bin_tiles_y) {
491 DRM_ERROR("Tile coordinates %d,%d > bin config %d,%d\n",
492 tile_x,
493 tile_y,
494 exec->bin_tiles_x,
495 exec->bin_tiles_y);
496 return -EINVAL;
497 }
498
499 return 0;
500 }
501
502 static int
503 validate_gem_handles(VALIDATE_ARGS)
504 {
505 memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index));
506 return 0;
507 }
508
509 static const struct cmd_info {
510 bool bin;
511 bool render;
512 uint16_t len;
513 const char *name;
514 int (*func)(struct exec_info *exec, void *validated, void *untrusted);
515 } cmd_info[] = {
516 [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL },
517 [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL },
518 [VC4_PACKET_FLUSH] = { 1, 1, 1, "flush", NULL },
519 [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", NULL },
520 [VC4_PACKET_START_TILE_BINNING] = { 1, 0, 1, "start tile binning", validate_start_tile_binning },
521 [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", NULL },
522 [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 1, 1, 1, "wait on semaphore", NULL },
523 /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but
524 * we only use it from the render CL in order to jump into the tile
525 * allocation BO.
526 */
527 [VC4_PACKET_BRANCH_TO_SUB_LIST] = { 0, 1, 5, "branch to sublist", validate_branch_to_sublist },
528 [VC4_PACKET_STORE_MS_TILE_BUFFER] = { 0, 1, 1, "store MS resolved tile color buffer", NULL },
529 [VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL },
530
531 [VC4_PACKET_STORE_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Store Tile Buffer General", validate_loadstore_tile_buffer_general },
532 [VC4_PACKET_LOAD_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Load Tile Buffer General", validate_loadstore_tile_buffer_general },
533
534 [VC4_PACKET_GL_INDEXED_PRIMITIVE] = { 1, 1, 14, "Indexed Primitive List", validate_indexed_prim_list },
535
536 /* XXX: bounds check verts? */
537 [VC4_PACKET_GL_ARRAY_PRIMITIVE] = { 1, 1, 10, "Vertex Array Primitives", NULL },
538
539 [VC4_PACKET_PRIMITIVE_LIST_FORMAT] = { 1, 1, 2, "primitive list format", NULL }, /* XXX: bin valid? */
540
541 [VC4_PACKET_GL_SHADER_STATE] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state },
542 [VC4_PACKET_NV_SHADER_STATE] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state },
543
544 [VC4_PACKET_CONFIGURATION_BITS] = { 1, 1, 4, "configuration bits", NULL },
545 [VC4_PACKET_FLAT_SHADE_FLAGS] = { 1, 1, 5, "flat shade flags", NULL },
546 [VC4_PACKET_POINT_SIZE] = { 1, 1, 5, "point size", NULL },
547 [VC4_PACKET_LINE_WIDTH] = { 1, 1, 5, "line width", NULL },
548 [VC4_PACKET_RHT_X_BOUNDARY] = { 1, 1, 3, "RHT X boundary", NULL },
549 [VC4_PACKET_DEPTH_OFFSET] = { 1, 1, 5, "Depth Offset", NULL },
550 [VC4_PACKET_CLIP_WINDOW] = { 1, 1, 9, "Clip Window", NULL },
551 [VC4_PACKET_VIEWPORT_OFFSET] = { 1, 1, 5, "Viewport Offset", NULL },
552 [VC4_PACKET_CLIPPER_XY_SCALING] = { 1, 1, 9, "Clipper XY Scaling", NULL },
553 /* Note: The docs say this was also 105, but it was 106 in the
554 * initial userland code drop.
555 */
556 [VC4_PACKET_CLIPPER_Z_SCALING] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL },
557
558 [VC4_PACKET_TILE_BINNING_MODE_CONFIG] = { 1, 0, 16, "tile binning configuration", validate_tile_binning_config },
559
560 [VC4_PACKET_TILE_RENDERING_MODE_CONFIG] = { 0, 1, 11, "tile rendering mode configuration", validate_tile_rendering_mode_config},
561
562 [VC4_PACKET_CLEAR_COLORS] = { 0, 1, 14, "Clear Colors", NULL },
563
564 [VC4_PACKET_TILE_COORDINATES] = { 0, 1, 3, "Tile Coordinates", validate_tile_coordinates },
565
566 [VC4_PACKET_GEM_HANDLES] = { 1, 1, 9, "GEM handles", validate_gem_handles },
567 };
568
569 int
570 vc4_validate_cl(struct drm_device *dev,
571 void *validated,
572 void *unvalidated,
573 uint32_t len,
574 bool is_bin,
575 struct exec_info *exec)
576 {
577 uint32_t dst_offset = 0;
578 uint32_t src_offset = 0;
579
580 while (src_offset < len) {
581 void *dst_pkt = validated + dst_offset;
582 void *src_pkt = unvalidated + src_offset;
583 u8 cmd = *(uint8_t *)src_pkt;
584 const struct cmd_info *info;
585
586 if (cmd > ARRAY_SIZE(cmd_info)) {
587 DRM_ERROR("0x%08x: packet %d out of bounds\n",
588 src_offset, cmd);
589 return -EINVAL;
590 }
591
592 info = &cmd_info[cmd];
593 if (!info->name) {
594 DRM_ERROR("0x%08x: packet %d invalid\n",
595 src_offset, cmd);
596 return -EINVAL;
597 }
598
599 #if 0
600 DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n",
601 src_offset, cmd, info->name, info->len);
602 #endif
603
604 if ((is_bin && !info->bin) ||
605 (!is_bin && !info->render)) {
606 DRM_ERROR("0x%08x: packet %d (%s) invalid for %s\n",
607 src_offset, cmd, info->name,
608 is_bin ? "binner" : "render");
609 return -EINVAL;
610 }
611
612 if (src_offset + info->len > len) {
613 DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x "
614 "exceeds bounds (0x%08x)\n",
615 src_offset, cmd, info->name, info->len,
616 src_offset + len);
617 return -EINVAL;
618 }
619
620 if (cmd != VC4_PACKET_GEM_HANDLES)
621 memcpy(dst_pkt, src_pkt, info->len);
622
623 if (info->func && info->func(exec,
624 dst_pkt + 1,
625 src_pkt + 1)) {
626 DRM_ERROR("0x%08x: packet %d (%s) failed to "
627 "validate\n",
628 src_offset, cmd, info->name);
629 return -EINVAL;
630 }
631
632 src_offset += info->len;
633 /* GEM handle loading doesn't produce HW packets. */
634 if (cmd != VC4_PACKET_GEM_HANDLES)
635 dst_offset += info->len;
636
637 /* When the CL hits halt, it'll stop reading anything else. */
638 if (cmd == VC4_PACKET_HALT)
639 break;
640 }
641
642 if (is_bin) {
643 exec->ct0ea = exec->ct0ca + dst_offset;
644
645 if (!exec->found_start_tile_binning_packet) {
646 DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n");
647 return -EINVAL;
648 }
649 } else {
650 if (!exec->found_tile_rendering_mode_config_packet) {
651 DRM_ERROR("Render CL missing VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n");
652 return -EINVAL;
653 }
654 exec->ct1ea = exec->ct1ca + dst_offset;
655 }
656
657 return 0;
658 }
659
660 static bool
661 reloc_tex(struct exec_info *exec,
662 void *uniform_data_u,
663 struct vc4_texture_sample_info *sample,
664 uint32_t texture_handle_index)
665
666 {
667 struct drm_gem_cma_object *tex;
668 uint32_t unvalidated_p0 = *(uint32_t *)(uniform_data_u +
669 sample->p_offset[0]);
670 uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0];
671
672 if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex))
673 return false;
674
675 *validated_p0 = tex->paddr + unvalidated_p0;
676
677 return true;
678 }
679
680 static int
681 validate_shader_rec(struct drm_device *dev,
682 struct exec_info *exec,
683 struct vc4_shader_state *state)
684 {
685 uint32_t *src_handles;
686 void *pkt_u, *pkt_v;
687 enum shader_rec_reloc_type {
688 RELOC_CODE,
689 RELOC_VBO,
690 };
691 struct shader_rec_reloc {
692 enum shader_rec_reloc_type type;
693 uint32_t offset;
694 };
695 static const struct shader_rec_reloc gl_relocs[] = {
696 { RELOC_CODE, 4 }, /* fs */
697 { RELOC_CODE, 16 }, /* vs */
698 { RELOC_CODE, 28 }, /* cs */
699 };
700 static const struct shader_rec_reloc nv_relocs[] = {
701 { RELOC_CODE, 4 }, /* fs */
702 { RELOC_VBO, 12 }
703 };
704 const struct shader_rec_reloc *relocs;
705 struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8];
706 uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size;
707 int i;
708 struct vc4_validated_shader_info *validated_shader = NULL;
709
710 if (state->packet == VC4_PACKET_NV_SHADER_STATE) {
711 relocs = nv_relocs;
712 nr_fixed_relocs = ARRAY_SIZE(nv_relocs);
713
714 packet_size = 16;
715 } else {
716 relocs = gl_relocs;
717 nr_fixed_relocs = ARRAY_SIZE(gl_relocs);
718
719 nr_attributes = state->addr & 0x7;
720 if (nr_attributes == 0)
721 nr_attributes = 8;
722 packet_size = gl_shader_rec_size(state->addr);
723 }
724 nr_relocs = nr_fixed_relocs + nr_attributes;
725
726 if (nr_relocs * 4 > exec->shader_rec_size) {
727 DRM_ERROR("overflowed shader recs reading %d handles "
728 "from %d bytes left\n",
729 nr_relocs, exec->shader_rec_size);
730 return -EINVAL;
731 }
732 src_handles = exec->shader_rec_u;
733 exec->shader_rec_u += nr_relocs * 4;
734 exec->shader_rec_size -= nr_relocs * 4;
735
736 if (packet_size > exec->shader_rec_size) {
737 DRM_ERROR("overflowed shader recs copying %db packet "
738 "from %d bytes left\n",
739 packet_size, exec->shader_rec_size);
740 return -EINVAL;
741 }
742 pkt_u = exec->shader_rec_u;
743 pkt_v = exec->shader_rec_v;
744 memcpy(pkt_v, pkt_u, packet_size);
745 exec->shader_rec_u += packet_size;
746 /* Shader recs have to be aligned to 16 bytes (due to the attribute
747 * flags being in the low bytes), so round the next validated shader
748 * rec address up. This should be safe, since we've got so many
749 * relocations in a shader rec packet.
750 */
751 BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4);
752 exec->shader_rec_v += roundup(packet_size, 16);
753 exec->shader_rec_size -= packet_size;
754
755 for (i = 0; i < nr_relocs; i++) {
756 enum vc4_bo_mode mode;
757
758 if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE)
759 mode = VC4_MODE_SHADER;
760 else
761 mode = VC4_MODE_RENDER;
762
763 if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) {
764 return false;
765 }
766 }
767
768 for (i = 0; i < nr_fixed_relocs; i++) {
769 uint32_t o = relocs[i].offset;
770 uint32_t src_offset = *(uint32_t *)(pkt_u + o);
771 uint32_t *texture_handles_u;
772 void *uniform_data_u;
773 uint32_t tex;
774
775 *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset;
776
777 switch (relocs[i].type) {
778 case RELOC_CODE:
779 kfree(validated_shader);
780 validated_shader = vc4_validate_shader(bo[i],
781 src_offset);
782 if (!validated_shader)
783 goto fail;
784
785 if (validated_shader->uniforms_src_size >
786 exec->uniforms_size) {
787 DRM_ERROR("Uniforms src buffer overflow\n");
788 goto fail;
789 }
790
791 texture_handles_u = exec->uniforms_u;
792 uniform_data_u = (texture_handles_u +
793 validated_shader->num_texture_samples);
794
795 memcpy(exec->uniforms_v, uniform_data_u,
796 validated_shader->uniforms_size);
797
798 for (tex = 0;
799 tex < validated_shader->num_texture_samples;
800 tex++) {
801 if (!reloc_tex(exec,
802 uniform_data_u,
803 &validated_shader->texture_samples[tex],
804 texture_handles_u[tex])) {
805 goto fail;
806 }
807 }
808
809 *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p;
810
811 exec->uniforms_u += validated_shader->uniforms_src_size;
812 exec->uniforms_v += validated_shader->uniforms_size;
813 exec->uniforms_p += validated_shader->uniforms_size;
814
815 break;
816
817 case RELOC_VBO:
818 break;
819 }
820 }
821
822 for (i = 0; i < nr_attributes; i++) {
823 /* XXX: validation */
824 uint32_t o = 36 + i * 8;
825 *(uint32_t *)(pkt_v + o) = (bo[nr_fixed_relocs + i]->paddr +
826 *(uint32_t *)(pkt_u + o));
827 }
828
829 kfree(validated_shader);
830
831 return 0;
832
833 fail:
834 kfree(validated_shader);
835 return -EINVAL;
836 }
837
838 int
839 vc4_validate_shader_recs(struct drm_device *dev,
840 struct exec_info *exec)
841 {
842 uint32_t i;
843 int ret = 0;
844
845 for (i = 0; i < exec->shader_state_count; i++) {
846 ret = validate_shader_rec(dev, exec, &exec->shader_state[i]);
847 if (ret)
848 return ret;
849 }
850
851 return ret;
852 }