Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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	if (!fw->client->ops->is_addr_reg)
101		return 0;
102
103	is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
104					       offset);
105	if (is_addr)
106		return -EINVAL;
107
108	return 0;
109}
110
111static int fw_check_class(struct tegra_drm_firewall *fw, u32 class)
112{
113	if (!fw->client->ops->is_valid_class) {
114		if (class == fw->client->base.class)
115			return 0;
116		else
117			return -EINVAL;
118	}
119
120	if (!fw->client->ops->is_valid_class(class))
121		return -EINVAL;
122
123	return 0;
124}
125
126enum {
127	HOST1X_OPCODE_SETCLASS  = 0x00,
128	HOST1X_OPCODE_INCR      = 0x01,
129	HOST1X_OPCODE_NONINCR   = 0x02,
130	HOST1X_OPCODE_MASK      = 0x03,
131	HOST1X_OPCODE_IMM       = 0x04,
132	HOST1X_OPCODE_RESTART   = 0x05,
133	HOST1X_OPCODE_GATHER    = 0x06,
134	HOST1X_OPCODE_SETSTRMID = 0x07,
135	HOST1X_OPCODE_SETAPPID  = 0x08,
136	HOST1X_OPCODE_SETPYLD   = 0x09,
137	HOST1X_OPCODE_INCR_W    = 0x0a,
138	HOST1X_OPCODE_NONINCR_W = 0x0b,
139	HOST1X_OPCODE_GATHER_W  = 0x0c,
140	HOST1X_OPCODE_RESTART_W = 0x0d,
141	HOST1X_OPCODE_EXTEND    = 0x0e,
142};
143
144int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start,
145			  u32 words, struct tegra_drm_submit_data *submit,
146			  u32 *job_class)
147{
148	struct tegra_drm_firewall fw = {
149		.submit = submit,
150		.client = client,
151		.data = data,
152		.pos = start,
153		.end = start+words,
154		.class = *job_class,
155	};
156	bool payload_valid = false;
157	u32 payload;
158	int err;
159
160	while (fw.pos != fw.end) {
161		u32 word, opcode, offset, count, mask, class;
162
163		err = fw_next(&fw, &word);
164		if (err)
165			return err;
166
167		opcode = (word & 0xf0000000) >> 28;
168
169		switch (opcode) {
170		case HOST1X_OPCODE_SETCLASS:
171			offset = word >> 16 & 0xfff;
172			mask = word & 0x3f;
173			class = (word >> 6) & 0x3ff;
174			err = fw_check_class(&fw, class);
175			fw.class = class;
176			*job_class = class;
177			if (!err)
178				err = fw_check_regs_mask(&fw, offset, mask);
179			if (err)
180				dev_warn(client->base.dev,
181					 "illegal SETCLASS(offset=0x%x, mask=0x%x, class=0x%x) at word %u",
182					 offset, mask, class, fw.pos-1);
183			break;
184		case HOST1X_OPCODE_INCR:
185			offset = (word >> 16) & 0xfff;
186			count = word & 0xffff;
187			err = fw_check_regs_seq(&fw, offset, count, true);
188			if (err)
189				dev_warn(client->base.dev,
190					 "illegal INCR(offset=0x%x, count=%u) in class 0x%x at word %u",
191					 offset, count, fw.class, fw.pos-1);
192			break;
193		case HOST1X_OPCODE_NONINCR:
194			offset = (word >> 16) & 0xfff;
195			count = word & 0xffff;
196			err = fw_check_regs_seq(&fw, offset, count, false);
197			if (err)
198				dev_warn(client->base.dev,
199					 "illegal NONINCR(offset=0x%x, count=%u) in class 0x%x at word %u",
200					 offset, count, fw.class, fw.pos-1);
201			break;
202		case HOST1X_OPCODE_MASK:
203			offset = (word >> 16) & 0xfff;
204			mask = word & 0xffff;
205			err = fw_check_regs_mask(&fw, offset, mask);
206			if (err)
207				dev_warn(client->base.dev,
208					 "illegal MASK(offset=0x%x, mask=0x%x) in class 0x%x at word %u",
209					 offset, mask, fw.class, fw.pos-1);
210			break;
211		case HOST1X_OPCODE_IMM:
212			/* IMM cannot reasonably be used to write a pointer */
213			offset = (word >> 16) & 0xfff;
214			err = fw_check_regs_imm(&fw, offset);
215			if (err)
216				dev_warn(client->base.dev,
217					 "illegal IMM(offset=0x%x) in class 0x%x at word %u",
218					 offset, fw.class, fw.pos-1);
219			break;
220		case HOST1X_OPCODE_SETPYLD:
221			payload = word & 0xffff;
222			payload_valid = true;
223			break;
224		case HOST1X_OPCODE_INCR_W:
225			if (!payload_valid)
226				return -EINVAL;
227
228			offset = word & 0x3fffff;
229			err = fw_check_regs_seq(&fw, offset, payload, true);
230			if (err)
231				dev_warn(client->base.dev,
232					 "illegal INCR_W(offset=0x%x) in class 0x%x at word %u",
233					 offset, fw.class, fw.pos-1);
234			break;
235		case HOST1X_OPCODE_NONINCR_W:
236			if (!payload_valid)
237				return -EINVAL;
238
239			offset = word & 0x3fffff;
240			err = fw_check_regs_seq(&fw, offset, payload, false);
241			if (err)
242				dev_warn(client->base.dev,
243					 "illegal NONINCR(offset=0x%x) in class 0x%x at word %u",
244					 offset, fw.class, fw.pos-1);
245			break;
246		default:
247			dev_warn(client->base.dev, "illegal opcode at word %u",
248				 fw.pos-1);
249			return -EINVAL;
250		}
251
252		if (err)
253			return err;
254	}
255
256	return 0;
257}