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 < i; 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 -ENOENT;
  76                        }
  77                        p->relocs_ptr[i] = &p->relocs[i];
  78                        p->relocs[i].robj = gem_to_radeon_bo(p->relocs[i].gobj);
  79                        p->relocs[i].lobj.bo = p->relocs[i].robj;
  80                        p->relocs[i].lobj.wdomain = r->write_domain;
  81                        p->relocs[i].lobj.rdomain = r->read_domains;
  82                        p->relocs[i].lobj.tv.bo = &p->relocs[i].robj->tbo;
  83                        p->relocs[i].handle = r->handle;
  84                        p->relocs[i].flags = r->flags;
  85                        radeon_bo_list_add_object(&p->relocs[i].lobj,
  86                                                  &p->validated);
  87
  88                } else
  89                        p->relocs[i].handle = 0;
  90        }
  91        return radeon_bo_list_validate(&p->validated);
  92}
  93
  94static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
  95{
  96        p->priority = priority;
  97
  98        switch (ring) {
  99        default:
 100                DRM_ERROR("unknown ring id: %d\n", ring);
 101                return -EINVAL;
 102        case RADEON_CS_RING_GFX:
 103                p->ring = RADEON_RING_TYPE_GFX_INDEX;
 104                break;
 105        case RADEON_CS_RING_COMPUTE:
 106                if (p->rdev->family >= CHIP_TAHITI) {
 107                        if (p->priority > 0)
 108                                p->ring = CAYMAN_RING_TYPE_CP1_INDEX;
 109                        else
 110                                p->ring = CAYMAN_RING_TYPE_CP2_INDEX;
 111                } else
 112                        p->ring = RADEON_RING_TYPE_GFX_INDEX;
 113                break;
 114        }
 115        return 0;
 116}
 117
 118static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
 119{
 120        bool sync_to_ring[RADEON_NUM_RINGS] = { };
 121        bool need_sync = false;
 122        int i, r;
 123
 124        for (i = 0; i < p->nrelocs; i++) {
 125                struct radeon_fence *fence;
 126
 127                if (!p->relocs[i].robj || !p->relocs[i].robj->tbo.sync_obj)
 128                        continue;
 129
 130                fence = p->relocs[i].robj->tbo.sync_obj;
 131                if (fence->ring != p->ring && !radeon_fence_signaled(fence)) {
 132                        sync_to_ring[fence->ring] = true;
 133                        need_sync = true;
 134                }
 135        }
 136
 137        if (!need_sync) {
 138                return 0;
 139        }
 140
 141        r = radeon_semaphore_create(p->rdev, &p->ib.semaphore);
 142        if (r) {
 143                return r;
 144        }
 145
 146        return radeon_semaphore_sync_rings(p->rdev, p->ib.semaphore,
 147                                           sync_to_ring, p->ring);
 148}
 149
 150/* XXX: note that this is called from the legacy UMS CS ioctl as well */
 151int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
 152{
 153        struct drm_radeon_cs *cs = data;
 154        uint64_t *chunk_array_ptr;
 155        unsigned size, i;
 156        u32 ring = RADEON_CS_RING_GFX;
 157        s32 priority = 0;
 158
 159        if (!cs->num_chunks) {
 160                return 0;
 161        }
 162        /* get chunks */
 163        INIT_LIST_HEAD(&p->validated);
 164        p->idx = 0;
 165        p->ib.sa_bo = NULL;
 166        p->ib.semaphore = NULL;
 167        p->const_ib.sa_bo = NULL;
 168        p->const_ib.semaphore = NULL;
 169        p->chunk_ib_idx = -1;
 170        p->chunk_relocs_idx = -1;
 171        p->chunk_flags_idx = -1;
 172        p->chunk_const_ib_idx = -1;
 173        p->chunks_array = kcalloc(cs->num_chunks, sizeof(uint64_t), GFP_KERNEL);
 174        if (p->chunks_array == NULL) {
 175                return -ENOMEM;
 176        }
 177        chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks);
 178        if (DRM_COPY_FROM_USER(p->chunks_array, chunk_array_ptr,
 179                               sizeof(uint64_t)*cs->num_chunks)) {
 180                return -EFAULT;
 181        }
 182        p->cs_flags = 0;
 183        p->nchunks = cs->num_chunks;
 184        p->chunks = kcalloc(p->nchunks, sizeof(struct radeon_cs_chunk), GFP_KERNEL);
 185        if (p->chunks == NULL) {
 186                return -ENOMEM;
 187        }
 188        for (i = 0; i < p->nchunks; i++) {
 189                struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
 190                struct drm_radeon_cs_chunk user_chunk;
 191                uint32_t __user *cdata;
 192
 193                chunk_ptr = (void __user*)(unsigned long)p->chunks_array[i];
 194                if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr,
 195                                       sizeof(struct drm_radeon_cs_chunk))) {
 196                        return -EFAULT;
 197                }
 198                p->chunks[i].length_dw = user_chunk.length_dw;
 199                p->chunks[i].kdata = NULL;
 200                p->chunks[i].chunk_id = user_chunk.chunk_id;
 201
 202                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) {
 203                        p->chunk_relocs_idx = i;
 204                }
 205                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) {
 206                        p->chunk_ib_idx = i;
 207                        /* zero length IB isn't useful */
 208                        if (p->chunks[i].length_dw == 0)
 209                                return -EINVAL;
 210                }
 211                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_CONST_IB) {
 212                        p->chunk_const_ib_idx = i;
 213                        /* zero length CONST IB isn't useful */
 214                        if (p->chunks[i].length_dw == 0)
 215                                return -EINVAL;
 216                }
 217                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
 218                        p->chunk_flags_idx = i;
 219                        /* zero length flags aren't useful */
 220                        if (p->chunks[i].length_dw == 0)
 221                                return -EINVAL;
 222                }
 223
 224                p->chunks[i].length_dw = user_chunk.length_dw;
 225                p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
 226
 227                cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data;
 228                if ((p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) ||
 229                    (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS)) {
 230                        size = p->chunks[i].length_dw * sizeof(uint32_t);
 231                        p->chunks[i].kdata = kmalloc(size, GFP_KERNEL);
 232                        if (p->chunks[i].kdata == NULL) {
 233                                return -ENOMEM;
 234                        }
 235                        if (DRM_COPY_FROM_USER(p->chunks[i].kdata,
 236                                               p->chunks[i].user_ptr, size)) {
 237                                return -EFAULT;
 238                        }
 239                        if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
 240                                p->cs_flags = p->chunks[i].kdata[0];
 241                                if (p->chunks[i].length_dw > 1)
 242                                        ring = p->chunks[i].kdata[1];
 243                                if (p->chunks[i].length_dw > 2)
 244                                        priority = (s32)p->chunks[i].kdata[2];
 245                        }
 246                }
 247        }
 248
 249        /* these are KMS only */
 250        if (p->rdev) {
 251                if ((p->cs_flags & RADEON_CS_USE_VM) &&
 252                    !p->rdev->vm_manager.enabled) {
 253                        DRM_ERROR("VM not active on asic!\n");
 254                        return -EINVAL;
 255                }
 256
 257                /* we only support VM on SI+ */
 258                if ((p->rdev->family >= CHIP_TAHITI) &&
 259                    ((p->cs_flags & RADEON_CS_USE_VM) == 0)) {
 260                        DRM_ERROR("VM required on SI+!\n");
 261                        return -EINVAL;
 262                }
 263
 264                if (radeon_cs_get_ring(p, ring, priority))
 265                        return -EINVAL;
 266        }
 267
 268        /* deal with non-vm */
 269        if ((p->chunk_ib_idx != -1) &&
 270            ((p->cs_flags & RADEON_CS_USE_VM) == 0) &&
 271            (p->chunks[p->chunk_ib_idx].chunk_id == RADEON_CHUNK_ID_IB)) {
 272                if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) {
 273                        DRM_ERROR("cs IB too big: %d\n",
 274                                  p->chunks[p->chunk_ib_idx].length_dw);
 275                        return -EINVAL;
 276                }
 277                if ((p->rdev->flags & RADEON_IS_AGP)) {
 278                        p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
 279                        p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
 280                        if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL ||
 281                            p->chunks[p->chunk_ib_idx].kpage[1] == NULL) {
 282                                kfree(p->chunks[i].kpage[0]);
 283                                kfree(p->chunks[i].kpage[1]);
 284                                return -ENOMEM;
 285                        }
 286                }
 287                p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1;
 288                p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1;
 289                p->chunks[p->chunk_ib_idx].last_copied_page = -1;
 290                p->chunks[p->chunk_ib_idx].last_page_index =
 291                        ((p->chunks[p->chunk_ib_idx].length_dw * 4) - 1) / PAGE_SIZE;
 292        }
 293
 294        return 0;
 295}
 296
 297/**
 298 * cs_parser_fini() - clean parser states
 299 * @parser:     parser structure holding parsing context.
 300 * @error:      error number
 301 *
 302 * If error is set than unvalidate buffer, otherwise just free memory
 303 * used by parsing context.
 304 **/
 305static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
 306{
 307        unsigned i;
 308
 309        if (!error)
 310                ttm_eu_fence_buffer_objects(&parser->validated,
 311                                            parser->ib.fence);
 312        else
 313                ttm_eu_backoff_reservation(&parser->validated);
 314
 315        if (parser->relocs != NULL) {
 316                for (i = 0; i < parser->nrelocs; i++) {
 317                        if (parser->relocs[i].gobj)
 318                                drm_gem_object_unreference_unlocked(parser->relocs[i].gobj);
 319                }
 320        }
 321        kfree(parser->track);
 322        kfree(parser->relocs);
 323        kfree(parser->relocs_ptr);
 324        for (i = 0; i < parser->nchunks; i++) {
 325                kfree(parser->chunks[i].kdata);
 326                if ((parser->rdev->flags & RADEON_IS_AGP)) {
 327                        kfree(parser->chunks[i].kpage[0]);
 328                        kfree(parser->chunks[i].kpage[1]);
 329                }
 330        }
 331        kfree(parser->chunks);
 332        kfree(parser->chunks_array);
 333        radeon_ib_free(parser->rdev, &parser->ib);
 334        radeon_ib_free(parser->rdev, &parser->const_ib);
 335}
 336
 337static int radeon_cs_ib_chunk(struct radeon_device *rdev,
 338                              struct radeon_cs_parser *parser)
 339{
 340        struct radeon_cs_chunk *ib_chunk;
 341        int r;
 342
 343        if (parser->chunk_ib_idx == -1)
 344                return 0;
 345
 346        if (parser->cs_flags & RADEON_CS_USE_VM)
 347                return 0;
 348
 349        ib_chunk = &parser->chunks[parser->chunk_ib_idx];
 350        /* Copy the packet into the IB, the parser will read from the
 351         * input memory (cached) and write to the IB (which can be
 352         * uncached).
 353         */
 354        r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
 355                           ib_chunk->length_dw * 4);
 356        if (r) {
 357                DRM_ERROR("Failed to get ib !\n");
 358                return r;
 359        }
 360        parser->ib.length_dw = ib_chunk->length_dw;
 361        r = radeon_cs_parse(rdev, parser->ring, parser);
 362        if (r || parser->parser_error) {
 363                DRM_ERROR("Invalid command stream !\n");
 364                return r;
 365        }
 366        r = radeon_cs_finish_pages(parser);
 367        if (r) {
 368                DRM_ERROR("Invalid command stream !\n");
 369                return r;
 370        }
 371        r = radeon_cs_sync_rings(parser);
 372        if (r) {
 373                DRM_ERROR("Failed to synchronize rings !\n");
 374        }
 375        parser->ib.vm_id = 0;
 376        r = radeon_ib_schedule(rdev, &parser->ib);
 377        if (r) {
 378                DRM_ERROR("Failed to schedule IB !\n");
 379        }
 380        return 0;
 381}
 382
 383static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser,
 384                                   struct radeon_vm *vm)
 385{
 386        struct radeon_bo_list *lobj;
 387        struct radeon_bo *bo;
 388        int r;
 389
 390        list_for_each_entry(lobj, &parser->validated, tv.head) {
 391                bo = lobj->bo;
 392                r = radeon_vm_bo_update_pte(parser->rdev, vm, bo, &bo->tbo.mem);
 393                if (r) {
 394                        return r;
 395                }
 396        }
 397        return 0;
 398}
 399
 400static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
 401                                 struct radeon_cs_parser *parser)
 402{
 403        struct radeon_cs_chunk *ib_chunk;
 404        struct radeon_fpriv *fpriv = parser->filp->driver_priv;
 405        struct radeon_vm *vm = &fpriv->vm;
 406        int r;
 407
 408        if (parser->chunk_ib_idx == -1)
 409                return 0;
 410
 411        if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
 412                return 0;
 413
 414        if ((rdev->family >= CHIP_TAHITI) &&
 415            (parser->chunk_const_ib_idx != -1)) {
 416                ib_chunk = &parser->chunks[parser->chunk_const_ib_idx];
 417                if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
 418                        DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw);
 419                        return -EINVAL;
 420                }
 421                r =  radeon_ib_get(rdev, parser->ring, &parser->const_ib,
 422                                   ib_chunk->length_dw * 4);
 423                if (r) {
 424                        DRM_ERROR("Failed to get const ib !\n");
 425                        return r;
 426                }
 427                parser->const_ib.is_const_ib = true;
 428                parser->const_ib.length_dw = ib_chunk->length_dw;
 429                /* Copy the packet into the IB */
 430                if (DRM_COPY_FROM_USER(parser->const_ib.ptr, ib_chunk->user_ptr,
 431                                       ib_chunk->length_dw * 4)) {
 432                        return -EFAULT;
 433                }
 434                r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib);
 435                if (r) {
 436                        return r;
 437                }
 438        }
 439
 440        ib_chunk = &parser->chunks[parser->chunk_ib_idx];
 441        if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
 442                DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw);
 443                return -EINVAL;
 444        }
 445        r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
 446                           ib_chunk->length_dw * 4);
 447        if (r) {
 448                DRM_ERROR("Failed to get ib !\n");
 449                return r;
 450        }
 451        parser->ib.length_dw = ib_chunk->length_dw;
 452        /* Copy the packet into the IB */
 453        if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr,
 454                               ib_chunk->length_dw * 4)) {
 455                return -EFAULT;
 456        }
 457        r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib);
 458        if (r) {
 459                return r;
 460        }
 461
 462        mutex_lock(&vm->mutex);
 463        r = radeon_vm_bind(rdev, vm);
 464        if (r) {
 465                goto out;
 466        }
 467        r = radeon_bo_vm_update_pte(parser, vm);
 468        if (r) {
 469                goto out;
 470        }
 471        r = radeon_cs_sync_rings(parser);
 472        if (r) {
 473                DRM_ERROR("Failed to synchronize rings !\n");
 474        }
 475
 476        if ((rdev->family >= CHIP_TAHITI) &&
 477            (parser->chunk_const_ib_idx != -1)) {
 478                parser->const_ib.vm_id = vm->id;
 479                /* ib pool is bind at 0 in virtual address space to gpu_addr is the
 480                 * offset inside the pool bo
 481                 */
 482                parser->const_ib.gpu_addr = parser->const_ib.sa_bo->soffset;
 483                r = radeon_ib_schedule(rdev, &parser->const_ib);
 484                if (r)
 485                        goto out;
 486        }
 487
 488        parser->ib.vm_id = vm->id;
 489        /* ib pool is bind at 0 in virtual address space to gpu_addr is the
 490         * offset inside the pool bo
 491         */
 492        parser->ib.gpu_addr = parser->ib.sa_bo->soffset;
 493        parser->ib.is_const_ib = false;
 494        r = radeon_ib_schedule(rdev, &parser->ib);
 495out:
 496        if (!r) {
 497                if (vm->fence) {
 498                        radeon_fence_unref(&vm->fence);
 499                }
 500                vm->fence = radeon_fence_ref(parser->ib.fence);
 501        }
 502        mutex_unlock(&fpriv->vm.mutex);
 503        return r;
 504}
 505
 506static int radeon_cs_handle_lockup(struct radeon_device *rdev, int r)
 507{
 508        if (r == -EDEADLK) {
 509                r = radeon_gpu_reset(rdev);
 510                if (!r)
 511                        r = -EAGAIN;
 512        }
 513        return r;
 514}
 515
 516int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
 517{
 518        struct radeon_device *rdev = dev->dev_private;
 519        struct radeon_cs_parser parser;
 520        int r;
 521
 522        radeon_mutex_lock(&rdev->cs_mutex);
 523        if (!rdev->accel_working) {
 524                radeon_mutex_unlock(&rdev->cs_mutex);
 525                return -EBUSY;
 526        }
 527        /* initialize parser */
 528        memset(&parser, 0, sizeof(struct radeon_cs_parser));
 529        parser.filp = filp;
 530        parser.rdev = rdev;
 531        parser.dev = rdev->dev;
 532        parser.family = rdev->family;
 533        r = radeon_cs_parser_init(&parser, data);
 534        if (r) {
 535                DRM_ERROR("Failed to initialize parser !\n");
 536                radeon_cs_parser_fini(&parser, r);
 537                r = radeon_cs_handle_lockup(rdev, r);
 538                radeon_mutex_unlock(&rdev->cs_mutex);
 539                return r;
 540        }
 541        r = radeon_cs_parser_relocs(&parser);
 542        if (r) {
 543                if (r != -ERESTARTSYS)
 544                        DRM_ERROR("Failed to parse relocation %d!\n", r);
 545                radeon_cs_parser_fini(&parser, r);
 546                r = radeon_cs_handle_lockup(rdev, r);
 547                radeon_mutex_unlock(&rdev->cs_mutex);
 548                return r;
 549        }
 550        r = radeon_cs_ib_chunk(rdev, &parser);
 551        if (r) {
 552                goto out;
 553        }
 554        r = radeon_cs_ib_vm_chunk(rdev, &parser);
 555        if (r) {
 556                goto out;
 557        }
 558out:
 559        radeon_cs_parser_fini(&parser, r);
 560        r = radeon_cs_handle_lockup(rdev, r);
 561        radeon_mutex_unlock(&rdev->cs_mutex);
 562        return r;
 563}
 564
 565int radeon_cs_finish_pages(struct radeon_cs_parser *p)
 566{
 567        struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
 568        int i;
 569        int size = PAGE_SIZE;
 570
 571        for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) {
 572                if (i == ibc->last_page_index) {
 573                        size = (ibc->length_dw * 4) % PAGE_SIZE;
 574                        if (size == 0)
 575                                size = PAGE_SIZE;
 576                }
 577                
 578                if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)),
 579                                       ibc->user_ptr + (i * PAGE_SIZE),
 580                                       size))
 581                        return -EFAULT;
 582        }
 583        return 0;
 584}
 585
 586static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx)
 587{
 588        int new_page;
 589        struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
 590        int i;
 591        int size = PAGE_SIZE;
 592        bool copy1 = (p->rdev->flags & RADEON_IS_AGP) ? false : true;
 593
 594        for (i = ibc->last_copied_page + 1; i < pg_idx; i++) {
 595                if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)),
 596                                       ibc->user_ptr + (i * PAGE_SIZE),
 597                                       PAGE_SIZE)) {
 598                        p->parser_error = -EFAULT;
 599                        return 0;
 600                }
 601        }
 602
 603        if (pg_idx == ibc->last_page_index) {
 604                size = (ibc->length_dw * 4) % PAGE_SIZE;
 605                if (size == 0)
 606                        size = PAGE_SIZE;
 607        }
 608
 609        new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1;
 610        if (copy1)
 611                ibc->kpage[new_page] = p->ib.ptr + (pg_idx * (PAGE_SIZE / 4));
 612
 613        if (DRM_COPY_FROM_USER(ibc->kpage[new_page],
 614                               ibc->user_ptr + (pg_idx * PAGE_SIZE),
 615                               size)) {
 616                p->parser_error = -EFAULT;
 617                return 0;
 618        }
 619
 620        /* copy to IB for non single case */
 621        if (!copy1)
 622                memcpy((void *)(p->ib.ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size);
 623
 624        ibc->last_copied_page = pg_idx;
 625        ibc->kpage_idx[new_page] = pg_idx;
 626
 627        return new_page;
 628}
 629
 630u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
 631{
 632        struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
 633        u32 pg_idx, pg_offset;
 634        u32 idx_value = 0;
 635        int new_page;
 636
 637        pg_idx = (idx * 4) / PAGE_SIZE;
 638        pg_offset = (idx * 4) % PAGE_SIZE;
 639
 640        if (ibc->kpage_idx[0] == pg_idx)
 641                return ibc->kpage[0][pg_offset/4];
 642        if (ibc->kpage_idx[1] == pg_idx)
 643                return ibc->kpage[1][pg_offset/4];
 644
 645        new_page = radeon_cs_update_pages(p, pg_idx);
 646        if (new_page < 0) {
 647                p->parser_error = new_page;
 648                return 0;
 649        }
 650
 651        idx_value = ibc->kpage[new_page][pg_offset/4];
 652        return idx_value;
 653}
 654