diff options
author | Thomas White <taw@bitwiz.org.uk> | 2009-05-06 01:21:31 +0100 |
---|---|---|
committer | Thomas White <taw@bitwiz.org.uk> | 2009-05-06 01:21:31 +0100 |
commit | e8950ddf2823a4b2698870b057409fd98abb3aae (patch) | |
tree | 60ea79facadebd338a935d67626ff7c60b0436d2 | |
parent | cd1da974e3e53be130cda68a4fd97f943ad7e5c8 (diff) |
Buffer relocation stuff
This allows command sequences to make use of buffer objects by providing
their GEM handles.
It also reorganises the code a bit, and adds a lot of missing security stuff.
Signed-off-by: Thomas White <taw@bitwiz.org.uk>
-rw-r--r-- | drivers/mfd/glamo/glamo-cmdq.c | 155 | ||||
-rw-r--r-- | include/drm/glamo_drm.h | 5 |
2 files changed, 140 insertions, 20 deletions
diff --git a/drivers/mfd/glamo/glamo-cmdq.c b/drivers/mfd/glamo/glamo-cmdq.c index aaae86a98fc..199cb6cc170 100644 --- a/drivers/mfd/glamo/glamo-cmdq.c +++ b/drivers/mfd/glamo/glamo-cmdq.c @@ -120,23 +120,12 @@ glamo_cmdq_wait(struct glamodrm_handle *gdrm, enum glamo_engine engine) } -/* This is DRM_IOCTL_GLAMO_CMDBUF */ -int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, - struct drm_file *file_priv) +/* Add commands to the ring buffer */ +static int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr, + unsigned int count) { size_t ring_write, ring_read; size_t new_ring_write; - struct glamodrm_handle *gdrm; - size_t count; - drm_glamo_cmd_buffer_t *cbuf = data; - u16 *addr; - - gdrm = dev->dev_private; - - count = cbuf->bufsz; - addr = (u16 *)cbuf->buf; - - /* TODO: Sanitise buffer before doing anything else */ ring_write = reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL); ring_write |= (reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH) << 16); @@ -190,9 +179,6 @@ int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, new_ring_write = 4; } - /* Before changing write position, read has to stop */ - glamo_cmdq_wait(gdrm, GLAMO_ENGINE_CMDQ); - /* Suppose we just filled the WHOLE ring buffer, and so the * write position ends up in the same place as it started. * No change in pointer means no activity from the command @@ -210,11 +196,14 @@ int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, for ( i=0; i<count/2; i++ ) { /* Number of words */ iowrite16(*(addr+i), gdrm->cmdq_base+ring_write+(i*2)); } - glamo_cmdq_wait(gdrm, GLAMO_ENGINE_CMDQ); } - /* Finally, update the write pointer */ + /* Before changing write position, read has to stop + * (Brought across from xf86-video-glamo) + * TAW: Really? Is pausing the clock not enough? */ + glamo_cmdq_wait(gdrm, GLAMO_ENGINE_CMDQ); + /* Note that CLOCK_2D_EN_M6CLK has nothing to do with the 2D engine */ glamo_engine_clkreg_set(gdrm->glamo_core, GLAMO_ENGINE_2D, GLAMO_CLOCK_2D_EN_M6CLK, 0x0000); reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH, @@ -224,8 +213,136 @@ int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, glamo_engine_clkreg_set(gdrm->glamo_core, GLAMO_ENGINE_2D, GLAMO_CLOCK_2D_EN_M6CLK, 0xffff); + return 0; +} + + +/* Return true for a legal sequence of commands, otherwise false */ +static int glamo_sanitize_buffer(u16 *cmds, unsigned int count) +{ + /* XXX FIXME TODO: Implementation... */ + return 1; +} + + +/* Substitute the real addresses in VRAM for any required buffer objects */ +static int glamo_do_relocation(struct glamodrm_handle *gdrm, + drm_glamo_cmd_buffer_t *cbuf, u16 *cmds, + struct drm_device *dev, + struct drm_file *file_priv) +{ + uint32_t *handles; + int *offsets; + int nobjs = cbuf->nobjs; + int i; + + if ( nobjs > 32 ) return -EINVAL; /* Get real... */ + + handles = drm_alloc(nobjs*sizeof(uint32_t), DRM_MEM_DRIVER); + if ( handles == NULL ) return -1; + if ( copy_from_user(handles, cbuf->objs, nobjs*sizeof(uint32_t)) ) + return -1; + + offsets = drm_alloc(nobjs*sizeof(int), DRM_MEM_DRIVER); + if ( offsets == NULL ) return -1; + if ( copy_from_user(offsets, cbuf->obj_pos, nobjs*sizeof(int)) ) + return -1; + + for ( i=0; i<nobjs; i++ ) { + + uint32_t handle = handles[i]; + int offset = offsets[i]; + struct drm_gem_object *obj; + struct drm_glamo_gem_object *gobj; + u32 addr; + u16 addr_low, addr_high; + + printk(KERN_INFO "[glamo-drm] Relocating object handle %i " + "at position 0x%x\n", handle, offset); + + if ( offset > cbuf->bufsz ) { + printk(KERN_WARNING "[glamo-drm] Offset out of range " + "for this relocation!\n"); + goto fail; + } + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if ( obj == NULL ) return -1; + + /* Unref the object now, or it'll never get freed. + * This should really happen after the GPU has finished + * the commands which are about to be submitted. */ + drm_gem_object_unreference(obj); + + gobj = obj->driver_private; + if ( gobj == NULL ) { + printk(KERN_WARNING "[glamo-drm] This object has no " + "private data!\n"); + goto fail; + } + + addr = GLAMO_OFFSET_WORK + gobj->block->start; + addr_low = addr & 0xffff; + addr_high = (addr >> 16) & 0x7f; + printk(KERN_INFO "Addr low 0x%x, high 0x%x\n", + addr_low, addr_high); + + /* FIXME: Should really check that the register is a + * valid one for this relocation. */ + + *(cmds+(offset/2)+1) = addr_low; + *(cmds+(offset/2)+3) = addr_high; + + } + + drm_free(handles, 1, DRM_MEM_DRIVER); + drm_free(offsets, 1, DRM_MEM_DRIVER); + return 0; + +fail: + drm_free(handles, 1, DRM_MEM_DRIVER); + drm_free(offsets, 1, DRM_MEM_DRIVER); + return -1; +} + + +/* This is DRM_IOCTL_GLAMO_CMDBUF */ +int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct glamodrm_handle *gdrm; + unsigned int count; + drm_glamo_cmd_buffer_t *cbuf = data; + u16 *cmds; + + gdrm = dev->dev_private; + + count = cbuf->bufsz; + + if ( count > PAGE_SIZE ) return -EINVAL; + + cmds = drm_alloc(count, DRM_MEM_DRIVER); + if ( cmds == NULL ) return -ENOMEM; + if ( copy_from_user(cmds, cbuf->buf, count) ) return -EINVAL; + + /* Check the buffer isn't going to tell Glamo to enact naughtiness */ + if ( !glamo_sanitize_buffer(cmds, count) ) return -EINVAL; + + /* Perform relocation, if necessary */ + if ( cbuf->nobjs ) { + if ( glamo_do_relocation(gdrm, cbuf, cmds, dev, file_priv) ) + return -EINVAL; /* Relocation failed */ + } + + glamo_add_to_ring(gdrm, cmds, count); + + /* Having the CPU wait for the CPU is, to put it mildly, + * less than optimal. + * TODO: Avoid doing this, by some suitable means */ glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL); + drm_free(cmds, 1, DRM_MEM_DRIVER); + return 0; } diff --git a/include/drm/glamo_drm.h b/include/drm/glamo_drm.h index 2b0a81146d5..c14873d6527 100644 --- a/include/drm/glamo_drm.h +++ b/include/drm/glamo_drm.h @@ -65,8 +65,11 @@ #define DRM_IOCTL_GLAMO_GEM_WAIT_RENDERING DRM_IOW(DRM_COMMAND_BASE + DRM_GLAMO_GEM_WAIT_RENDERING, struct drm_glamo_gem_wait_rendering) typedef struct drm_glamo_cmd_buffer { - int bufsz; /* Size of buffer, in bytes */ + unsigned int bufsz; /* Size of buffer, in bytes */ char __user *buf; /* Buffer of stuff to go onto the ring buffer */ + unsigned int *obj_pos; /* Offsets (in bytes) at which to put objs */ + uint32_t *objs; /* List of buffer object (handles) to use */ + unsigned int nobjs; /* Number of objects referenced */ int nbox; struct drm_clip_rect __user *boxes; } drm_glamo_cmd_buffer_t; |