Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (c) 2010-2020 NVIDIA Corporation */
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             /* IMM cannot reasonably be used to write a pointer */
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 }