brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · d53f890 Raw
258 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* 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		else117			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}258