diff options
-rw-r--r-- | shared-core/i915_dma.c | 135 | ||||
-rw-r--r-- | shared-core/i915_drm.h | 10 |
2 files changed, 68 insertions, 77 deletions
diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index a36ca37e..287e95ac 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -739,9 +739,15 @@ int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, unsigned index; unsigned long new_cmd_offset; u32 val; - int ret; + int ret, i; + int buf_index = -1; + + for (i = 0; i <= num_buffers; i++) + if (buffers[i].buffer) + if (reloc[2] == buffers[i].buffer->base.hash.key) + buf_index = i; - if (reloc[2] >= num_buffers) { + if (buf_index == -1) { DRM_ERROR("Illegal relocation buffer %08X\n", reloc[2]); return -EINVAL; } @@ -750,7 +756,7 @@ int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, * Short-circuit relocations that were correctly * guessed by the client */ - if (buffers[reloc[2]].presumed_offset_correct && !DRM_DEBUG_RELOCATION) + if (buffers[buf_index].presumed_offset_correct && !DRM_DEBUG_RELOCATION) return 0; new_cmd_offset = reloc[0]; @@ -777,17 +783,17 @@ int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, relocatee->page_offset = (relocatee->offset & PAGE_MASK); } - val = buffers[reloc[2]].buffer->offset; + val = buffers[buf_index].buffer->offset; index = (reloc[0] - relocatee->page_offset) >> 2; /* add in validate */ val = val + reloc[1]; if (DRM_DEBUG_RELOCATION) { - if (buffers[reloc[2]].presumed_offset_correct && + if (buffers[buf_index].presumed_offset_correct && relocatee->data_page[index] != val) { DRM_DEBUG ("Relocation mismatch source %d target %d buffer %d user %08x kernel %08x\n", - reloc[0], reloc[1], reloc[2], relocatee->data_page[index], val); + reloc[0], reloc[1], buf_index, relocatee->data_page[index], val); } } relocatee->data_page[index] = val; @@ -796,94 +802,79 @@ int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, int i915_process_relocs(struct drm_file *file_priv, uint32_t buf_handle, - uint32_t *reloc_buf_handle, + uint32_t __user **reloc_user_ptr, struct i915_relocatee_info *relocatee, struct drm_i915_validate_buffer *buffers, uint32_t num_buffers) { - struct drm_device *dev = file_priv->head->dev; - struct drm_buffer_object *reloc_list_object; - uint32_t cur_handle = *reloc_buf_handle; - uint32_t *reloc_page; - int ret, reloc_is_iomem, reloc_stride; - uint32_t num_relocs, reloc_offset, reloc_end, reloc_page_offset, next_offset, cur_offset; - struct drm_bo_kmap_obj reloc_kmap; - - memset(&reloc_kmap, 0, sizeof(reloc_kmap)); - - mutex_lock(&dev->struct_mutex); - reloc_list_object = drm_lookup_buffer_object(file_priv, cur_handle, 1); - mutex_unlock(&dev->struct_mutex); - if (!reloc_list_object) - return -EINVAL; + int ret, reloc_stride; + uint32_t cur_offset; + uint32_t reloc_count; + uint32_t reloc_type; + uint32_t reloc_buf_size; + uint32_t *reloc_buf = NULL; + int i; - ret = drm_bo_kmap(reloc_list_object, 0, 1, &reloc_kmap); + /* do a copy from user from the user ptr */ + ret = get_user(reloc_count, *reloc_user_ptr); 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; + ret = get_user(reloc_type, (*reloc_user_ptr)+1); + if (ret) { + DRM_ERROR("Could not map relocation buffer.\n"); + goto out; + } - if ((reloc_page[0] >> 16) & 0xffff) { + if (reloc_type != 0) { DRM_ERROR("Unsupported relocation type requested\n"); + ret = -EINVAL; goto out; } - /* get next relocate buffer handle */ - *reloc_buf_handle = reloc_page[1]; - reloc_stride = I915_RELOC0_STRIDE * sizeof(uint32_t); /* 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 = I915_RELOC_HEADER * sizeof(uint32_t); - reloc_end = reloc_offset + (num_relocs * reloc_stride); - - do { - next_offset = drm_bo_offset_end(reloc_offset, reloc_end); + reloc_buf_size = (I915_RELOC_HEADER + (reloc_count * I915_RELOC0_STRIDE)) * sizeof(uint32_t); + reloc_buf = kmalloc(reloc_buf_size, GFP_KERNEL); + if (!reloc_buf) { + DRM_ERROR("Out of memory for reloc buffer\n"); + ret = -ENOMEM; + goto out; + } - 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; + if (copy_from_user(reloc_buf, *reloc_user_ptr, reloc_buf_size)) { + ret = -EFAULT; + goto out; + } - reloc_offset += reloc_stride; - } while (reloc_offset < next_offset); + /* get next relocate buffer handle */ + *reloc_user_ptr = (uint32_t *)*(unsigned long *)&reloc_buf[2]; - drm_bo_kunmap(&reloc_kmap); + reloc_stride = I915_RELOC0_STRIDE * sizeof(uint32_t); /* may be different for other types of relocs */ - 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; - } + DRM_DEBUG("num relocs is %d, next is %p\n", reloc_count, *reloc_user_ptr); - reloc_page = drm_bmo_virtual(&reloc_kmap, &reloc_is_iomem); - reloc_page_offset = reloc_offset & ~PAGE_MASK; - } + for (i = 0; i < reloc_count; i++) { + cur_offset = I915_RELOC_HEADER + (i * I915_RELOC0_STRIDE); + + ret = i915_apply_reloc(file_priv, num_buffers, buffers, + relocatee, reloc_buf + cur_offset); + if (ret) + goto out; + } - } while (reloc_offset != reloc_end); out: + + if (reloc_buf) + kfree(reloc_buf); drm_bo_kunmap(&relocatee->kmap); relocatee->data_page = NULL; - 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; } static int i915_exec_reloc(struct drm_file *file_priv, drm_handle_t buf_handle, - drm_handle_t buf_reloc_handle, + uint32_t __user *reloc_user_ptr, struct drm_i915_validate_buffer *buffers, uint32_t buf_count) { @@ -917,8 +908,8 @@ static int i915_exec_reloc(struct drm_file *file_priv, drm_handle_t buf_handle, goto out_err; } - while (buf_reloc_handle) { - ret = i915_process_relocs(file_priv, buf_handle, &buf_reloc_handle, &relocatee, buffers, buf_count); + while (reloc_user_ptr) { + ret = i915_process_relocs(file_priv, buf_handle, &reloc_user_ptr, &relocatee, buffers, buf_count); if (ret) { DRM_ERROR("process relocs failed\n"); break; @@ -948,8 +939,8 @@ int i915_validate_buffer_list(struct drm_file *file_priv, int ret = 0; unsigned buf_count = 0; struct drm_device *dev = file_priv->head->dev; - uint32_t buf_reloc_handle, buf_handle; - + uint32_t buf_handle; + uint32_t __user *reloc_user_ptr; do { if (buf_count >= *num_buffers) { @@ -984,10 +975,10 @@ int i915_validate_buffer_list(struct drm_file *file_priv, } buf_handle = req->bo_req.handle; - buf_reloc_handle = arg.reloc_handle; + reloc_user_ptr = (uint32_t *)(unsigned long)arg.reloc_ptr; - if (buf_reloc_handle) { - ret = i915_exec_reloc(file_priv, buf_handle, buf_reloc_handle, buffers, buf_count); + if (reloc_user_ptr) { + ret = i915_exec_reloc(file_priv, buf_handle, reloc_user_ptr, buffers, buf_count); if (ret) goto out_err; DRM_MEMORYBARRIER(); diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h index cfa3f93a..c8a9cb76 100644 --- a/shared-core/i915_drm.h +++ b/shared-core/i915_drm.h @@ -329,9 +329,9 @@ typedef struct drm_i915_hws_addr { /* * Relocation header is 4 uint32_ts - * 0 - (16-bit relocation type << 16)| 16 bit reloc count - * 1 - buffer handle for another list of relocs - * 2-3 - spare. + * 0 - 32 bit reloc count + * 1 - 32-bit relocation type + * 2-3 - 64-bit user buffer handle ptr for another list of relocs. */ #define I915_RELOC_HEADER 4 @@ -339,7 +339,7 @@ typedef struct drm_i915_hws_addr { * type 0 relocation has 4-uint32_t stride * 0 - offset into buffer * 1 - delta to add in - * 2 - index into buffer list + * 2 - buffer handle * 3 - reserved (for optimisations later). */ #define I915_RELOC_TYPE_0 0 @@ -347,7 +347,7 @@ typedef struct drm_i915_hws_addr { struct drm_i915_op_arg { uint64_t next; - uint32_t reloc_handle; + uint64_t reloc_ptr; int handled; union { struct drm_bo_op_req req; |