Two fixes on top of previous patches. The first removes a DOS vulnerabilty. The second one implements some screen object fixes.
Contexts, surfaces and streams allocate persistent kernel memory as the direct result of user-space requests. Make sure this memory is accounted as graphics memory, to avoid DOS vulnerabilities.
Also take the TTM read lock around resource creation to block switched-out dri clients from allocating resources.
Signed-off-by: Thomas Hellstrom thellstrom@vmware.com Reviewed-by: Jakob Bornecrantz jakob@vmware.com --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 172 +++++++++++++++++++++++++----- 1 files changed, 146 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 93a68a6..c7cff3d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -39,6 +39,7 @@ struct vmw_user_context { struct vmw_user_surface { struct ttm_base_object base; struct vmw_surface srf; + uint32_t size; };
struct vmw_user_dma_buffer { @@ -67,6 +68,11 @@ struct vmw_surface_offset { uint32_t bo_offset; };
+ +static uint64_t vmw_user_context_size; +static uint64_t vmw_user_surface_size; +static uint64_t vmw_user_stream_size; + static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -343,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res) { struct vmw_user_context *ctx = container_of(res, struct vmw_user_context, res); + struct vmw_private *dev_priv = res->dev_priv;
kfree(ctx); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); }
/** @@ -398,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + struct vmw_user_context *ctx; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret;
- if (unlikely(ctx == NULL)) - return -ENOMEM; + + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of contexts anyway. + */ + + if (unlikely(vmw_user_context_size == 0)) + vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_context_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for context" + " creation.\n"); + goto out_unlock; + } + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(ctx == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); + ret = -ENOMEM; + goto out_unlock; + }
res = &ctx->res; ctx->base.shareable = false; ctx->base.tfile = NULL;
+ /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_context_init(dev_priv, res, vmw_user_context_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock;
tmp = vmw_resource_reference(&ctx->res); ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, @@ -428,6 +470,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, arg->cid = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret;
} @@ -1095,6 +1139,8 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_surface *srf = container_of(res, struct vmw_surface, res); struct vmw_user_surface *user_srf = container_of(srf, struct vmw_user_surface, srf); + struct vmw_private *dev_priv = srf->res.dev_priv; + uint32_t size = user_srf->size;
if (srf->backup) ttm_bo_unref(&srf->backup); @@ -1102,6 +1148,7 @@ static void vmw_user_surface_free(struct vmw_resource *res) kfree(srf->sizes); kfree(srf->snooper.image); kfree(user_srf); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); }
/** @@ -1226,9 +1273,45 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, struct vmw_surface_offset *cur_offset; uint32_t stride_bpp; uint32_t bpp; + uint32_t num_sizes; + uint32_t size; + struct vmw_master *vmaster = vmw_master(file_priv->master);
- if (unlikely(user_srf == NULL)) - return -ENOMEM; + if (unlikely(vmw_user_surface_size == 0)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + + 128; + + num_sizes = 0; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + num_sizes += req->mip_levels[i]; + + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * + DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + + size = vmw_user_surface_size + 128 + + ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + + ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); + + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + size, false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for surface" + " creation.\n"); + goto out_unlock; + } + + user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(user_srf == NULL)) { + ret = -ENOMEM; + goto out_no_user_srf; + }
srf = &user_srf->srf; res = &srf->res; @@ -1239,20 +1322,13 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->backup = NULL;
memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); - srf->num_sizes = 0; - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) - srf->num_sizes += srf->mip_levels[i]; - - if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES * - DRM_VMW_MAX_MIP_LEVELS) { - ret = -EINVAL; - goto out_err0; - } + srf->num_sizes = num_sizes; + user_srf->size = size;
srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); if (unlikely(srf->sizes == NULL)) { ret = -ENOMEM; - goto out_err0; + goto out_no_sizes; } srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), GFP_KERNEL); @@ -1268,7 +1344,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->num_sizes * sizeof(*srf->sizes)); if (unlikely(ret != 0)) { ret = -EFAULT; - goto out_err1; + goto out_no_copy; }
cur_bo_offset = 0; @@ -1305,7 +1381,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (!srf->snooper.image) { DRM_ERROR("Failed to allocate cursor_image\n"); ret = -ENOMEM; - goto out_err1; + goto out_no_copy; } } else { srf->snooper.image = NULL; @@ -1322,7 +1398,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock;
tmp = vmw_resource_reference(&srf->res); ret = ttm_base_object_init(tfile, &user_srf->base, @@ -1332,7 +1408,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) { vmw_resource_unreference(&tmp); vmw_resource_unreference(&res); - return ret; + goto out_unlock; }
rep->sid = user_srf->base.hash.key; @@ -1340,13 +1416,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, DRM_ERROR("Created bad Surface ID.\n");
vmw_resource_unreference(&res); + + ttm_read_unlock(&vmaster->lock); return 0; -out_err1: +out_no_copy: kfree(srf->offsets); out_no_offsets: kfree(srf->sizes); -out_err0: +out_no_sizes: kfree(user_srf); +out_no_user_srf: + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; }
@@ -1690,8 +1772,11 @@ static void vmw_user_stream_free(struct vmw_resource *res) { struct vmw_user_stream *stream = container_of(res, struct vmw_user_stream, stream.res); + struct vmw_private *dev_priv = res->dev_priv;
kfree(stream); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); }
/** @@ -1745,23 +1830,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); + struct vmw_user_stream *stream; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret;
- if (unlikely(stream == NULL)) - return -ENOMEM; + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of streams anyway? + */ + + if (unlikely(vmw_user_stream_size == 0)) + vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_stream_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for stream" + " creation.\n"); + goto out_unlock; + } + + + stream = kmalloc(sizeof(*stream), GFP_KERNEL); + if (unlikely(stream == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); + ret = -ENOMEM; + goto out_unlock; + }
res = &stream->stream.res; stream->base.shareable = false; stream->base.tfile = NULL;
+ /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock;
tmp = vmw_resource_reference(res); ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, @@ -1775,6 +1893,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, arg->stream_id = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; }
From: Jakob Bornecrantz jakob@vmware.com
Signed-off-by: Jakob Bornecrantz jakob@vmware.com Signed-off-by: Thomas Hellstrom thellstrom@vmware.com --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 272 +++++++++++++++++++++++++++-------- 1 files changed, 215 insertions(+), 57 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index fc62c87..2421d0c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -358,49 +358,109 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv, struct drm_clip_rect *clips, unsigned num_clips, int inc) { - int left = clips->x2, right = clips->x1; - int top = clips->y2, bottom = clips->y1; + struct drm_clip_rect *clips_ptr; + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; size_t fifo_size; - int i, ret; + int i, num_units; + int ret = 0; /* silence warning */ + int left, right, top, bottom;
struct { SVGA3dCmdHeader header; SVGA3dCmdBlitSurfaceToScreen body; } *cmd; + SVGASignedRect *blits;
- fifo_size = sizeof(*cmd); + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, + head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + BUG_ON(surf == NULL); + BUG_ON(!clips || !num_clips); + + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; cmd = kzalloc(fifo_size, GFP_KERNEL); if (unlikely(cmd == NULL)) { DRM_ERROR("Temporary fifo memory alloc failed.\n"); return -ENOMEM; }
- cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); - cmd->header.size = cpu_to_le32(sizeof(cmd->body)); - - cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle); - cmd->body.destScreenId = SVGA_ID_INVALID; /* virtual coords */ - - for (i = 0; i < num_clips; i++, clips += inc) { - left = min_t(int, left, (int)clips->x1); - right = max_t(int, right, (int)clips->x2); - top = min_t(int, top, (int)clips->y1); - bottom = max_t(int, bottom, (int)clips->y2); + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + clips_ptr = clips; + for (i = 1; i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); }
+ /* only need to do this once */ + memset(cmd, 0, fifo_size); + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + cmd->body.srcRect.left = left; cmd->body.srcRect.right = right; cmd->body.srcRect.top = top; cmd->body.srcRect.bottom = bottom;
- cmd->body.destRect.left = left; - cmd->body.destRect.right = right; - cmd->body.destRect.top = top; - cmd->body.destRect.bottom = bottom; + clips_ptr = clips; + blits = (SVGASignedRect *)&cmd[1]; + for (i = 0; i < num_clips; i++, clips_ptr += inc) { + blits[i].left = clips_ptr->x1 - left; + blits[i].right = clips_ptr->x2 - left; + blits[i].top = clips_ptr->y1 - top; + blits[i].bottom = clips_ptr->y2 - top; + } + + /* do per unit writing, reuse fifo for each */ + for (i = 0; i < num_units; i++) { + struct vmw_display_unit *unit = units[i]; + int clip_x1 = left - unit->crtc.x; + int clip_y1 = top - unit->crtc.y; + int clip_x2 = right - unit->crtc.x; + int clip_y2 = bottom - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle); + + cmd->body.destScreenId = unit->unit; + + /* + * The blit command is a lot more resilient then the + * readback command when it comes to clip rects. So its + * okay to go out of bounds. + */ + + cmd->body.destRect.left = clip_x1; + cmd->body.destRect.right = clip_x2; + cmd->body.destRect.top = clip_y1; + cmd->body.destRect.bottom = clip_y2; + + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + }
- ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size, - 0, NULL); kfree(cmd);
return ret; @@ -615,27 +675,19 @@ static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv, return 0; }
-static int do_dmabuf_dirty_sou(struct drm_file *file_priv, - struct vmw_private *dev_priv, - struct vmw_framebuffer *framebuffer, - struct vmw_dma_buffer *buffer, - unsigned flags, unsigned color, - struct drm_clip_rect *clips, - unsigned num_clips, int increment) +static int do_dmabuf_define_gmrfb(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer) { size_t fifo_size; - int i, ret; + int ret;
struct { uint32_t header; SVGAFifoCmdDefineGMRFB body; } *cmd; - struct { - uint32_t header; - SVGAFifoCmdBlitGMRFBToScreen body; - } *blits;
- fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips; + fifo_size = sizeof(*cmd); cmd = kmalloc(fifo_size, GFP_KERNEL); if (unlikely(cmd == NULL)) { DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); @@ -651,17 +703,6 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv, cmd->body.ptr.gmrId = framebuffer->user_handle; cmd->body.ptr.offset = 0;
- blits = (void *)&cmd[1]; - for (i = 0; i < num_clips; i++, clips += increment) { - blits[i].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; - blits[i].body.srcOrigin.x = clips->x1; - blits[i].body.srcOrigin.y = clips->y1; - blits[i].body.destRect.left = clips->x1; - blits[i].body.destRect.top = clips->y1; - blits[i].body.destRect.right = clips->x2; - blits[i].body.destRect.bottom = clips->y2; - } - ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size, 0, NULL);
@@ -670,6 +711,88 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv, return ret; }
+static int do_dmabuf_dirty_sou(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct vmw_dma_buffer *buffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int increment) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_clip_rect *clips_ptr; + int i, k, num_units, ret; + struct drm_crtc *crtc; + size_t fifo_size; + + struct { + uint32_t header; + SVGAFifoCmdBlitGMRFBToScreen body; + } *blits; + + ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer); + if (unlikely(ret != 0)) + return ret; /* define_gmrfb prints warnings */ + + fifo_size = sizeof(*blits) * num_clips; + blits = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(blits == NULL)) { + DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); + return -ENOMEM; + } + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + int hit_num = 0; + + clips_ptr = clips; + for (i = 0; i < num_clips; i++, clips_ptr += increment) { + int clip_x1 = clips_ptr->x1 - unit->crtc.x; + int clip_y1 = clips_ptr->y1 - unit->crtc.y; + int clip_x2 = clips_ptr->x2 - unit->crtc.x; + int clip_y2 = clips_ptr->y2 - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; + blits[hit_num].body.destScreenId = unit->unit; + blits[hit_num].body.srcOrigin.x = clips_ptr->x1; + blits[hit_num].body.srcOrigin.y = clips_ptr->y1; + blits[hit_num].body.destRect.left = clip_x1; + blits[hit_num].body.destRect.top = clip_y1; + blits[hit_num].body.destRect.right = clip_x2; + blits[hit_num].body.destRect.bottom = clip_y2; + hit_num++; + } + + /* no clips hit the crtc */ + if (hit_num == 0) + continue; + + fifo_size = sizeof(*blits) * hit_num; + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(blits); + + return ret; +} + int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, struct drm_file *file_priv, unsigned flags, unsigned color, @@ -959,8 +1082,11 @@ int vmw_kms_present(struct vmw_private *dev_priv, struct drm_vmw_rect *clips, uint32_t num_clips) { + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; size_t fifo_size; - int i, ret; + int i, k, num_units; + int ret = 0; /* silence warning */
struct { SVGA3dCmdHeader header; @@ -968,6 +1094,13 @@ int vmw_kms_present(struct vmw_private *dev_priv, } *cmd; SVGASignedRect *blits;
+ num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &vfb->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + BUG_ON(surface == NULL); BUG_ON(!clips || !num_clips);
@@ -978,24 +1111,16 @@ int vmw_kms_present(struct vmw_private *dev_priv, return -ENOMEM; }
+ /* only need to do this once */ memset(cmd, 0, fifo_size); - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
- cmd->body.srcImage.sid = sid; - cmd->body.destScreenId = SVGA_ID_INVALID; /* virtual coords */ - cmd->body.srcRect.left = 0; cmd->body.srcRect.right = surface->sizes[0].width; cmd->body.srcRect.top = 0; cmd->body.srcRect.bottom = surface->sizes[0].height;
- cmd->body.destRect.left = destX; - cmd->body.destRect.right = destX + surface->sizes[0].width; - cmd->body.destRect.top = destY; - cmd->body.destRect.bottom = destY + surface->sizes[0].height; - blits = (SVGASignedRect *)&cmd[1]; for (i = 0; i < num_clips; i++) { blits[i].left = clips[i].x; @@ -1004,8 +1129,41 @@ int vmw_kms_present(struct vmw_private *dev_priv, blits[i].bottom = clips[i].y + clips[i].h; }
- ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, - fifo_size, 0, NULL); + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + int clip_x1 = destX - unit->crtc.x; + int clip_y1 = destY - unit->crtc.y; + int clip_x2 = clip_x1 + surface->sizes[0].width; + int clip_y2 = clip_y1 + surface->sizes[0].height; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = sid; + + cmd->body.destScreenId = unit->unit; + + /* + * The blit command is a lot more resilient then the + * readback command when it comes to clip rects. So its + * okay to go out of bounds. + */ + + cmd->body.destRect.left = clip_x1; + cmd->body.destRect.right = clip_x2; + cmd->body.destRect.top = clip_y1; + cmd->body.destRect.bottom = clip_y2; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + }
kfree(cmd);
dri-devel@lists.freedesktop.org