2 * Copyright © 2014 Broadcom
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:
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
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
25 * DOC: Shader validator for VC4.
27 * The VC4 has no IOMMU between it and system memory. So, a user with access
28 * to execute shaders could escalate privilege by overwriting system memory
29 * (using the VPM write address register in the general-purpose DMA mode) or
30 * reading system memory it shouldn't (reading it as a texture, or uniform
31 * data, or vertex data).
33 * This walks over a shader starting from some offset within a BO, ensuring
34 * that its accesses are appropriately bounded, and recording how many texture
35 * accesses are made and where so that we can do relocations for them in the
38 * The kernel API has shaders stored in user-mapped BOs. The BOs will be
39 * forcibly unmapped from the process before validation, and any cache of
40 * validated state will be flushed if the mapping is faulted back in.
42 * Storing the shaders in BOs means that the validation process will be slow
43 * due to uncached reads, but since shaders are long-lived and shader BOs are
44 * never actually modified, this shouldn't be a problem.
47 #include "vc4_simulator_validate.h"
49 #include "vc4_qpu_defines.h"
51 struct vc4_shader_validation_state
{
52 struct vc4_texture_sample_info tmu_setup
[2];
53 int tmu_write_count
[2];
57 is_tmu_write(uint32_t waddr
)
59 return (waddr
>= QPU_W_TMU0_S
&&
60 waddr
<= QPU_W_TMU1_B
);
64 record_validated_texture_sample(struct vc4_validated_shader_info
*validated_shader
,
65 struct vc4_shader_validation_state
*validation_state
,
68 uint32_t s
= validated_shader
->num_texture_samples
;
70 struct vc4_texture_sample_info
*temp_samples
;
72 temp_samples
= krealloc(validated_shader
->texture_samples
,
73 (s
+ 1) * sizeof(*temp_samples
),
78 memcpy(temp_samples
[s
].p_offset
,
79 validation_state
->tmu_setup
[tmu
].p_offset
,
80 validation_state
->tmu_write_count
[tmu
] * sizeof(uint32_t));
81 for (i
= validation_state
->tmu_write_count
[tmu
]; i
< 4; i
++)
82 temp_samples
[s
].p_offset
[i
] = ~0;
84 validated_shader
->num_texture_samples
= s
+ 1;
85 validated_shader
->texture_samples
= temp_samples
;
91 check_tmu_write(struct vc4_validated_shader_info
*validated_shader
,
92 struct vc4_shader_validation_state
*validation_state
,
95 int tmu
= waddr
> QPU_W_TMU0_B
;
97 if (!is_tmu_write(waddr
))
100 if (validation_state
->tmu_write_count
[tmu
] >= 4) {
101 DRM_ERROR("TMU%d got too many parameters before dispatch\n",
105 validation_state
->tmu_setup
[tmu
].p_offset
[validation_state
->tmu_write_count
[tmu
]] =
106 validated_shader
->uniforms_size
;
107 validation_state
->tmu_write_count
[tmu
]++;
108 validated_shader
->uniforms_size
+= 4;
110 if (waddr
== QPU_W_TMU0_S
|| waddr
== QPU_W_TMU1_S
) {
111 if (!record_validated_texture_sample(validated_shader
,
112 validation_state
, tmu
)) {
116 validation_state
->tmu_write_count
[tmu
] = 0;
123 check_register_write(struct vc4_validated_shader_info
*validated_shader
,
124 struct vc4_shader_validation_state
*validation_state
,
128 case QPU_W_UNIFORMS_ADDRESS
:
129 /* XXX: We'll probably need to support this for reladdr, but
130 * it's definitely a security-related one.
132 DRM_ERROR("uniforms address load unsupported\n");
135 case QPU_W_TLB_COLOR_MS
:
136 case QPU_W_TLB_COLOR_ALL
:
138 /* These only interact with the tile buffer, not main memory,
151 return check_tmu_write(validated_shader
, validation_state
,
155 case QPU_W_TMU_NOSWAP
:
156 case QPU_W_TLB_STENCIL_SETUP
:
157 case QPU_W_TLB_ALPHA_MASK
:
158 case QPU_W_MUTEX_RELEASE
:
159 /* XXX: I haven't thought about these, so don't support them
162 DRM_ERROR("Unsupported waddr %d\n", waddr
);
166 DRM_ERROR("General VPM DMA unsupported\n");
170 case QPU_W_VPMVCD_SETUP
:
171 /* We allow VPM setup in general, even including VPM DMA
172 * configuration setup, because the (unsafe) DMA can only be
173 * triggered by QPU_W_VPM_ADDR writes.
182 check_instruction_writes(uint64_t inst
,
183 struct vc4_validated_shader_info
*validated_shader
,
184 struct vc4_shader_validation_state
*validation_state
)
186 uint32_t waddr_add
= QPU_GET_FIELD(inst
, QPU_WADDR_ADD
);
187 uint32_t waddr_mul
= QPU_GET_FIELD(inst
, QPU_WADDR_MUL
);
189 if (is_tmu_write(waddr_add
) && is_tmu_write(waddr_mul
)) {
190 DRM_ERROR("ADD and MUL both set up textures\n");
194 return (check_register_write(validated_shader
, validation_state
, waddr_add
) &&
195 check_register_write(validated_shader
, validation_state
, waddr_mul
));
199 check_instruction_reads(uint64_t inst
,
200 struct vc4_validated_shader_info
*validated_shader
)
202 uint32_t waddr_add
= QPU_GET_FIELD(inst
, QPU_WADDR_ADD
);
203 uint32_t waddr_mul
= QPU_GET_FIELD(inst
, QPU_WADDR_MUL
);
204 uint32_t raddr_a
= QPU_GET_FIELD(inst
, QPU_RADDR_A
);
205 uint32_t raddr_b
= QPU_GET_FIELD(inst
, QPU_RADDR_B
);
207 if (raddr_a
== QPU_R_UNIF
||
208 raddr_b
== QPU_R_UNIF
) {
209 if (is_tmu_write(waddr_add
) || is_tmu_write(waddr_mul
)) {
210 DRM_ERROR("uniform read in the same instruction as "
215 /* This can't overflow the uint32_t, because we're reading 8
216 * bytes of instruction to increment by 4 here, so we'd
219 validated_shader
->uniforms_size
+= 4;
225 struct vc4_validated_shader_info
*
226 vc4_validate_shader(struct drm_gem_cma_object
*shader_obj
,
227 uint32_t start_offset
)
229 bool found_shader_end
= false;
230 int shader_end_ip
= 0;
233 struct vc4_validated_shader_info
*validated_shader
;
234 struct vc4_shader_validation_state validation_state
;
236 memset(&validation_state
, 0, sizeof(validation_state
));
238 if (start_offset
+ sizeof(uint64_t) > shader_obj
->base
.size
) {
239 DRM_ERROR("shader starting at %d outside of BO sized %d\n",
241 shader_obj
->base
.size
);
244 shader
= shader_obj
->vaddr
+ start_offset
;
245 max_ip
= (shader_obj
->base
.size
- start_offset
) / sizeof(uint64_t);
247 validated_shader
= kcalloc(sizeof(*validated_shader
), 1, GFP_KERNEL
);
248 if (!validated_shader
)
251 for (ip
= 0; ip
< max_ip
; ip
++) {
252 uint64_t inst
= shader
[ip
];
253 uint32_t sig
= QPU_GET_FIELD(inst
, QPU_SIG
);
257 case QPU_SIG_WAIT_FOR_SCOREBOARD
:
258 case QPU_SIG_SCOREBOARD_UNLOCK
:
259 case QPU_SIG_COLOR_LOAD
:
260 case QPU_SIG_LOAD_TMU0
:
261 case QPU_SIG_LOAD_TMU1
:
262 if (!check_instruction_writes(inst
, validated_shader
,
263 &validation_state
)) {
264 DRM_ERROR("Bad write at ip %d\n", ip
);
268 if (!check_instruction_reads(inst
, validated_shader
))
273 case QPU_SIG_LOAD_IMM
:
274 if (!check_instruction_writes(inst
, validated_shader
,
275 &validation_state
)) {
276 DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip
);
281 case QPU_SIG_PROG_END
:
282 found_shader_end
= true;
287 DRM_ERROR("Unsupported QPU signal %d at "
288 "instruction %d\n", sig
, ip
);
292 /* There are two delay slots after program end is signaled
293 * that are still executed, then we're finished.
295 if (found_shader_end
&& ip
== shader_end_ip
+ 2)
300 DRM_ERROR("shader starting at %d failed to terminate before "
301 "shader BO end at %d\n",
303 shader_obj
->base
.size
);
307 /* Again, no chance of integer overflow here because the worst case
308 * scenario is 8 bytes of uniforms plus handles per 8-byte
311 validated_shader
->uniforms_src_size
=
312 (validated_shader
->uniforms_size
+
313 4 * validated_shader
->num_texture_samples
);
315 return validated_shader
;
318 kfree(validated_shader
);