cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

firewall.c (5724B)


      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}