1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2015-2018 Etnaviv Project
6 #include <linux/kernel.h>
8 #include "etnaviv_gem.h"
9 #include "etnaviv_gpu.h"
11 #include "cmdstream.xml.h"
13 #define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT)
15 struct etna_validation_state
{
16 struct etnaviv_gpu
*gpu
;
17 const struct drm_etnaviv_gem_submit_reloc
*relocs
;
18 unsigned int num_relocs
;
25 } etnaviv_sensitive_states
[] __initconst
= {
26 #define ST(start, num) { (start) >> 2, (num) }
77 #define ETNAVIV_STATES_SIZE (VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK + 1u)
78 static DECLARE_BITMAP(etnaviv_states
, ETNAVIV_STATES_SIZE
);
80 void __init
etnaviv_validate_init(void)
84 for (i
= 0; i
< ARRAY_SIZE(etnaviv_sensitive_states
); i
++)
85 bitmap_set(etnaviv_states
, etnaviv_sensitive_states
[i
].offset
,
86 etnaviv_sensitive_states
[i
].size
);
89 static void etnaviv_warn_if_non_sensitive(struct etna_validation_state
*state
,
90 unsigned int buf_offset
, unsigned int state_addr
)
92 if (state
->num_relocs
&& state
->relocs
->submit_offset
< buf_offset
) {
93 dev_warn_once(state
->gpu
->dev
,
94 "%s: relocation for non-sensitive state 0x%x at offset %u\n",
96 state
->relocs
->submit_offset
);
97 while (state
->num_relocs
&&
98 state
->relocs
->submit_offset
< buf_offset
) {
105 static bool etnaviv_validate_load_state(struct etna_validation_state
*state
,
106 u32
*ptr
, unsigned int state_offset
, unsigned int num
)
108 unsigned int size
= min(ETNAVIV_STATES_SIZE
, state_offset
+ num
);
109 unsigned int st_offset
= state_offset
, buf_offset
;
111 for_each_set_bit_from(st_offset
, etnaviv_states
, size
) {
112 buf_offset
= (ptr
- state
->start
+
113 st_offset
- state_offset
) * 4;
115 etnaviv_warn_if_non_sensitive(state
, buf_offset
, st_offset
* 4);
116 if (state
->num_relocs
&&
117 state
->relocs
->submit_offset
== buf_offset
) {
123 dev_warn_ratelimited(state
->gpu
->dev
,
124 "%s: load state touches restricted state 0x%x at offset %u\n",
125 __func__
, st_offset
* 4, buf_offset
);
129 if (state
->num_relocs
) {
130 buf_offset
= (ptr
- state
->start
+ num
) * 4;
131 etnaviv_warn_if_non_sensitive(state
, buf_offset
, st_offset
* 4 +
132 state
->relocs
->submit_offset
-
139 static uint8_t cmd_length
[32] = {
140 [FE_OPCODE_DRAW_PRIMITIVES
] = 4,
141 [FE_OPCODE_DRAW_INDEXED_PRIMITIVES
] = 6,
142 [FE_OPCODE_DRAW_INSTANCED
] = 4,
144 [FE_OPCODE_STALL
] = 2,
147 bool etnaviv_cmd_validate_one(struct etnaviv_gpu
*gpu
, u32
*stream
,
149 struct drm_etnaviv_gem_submit_reloc
*relocs
,
150 unsigned int reloc_size
)
152 struct etna_validation_state state
;
154 u32
*end
= buf
+ size
;
157 state
.relocs
= relocs
;
158 state
.num_relocs
= reloc_size
;
159 state
.start
= stream
;
163 unsigned int len
, n
, off
;
164 unsigned int op
= cmd
>> 27;
167 case FE_OPCODE_LOAD_STATE
:
168 n
= EXTRACT(cmd
, VIV_FE_LOAD_STATE_HEADER_COUNT
);
169 len
= ALIGN(1 + n
, 2);
173 off
= EXTRACT(cmd
, VIV_FE_LOAD_STATE_HEADER_OFFSET
);
174 if (!etnaviv_validate_load_state(&state
, buf
+ 1,
179 case FE_OPCODE_DRAW_2D
:
180 n
= EXTRACT(cmd
, VIV_FE_DRAW_2D_HEADER_COUNT
);
187 len
= cmd_length
[op
];
189 dev_err(gpu
->dev
, "%s: op %u not permitted at offset %tu\n",
190 __func__
, op
, buf
- state
.start
);
200 dev_err(gpu
->dev
, "%s: commands overflow end of buffer: %tu > %u\n",
201 __func__
, buf
- state
.start
, size
);