Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Copyright (c) 2010-2020 NVIDIA Corporation */
  3
  4#include "drm.h"
  5#include "submit.h"
  6#include "uapi.h"
  7
  8struct tegra_drm_firewall {
  9	struct tegra_drm_submit_data *submit;
 10	struct tegra_drm_client *client;
 11	u32 *data;
 12	u32 pos;
 13	u32 end;
 14	u32 class;
 15};
 16
 17static int fw_next(struct tegra_drm_firewall *fw, u32 *word)
 18{
 19	if (fw->pos == fw->end)
 20		return -EINVAL;
 21
 22	*word = fw->data[fw->pos++];
 23
 24	return 0;
 25}
 26
 27static bool fw_check_addr_valid(struct tegra_drm_firewall *fw, u32 offset)
 28{
 29	u32 i;
 30
 31	for (i = 0; i < fw->submit->num_used_mappings; i++) {
 32		struct tegra_drm_mapping *m = fw->submit->used_mappings[i].mapping;
 33
 34		if (offset >= m->iova && offset <= m->iova_end)
 35			return true;
 36	}
 37
 38	return false;
 39}
 40
 41static int fw_check_reg(struct tegra_drm_firewall *fw, u32 offset)
 42{
 43	bool is_addr;
 44	u32 word;
 45	int err;
 46
 47	err = fw_next(fw, &word);
 48	if (err)
 49		return err;
 50
 51	if (!fw->client->ops->is_addr_reg)
 52		return 0;
 53
 54	is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
 55					       offset);
 56
 57	if (!is_addr)
 58		return 0;
 59
 60	if (!fw_check_addr_valid(fw, word))
 61		return -EINVAL;
 62
 63	return 0;
 64}
 65
 66static int fw_check_regs_seq(struct tegra_drm_firewall *fw, u32 offset,
 67			     u32 count, bool incr)
 68{
 69	u32 i;
 70
 71	for (i = 0; i < count; i++) {
 72		if (fw_check_reg(fw, offset))
 73			return -EINVAL;
 74
 75		if (incr)
 76			offset++;
 77	}
 78
 79	return 0;
 80}
 81
 82static int fw_check_regs_mask(struct tegra_drm_firewall *fw, u32 offset,
 83			      u16 mask)
 84{
 85	unsigned long bmask = mask;
 86	unsigned int bit;
 87
 88	for_each_set_bit(bit, &bmask, 16) {
 89		if (fw_check_reg(fw, offset+bit))
 90			return -EINVAL;
 91	}
 92
 93	return 0;
 94}
 95
 96static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset)
 97{
 98	bool is_addr;
 99
100	is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
101					       offset);
102	if (is_addr)
103		return -EINVAL;
104
105	return 0;
106}
107
108static int fw_check_class(struct tegra_drm_firewall *fw, u32 class)
109{
110	if (!fw->client->ops->is_valid_class) {
111		if (class == fw->client->base.class)
112			return 0;
113		else
114			return -EINVAL;
115	}
116
117	if (!fw->client->ops->is_valid_class(class))
118		return -EINVAL;
119
120	return 0;
121}
122
123enum {
124	HOST1X_OPCODE_SETCLASS  = 0x00,
125	HOST1X_OPCODE_INCR      = 0x01,
126	HOST1X_OPCODE_NONINCR   = 0x02,
127	HOST1X_OPCODE_MASK      = 0x03,
128	HOST1X_OPCODE_IMM       = 0x04,
129	HOST1X_OPCODE_RESTART   = 0x05,
130	HOST1X_OPCODE_GATHER    = 0x06,
131	HOST1X_OPCODE_SETSTRMID = 0x07,
132	HOST1X_OPCODE_SETAPPID  = 0x08,
133	HOST1X_OPCODE_SETPYLD   = 0x09,
134	HOST1X_OPCODE_INCR_W    = 0x0a,
135	HOST1X_OPCODE_NONINCR_W = 0x0b,
136	HOST1X_OPCODE_GATHER_W  = 0x0c,
137	HOST1X_OPCODE_RESTART_W = 0x0d,
138	HOST1X_OPCODE_EXTEND    = 0x0e,
139};
140
141int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start,
142			  u32 words, struct tegra_drm_submit_data *submit,
143			  u32 *job_class)
144{
145	struct tegra_drm_firewall fw = {
146		.submit = submit,
147		.client = client,
148		.data = data,
149		.pos = start,
150		.end = start+words,
151		.class = *job_class,
152	};
153	bool payload_valid = false;
154	u32 payload;
155	int err;
156
157	while (fw.pos != fw.end) {
158		u32 word, opcode, offset, count, mask, class;
159
160		err = fw_next(&fw, &word);
161		if (err)
162			return err;
163
164		opcode = (word & 0xf0000000) >> 28;
165
166		switch (opcode) {
167		case HOST1X_OPCODE_SETCLASS:
168			offset = word >> 16 & 0xfff;
169			mask = word & 0x3f;
170			class = (word >> 6) & 0x3ff;
171			err = fw_check_class(&fw, class);
172			fw.class = class;
173			*job_class = class;
174			if (!err)
175				err = fw_check_regs_mask(&fw, offset, mask);
176			if (err)
177				dev_warn(client->base.dev,
178					 "illegal SETCLASS(offset=0x%x, mask=0x%x, class=0x%x) at word %u",
179					 offset, mask, class, fw.pos-1);
180			break;
181		case HOST1X_OPCODE_INCR:
182			offset = (word >> 16) & 0xfff;
183			count = word & 0xffff;
184			err = fw_check_regs_seq(&fw, offset, count, true);
185			if (err)
186				dev_warn(client->base.dev,
187					 "illegal INCR(offset=0x%x, count=%u) in class 0x%x at word %u",
188					 offset, count, fw.class, fw.pos-1);
189			break;
190		case HOST1X_OPCODE_NONINCR:
191			offset = (word >> 16) & 0xfff;
192			count = word & 0xffff;
193			err = fw_check_regs_seq(&fw, offset, count, false);
194			if (err)
195				dev_warn(client->base.dev,
196					 "illegal NONINCR(offset=0x%x, count=%u) in class 0x%x at word %u",
197					 offset, count, fw.class, fw.pos-1);
198			break;
199		case HOST1X_OPCODE_MASK:
200			offset = (word >> 16) & 0xfff;
201			mask = word & 0xffff;
202			err = fw_check_regs_mask(&fw, offset, mask);
203			if (err)
204				dev_warn(client->base.dev,
205					 "illegal MASK(offset=0x%x, mask=0x%x) in class 0x%x at word %u",
206					 offset, mask, fw.class, fw.pos-1);
207			break;
208		case HOST1X_OPCODE_IMM:
209			/* IMM cannot reasonably be used to write a pointer */
210			offset = (word >> 16) & 0xfff;
211			err = fw_check_regs_imm(&fw, offset);
212			if (err)
213				dev_warn(client->base.dev,
214					 "illegal IMM(offset=0x%x) in class 0x%x at word %u",
215					 offset, fw.class, fw.pos-1);
216			break;
217		case HOST1X_OPCODE_SETPYLD:
218			payload = word & 0xffff;
219			payload_valid = true;
220			break;
221		case HOST1X_OPCODE_INCR_W:
222			if (!payload_valid)
223				return -EINVAL;
224
225			offset = word & 0x3fffff;
226			err = fw_check_regs_seq(&fw, offset, payload, true);
227			if (err)
228				dev_warn(client->base.dev,
229					 "illegal INCR_W(offset=0x%x) in class 0x%x at word %u",
230					 offset, fw.class, fw.pos-1);
231			break;
232		case HOST1X_OPCODE_NONINCR_W:
233			if (!payload_valid)
234				return -EINVAL;
235
236			offset = word & 0x3fffff;
237			err = fw_check_regs_seq(&fw, offset, payload, false);
238			if (err)
239				dev_warn(client->base.dev,
240					 "illegal NONINCR(offset=0x%x) in class 0x%x at word %u",
241					 offset, fw.class, fw.pos-1);
242			break;
243		default:
244			dev_warn(client->base.dev, "illegal opcode at word %u",
245				 fw.pos-1);
246			return -EINVAL;
247		}
248
249		if (err)
250			return err;
251	}
252
253	return 0;
254}