162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright © 2014 Broadcom
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1362306a36Sopenharmony_ci * Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2162306a36Sopenharmony_ci * IN THE SOFTWARE.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * DOC: Command list validator for VC4.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Since the VC4 has no IOMMU between it and system memory, a user
2862306a36Sopenharmony_ci * with access to execute command lists could escalate privilege by
2962306a36Sopenharmony_ci * overwriting system memory (drawing to it as a framebuffer) or
3062306a36Sopenharmony_ci * reading system memory it shouldn't (reading it as a vertex buffer
3162306a36Sopenharmony_ci * or index buffer)
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * We validate binner command lists to ensure that all accesses are
3462306a36Sopenharmony_ci * within the bounds of the GEM objects referenced by the submitted
3562306a36Sopenharmony_ci * job.  It explicitly whitelists packets, and looks at the offsets in
3662306a36Sopenharmony_ci * any address fields to make sure they're contained within the BOs
3762306a36Sopenharmony_ci * they reference.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * Note that because CL validation is already reading the
4062306a36Sopenharmony_ci * user-submitted CL and writing the validated copy out to the memory
4162306a36Sopenharmony_ci * that the GPU will actually read, this is also where GEM relocation
4262306a36Sopenharmony_ci * processing (turning BO references into actual addresses for the GPU
4362306a36Sopenharmony_ci * to use) happens.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h"
4762306a36Sopenharmony_ci#include "vc4_drv.h"
4862306a36Sopenharmony_ci#include "vc4_packet.h"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define VALIDATE_ARGS \
5162306a36Sopenharmony_ci	struct vc4_exec_info *exec,			\
5262306a36Sopenharmony_ci	void *validated,				\
5362306a36Sopenharmony_ci	void *untrusted
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/** Return the width in pixels of a 64-byte microtile. */
5662306a36Sopenharmony_cistatic uint32_t
5762306a36Sopenharmony_ciutile_width(int cpp)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	switch (cpp) {
6062306a36Sopenharmony_ci	case 1:
6162306a36Sopenharmony_ci	case 2:
6262306a36Sopenharmony_ci		return 8;
6362306a36Sopenharmony_ci	case 4:
6462306a36Sopenharmony_ci		return 4;
6562306a36Sopenharmony_ci	case 8:
6662306a36Sopenharmony_ci		return 2;
6762306a36Sopenharmony_ci	default:
6862306a36Sopenharmony_ci		DRM_ERROR("unknown cpp: %d\n", cpp);
6962306a36Sopenharmony_ci		return 1;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/** Return the height in pixels of a 64-byte microtile. */
7462306a36Sopenharmony_cistatic uint32_t
7562306a36Sopenharmony_ciutile_height(int cpp)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	switch (cpp) {
7862306a36Sopenharmony_ci	case 1:
7962306a36Sopenharmony_ci		return 8;
8062306a36Sopenharmony_ci	case 2:
8162306a36Sopenharmony_ci	case 4:
8262306a36Sopenharmony_ci	case 8:
8362306a36Sopenharmony_ci		return 4;
8462306a36Sopenharmony_ci	default:
8562306a36Sopenharmony_ci		DRM_ERROR("unknown cpp: %d\n", cpp);
8662306a36Sopenharmony_ci		return 1;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/**
9162306a36Sopenharmony_ci * size_is_lt() - Returns whether a miplevel of the given size will
9262306a36Sopenharmony_ci * use the lineartile (LT) tiling layout rather than the normal T
9362306a36Sopenharmony_ci * tiling layout.
9462306a36Sopenharmony_ci * @width: Width in pixels of the miplevel
9562306a36Sopenharmony_ci * @height: Height in pixels of the miplevel
9662306a36Sopenharmony_ci * @cpp: Bytes per pixel of the pixel format
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistatic bool
9962306a36Sopenharmony_cisize_is_lt(uint32_t width, uint32_t height, int cpp)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return (width <= 4 * utile_width(cpp) ||
10262306a36Sopenharmony_ci		height <= 4 * utile_height(cpp));
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct drm_gem_dma_object *
10662306a36Sopenharmony_civc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct vc4_dev *vc4 = exec->dev;
10962306a36Sopenharmony_ci	struct drm_gem_dma_object *obj;
11062306a36Sopenharmony_ci	struct vc4_bo *bo;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
11362306a36Sopenharmony_ci		return NULL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (hindex >= exec->bo_count) {
11662306a36Sopenharmony_ci		DRM_DEBUG("BO index %d greater than BO count %d\n",
11762306a36Sopenharmony_ci			  hindex, exec->bo_count);
11862306a36Sopenharmony_ci		return NULL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	obj = to_drm_gem_dma_obj(exec->bo[hindex]);
12162306a36Sopenharmony_ci	bo = to_vc4_bo(&obj->base);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (bo->validated_shader) {
12462306a36Sopenharmony_ci		DRM_DEBUG("Trying to use shader BO as something other than "
12562306a36Sopenharmony_ci			  "a shader\n");
12662306a36Sopenharmony_ci		return NULL;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return obj;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct drm_gem_dma_object *
13362306a36Sopenharmony_civc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic bool
13962306a36Sopenharmony_civalidate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	/* Note that the untrusted pointer passed to these functions is
14262306a36Sopenharmony_ci	 * incremented past the packet byte.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	return (untrusted - 1 == exec->bin_u + pos);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic uint32_t
14862306a36Sopenharmony_cigl_shader_rec_size(uint32_t pointer_bits)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	uint32_t attribute_count = pointer_bits & 7;
15162306a36Sopenharmony_ci	bool extended = pointer_bits & 8;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (attribute_count == 0)
15462306a36Sopenharmony_ci		attribute_count = 8;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (extended)
15762306a36Sopenharmony_ci		return 100 + attribute_count * 4;
15862306a36Sopenharmony_ci	else
15962306a36Sopenharmony_ci		return 36 + attribute_count * 8;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cibool
16362306a36Sopenharmony_civc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_dma_object *fbo,
16462306a36Sopenharmony_ci		   uint32_t offset, uint8_t tiling_format,
16562306a36Sopenharmony_ci		   uint32_t width, uint32_t height, uint8_t cpp)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct vc4_dev *vc4 = exec->dev;
16862306a36Sopenharmony_ci	uint32_t aligned_width, aligned_height, stride, size;
16962306a36Sopenharmony_ci	uint32_t utile_w = utile_width(cpp);
17062306a36Sopenharmony_ci	uint32_t utile_h = utile_height(cpp);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
17362306a36Sopenharmony_ci		return false;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* The shaded vertex format stores signed 12.4 fixed point
17662306a36Sopenharmony_ci	 * (-2048,2047) offsets from the viewport center, so we should
17762306a36Sopenharmony_ci	 * never have a render target larger than 4096.  The texture
17862306a36Sopenharmony_ci	 * unit can only sample from 2048x2048, so it's even more
17962306a36Sopenharmony_ci	 * restricted.  This lets us avoid worrying about overflow in
18062306a36Sopenharmony_ci	 * our math.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	if (width > 4096 || height > 4096) {
18362306a36Sopenharmony_ci		DRM_DEBUG("Surface dimensions (%d,%d) too large",
18462306a36Sopenharmony_ci			  width, height);
18562306a36Sopenharmony_ci		return false;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	switch (tiling_format) {
18962306a36Sopenharmony_ci	case VC4_TILING_FORMAT_LINEAR:
19062306a36Sopenharmony_ci		aligned_width = round_up(width, utile_w);
19162306a36Sopenharmony_ci		aligned_height = height;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	case VC4_TILING_FORMAT_T:
19462306a36Sopenharmony_ci		aligned_width = round_up(width, utile_w * 8);
19562306a36Sopenharmony_ci		aligned_height = round_up(height, utile_h * 8);
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	case VC4_TILING_FORMAT_LT:
19862306a36Sopenharmony_ci		aligned_width = round_up(width, utile_w);
19962306a36Sopenharmony_ci		aligned_height = round_up(height, utile_h);
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	default:
20262306a36Sopenharmony_ci		DRM_DEBUG("buffer tiling %d unsupported\n", tiling_format);
20362306a36Sopenharmony_ci		return false;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	stride = aligned_width * cpp;
20762306a36Sopenharmony_ci	size = stride * aligned_height;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (size + offset < size ||
21062306a36Sopenharmony_ci	    size + offset > fbo->base.size) {
21162306a36Sopenharmony_ci		DRM_DEBUG("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n",
21262306a36Sopenharmony_ci			  width, height,
21362306a36Sopenharmony_ci			  aligned_width, aligned_height,
21462306a36Sopenharmony_ci			  size, offset, fbo->base.size);
21562306a36Sopenharmony_ci		return false;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return true;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int
22262306a36Sopenharmony_civalidate_flush(VALIDATE_ARGS)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) {
22562306a36Sopenharmony_ci		DRM_DEBUG("Bin CL must end with VC4_PACKET_FLUSH\n");
22662306a36Sopenharmony_ci		return -EINVAL;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	exec->found_flush = true;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int
23462306a36Sopenharmony_civalidate_start_tile_binning(VALIDATE_ARGS)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	if (exec->found_start_tile_binning_packet) {
23762306a36Sopenharmony_ci		DRM_DEBUG("Duplicate VC4_PACKET_START_TILE_BINNING\n");
23862306a36Sopenharmony_ci		return -EINVAL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	exec->found_start_tile_binning_packet = true;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (!exec->found_tile_binning_mode_config_packet) {
24362306a36Sopenharmony_ci		DRM_DEBUG("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
24462306a36Sopenharmony_ci		return -EINVAL;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int
25162306a36Sopenharmony_civalidate_increment_semaphore(VALIDATE_ARGS)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) {
25462306a36Sopenharmony_ci		DRM_DEBUG("Bin CL must end with "
25562306a36Sopenharmony_ci			  "VC4_PACKET_INCREMENT_SEMAPHORE\n");
25662306a36Sopenharmony_ci		return -EINVAL;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	exec->found_increment_semaphore_packet = true;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int
26462306a36Sopenharmony_civalidate_indexed_prim_list(VALIDATE_ARGS)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct drm_gem_dma_object *ib;
26762306a36Sopenharmony_ci	uint32_t length = *(uint32_t *)(untrusted + 1);
26862306a36Sopenharmony_ci	uint32_t offset = *(uint32_t *)(untrusted + 5);
26962306a36Sopenharmony_ci	uint32_t max_index = *(uint32_t *)(untrusted + 9);
27062306a36Sopenharmony_ci	uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1;
27162306a36Sopenharmony_ci	struct vc4_shader_state *shader_state;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Check overflow condition */
27462306a36Sopenharmony_ci	if (exec->shader_state_count == 0) {
27562306a36Sopenharmony_ci		DRM_DEBUG("shader state must precede primitives\n");
27662306a36Sopenharmony_ci		return -EINVAL;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	shader_state = &exec->shader_state[exec->shader_state_count - 1];
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (max_index > shader_state->max_index)
28162306a36Sopenharmony_ci		shader_state->max_index = max_index;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ib = vc4_use_handle(exec, 0);
28462306a36Sopenharmony_ci	if (!ib)
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	exec->bin_dep_seqno = max(exec->bin_dep_seqno,
28862306a36Sopenharmony_ci				  to_vc4_bo(&ib->base)->write_seqno);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (offset > ib->base.size ||
29162306a36Sopenharmony_ci	    (ib->base.size - offset) / index_size < length) {
29262306a36Sopenharmony_ci		DRM_DEBUG("IB access overflow (%d + %d*%d > %zd)\n",
29362306a36Sopenharmony_ci			  offset, length, index_size, ib->base.size);
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	*(uint32_t *)(validated + 5) = ib->dma_addr + offset;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int
30362306a36Sopenharmony_civalidate_gl_array_primitive(VALIDATE_ARGS)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	uint32_t length = *(uint32_t *)(untrusted + 1);
30662306a36Sopenharmony_ci	uint32_t base_index = *(uint32_t *)(untrusted + 5);
30762306a36Sopenharmony_ci	uint32_t max_index;
30862306a36Sopenharmony_ci	struct vc4_shader_state *shader_state;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Check overflow condition */
31162306a36Sopenharmony_ci	if (exec->shader_state_count == 0) {
31262306a36Sopenharmony_ci		DRM_DEBUG("shader state must precede primitives\n");
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	shader_state = &exec->shader_state[exec->shader_state_count - 1];
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (length + base_index < length) {
31862306a36Sopenharmony_ci		DRM_DEBUG("primitive vertex count overflow\n");
31962306a36Sopenharmony_ci		return -EINVAL;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	max_index = length + base_index - 1;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (max_index > shader_state->max_index)
32462306a36Sopenharmony_ci		shader_state->max_index = max_index;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int
33062306a36Sopenharmony_civalidate_gl_shader_state(VALIDATE_ARGS)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	uint32_t i = exec->shader_state_count++;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (i >= exec->shader_state_size) {
33562306a36Sopenharmony_ci		DRM_DEBUG("More requests for shader states than declared\n");
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	exec->shader_state[i].addr = *(uint32_t *)untrusted;
34062306a36Sopenharmony_ci	exec->shader_state[i].max_index = 0;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (exec->shader_state[i].addr & ~0xf) {
34362306a36Sopenharmony_ci		DRM_DEBUG("high bits set in GL shader rec reference\n");
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	*(uint32_t *)validated = (exec->shader_rec_p +
34862306a36Sopenharmony_ci				  exec->shader_state[i].addr);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	exec->shader_rec_p +=
35162306a36Sopenharmony_ci		roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return 0;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int
35762306a36Sopenharmony_civalidate_tile_binning_config(VALIDATE_ARGS)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct drm_device *dev = exec->exec_bo->base.dev;
36062306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
36162306a36Sopenharmony_ci	uint8_t flags;
36262306a36Sopenharmony_ci	uint32_t tile_state_size;
36362306a36Sopenharmony_ci	uint32_t tile_count, bin_addr;
36462306a36Sopenharmony_ci	int bin_slot;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (exec->found_tile_binning_mode_config_packet) {
36762306a36Sopenharmony_ci		DRM_DEBUG("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	exec->found_tile_binning_mode_config_packet = true;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	exec->bin_tiles_x = *(uint8_t *)(untrusted + 12);
37362306a36Sopenharmony_ci	exec->bin_tiles_y = *(uint8_t *)(untrusted + 13);
37462306a36Sopenharmony_ci	tile_count = exec->bin_tiles_x * exec->bin_tiles_y;
37562306a36Sopenharmony_ci	flags = *(uint8_t *)(untrusted + 14);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (exec->bin_tiles_x == 0 ||
37862306a36Sopenharmony_ci	    exec->bin_tiles_y == 0) {
37962306a36Sopenharmony_ci		DRM_DEBUG("Tile binning config of %dx%d too small\n",
38062306a36Sopenharmony_ci			  exec->bin_tiles_x, exec->bin_tiles_y);
38162306a36Sopenharmony_ci		return -EINVAL;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (flags & (VC4_BIN_CONFIG_DB_NON_MS |
38562306a36Sopenharmony_ci		     VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) {
38662306a36Sopenharmony_ci		DRM_DEBUG("unsupported binning config flags 0x%02x\n", flags);
38762306a36Sopenharmony_ci		return -EINVAL;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	bin_slot = vc4_v3d_get_bin_slot(vc4);
39162306a36Sopenharmony_ci	if (bin_slot < 0) {
39262306a36Sopenharmony_ci		if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
39362306a36Sopenharmony_ci			DRM_ERROR("Failed to allocate binner memory: %d\n",
39462306a36Sopenharmony_ci				  bin_slot);
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci		return bin_slot;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* The slot we allocated will only be used by this job, and is
40062306a36Sopenharmony_ci	 * free when the job completes rendering.
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	exec->bin_slots |= BIT(bin_slot);
40362306a36Sopenharmony_ci	bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* The tile state data array is 48 bytes per tile, and we put it at
40662306a36Sopenharmony_ci	 * the start of a BO containing both it and the tile alloc.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	tile_state_size = 48 * tile_count;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Since the tile alloc array will follow us, align. */
41162306a36Sopenharmony_ci	exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	*(uint8_t *)(validated + 14) =
41462306a36Sopenharmony_ci		((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
41562306a36Sopenharmony_ci			    VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) |
41662306a36Sopenharmony_ci		 VC4_BIN_CONFIG_AUTO_INIT_TSDA |
41762306a36Sopenharmony_ci		 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32,
41862306a36Sopenharmony_ci			       VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) |
41962306a36Sopenharmony_ci		 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
42062306a36Sopenharmony_ci			       VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* tile alloc address. */
42362306a36Sopenharmony_ci	*(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
42462306a36Sopenharmony_ci	/* tile alloc size. */
42562306a36Sopenharmony_ci	*(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
42662306a36Sopenharmony_ci					exec->tile_alloc_offset);
42762306a36Sopenharmony_ci	/* tile state address. */
42862306a36Sopenharmony_ci	*(uint32_t *)(validated + 8) = bin_addr;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int
43462306a36Sopenharmony_civalidate_gem_handles(VALIDATE_ARGS)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index));
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci#define VC4_DEFINE_PACKET(packet, func) \
44162306a36Sopenharmony_ci	[packet] = { packet ## _SIZE, #packet, func }
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic const struct cmd_info {
44462306a36Sopenharmony_ci	uint16_t len;
44562306a36Sopenharmony_ci	const char *name;
44662306a36Sopenharmony_ci	int (*func)(struct vc4_exec_info *exec, void *validated,
44762306a36Sopenharmony_ci		    void *untrusted);
44862306a36Sopenharmony_ci} cmd_info[] = {
44962306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL),
45062306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL),
45162306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush),
45262306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL),
45362306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING,
45462306a36Sopenharmony_ci			  validate_start_tile_binning),
45562306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE,
45662306a36Sopenharmony_ci			  validate_increment_semaphore),
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE,
45962306a36Sopenharmony_ci			  validate_indexed_prim_list),
46062306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE,
46162306a36Sopenharmony_ci			  validate_gl_array_primitive),
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL),
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state),
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL),
46862306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL),
46962306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL),
47062306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL),
47162306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL),
47262306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL),
47362306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL),
47462306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL),
47562306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL),
47662306a36Sopenharmony_ci	/* Note: The docs say this was also 105, but it was 106 in the
47762306a36Sopenharmony_ci	 * initial userland code drop.
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL),
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG,
48262306a36Sopenharmony_ci			  validate_tile_binning_config),
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles),
48562306a36Sopenharmony_ci};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciint
48862306a36Sopenharmony_civc4_validate_bin_cl(struct drm_device *dev,
48962306a36Sopenharmony_ci		    void *validated,
49062306a36Sopenharmony_ci		    void *unvalidated,
49162306a36Sopenharmony_ci		    struct vc4_exec_info *exec)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
49462306a36Sopenharmony_ci	uint32_t len = exec->args->bin_cl_size;
49562306a36Sopenharmony_ci	uint32_t dst_offset = 0;
49662306a36Sopenharmony_ci	uint32_t src_offset = 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
49962306a36Sopenharmony_ci		return -ENODEV;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	while (src_offset < len) {
50262306a36Sopenharmony_ci		void *dst_pkt = validated + dst_offset;
50362306a36Sopenharmony_ci		void *src_pkt = unvalidated + src_offset;
50462306a36Sopenharmony_ci		u8 cmd = *(uint8_t *)src_pkt;
50562306a36Sopenharmony_ci		const struct cmd_info *info;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		if (cmd >= ARRAY_SIZE(cmd_info)) {
50862306a36Sopenharmony_ci			DRM_DEBUG("0x%08x: packet %d out of bounds\n",
50962306a36Sopenharmony_ci				  src_offset, cmd);
51062306a36Sopenharmony_ci			return -EINVAL;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		info = &cmd_info[cmd];
51462306a36Sopenharmony_ci		if (!info->name) {
51562306a36Sopenharmony_ci			DRM_DEBUG("0x%08x: packet %d invalid\n",
51662306a36Sopenharmony_ci				  src_offset, cmd);
51762306a36Sopenharmony_ci			return -EINVAL;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (src_offset + info->len > len) {
52162306a36Sopenharmony_ci			DRM_DEBUG("0x%08x: packet %d (%s) length 0x%08x "
52262306a36Sopenharmony_ci				  "exceeds bounds (0x%08x)\n",
52362306a36Sopenharmony_ci				  src_offset, cmd, info->name, info->len,
52462306a36Sopenharmony_ci				  src_offset + len);
52562306a36Sopenharmony_ci			return -EINVAL;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		if (cmd != VC4_PACKET_GEM_HANDLES)
52962306a36Sopenharmony_ci			memcpy(dst_pkt, src_pkt, info->len);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		if (info->func && info->func(exec,
53262306a36Sopenharmony_ci					     dst_pkt + 1,
53362306a36Sopenharmony_ci					     src_pkt + 1)) {
53462306a36Sopenharmony_ci			DRM_DEBUG("0x%08x: packet %d (%s) failed to validate\n",
53562306a36Sopenharmony_ci				  src_offset, cmd, info->name);
53662306a36Sopenharmony_ci			return -EINVAL;
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		src_offset += info->len;
54062306a36Sopenharmony_ci		/* GEM handle loading doesn't produce HW packets. */
54162306a36Sopenharmony_ci		if (cmd != VC4_PACKET_GEM_HANDLES)
54262306a36Sopenharmony_ci			dst_offset += info->len;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		/* When the CL hits halt, it'll stop reading anything else. */
54562306a36Sopenharmony_ci		if (cmd == VC4_PACKET_HALT)
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	exec->ct0ea = exec->ct0ca + dst_offset;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (!exec->found_start_tile_binning_packet) {
55262306a36Sopenharmony_ci		DRM_DEBUG("Bin CL missing VC4_PACKET_START_TILE_BINNING\n");
55362306a36Sopenharmony_ci		return -EINVAL;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH.  The
55762306a36Sopenharmony_ci	 * semaphore is used to trigger the render CL to start up, and the
55862306a36Sopenharmony_ci	 * FLUSH is what caps the bin lists with
55962306a36Sopenharmony_ci	 * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main
56062306a36Sopenharmony_ci	 * render CL when they get called to) and actually triggers the queued
56162306a36Sopenharmony_ci	 * semaphore increment.
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	if (!exec->found_increment_semaphore_packet || !exec->found_flush) {
56462306a36Sopenharmony_ci		DRM_DEBUG("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + "
56562306a36Sopenharmony_ci			  "VC4_PACKET_FLUSH\n");
56662306a36Sopenharmony_ci		return -EINVAL;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic bool
57362306a36Sopenharmony_cireloc_tex(struct vc4_exec_info *exec,
57462306a36Sopenharmony_ci	  void *uniform_data_u,
57562306a36Sopenharmony_ci	  struct vc4_texture_sample_info *sample,
57662306a36Sopenharmony_ci	  uint32_t texture_handle_index, bool is_cs)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct drm_gem_dma_object *tex;
57962306a36Sopenharmony_ci	uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]);
58062306a36Sopenharmony_ci	uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]);
58162306a36Sopenharmony_ci	uint32_t p2 = (sample->p_offset[2] != ~0 ?
58262306a36Sopenharmony_ci		       *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0);
58362306a36Sopenharmony_ci	uint32_t p3 = (sample->p_offset[3] != ~0 ?
58462306a36Sopenharmony_ci		       *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0);
58562306a36Sopenharmony_ci	uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0];
58662306a36Sopenharmony_ci	uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK;
58762306a36Sopenharmony_ci	uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS);
58862306a36Sopenharmony_ci	uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH);
58962306a36Sopenharmony_ci	uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT);
59062306a36Sopenharmony_ci	uint32_t cpp, tiling_format, utile_w, utile_h;
59162306a36Sopenharmony_ci	uint32_t i;
59262306a36Sopenharmony_ci	uint32_t cube_map_stride = 0;
59362306a36Sopenharmony_ci	enum vc4_texture_data_type type;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	tex = vc4_use_bo(exec, texture_handle_index);
59662306a36Sopenharmony_ci	if (!tex)
59762306a36Sopenharmony_ci		return false;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (sample->is_direct) {
60062306a36Sopenharmony_ci		uint32_t remaining_size = tex->base.size - p0;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (p0 > tex->base.size - 4) {
60362306a36Sopenharmony_ci			DRM_DEBUG("UBO offset greater than UBO size\n");
60462306a36Sopenharmony_ci			goto fail;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci		if (p1 > remaining_size - 4) {
60762306a36Sopenharmony_ci			DRM_DEBUG("UBO clamp would allow reads "
60862306a36Sopenharmony_ci				  "outside of UBO\n");
60962306a36Sopenharmony_ci			goto fail;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		*validated_p0 = tex->dma_addr + p0;
61262306a36Sopenharmony_ci		return true;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (width == 0)
61662306a36Sopenharmony_ci		width = 2048;
61762306a36Sopenharmony_ci	if (height == 0)
61862306a36Sopenharmony_ci		height = 2048;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (p0 & VC4_TEX_P0_CMMODE_MASK) {
62162306a36Sopenharmony_ci		if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) ==
62262306a36Sopenharmony_ci		    VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE)
62362306a36Sopenharmony_ci			cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK;
62462306a36Sopenharmony_ci		if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) ==
62562306a36Sopenharmony_ci		    VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) {
62662306a36Sopenharmony_ci			if (cube_map_stride) {
62762306a36Sopenharmony_ci				DRM_DEBUG("Cube map stride set twice\n");
62862306a36Sopenharmony_ci				goto fail;
62962306a36Sopenharmony_ci			}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci			cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci		if (!cube_map_stride) {
63462306a36Sopenharmony_ci			DRM_DEBUG("Cube map stride not set\n");
63562306a36Sopenharmony_ci			goto fail;
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) |
64062306a36Sopenharmony_ci		(VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4));
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	switch (type) {
64362306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBA8888:
64462306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBX8888:
64562306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBA32R:
64662306a36Sopenharmony_ci		cpp = 4;
64762306a36Sopenharmony_ci		break;
64862306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBA4444:
64962306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBA5551:
65062306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGB565:
65162306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_LUMALPHA:
65262306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_S16F:
65362306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_S16:
65462306a36Sopenharmony_ci		cpp = 2;
65562306a36Sopenharmony_ci		break;
65662306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_LUMINANCE:
65762306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_ALPHA:
65862306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_S8:
65962306a36Sopenharmony_ci		cpp = 1;
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_ETC1:
66262306a36Sopenharmony_ci		/* ETC1 is arranged as 64-bit blocks, where each block is 4x4
66362306a36Sopenharmony_ci		 * pixels.
66462306a36Sopenharmony_ci		 */
66562306a36Sopenharmony_ci		cpp = 8;
66662306a36Sopenharmony_ci		width = (width + 3) >> 2;
66762306a36Sopenharmony_ci		height = (height + 3) >> 2;
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_BW1:
67062306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_A4:
67162306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_A1:
67262306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_RGBA64:
67362306a36Sopenharmony_ci	case VC4_TEXTURE_TYPE_YUV422R:
67462306a36Sopenharmony_ci	default:
67562306a36Sopenharmony_ci		DRM_DEBUG("Texture format %d unsupported\n", type);
67662306a36Sopenharmony_ci		goto fail;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	utile_w = utile_width(cpp);
67962306a36Sopenharmony_ci	utile_h = utile_height(cpp);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (type == VC4_TEXTURE_TYPE_RGBA32R) {
68262306a36Sopenharmony_ci		tiling_format = VC4_TILING_FORMAT_LINEAR;
68362306a36Sopenharmony_ci	} else {
68462306a36Sopenharmony_ci		if (size_is_lt(width, height, cpp))
68562306a36Sopenharmony_ci			tiling_format = VC4_TILING_FORMAT_LT;
68662306a36Sopenharmony_ci		else
68762306a36Sopenharmony_ci			tiling_format = VC4_TILING_FORMAT_T;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5,
69162306a36Sopenharmony_ci				tiling_format, width, height, cpp)) {
69262306a36Sopenharmony_ci		goto fail;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	/* The mipmap levels are stored before the base of the texture.  Make
69662306a36Sopenharmony_ci	 * sure there is actually space in the BO.
69762306a36Sopenharmony_ci	 */
69862306a36Sopenharmony_ci	for (i = 1; i <= miplevels; i++) {
69962306a36Sopenharmony_ci		uint32_t level_width = max(width >> i, 1u);
70062306a36Sopenharmony_ci		uint32_t level_height = max(height >> i, 1u);
70162306a36Sopenharmony_ci		uint32_t aligned_width, aligned_height;
70262306a36Sopenharmony_ci		uint32_t level_size;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		/* Once the levels get small enough, they drop from T to LT. */
70562306a36Sopenharmony_ci		if (tiling_format == VC4_TILING_FORMAT_T &&
70662306a36Sopenharmony_ci		    size_is_lt(level_width, level_height, cpp)) {
70762306a36Sopenharmony_ci			tiling_format = VC4_TILING_FORMAT_LT;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		switch (tiling_format) {
71162306a36Sopenharmony_ci		case VC4_TILING_FORMAT_T:
71262306a36Sopenharmony_ci			aligned_width = round_up(level_width, utile_w * 8);
71362306a36Sopenharmony_ci			aligned_height = round_up(level_height, utile_h * 8);
71462306a36Sopenharmony_ci			break;
71562306a36Sopenharmony_ci		case VC4_TILING_FORMAT_LT:
71662306a36Sopenharmony_ci			aligned_width = round_up(level_width, utile_w);
71762306a36Sopenharmony_ci			aligned_height = round_up(level_height, utile_h);
71862306a36Sopenharmony_ci			break;
71962306a36Sopenharmony_ci		default:
72062306a36Sopenharmony_ci			aligned_width = round_up(level_width, utile_w);
72162306a36Sopenharmony_ci			aligned_height = level_height;
72262306a36Sopenharmony_ci			break;
72362306a36Sopenharmony_ci		}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		level_size = aligned_width * cpp * aligned_height;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		if (offset < level_size) {
72862306a36Sopenharmony_ci			DRM_DEBUG("Level %d (%dx%d -> %dx%d) size %db "
72962306a36Sopenharmony_ci				  "overflowed buffer bounds (offset %d)\n",
73062306a36Sopenharmony_ci				  i, level_width, level_height,
73162306a36Sopenharmony_ci				  aligned_width, aligned_height,
73262306a36Sopenharmony_ci				  level_size, offset);
73362306a36Sopenharmony_ci			goto fail;
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		offset -= level_size;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	*validated_p0 = tex->dma_addr + p0;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (is_cs) {
74262306a36Sopenharmony_ci		exec->bin_dep_seqno = max(exec->bin_dep_seqno,
74362306a36Sopenharmony_ci					  to_vc4_bo(&tex->base)->write_seqno);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return true;
74762306a36Sopenharmony_ci fail:
74862306a36Sopenharmony_ci	DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0);
74962306a36Sopenharmony_ci	DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1);
75062306a36Sopenharmony_ci	DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2);
75162306a36Sopenharmony_ci	DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3);
75262306a36Sopenharmony_ci	return false;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic int
75662306a36Sopenharmony_civalidate_gl_shader_rec(struct drm_device *dev,
75762306a36Sopenharmony_ci		       struct vc4_exec_info *exec,
75862306a36Sopenharmony_ci		       struct vc4_shader_state *state)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	uint32_t *src_handles;
76162306a36Sopenharmony_ci	void *pkt_u, *pkt_v;
76262306a36Sopenharmony_ci	static const uint32_t shader_reloc_offsets[] = {
76362306a36Sopenharmony_ci		4, /* fs */
76462306a36Sopenharmony_ci		16, /* vs */
76562306a36Sopenharmony_ci		28, /* cs */
76662306a36Sopenharmony_ci	};
76762306a36Sopenharmony_ci	uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets);
76862306a36Sopenharmony_ci	struct drm_gem_dma_object *bo[ARRAY_SIZE(shader_reloc_offsets) + 8];
76962306a36Sopenharmony_ci	uint32_t nr_attributes, nr_relocs, packet_size;
77062306a36Sopenharmony_ci	int i;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	nr_attributes = state->addr & 0x7;
77362306a36Sopenharmony_ci	if (nr_attributes == 0)
77462306a36Sopenharmony_ci		nr_attributes = 8;
77562306a36Sopenharmony_ci	packet_size = gl_shader_rec_size(state->addr);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes;
77862306a36Sopenharmony_ci	if (nr_relocs * 4 > exec->shader_rec_size) {
77962306a36Sopenharmony_ci		DRM_DEBUG("overflowed shader recs reading %d handles "
78062306a36Sopenharmony_ci			  "from %d bytes left\n",
78162306a36Sopenharmony_ci			  nr_relocs, exec->shader_rec_size);
78262306a36Sopenharmony_ci		return -EINVAL;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci	src_handles = exec->shader_rec_u;
78562306a36Sopenharmony_ci	exec->shader_rec_u += nr_relocs * 4;
78662306a36Sopenharmony_ci	exec->shader_rec_size -= nr_relocs * 4;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (packet_size > exec->shader_rec_size) {
78962306a36Sopenharmony_ci		DRM_DEBUG("overflowed shader recs copying %db packet "
79062306a36Sopenharmony_ci			  "from %d bytes left\n",
79162306a36Sopenharmony_ci			  packet_size, exec->shader_rec_size);
79262306a36Sopenharmony_ci		return -EINVAL;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci	pkt_u = exec->shader_rec_u;
79562306a36Sopenharmony_ci	pkt_v = exec->shader_rec_v;
79662306a36Sopenharmony_ci	memcpy(pkt_v, pkt_u, packet_size);
79762306a36Sopenharmony_ci	exec->shader_rec_u += packet_size;
79862306a36Sopenharmony_ci	/* Shader recs have to be aligned to 16 bytes (due to the attribute
79962306a36Sopenharmony_ci	 * flags being in the low bytes), so round the next validated shader
80062306a36Sopenharmony_ci	 * rec address up.  This should be safe, since we've got so many
80162306a36Sopenharmony_ci	 * relocations in a shader rec packet.
80262306a36Sopenharmony_ci	 */
80362306a36Sopenharmony_ci	BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4);
80462306a36Sopenharmony_ci	exec->shader_rec_v += roundup(packet_size, 16);
80562306a36Sopenharmony_ci	exec->shader_rec_size -= packet_size;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	for (i = 0; i < shader_reloc_count; i++) {
80862306a36Sopenharmony_ci		if (src_handles[i] > exec->bo_count) {
80962306a36Sopenharmony_ci			DRM_DEBUG("Shader handle %d too big\n", src_handles[i]);
81062306a36Sopenharmony_ci			return -EINVAL;
81162306a36Sopenharmony_ci		}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		bo[i] = to_drm_gem_dma_obj(exec->bo[src_handles[i]]);
81462306a36Sopenharmony_ci		if (!bo[i])
81562306a36Sopenharmony_ci			return -EINVAL;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci	for (i = shader_reloc_count; i < nr_relocs; i++) {
81862306a36Sopenharmony_ci		bo[i] = vc4_use_bo(exec, src_handles[i]);
81962306a36Sopenharmony_ci		if (!bo[i])
82062306a36Sopenharmony_ci			return -EINVAL;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (((*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD) == 0) !=
82462306a36Sopenharmony_ci	    to_vc4_bo(&bo[0]->base)->validated_shader->is_threaded) {
82562306a36Sopenharmony_ci		DRM_DEBUG("Thread mode of CL and FS do not match\n");
82662306a36Sopenharmony_ci		return -EINVAL;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (to_vc4_bo(&bo[1]->base)->validated_shader->is_threaded ||
83062306a36Sopenharmony_ci	    to_vc4_bo(&bo[2]->base)->validated_shader->is_threaded) {
83162306a36Sopenharmony_ci		DRM_DEBUG("cs and vs cannot be threaded\n");
83262306a36Sopenharmony_ci		return -EINVAL;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	for (i = 0; i < shader_reloc_count; i++) {
83662306a36Sopenharmony_ci		struct vc4_validated_shader_info *validated_shader;
83762306a36Sopenharmony_ci		uint32_t o = shader_reloc_offsets[i];
83862306a36Sopenharmony_ci		uint32_t src_offset = *(uint32_t *)(pkt_u + o);
83962306a36Sopenharmony_ci		uint32_t *texture_handles_u;
84062306a36Sopenharmony_ci		void *uniform_data_u;
84162306a36Sopenharmony_ci		uint32_t tex, uni;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		*(uint32_t *)(pkt_v + o) = bo[i]->dma_addr + src_offset;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		if (src_offset != 0) {
84662306a36Sopenharmony_ci			DRM_DEBUG("Shaders must be at offset 0 of "
84762306a36Sopenharmony_ci				  "the BO.\n");
84862306a36Sopenharmony_ci			return -EINVAL;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader;
85262306a36Sopenharmony_ci		if (!validated_shader)
85362306a36Sopenharmony_ci			return -EINVAL;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		if (validated_shader->uniforms_src_size >
85662306a36Sopenharmony_ci		    exec->uniforms_size) {
85762306a36Sopenharmony_ci			DRM_DEBUG("Uniforms src buffer overflow\n");
85862306a36Sopenharmony_ci			return -EINVAL;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		texture_handles_u = exec->uniforms_u;
86262306a36Sopenharmony_ci		uniform_data_u = (texture_handles_u +
86362306a36Sopenharmony_ci				  validated_shader->num_texture_samples);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		memcpy(exec->uniforms_v, uniform_data_u,
86662306a36Sopenharmony_ci		       validated_shader->uniforms_size);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		for (tex = 0;
86962306a36Sopenharmony_ci		     tex < validated_shader->num_texture_samples;
87062306a36Sopenharmony_ci		     tex++) {
87162306a36Sopenharmony_ci			if (!reloc_tex(exec,
87262306a36Sopenharmony_ci				       uniform_data_u,
87362306a36Sopenharmony_ci				       &validated_shader->texture_samples[tex],
87462306a36Sopenharmony_ci				       texture_handles_u[tex],
87562306a36Sopenharmony_ci				       i == 2)) {
87662306a36Sopenharmony_ci				return -EINVAL;
87762306a36Sopenharmony_ci			}
87862306a36Sopenharmony_ci		}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci		/* Fill in the uniform slots that need this shader's
88162306a36Sopenharmony_ci		 * start-of-uniforms address (used for resetting the uniform
88262306a36Sopenharmony_ci		 * stream in the presence of control flow).
88362306a36Sopenharmony_ci		 */
88462306a36Sopenharmony_ci		for (uni = 0;
88562306a36Sopenharmony_ci		     uni < validated_shader->num_uniform_addr_offsets;
88662306a36Sopenharmony_ci		     uni++) {
88762306a36Sopenharmony_ci			uint32_t o = validated_shader->uniform_addr_offsets[uni];
88862306a36Sopenharmony_ci			((uint32_t *)exec->uniforms_v)[o] = exec->uniforms_p;
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		*(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		exec->uniforms_u += validated_shader->uniforms_src_size;
89462306a36Sopenharmony_ci		exec->uniforms_v += validated_shader->uniforms_size;
89562306a36Sopenharmony_ci		exec->uniforms_p += validated_shader->uniforms_size;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	for (i = 0; i < nr_attributes; i++) {
89962306a36Sopenharmony_ci		struct drm_gem_dma_object *vbo =
90062306a36Sopenharmony_ci			bo[ARRAY_SIZE(shader_reloc_offsets) + i];
90162306a36Sopenharmony_ci		uint32_t o = 36 + i * 8;
90262306a36Sopenharmony_ci		uint32_t offset = *(uint32_t *)(pkt_u + o + 0);
90362306a36Sopenharmony_ci		uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1;
90462306a36Sopenharmony_ci		uint32_t stride = *(uint8_t *)(pkt_u + o + 5);
90562306a36Sopenharmony_ci		uint32_t max_index;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		exec->bin_dep_seqno = max(exec->bin_dep_seqno,
90862306a36Sopenharmony_ci					  to_vc4_bo(&vbo->base)->write_seqno);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		if (state->addr & 0x8)
91162306a36Sopenharmony_ci			stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		if (vbo->base.size < offset ||
91462306a36Sopenharmony_ci		    vbo->base.size - offset < attr_size) {
91562306a36Sopenharmony_ci			DRM_DEBUG("BO offset overflow (%d + %d > %zu)\n",
91662306a36Sopenharmony_ci				  offset, attr_size, vbo->base.size);
91762306a36Sopenharmony_ci			return -EINVAL;
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		if (stride != 0) {
92162306a36Sopenharmony_ci			max_index = ((vbo->base.size - offset - attr_size) /
92262306a36Sopenharmony_ci				     stride);
92362306a36Sopenharmony_ci			if (state->max_index > max_index) {
92462306a36Sopenharmony_ci				DRM_DEBUG("primitives use index %d out of "
92562306a36Sopenharmony_ci					  "supplied %d\n",
92662306a36Sopenharmony_ci					  state->max_index, max_index);
92762306a36Sopenharmony_ci				return -EINVAL;
92862306a36Sopenharmony_ci			}
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		*(uint32_t *)(pkt_v + o) = vbo->dma_addr + offset;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return 0;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciint
93862306a36Sopenharmony_civc4_validate_shader_recs(struct drm_device *dev,
93962306a36Sopenharmony_ci			 struct vc4_exec_info *exec)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
94262306a36Sopenharmony_ci	uint32_t i;
94362306a36Sopenharmony_ci	int ret = 0;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
94662306a36Sopenharmony_ci		return -ENODEV;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	for (i = 0; i < exec->shader_state_count; i++) {
94962306a36Sopenharmony_ci		ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]);
95062306a36Sopenharmony_ci		if (ret)
95162306a36Sopenharmony_ci			return ret;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return ret;
95562306a36Sopenharmony_ci}
956