linux/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Etnaviv Project
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms of the GNU General Public License version 2 as published by
   6 * the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along with
  14 * this program.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16
  17#include <linux/kernel.h>
  18
  19#include "etnaviv_gem.h"
  20#include "etnaviv_gpu.h"
  21
  22#include "cmdstream.xml.h"
  23
  24#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT)
  25
  26struct etna_validation_state {
  27        struct etnaviv_gpu *gpu;
  28        const struct drm_etnaviv_gem_submit_reloc *relocs;
  29        unsigned int num_relocs;
  30        u32 *start;
  31};
  32
  33static const struct {
  34        u16 offset;
  35        u16 size;
  36} etnaviv_sensitive_states[] __initconst = {
  37#define ST(start, num) { (start) >> 2, (num) }
  38        /* 2D */
  39        ST(0x1200, 1),
  40        ST(0x1228, 1),
  41        ST(0x1238, 1),
  42        ST(0x1284, 1),
  43        ST(0x128c, 1),
  44        ST(0x1304, 1),
  45        ST(0x1310, 1),
  46        ST(0x1318, 1),
  47        ST(0x12800, 4),
  48        ST(0x128a0, 4),
  49        ST(0x128c0, 4),
  50        ST(0x12970, 4),
  51        ST(0x12a00, 8),
  52        ST(0x12b40, 8),
  53        ST(0x12b80, 8),
  54        ST(0x12ce0, 8),
  55        /* 3D */
  56        ST(0x0644, 1),
  57        ST(0x064c, 1),
  58        ST(0x0680, 8),
  59        ST(0x1410, 1),
  60        ST(0x1430, 1),
  61        ST(0x1458, 1),
  62        ST(0x1460, 8),
  63        ST(0x1480, 8),
  64        ST(0x1500, 8),
  65        ST(0x1520, 8),
  66        ST(0x1608, 1),
  67        ST(0x1610, 1),
  68        ST(0x1658, 1),
  69        ST(0x165c, 1),
  70        ST(0x1664, 1),
  71        ST(0x1668, 1),
  72        ST(0x16a4, 1),
  73        ST(0x16c0, 8),
  74        ST(0x16e0, 8),
  75        ST(0x1740, 8),
  76        ST(0x2400, 14 * 16),
  77        ST(0x10800, 32 * 16),
  78#undef ST
  79};
  80
  81#define ETNAVIV_STATES_SIZE (VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK + 1u)
  82static DECLARE_BITMAP(etnaviv_states, ETNAVIV_STATES_SIZE);
  83
  84void __init etnaviv_validate_init(void)
  85{
  86        unsigned int i;
  87
  88        for (i = 0; i < ARRAY_SIZE(etnaviv_sensitive_states); i++)
  89                bitmap_set(etnaviv_states, etnaviv_sensitive_states[i].offset,
  90                           etnaviv_sensitive_states[i].size);
  91}
  92
  93static void etnaviv_warn_if_non_sensitive(struct etna_validation_state *state,
  94        unsigned int buf_offset, unsigned int state_addr)
  95{
  96        if (state->num_relocs && state->relocs->submit_offset < buf_offset) {
  97                dev_warn_once(state->gpu->dev,
  98                              "%s: relocation for non-sensitive state 0x%x at offset %u\n",
  99                              __func__, state_addr,
 100                              state->relocs->submit_offset);
 101                while (state->num_relocs &&
 102                       state->relocs->submit_offset < buf_offset) {
 103                        state->relocs++;
 104                        state->num_relocs--;
 105                }
 106        }
 107}
 108
 109static bool etnaviv_validate_load_state(struct etna_validation_state *state,
 110        u32 *ptr, unsigned int state_offset, unsigned int num)
 111{
 112        unsigned int size = min(ETNAVIV_STATES_SIZE, state_offset + num);
 113        unsigned int st_offset = state_offset, buf_offset;
 114
 115        for_each_set_bit_from(st_offset, etnaviv_states, size) {
 116                buf_offset = (ptr - state->start +
 117                              st_offset - state_offset) * 4;
 118
 119                etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4);
 120                if (state->num_relocs &&
 121                    state->relocs->submit_offset == buf_offset) {
 122                        state->relocs++;
 123                        state->num_relocs--;
 124                        continue;
 125                }
 126
 127                dev_warn_ratelimited(state->gpu->dev,
 128                                     "%s: load state touches restricted state 0x%x at offset %u\n",
 129                                     __func__, st_offset * 4, buf_offset);
 130                return false;
 131        }
 132
 133        if (state->num_relocs) {
 134                buf_offset = (ptr - state->start + num) * 4;
 135                etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4 +
 136                                              state->relocs->submit_offset -
 137                                              buf_offset);
 138        }
 139
 140        return true;
 141}
 142
 143static uint8_t cmd_length[32] = {
 144        [FE_OPCODE_DRAW_PRIMITIVES] = 4,
 145        [FE_OPCODE_DRAW_INDEXED_PRIMITIVES] = 6,
 146        [FE_OPCODE_NOP] = 2,
 147        [FE_OPCODE_STALL] = 2,
 148};
 149
 150bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, u32 *stream,
 151                              unsigned int size,
 152                              struct drm_etnaviv_gem_submit_reloc *relocs,
 153                              unsigned int reloc_size)
 154{
 155        struct etna_validation_state state;
 156        u32 *buf = stream;
 157        u32 *end = buf + size;
 158
 159        state.gpu = gpu;
 160        state.relocs = relocs;
 161        state.num_relocs = reloc_size;
 162        state.start = stream;
 163
 164        while (buf < end) {
 165                u32 cmd = *buf;
 166                unsigned int len, n, off;
 167                unsigned int op = cmd >> 27;
 168
 169                switch (op) {
 170                case FE_OPCODE_LOAD_STATE:
 171                        n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT);
 172                        len = ALIGN(1 + n, 2);
 173                        if (buf + len > end)
 174                                break;
 175
 176                        off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET);
 177                        if (!etnaviv_validate_load_state(&state, buf + 1,
 178                                                         off, n))
 179                                return false;
 180                        break;
 181
 182                case FE_OPCODE_DRAW_2D:
 183                        n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT);
 184                        if (n == 0)
 185                                n = 256;
 186                        len = 2 + n * 2;
 187                        break;
 188
 189                default:
 190                        len = cmd_length[op];
 191                        if (len == 0) {
 192                                dev_err(gpu->dev, "%s: op %u not permitted at offset %tu\n",
 193                                        __func__, op, buf - state.start);
 194                                return false;
 195                        }
 196                        break;
 197                }
 198
 199                buf += len;
 200        }
 201
 202        if (buf > end) {
 203                dev_err(gpu->dev, "%s: commands overflow end of buffer: %tu > %u\n",
 204                        __func__, buf - state.start, size);
 205                return false;
 206        }
 207
 208        return true;
 209}
 210