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 8 struct 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 17 static 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 27 static 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 41 static 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 66 static 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 82 static 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 96 static 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 111 static 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 126 enum { 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 144 int 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