diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c index 75763e7..4ea0478 100644 --- a/linux-core/i915_buffer.c +++ b/linux-core/i915_buffer.c @@ -42,7 +42,7 @@ int i915_fence_types(struct drm_buffer_object *bo, uint32_t * fclass, uint32_t * type) { - if (bo->mem.flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) + if ((bo->mem.mask | bo->mem.flags) & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) *type = 3; else *type = 1; diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 3a9ecab..58f6735 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -725,6 +725,338 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, return 0; } +struct i915_relocatee_info { + struct drm_buffer_object *buf; + unsigned long offset; + u32 *data_page; + unsigned page_offset; + struct drm_bo_kmap_obj kmap; + int is_iomem; +}; + +static void i915_dereference_buffers_locked(struct drm_buffer_object **buffers, + unsigned num_buffers) +{ + while (num_buffers--) + drm_bo_usage_deref_locked(&buffers[num_buffers]); +} + +int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, + struct drm_buffer_object **buffers, + struct i915_relocatee_info *relocatee, + uint32_t *reloc) +{ + unsigned index; + unsigned long new_cmd_offset; + u32 val; + int ret; + + if (reloc[2] >= num_buffers) { + DRM_ERROR("Illegal relocation buffer %08X\n", reloc[2]); + return -EINVAL; + } + + new_cmd_offset = reloc[0]; + if (!relocatee->data_page || + !drm_bo_same_page(relocatee->offset, new_cmd_offset)) { + drm_bo_kunmap(&relocatee->kmap); + relocatee->offset = new_cmd_offset; + ret = drm_bo_kmap(relocatee->buf, new_cmd_offset >> PAGE_SHIFT, + 1, &relocatee->kmap); + if (ret) { + DRM_ERROR("Could not map command buffer to apply relocs\n %08x", new_cmd_offset); + return ret; + } + + relocatee->data_page = drm_bmo_virtual(&relocatee->kmap, + &relocatee->is_iomem); + relocatee->page_offset = (relocatee->offset & PAGE_MASK); + } + + val = buffers[reloc[2]]->offset; + index = (reloc[0] - relocatee->page_offset) >> 2; + + /* add in validate */ + val = val + reloc[1]; + + relocatee->data_page[index] = val; + return 0; +} + +#define RELOC_START_OFFSET 2 +#define RELOC_NUM_INTS 3 +int i915_process_relocs(struct drm_file *file_priv, + uint32_t buf_handle, + uint32_t *reloc_buf_handle, + struct i915_relocatee_info *relocatee, + struct drm_buffer_object **buffers, + uint32_t num_buffers) +{ + struct drm_device *dev = file_priv->head->dev; + struct drm_buffer_object *reloc_list_object; + int ret; + uint32_t cur_handle = *reloc_buf_handle; + uint32_t num_relocs; + struct drm_bo_kmap_obj reloc_kmap; + uint32_t *reloc_page; + int reloc_is_iomem; + uint32_t reloc_offset, reloc_end, reloc_page_offset, next_offset; + int reloc_stride; + uint32_t cur_offset; + + memset(&reloc_kmap, 0, sizeof(reloc_kmap)); + + reloc_list_object = drm_lookup_buffer_object(file_priv, cur_handle, 1); + if (!reloc_list_object) + return -EINVAL; + + ret = drm_bo_kmap(reloc_list_object, 0, 1, &reloc_kmap); + if (ret) { + DRM_ERROR("Could not map relocation buffer.\n"); + goto out; + } + + reloc_page = drm_bmo_virtual(&reloc_kmap, &reloc_is_iomem); + num_relocs = reloc_page[0] & 0xffff; + + if ((reloc_page[0] >> 16) & 0xffff) { + DRM_ERROR("Unsupported relocation type requested\n"); + goto out; + } + + /* get next relocate buffer handle */ + *reloc_buf_handle = reloc_page[1]; + reloc_stride = 4; /* may be different for other types of relocs */ + + DRM_DEBUG("num relocs is %d, next is %08X\n", num_relocs, reloc_page[1]); + + reloc_page_offset = 0; + reloc_offset = 4 * sizeof(uint32_t); + reloc_end = reloc_offset + (num_relocs * reloc_stride * sizeof(uint32_t)); + + do { + next_offset = drm_bo_offset_end(reloc_offset, reloc_end); + + do { + cur_offset = ((reloc_offset + reloc_page_offset) & ~PAGE_MASK) / sizeof(uint32_t); + ret = i915_apply_reloc(file_priv, num_buffers, + buffers, relocatee, &reloc_page[cur_offset]); + if (ret) + goto out; + + reloc_offset += reloc_stride * sizeof(uint32_t); + } while(reloc_offset < next_offset); + + drm_bo_kunmap(&reloc_kmap); + + reloc_offset = next_offset; + if (reloc_offset != reloc_end) { + ret = drm_bo_kmap(reloc_list_object, reloc_offset >> PAGE_SHIFT, 1, &reloc_kmap); + if (ret){ + DRM_ERROR("Could not map relocation buffer.\n"); + goto out; + } + + reloc_page = drm_bmo_virtual(&reloc_kmap, &reloc_is_iomem); + reloc_page_offset = reloc_offset & ~PAGE_MASK; + } + + } while (reloc_offset != reloc_end); +out: + drm_bo_kunmap(&reloc_kmap); + + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&reloc_list_object); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +/* + * Validate, add fence and relocate a block of bos from a userspace list + */ +int i915_validate_buffer_list(struct drm_file *file_priv, + unsigned int fence_class, uint64_t data, + struct drm_buffer_object **buffers, + uint32_t *num_buffers) +{ + struct drm_i915_op_arg arg; + struct drm_bo_op_req *req = &arg.d.req; + struct drm_bo_arg_rep rep; + unsigned long next = 0; + int ret = 0; + unsigned buf_count = 0; + struct drm_device *dev = file_priv->head->dev; + uint32_t buf_reloc_handle, buf_handle; + struct i915_relocatee_info relocatee; + + do { + if (buf_count >= *num_buffers) { + DRM_ERROR("Buffer count exceeded %d\n.", *num_buffers); + ret = -EINVAL; + goto out_err; + } + + buffers[buf_count] = NULL; + + if (copy_from_user(&arg, (void __user *)(unsigned)data, sizeof(arg))) { + ret = -EFAULT; + goto out_err; + } + + if (arg.handled) { + data = arg.next; + buffers[buf_count] = drm_lookup_buffer_object(file_priv, req->arg_handle, 1); + buf_count++; + continue; + } + + rep.ret = 0; + if (req->op != drm_bo_validate) { + DRM_ERROR + ("Buffer object operation wasn't \"validate\".\n"); + rep.ret = -EINVAL; + goto out_err; + } + + buf_handle = req->bo_req.handle; + buf_reloc_handle = arg.reloc_handle; + + rep.ret = drm_bo_handle_validate(file_priv, req->bo_req.handle, + req->bo_req.fence_class, + req->bo_req.flags, + req->bo_req.mask, + req->bo_req.hint, + &rep.bo_info, + &buffers[buf_count]); + + if (rep.ret) { + DRM_ERROR("error on handle validate %d\n", rep.ret); + goto out_err; + } + + + next = arg.next; + arg.handled = 1; + arg.d.rep = rep; + + if (copy_to_user((void __user *)(unsigned)data, &arg, sizeof(arg))) + return -EFAULT; + + data = next; + buf_count++; + + if (buf_reloc_handle) { + memset(&relocatee, 0, sizeof(relocatee)); + + relocatee.buf = drm_lookup_buffer_object(file_priv, buf_handle, 1); + if (!relocatee.buf) { + DRM_DEBUG("relocatee buffer invalid %08x\n", buf_handle); + ret = -EINVAL; + goto out_err; + } + + while (buf_reloc_handle) { + ret = i915_process_relocs(file_priv, buf_handle, &buf_reloc_handle, &relocatee, buffers, buf_count); + if (ret) { + DRM_ERROR("process relocs failed\n"); + break; + } + } + + drm_bo_kunmap(&relocatee.kmap); + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&relocatee.buf); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto out_err; + + } + } while (next != 0); + *num_buffers = buf_count; + return 0; +out_err: + mutex_lock(&dev->struct_mutex); + i915_dereference_buffers_locked(buffers, buf_count); + mutex_unlock(&dev->struct_mutex); + *num_buffers = 0; + return (ret) ? ret : rep.ret; +} + + + + +static int i915_execbuffer(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) + dev_priv->sarea_priv; + struct drm_i915_execbuffer *exec_buf = data; + struct _drm_i915_batchbuffer *batch = &exec_buf->batch; + struct drm_fence_arg *fence_arg = &exec_buf->fence_arg; + int num_buffers; + int ret; + struct drm_fence_object *fence; + + if (!dev_priv->allow_batchbuffer) { + DRM_ERROR("Batchbuffer ioctl disabled\n"); + return -EINVAL; + } + + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects, + batch->num_cliprects * + sizeof(struct drm_clip_rect))) + return -EFAULT; + + num_buffers = I915_NUM_VALIDATE_BUFFERS; + + /* validate buffer list + fixup relocations */ + ret = i915_validate_buffer_list(file_priv, 0, exec_buf->ops_list, dev_priv->buffers, &num_buffers); + if (ret) + return ret; + + /* submit buffer */ + batch->start = dev_priv->buffers[num_buffers-1]->offset; + + DRM_DEBUG("i915 exec batchbuffer, start %x used %d cliprects %d\n", + batch->start, batch->used, batch->num_cliprects); + + ret = i915_dispatch_batchbuffer(dev, batch); + if (ret) + goto out_err0; + + sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + + /* fence */ + ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &fence); + if (ret) + goto out_err0; + + if (!(fence_arg->flags & DRM_FENCE_FLAG_NO_USER)) { + ret = drm_fence_add_user_object(file_priv, fence, fence_arg->flags & DRM_FENCE_FLAG_SHAREABLE); + if (!ret) { + fence_arg->handle = fence->base.hash.key; + fence_arg->fence_class = fence->fence_class; + fence_arg->type = fence->type; + fence_arg->signaled = fence->signaled; + } + } + drm_fence_usage_deref_unlocked(&fence); +out_err0: + + /* handle errors */ + mutex_lock(&dev->struct_mutex); + i915_dereference_buffers_locked(dev_priv->buffers, num_buffers); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + static int i915_do_cleanup_pageflip(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -977,6 +1309,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_MMIO, i915_mmio, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH), + DRM_IOCTL_DEF(DRM_I915_EXECBUFFER, i915_execbuffer, DRM_AUTH), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h index 9976804..4ded5b4 100644 --- a/shared-core/i915_drm.h +++ b/shared-core/i915_drm.h @@ -160,6 +160,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_VBLANK_SWAP 0x0f #define DRM_I915_MMIO 0x10 #define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_EXECBUFFER 0x12 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -177,7 +178,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) - +#define DRM_IOCTL_I915_EXECBUFFER DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_EXECBUFFER, struct drm_i915_execbuffer) /* Asynchronous page flipping: */ @@ -325,4 +326,29 @@ typedef struct drm_i915_hws_addr { uint64_t addr; } drm_i915_hws_addr_t; +struct drm_i915_reloc { + uint32_t handle; + uint32_t offset; + uint32_t delta; +}; + +#define I915_RELOC_TYPE_0 0 + +struct drm_i915_op_arg { + uint64_t next; + uint32_t reloc_handle; + int handled; + union { + struct drm_bo_op_req req; + struct drm_bo_arg_rep rep; + } d; + +}; + +struct drm_i915_execbuffer { + struct _drm_i915_batchbuffer batch; + uint64_t ops_list; + struct drm_fence_arg fence_arg; +}; + #endif /* _I915_DRM_H_ */ diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 3b26040..c86fd8a 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -65,6 +65,10 @@ #endif #define DRIVER_PATCHLEVEL 0 +#ifdef I915_HAVE_BUFFER +#define I915_NUM_VALIDATE_BUFFERS 256 +#endif + typedef struct _drm_i915_ring_buffer { int tail_mask; unsigned long Start; @@ -133,10 +137,12 @@ typedef struct drm_i915_private { #endif #ifdef I915_HAVE_BUFFER void *agp_iomap; + struct drm_buffer_object *buffers[I915_NUM_VALIDATE_BUFFERS]; #endif DRM_SPINTYPE swaps_lock; drm_i915_vbl_swap_t vbl_swaps; unsigned int swaps_pending; + } drm_i915_private_t; enum intel_chip_family {