From 7a73ba7469cbea631050094fd14f73acebb97cf9 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 22 Dec 2009 16:53:41 +0100 Subject: [PATCH] drm/vmwgfx: Use TTM handles instead of SIDs as user-space surface handles. Improve the command verifier to catch all occurences of surface handles, and translate to SIDs. This way DMA buffers and 3D surfaces share a common handle space, which makes it possible for the kms code to differentiate. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 10 +- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 153 +++++++++++++++++++---- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 8 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 149 +++++++++++----------- 4 files changed, 209 insertions(+), 111 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 43546d09d1b0..e61bd85b6975 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -123,6 +123,7 @@ struct vmw_sw_context{ uint32_t last_cid; bool cid_valid; uint32_t last_sid; + uint32_t sid_translation; bool sid_valid; struct ttm_object_file *tfile; struct list_head validate_nodes; @@ -317,9 +318,10 @@ extern void vmw_surface_res_free(struct vmw_resource *res); extern int vmw_surface_init(struct vmw_private *dev_priv, struct vmw_surface *srf, void (*res_free) (struct vmw_resource *res)); -extern int vmw_user_surface_lookup(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - int sid, struct vmw_surface **out); +extern int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + struct vmw_surface **out); extern int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_surface_define_ioctl(struct drm_device *dev, void *data, @@ -328,7 +330,7 @@ extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_surface_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, - int id); + uint32_t handle, int *id); extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo); extern int vmw_dmabuf_init(struct vmw_private *dev_priv, struct vmw_dma_buffer *vmw_bo, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 7e73cf51e298..2e92da567403 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -73,21 +73,32 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv, static int vmw_cmd_sid_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, - uint32_t sid) + uint32_t *sid) { - if (unlikely((!sw_context->sid_valid || sid != sw_context->last_sid) && - sid != SVGA3D_INVALID_ID)) { - int ret = vmw_surface_check(dev_priv, sw_context->tfile, sid); + if (*sid == SVGA3D_INVALID_ID) + return 0; + + if (unlikely((!sw_context->sid_valid || + *sid != sw_context->last_sid))) { + int real_id; + int ret = vmw_surface_check(dev_priv, sw_context->tfile, + *sid, &real_id); if (unlikely(ret != 0)) { - DRM_ERROR("Could ot find or use surface %u\n", - (unsigned) sid); + DRM_ERROR("Could ot find or use surface 0x%08x " + "address 0x%08lx\n", + (unsigned int) *sid, + (unsigned long) sid); return ret; } - sw_context->last_sid = sid; + sw_context->last_sid = *sid; sw_context->sid_valid = true; - } + *sid = real_id; + sw_context->sid_translation = real_id; + } else + *sid = sw_context->sid_translation; + return 0; } @@ -107,7 +118,8 @@ static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv, return ret; cmd = container_of(header, struct vmw_sid_cmd, header); - return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.target.sid); + ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid); + return ret; } static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv, @@ -121,10 +133,10 @@ static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv, int ret; cmd = container_of(header, struct vmw_sid_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.src.sid); + ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); if (unlikely(ret != 0)) return ret; - return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.dest.sid); + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); } static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, @@ -138,10 +150,10 @@ static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, int ret; cmd = container_of(header, struct vmw_sid_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.src.sid); + ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); if (unlikely(ret != 0)) return ret; - return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.dest.sid); + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); } static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, @@ -154,7 +166,7 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, } *cmd; cmd = container_of(header, struct vmw_sid_cmd, header); - return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.srcImage.sid); + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); } static int vmw_cmd_present_check(struct vmw_private *dev_priv, @@ -167,7 +179,7 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv, } *cmd; cmd = container_of(header, struct vmw_sid_cmd, header); - return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.sid); + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); } static int vmw_cmd_dma(struct vmw_private *dev_priv, @@ -187,12 +199,7 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, uint32_t cur_validate_node; struct ttm_validate_buffer *val_buf; - cmd = container_of(header, struct vmw_dma_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->dma.host.sid); - if (unlikely(ret != 0)) - return ret; - handle = cmd->dma.guest.ptr.gmrId; ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo); if (unlikely(ret != 0)) { @@ -228,14 +235,23 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, ++sw_context->cur_val_buf; } - ret = vmw_user_surface_lookup(dev_priv, sw_context->tfile, - cmd->dma.host.sid, &srf); + ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, + cmd->dma.host.sid, &srf); if (ret) { DRM_ERROR("could not find surface\n"); goto out_no_reloc; } + /** + * Patch command stream with device SID. + */ + + cmd->dma.host.sid = srf->res.id; vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header); + /** + * FIXME: May deadlock here when called from the + * command parsing code. + */ vmw_surface_unreference(&srf); out_no_reloc: @@ -243,6 +259,90 @@ out_no_reloc: return ret; } +static int vmw_cmd_draw(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_draw_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdDrawPrimitives body; + } *cmd; + SVGA3dVertexDecl *decl = (SVGA3dVertexDecl *)( + (unsigned long)header + sizeof(*cmd)); + SVGA3dPrimitiveRange *range; + uint32_t i; + uint32_t maxnum; + int ret; + + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); + if (unlikely(ret != 0)) + return ret; + + cmd = container_of(header, struct vmw_draw_cmd, header); + maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); + + if (unlikely(cmd->body.numVertexDecls > maxnum)) { + DRM_ERROR("Illegal number of vertex declarations.\n"); + return -EINVAL; + } + + for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) { + ret = vmw_cmd_sid_check(dev_priv, sw_context, + &decl->array.surfaceId); + if (unlikely(ret != 0)) + return ret; + } + + maxnum = (header->size - sizeof(cmd->body) - + cmd->body.numVertexDecls * sizeof(*decl)) / sizeof(*range); + if (unlikely(cmd->body.numRanges > maxnum)) { + DRM_ERROR("Illegal number of index ranges.\n"); + return -EINVAL; + } + + range = (SVGA3dPrimitiveRange *) decl; + for (i = 0; i < cmd->body.numRanges; ++i, ++range) { + ret = vmw_cmd_sid_check(dev_priv, sw_context, + &range->indexArray.surfaceId); + if (unlikely(ret != 0)) + return ret; + } + return 0; +} + + +static int vmw_cmd_tex_state(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_tex_state_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdSetTextureState state; + }; + + SVGA3dTextureState *last_state = (SVGA3dTextureState *) + ((unsigned long) header + header->size + sizeof(header)); + SVGA3dTextureState *cur_state = (SVGA3dTextureState *) + ((unsigned long) header + sizeof(struct vmw_tex_state_cmd)); + int ret; + + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); + if (unlikely(ret != 0)) + return ret; + + for (; cur_state < last_state; ++cur_state) { + if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE)) + continue; + + ret = vmw_cmd_sid_check(dev_priv, sw_context, + &cur_state->value); + if (unlikely(ret != 0)) + return ret; + } + + return 0; +} + typedef int (*vmw_cmd_func) (struct vmw_private *, struct vmw_sw_context *, @@ -264,7 +364,7 @@ static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = { VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERSTATE, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERTARGET, &vmw_cmd_set_render_target_check), - VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_cid_check), + VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_tex_state), VMW_CMD_DEF(SVGA_3D_CMD_SETMATERIAL, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTDATA, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTENABLED, &vmw_cmd_cid_check), @@ -276,7 +376,7 @@ static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = { VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check), - VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_cid_check), + VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw), VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check), @@ -291,6 +391,7 @@ static int vmw_cmd_check(struct vmw_private *dev_priv, void *buf, uint32_t *size) { uint32_t cmd_id; + uint32_t size_remaining = *size; SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf; int ret; @@ -304,6 +405,9 @@ static int vmw_cmd_check(struct vmw_private *dev_priv, *size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader); cmd_id -= SVGA_3D_CMD_BASE; + if (unlikely(*size > size_remaining)) + goto out_err; + if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE)) goto out_err; @@ -326,6 +430,7 @@ static int vmw_cmd_check_all(struct vmw_private *dev_priv, int ret; while (cur_size > 0) { + size = cur_size; ret = vmw_cmd_check(dev_priv, sw_context, buf, &size); if (unlikely(ret != 0)) return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e9403be446fe..b1af76e371c3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -106,8 +106,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, int ret; if (handle) { - ret = vmw_user_surface_lookup(dev_priv, tfile, - handle, &surface); + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, + handle, &surface); if (!ret) { if (!surface->snooper.image) { DRM_ERROR("surface not suitable for cursor\n"); @@ -704,8 +704,8 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_dma_buffer *bo = NULL; int ret; - ret = vmw_user_surface_lookup(dev_priv, tfile, - mode_cmd->handle, &surface); + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, + mode_cmd->handle, &surface); if (ret) goto try_dmabuf; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index a1ceed0c8e07..c012d5927f65 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -488,28 +488,44 @@ static void vmw_user_surface_free(struct vmw_resource *res) kfree(user_srf); } -int vmw_user_surface_lookup(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - int sid, struct vmw_surface **out) +int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, struct vmw_surface **out) { struct vmw_resource *res; struct vmw_surface *srf; struct vmw_user_surface *user_srf; + struct ttm_base_object *base; + int ret = -EINVAL; - res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, sid); - if (unlikely(res == NULL)) + base = ttm_base_object_lookup(tfile, handle); + if (unlikely(base == NULL)) return -EINVAL; - if (res->res_free != &vmw_user_surface_free) - return -EINVAL; + if (unlikely(base->object_type != VMW_RES_SURFACE)) + goto out_bad_resource; - srf = container_of(res, struct vmw_surface, res); - user_srf = container_of(srf, struct vmw_user_surface, srf); - if (user_srf->base.tfile != tfile && !user_srf->base.shareable) - return -EPERM; + user_srf = container_of(base, struct vmw_user_surface, base); + srf = &user_srf->srf; + res = &srf->res; + + read_lock(&dev_priv->resource_lock); + + if (!res->avail || res->res_free != &vmw_user_surface_free) { + read_unlock(&dev_priv->resource_lock); + goto out_bad_resource; + } + + kref_get(&res->kref); + read_unlock(&dev_priv->resource_lock); *out = srf; - return 0; + ret = 0; + +out_bad_resource: + ttm_base_object_unref(&base); + + return ret; } static void vmw_user_surface_base_release(struct ttm_base_object **p_base) @@ -526,35 +542,10 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base) int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_resource *res; - struct vmw_surface *srf; - struct vmw_user_surface *user_srf; struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - int ret = 0; - - res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, arg->sid); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_surface_free) { - ret = -EINVAL; - goto out; - } - srf = container_of(res, struct vmw_surface, res); - user_srf = container_of(srf, struct vmw_user_surface, srf); - if (user_srf->base.tfile != tfile && !user_srf->base.shareable) { - ret = -EPERM; - goto out; - } - - ttm_ref_object_base_unref(tfile, user_srf->base.hash.key, - TTM_REF_USAGE); -out: - vmw_resource_unreference(&res); - return ret; + return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); } int vmw_surface_define_ioctl(struct drm_device *dev, void *data, @@ -649,7 +640,10 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, } srf->snooper.crtc = NULL; - rep->sid = res->id; + rep->sid = user_srf->base.hash.key; + if (rep->sid == SVGA3D_INVALID_ID) + DRM_ERROR("Created bad Surface ID.\n"); + vmw_resource_unreference(&res); return 0; out_err1: @@ -662,39 +656,33 @@ out_err0: int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct vmw_private *dev_priv = vmw_priv(dev); union drm_vmw_surface_reference_arg *arg = (union drm_vmw_surface_reference_arg *)data; struct drm_vmw_surface_arg *req = &arg->req; struct drm_vmw_surface_create_req *rep = &arg->rep; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_resource *res; struct vmw_surface *srf; struct vmw_user_surface *user_srf; struct drm_vmw_size __user *user_sizes; - int ret; + struct ttm_base_object *base; + int ret = -EINVAL; - res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, req->sid); - if (unlikely(res == NULL)) + base = ttm_base_object_lookup(tfile, req->sid); + if (unlikely(base == NULL)) { + DRM_ERROR("Could not find surface to reference.\n"); return -EINVAL; - - if (res->res_free != &vmw_user_surface_free) { - ret = -EINVAL; - goto out; } - srf = container_of(res, struct vmw_surface, res); - user_srf = container_of(srf, struct vmw_user_surface, srf); - if (user_srf->base.tfile != tfile && !user_srf->base.shareable) { - DRM_ERROR("Tried to reference none shareable surface\n"); - ret = -EPERM; - goto out; - } + if (unlikely(base->object_type != VMW_RES_SURFACE)) + goto out_bad_resource; + + user_srf = container_of(base, struct vmw_user_surface, base); + srf = &user_srf->srf; ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL); if (unlikely(ret != 0)) { DRM_ERROR("Could not add a reference to a surface.\n"); - goto out; + goto out_no_reference; } rep->flags = srf->flags; @@ -706,40 +694,43 @@ int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, if (user_sizes) ret = copy_to_user(user_sizes, srf->sizes, srf->num_sizes * sizeof(*srf->sizes)); - if (unlikely(ret != 0)) { + if (unlikely(ret != 0)) DRM_ERROR("copy_to_user failed %p %u\n", user_sizes, srf->num_sizes); - /** - * FIXME: Unreference surface here? - */ - goto out; - } -out: - vmw_resource_unreference(&res); +out_bad_resource: +out_no_reference: + ttm_base_object_unref(&base); + return ret; } int vmw_surface_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, - int id) + uint32_t handle, int *id) { - struct vmw_resource *res; - int ret = 0; + struct ttm_base_object *base; + struct vmw_user_surface *user_srf; - read_lock(&dev_priv->resource_lock); - res = idr_find(&dev_priv->surface_idr, id); - if (res && res->avail) { - struct vmw_surface *srf = - container_of(res, struct vmw_surface, res); - struct vmw_user_surface *usrf = - container_of(srf, struct vmw_user_surface, srf); + int ret = -EPERM; - if (usrf->base.tfile != tfile && !usrf->base.shareable) - ret = -EPERM; - } else - ret = -EINVAL; - read_unlock(&dev_priv->resource_lock); + base = ttm_base_object_lookup(tfile, handle); + if (unlikely(base == NULL)) + return -EINVAL; + + if (unlikely(base->object_type != VMW_RES_SURFACE)) + goto out_bad_surface; + user_srf = container_of(base, struct vmw_user_surface, base); + *id = user_srf->srf.res.id; + ret = 0; + +out_bad_surface: + /** + * FIXME: May deadlock here when called from the + * command parsing code. + */ + + ttm_base_object_unref(&base); return ret; } -- 2.34.1