0001
0002
0003
0004 #include "drm.h"
0005 #include "submit.h"
0006 #include "uapi.h"
0007
0008 struct tegra_drm_firewall {
0009 struct tegra_drm_submit_data *submit;
0010 struct tegra_drm_client *client;
0011 u32 *data;
0012 u32 pos;
0013 u32 end;
0014 u32 class;
0015 };
0016
0017 static int fw_next(struct tegra_drm_firewall *fw, u32 *word)
0018 {
0019 if (fw->pos == fw->end)
0020 return -EINVAL;
0021
0022 *word = fw->data[fw->pos++];
0023
0024 return 0;
0025 }
0026
0027 static bool fw_check_addr_valid(struct tegra_drm_firewall *fw, u32 offset)
0028 {
0029 u32 i;
0030
0031 for (i = 0; i < fw->submit->num_used_mappings; i++) {
0032 struct tegra_drm_mapping *m = fw->submit->used_mappings[i].mapping;
0033
0034 if (offset >= m->iova && offset <= m->iova_end)
0035 return true;
0036 }
0037
0038 return false;
0039 }
0040
0041 static int fw_check_reg(struct tegra_drm_firewall *fw, u32 offset)
0042 {
0043 bool is_addr;
0044 u32 word;
0045 int err;
0046
0047 err = fw_next(fw, &word);
0048 if (err)
0049 return err;
0050
0051 if (!fw->client->ops->is_addr_reg)
0052 return 0;
0053
0054 is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
0055 offset);
0056
0057 if (!is_addr)
0058 return 0;
0059
0060 if (!fw_check_addr_valid(fw, word))
0061 return -EINVAL;
0062
0063 return 0;
0064 }
0065
0066 static int fw_check_regs_seq(struct tegra_drm_firewall *fw, u32 offset,
0067 u32 count, bool incr)
0068 {
0069 u32 i;
0070
0071 for (i = 0; i < count; i++) {
0072 if (fw_check_reg(fw, offset))
0073 return -EINVAL;
0074
0075 if (incr)
0076 offset++;
0077 }
0078
0079 return 0;
0080 }
0081
0082 static int fw_check_regs_mask(struct tegra_drm_firewall *fw, u32 offset,
0083 u16 mask)
0084 {
0085 unsigned long bmask = mask;
0086 unsigned int bit;
0087
0088 for_each_set_bit(bit, &bmask, 16) {
0089 if (fw_check_reg(fw, offset+bit))
0090 return -EINVAL;
0091 }
0092
0093 return 0;
0094 }
0095
0096 static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset)
0097 {
0098 bool is_addr;
0099
0100 is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
0101 offset);
0102 if (is_addr)
0103 return -EINVAL;
0104
0105 return 0;
0106 }
0107
0108 static int fw_check_class(struct tegra_drm_firewall *fw, u32 class)
0109 {
0110 if (!fw->client->ops->is_valid_class) {
0111 if (class == fw->client->base.class)
0112 return 0;
0113 else
0114 return -EINVAL;
0115 }
0116
0117 if (!fw->client->ops->is_valid_class(class))
0118 return -EINVAL;
0119
0120 return 0;
0121 }
0122
0123 enum {
0124 HOST1X_OPCODE_SETCLASS = 0x00,
0125 HOST1X_OPCODE_INCR = 0x01,
0126 HOST1X_OPCODE_NONINCR = 0x02,
0127 HOST1X_OPCODE_MASK = 0x03,
0128 HOST1X_OPCODE_IMM = 0x04,
0129 HOST1X_OPCODE_RESTART = 0x05,
0130 HOST1X_OPCODE_GATHER = 0x06,
0131 HOST1X_OPCODE_SETSTRMID = 0x07,
0132 HOST1X_OPCODE_SETAPPID = 0x08,
0133 HOST1X_OPCODE_SETPYLD = 0x09,
0134 HOST1X_OPCODE_INCR_W = 0x0a,
0135 HOST1X_OPCODE_NONINCR_W = 0x0b,
0136 HOST1X_OPCODE_GATHER_W = 0x0c,
0137 HOST1X_OPCODE_RESTART_W = 0x0d,
0138 HOST1X_OPCODE_EXTEND = 0x0e,
0139 };
0140
0141 int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start,
0142 u32 words, struct tegra_drm_submit_data *submit,
0143 u32 *job_class)
0144 {
0145 struct tegra_drm_firewall fw = {
0146 .submit = submit,
0147 .client = client,
0148 .data = data,
0149 .pos = start,
0150 .end = start+words,
0151 .class = *job_class,
0152 };
0153 bool payload_valid = false;
0154 u32 payload;
0155 int err;
0156
0157 while (fw.pos != fw.end) {
0158 u32 word, opcode, offset, count, mask, class;
0159
0160 err = fw_next(&fw, &word);
0161 if (err)
0162 return err;
0163
0164 opcode = (word & 0xf0000000) >> 28;
0165
0166 switch (opcode) {
0167 case HOST1X_OPCODE_SETCLASS:
0168 offset = word >> 16 & 0xfff;
0169 mask = word & 0x3f;
0170 class = (word >> 6) & 0x3ff;
0171 err = fw_check_class(&fw, class);
0172 fw.class = class;
0173 *job_class = class;
0174 if (!err)
0175 err = fw_check_regs_mask(&fw, offset, mask);
0176 if (err)
0177 dev_warn(client->base.dev,
0178 "illegal SETCLASS(offset=0x%x, mask=0x%x, class=0x%x) at word %u",
0179 offset, mask, class, fw.pos-1);
0180 break;
0181 case HOST1X_OPCODE_INCR:
0182 offset = (word >> 16) & 0xfff;
0183 count = word & 0xffff;
0184 err = fw_check_regs_seq(&fw, offset, count, true);
0185 if (err)
0186 dev_warn(client->base.dev,
0187 "illegal INCR(offset=0x%x, count=%u) in class 0x%x at word %u",
0188 offset, count, fw.class, fw.pos-1);
0189 break;
0190 case HOST1X_OPCODE_NONINCR:
0191 offset = (word >> 16) & 0xfff;
0192 count = word & 0xffff;
0193 err = fw_check_regs_seq(&fw, offset, count, false);
0194 if (err)
0195 dev_warn(client->base.dev,
0196 "illegal NONINCR(offset=0x%x, count=%u) in class 0x%x at word %u",
0197 offset, count, fw.class, fw.pos-1);
0198 break;
0199 case HOST1X_OPCODE_MASK:
0200 offset = (word >> 16) & 0xfff;
0201 mask = word & 0xffff;
0202 err = fw_check_regs_mask(&fw, offset, mask);
0203 if (err)
0204 dev_warn(client->base.dev,
0205 "illegal MASK(offset=0x%x, mask=0x%x) in class 0x%x at word %u",
0206 offset, mask, fw.class, fw.pos-1);
0207 break;
0208 case HOST1X_OPCODE_IMM:
0209
0210 offset = (word >> 16) & 0xfff;
0211 err = fw_check_regs_imm(&fw, offset);
0212 if (err)
0213 dev_warn(client->base.dev,
0214 "illegal IMM(offset=0x%x) in class 0x%x at word %u",
0215 offset, fw.class, fw.pos-1);
0216 break;
0217 case HOST1X_OPCODE_SETPYLD:
0218 payload = word & 0xffff;
0219 payload_valid = true;
0220 break;
0221 case HOST1X_OPCODE_INCR_W:
0222 if (!payload_valid)
0223 return -EINVAL;
0224
0225 offset = word & 0x3fffff;
0226 err = fw_check_regs_seq(&fw, offset, payload, true);
0227 if (err)
0228 dev_warn(client->base.dev,
0229 "illegal INCR_W(offset=0x%x) in class 0x%x at word %u",
0230 offset, fw.class, fw.pos-1);
0231 break;
0232 case HOST1X_OPCODE_NONINCR_W:
0233 if (!payload_valid)
0234 return -EINVAL;
0235
0236 offset = word & 0x3fffff;
0237 err = fw_check_regs_seq(&fw, offset, payload, false);
0238 if (err)
0239 dev_warn(client->base.dev,
0240 "illegal NONINCR(offset=0x%x) in class 0x%x at word %u",
0241 offset, fw.class, fw.pos-1);
0242 break;
0243 default:
0244 dev_warn(client->base.dev, "illegal opcode at word %u",
0245 fw.pos-1);
0246 return -EINVAL;
0247 }
0248
0249 if (err)
0250 return err;
0251 }
0252
0253 return 0;
0254 }