linux/drivers/gpu/drm/radeon/radeon_cs.c
<<
>>
Prefs
   1/*
   2 * Copyright 2008 Jerome Glisse.
   3 * All Rights Reserved.
   4 *
   5 * Permission is hereby granted, free of charge, to any person obtaining a
   6 * copy of this software and associated documentation files (the "Software"),
   7 * to deal in the Software without restriction, including without limitation
   8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   9 * and/or sell copies of the Software, and to permit persons to whom the
  10 * Software is furnished to do so, subject to the following conditions:
  11 *
  12 * The above copyright notice and this permission notice (including the next
  13 * paragraph) shall be included in all copies or substantial portions of the
  14 * Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  19 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  22 * DEALINGS IN THE SOFTWARE.
  23 *
  24 * Authors:
  25 *    Jerome Glisse <glisse@freedesktop.org>
  26 */
  27#include "drmP.h"
  28#include "radeon_drm.h"
  29#include "radeon_reg.h"
  30#include "radeon.h"
  31
  32void r100_cs_dump_packet(struct radeon_cs_parser *p,
  33                         struct radeon_cs_packet *pkt);
  34
  35int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
  36{
  37        struct drm_device *ddev = p->rdev->ddev;
  38        struct radeon_cs_chunk *chunk;
  39        unsigned i, j;
  40        bool duplicate;
  41
  42        if (p->chunk_relocs_idx == -1) {
  43                return 0;
  44        }
  45        chunk = &p->chunks[p->chunk_relocs_idx];
  46        /* FIXME: we assume that each relocs use 4 dwords */
  47        p->nrelocs = chunk->length_dw / 4;
  48        p->relocs_ptr = kcalloc(p->nrelocs, sizeof(void *), GFP_KERNEL);
  49        if (p->relocs_ptr == NULL) {
  50                return -ENOMEM;
  51        }
  52        p->relocs = kcalloc(p->nrelocs, sizeof(struct radeon_cs_reloc), GFP_KERNEL);
  53        if (p->relocs == NULL) {
  54                return -ENOMEM;
  55        }
  56        for (i = 0; i < p->nrelocs; i++) {
  57                struct drm_radeon_cs_reloc *r;
  58
  59                duplicate = false;
  60                r = (struct drm_radeon_cs_reloc *)&chunk->kdata[i*4];
  61                for (j = 0; j < p->nrelocs; j++) {
  62                        if (r->handle == p->relocs[j].handle) {
  63                                p->relocs_ptr[i] = &p->relocs[j];
  64                                duplicate = true;
  65                                break;
  66                        }
  67                }
  68                if (!duplicate) {
  69                        p->relocs[i].gobj = drm_gem_object_lookup(ddev,
  70                                                                  p->filp,
  71                                                                  r->handle);
  72                        if (p->relocs[i].gobj == NULL) {
  73                                DRM_ERROR("gem object lookup failed 0x%x\n",
  74                                          r->handle);
  75                                return -EINVAL;
  76                        }
  77                        p->relocs_ptr[i] = &p->relocs[i];
  78                        p->relocs[i].robj = p->relocs[i].gobj->driver_private;
  79                        p->relocs[i].lobj.robj = p->relocs[i].robj;
  80                        p->relocs[i].lobj.rdomain = r->read_domains;
  81                        p->relocs[i].lobj.wdomain = r->write_domain;
  82                        p->relocs[i].handle = r->handle;
  83                        p->relocs[i].flags = r->flags;
  84                        INIT_LIST_HEAD(&p->relocs[i].lobj.list);
  85                        radeon_object_list_add_object(&p->relocs[i].lobj,
  86                                                      &p->validated);
  87                }
  88        }
  89        return radeon_object_list_validate(&p->validated, p->ib->fence);
  90}
  91
  92int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
  93{
  94        struct drm_radeon_cs *cs = data;
  95        uint64_t *chunk_array_ptr;
  96        unsigned size, i;
  97
  98        if (!cs->num_chunks) {
  99                return 0;
 100        }
 101        /* get chunks */
 102        INIT_LIST_HEAD(&p->validated);
 103        p->idx = 0;
 104        p->chunk_ib_idx = -1;
 105        p->chunk_relocs_idx = -1;
 106        p->chunks_array = kcalloc(cs->num_chunks, sizeof(uint64_t), GFP_KERNEL);
 107        if (p->chunks_array == NULL) {
 108                return -ENOMEM;
 109        }
 110        chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks);
 111        if (DRM_COPY_FROM_USER(p->chunks_array, chunk_array_ptr,
 112                               sizeof(uint64_t)*cs->num_chunks)) {
 113                return -EFAULT;
 114        }
 115        p->nchunks = cs->num_chunks;
 116        p->chunks = kcalloc(p->nchunks, sizeof(struct radeon_cs_chunk), GFP_KERNEL);
 117        if (p->chunks == NULL) {
 118                return -ENOMEM;
 119        }
 120        for (i = 0; i < p->nchunks; i++) {
 121                struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
 122                struct drm_radeon_cs_chunk user_chunk;
 123                uint32_t __user *cdata;
 124
 125                chunk_ptr = (void __user*)(unsigned long)p->chunks_array[i];
 126                if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr,
 127                                       sizeof(struct drm_radeon_cs_chunk))) {
 128                        return -EFAULT;
 129                }
 130                p->chunks[i].length_dw = user_chunk.length_dw;
 131                p->chunks[i].kdata = NULL;
 132                p->chunks[i].chunk_id = user_chunk.chunk_id;
 133
 134                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) {
 135                        p->chunk_relocs_idx = i;
 136                }
 137                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) {
 138                        p->chunk_ib_idx = i;
 139                        /* zero length IB isn't useful */
 140                        if (p->chunks[i].length_dw == 0)
 141                                return -EINVAL;
 142                }
 143
 144                p->chunks[i].length_dw = user_chunk.length_dw;
 145                p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
 146
 147                cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data;
 148                if (p->chunks[i].chunk_id != RADEON_CHUNK_ID_IB) {
 149                        size = p->chunks[i].length_dw * sizeof(uint32_t);
 150                        p->chunks[i].kdata = kmalloc(size, GFP_KERNEL);
 151                        if (p->chunks[i].kdata == NULL) {
 152                                return -ENOMEM;
 153                        }
 154                        if (DRM_COPY_FROM_USER(p->chunks[i].kdata,
 155                                               p->chunks[i].user_ptr, size)) {
 156                                return -EFAULT;
 157                        }
 158                } else {
 159                        p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
 160                        p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
 161                        if (p->chunks[i].kpage[0] == NULL || p->chunks[i].kpage[1] == NULL) {
 162                                kfree(p->chunks[i].kpage[0]);
 163                                kfree(p->chunks[i].kpage[1]);
 164                                return -ENOMEM;
 165                        }
 166                        p->chunks[i].kpage_idx[0] = -1;
 167                        p->chunks[i].kpage_idx[1] = -1;
 168                        p->chunks[i].last_copied_page = -1;
 169                        p->chunks[i].last_page_index = ((p->chunks[i].length_dw * 4) - 1) / PAGE_SIZE;
 170                }
 171        }
 172        if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) {
 173                DRM_ERROR("cs IB too big: %d\n",
 174                          p->chunks[p->chunk_ib_idx].length_dw);
 175                return -EINVAL;
 176        }
 177        return 0;
 178}
 179
 180/**
 181 * cs_parser_fini() - clean parser states
 182 * @parser:     parser structure holding parsing context.
 183 * @error:      error number
 184 *
 185 * If error is set than unvalidate buffer, otherwise just free memory
 186 * used by parsing context.
 187 **/
 188static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
 189{
 190        unsigned i;
 191
 192        if (error) {
 193                radeon_object_list_unvalidate(&parser->validated);
 194        } else {
 195                radeon_object_list_clean(&parser->validated);
 196        }
 197        for (i = 0; i < parser->nrelocs; i++) {
 198                if (parser->relocs[i].gobj) {
 199                        mutex_lock(&parser->rdev->ddev->struct_mutex);
 200                        drm_gem_object_unreference(parser->relocs[i].gobj);
 201                        mutex_unlock(&parser->rdev->ddev->struct_mutex);
 202                }
 203        }
 204        kfree(parser->track);
 205        kfree(parser->relocs);
 206        kfree(parser->relocs_ptr);
 207        for (i = 0; i < parser->nchunks; i++) {
 208                kfree(parser->chunks[i].kdata);
 209                kfree(parser->chunks[i].kpage[0]);
 210                kfree(parser->chunks[i].kpage[1]);
 211        }
 212        kfree(parser->chunks);
 213        kfree(parser->chunks_array);
 214        radeon_ib_free(parser->rdev, &parser->ib);
 215}
 216
 217int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
 218{
 219        struct radeon_device *rdev = dev->dev_private;
 220        struct radeon_cs_parser parser;
 221        struct radeon_cs_chunk *ib_chunk;
 222        int r;
 223
 224        mutex_lock(&rdev->cs_mutex);
 225        if (rdev->gpu_lockup) {
 226                mutex_unlock(&rdev->cs_mutex);
 227                return -EINVAL;
 228        }
 229        /* initialize parser */
 230        memset(&parser, 0, sizeof(struct radeon_cs_parser));
 231        parser.filp = filp;
 232        parser.rdev = rdev;
 233        r = radeon_cs_parser_init(&parser, data);
 234        if (r) {
 235                DRM_ERROR("Failed to initialize parser !\n");
 236                radeon_cs_parser_fini(&parser, r);
 237                mutex_unlock(&rdev->cs_mutex);
 238                return r;
 239        }
 240        r =  radeon_ib_get(rdev, &parser.ib);
 241        if (r) {
 242                DRM_ERROR("Failed to get ib !\n");
 243                radeon_cs_parser_fini(&parser, r);
 244                mutex_unlock(&rdev->cs_mutex);
 245                return r;
 246        }
 247        r = radeon_cs_parser_relocs(&parser);
 248        if (r) {
 249                DRM_ERROR("Failed to parse relocation !\n");
 250                radeon_cs_parser_fini(&parser, r);
 251                mutex_unlock(&rdev->cs_mutex);
 252                return r;
 253        }
 254        /* Copy the packet into the IB, the parser will read from the
 255         * input memory (cached) and write to the IB (which can be
 256         * uncached). */
 257        ib_chunk = &parser.chunks[parser.chunk_ib_idx];
 258        parser.ib->length_dw = ib_chunk->length_dw;
 259        r = radeon_cs_parse(&parser);
 260        if (r || parser.parser_error) {
 261                DRM_ERROR("Invalid command stream !\n");
 262                radeon_cs_parser_fini(&parser, r);
 263                mutex_unlock(&rdev->cs_mutex);
 264                return r;
 265        }
 266        r = radeon_cs_finish_pages(&parser);
 267        if (r) {
 268                DRM_ERROR("Invalid command stream !\n");
 269                radeon_cs_parser_fini(&parser, r);
 270                mutex_unlock(&rdev->cs_mutex);
 271                return r;
 272        }
 273        r = radeon_ib_schedule(rdev, parser.ib);
 274        if (r) {
 275                DRM_ERROR("Faild to schedule IB !\n");
 276        }
 277        radeon_cs_parser_fini(&parser, r);
 278        mutex_unlock(&rdev->cs_mutex);
 279        return r;
 280}
 281
 282int radeon_cs_finish_pages(struct radeon_cs_parser *p)
 283{
 284        struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
 285        int i;
 286        int size = PAGE_SIZE;
 287
 288        for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) {
 289                if (i == ibc->last_page_index) {
 290                        size = (ibc->length_dw * 4) % PAGE_SIZE;
 291                        if (size == 0)
 292                                size = PAGE_SIZE;
 293                }
 294                
 295                if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)),
 296                                       ibc->user_ptr + (i * PAGE_SIZE),
 297                                       size))
 298                        return -EFAULT;
 299        }
 300        return 0;
 301}
 302
 303int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx)
 304{
 305        int new_page;
 306        struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
 307        int i;
 308        int size = PAGE_SIZE;
 309
 310        for (i = ibc->last_copied_page + 1; i < pg_idx; i++) {
 311                if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)),
 312                                       ibc->user_ptr + (i * PAGE_SIZE),
 313                                       PAGE_SIZE)) {
 314                        p->parser_error = -EFAULT;
 315                        return 0;
 316                }
 317        }
 318
 319        new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1;
 320
 321        if (pg_idx == ibc->last_page_index) {
 322                size = (ibc->length_dw * 4) % PAGE_SIZE;
 323                        if (size == 0)
 324                                size = PAGE_SIZE;
 325        }
 326
 327        if (DRM_COPY_FROM_USER(ibc->kpage[new_page],
 328                               ibc->user_ptr + (pg_idx * PAGE_SIZE),
 329                               size)) {
 330                p->parser_error = -EFAULT;
 331                return 0;
 332        }
 333
 334        /* copy to IB here */
 335        memcpy((void *)(p->ib->ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size);
 336
 337        ibc->last_copied_page = pg_idx;
 338        ibc->kpage_idx[new_page] = pg_idx;
 339
 340        return new_page;
 341}
 342