// SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #include "vmwgfx_bo.h" #include "vmwgfx_drv.h" #include "vmwgfx_resource_priv.h" #include "vmwgfx_so.h" #include "vmwgfx_binding.h" #include "vmw_surface_cache.h" #include "device_include/svga3d_surfacedefs.h" #include <drm/ttm/ttm_placement.h> #define SVGA3D_FLAGS_64(upper32, lower32) … /** * struct vmw_user_surface - User-space visible surface resource * * @prime: The TTM prime object. * @srf: The surface metadata. * @master: Master of the creating client. Used for security check. */ struct vmw_user_surface { … }; /** * struct vmw_surface_offset - Backing store mip level offset info * * @face: Surface face. * @mip: Mip level. * @bo_offset: Offset into backing store of this mip level. * */ struct vmw_surface_offset { … }; /** * struct vmw_surface_dirty - Surface dirty-tracker * @cache: Cached layout information of the surface. * @num_subres: Number of subresources. * @boxes: Array of SVGA3dBoxes indicating dirty regions. One per subresource. */ struct vmw_surface_dirty { … }; static void vmw_user_surface_free(struct vmw_resource *res); static struct vmw_resource * vmw_user_surface_base_to_res(struct ttm_base_object *base); static int vmw_legacy_srf_bind(struct vmw_resource *res, struct ttm_validate_buffer *val_buf); static int vmw_legacy_srf_unbind(struct vmw_resource *res, bool readback, struct ttm_validate_buffer *val_buf); static int vmw_legacy_srf_create(struct vmw_resource *res); static int vmw_legacy_srf_destroy(struct vmw_resource *res); static int vmw_gb_surface_create(struct vmw_resource *res); static int vmw_gb_surface_bind(struct vmw_resource *res, struct ttm_validate_buffer *val_buf); static int vmw_gb_surface_unbind(struct vmw_resource *res, bool readback, struct ttm_validate_buffer *val_buf); static int vmw_gb_surface_destroy(struct vmw_resource *res); static int vmw_gb_surface_define_internal(struct drm_device *dev, struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv); static int vmw_gb_surface_reference_internal(struct drm_device *dev, struct drm_vmw_surface_arg *req, struct drm_vmw_gb_surface_ref_ext_rep *rep, struct drm_file *file_priv); static void vmw_surface_dirty_free(struct vmw_resource *res); static int vmw_surface_dirty_alloc(struct vmw_resource *res); static int vmw_surface_dirty_sync(struct vmw_resource *res); static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, size_t end); static int vmw_surface_clean(struct vmw_resource *res); static const struct vmw_user_resource_conv user_surface_conv = …; const struct vmw_user_resource_conv *user_surface_converter = …; static const struct vmw_res_func vmw_legacy_surface_func = …; static const struct vmw_res_func vmw_gb_surface_func = …; /* * struct vmw_surface_dma - SVGA3D DMA command */ struct vmw_surface_dma { … }; /* * struct vmw_surface_define - SVGA3D Surface Define command */ struct vmw_surface_define { … }; /* * struct vmw_surface_destroy - SVGA3D Surface Destroy command */ struct vmw_surface_destroy { … }; /** * vmw_surface_dma_size - Compute fifo size for a dma command. * * @srf: Pointer to a struct vmw_surface * * Computes the required size for a surface dma command for backup or * restoration of the surface represented by @srf. */ static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) { … } /** * vmw_surface_define_size - Compute fifo size for a surface define command. * * @srf: Pointer to a struct vmw_surface * * Computes the required size for a surface define command for the definition * of the surface represented by @srf. */ static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) { … } /** * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. * * Computes the required size for a surface destroy command for the destruction * of a hw surface. */ static inline uint32_t vmw_surface_destroy_size(void) { … } /** * vmw_surface_destroy_encode - Encode a surface_destroy command. * * @id: The surface id * @cmd_space: Pointer to memory area in which the commands should be encoded. */ static void vmw_surface_destroy_encode(uint32_t id, void *cmd_space) { … } /** * vmw_surface_define_encode - Encode a surface_define command. * * @srf: Pointer to a struct vmw_surface object. * @cmd_space: Pointer to memory area in which the commands should be encoded. */ static void vmw_surface_define_encode(const struct vmw_surface *srf, void *cmd_space) { … } /** * vmw_surface_dma_encode - Encode a surface_dma command. * * @srf: Pointer to a struct vmw_surface object. * @cmd_space: Pointer to memory area in which the commands should be encoded. * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents * should be placed or read from. * @to_surface: Boolean whether to DMA to the surface or from the surface. */ static void vmw_surface_dma_encode(struct vmw_surface *srf, void *cmd_space, const SVGAGuestPtr *ptr, bool to_surface) { uint32_t i; struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; const struct SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(srf->metadata.format); for (i = 0; i < srf->metadata.num_sizes; ++i) { SVGA3dCmdHeader *header = &cmd->header; SVGA3dCmdSurfaceDMA *body = &cmd->body; SVGA3dCopyBox *cb = &cmd->cb; SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; const struct drm_vmw_size *cur_size = &srf->metadata.sizes[i]; header->id = SVGA_3D_CMD_SURFACE_DMA; header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); body->guest.ptr = *ptr; body->guest.ptr.offset += cur_offset->bo_offset; body->guest.pitch = vmw_surface_calculate_pitch(desc, cur_size); body->host.sid = srf->res.id; body->host.face = cur_offset->face; body->host.mipmap = cur_offset->mip; body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : SVGA3D_READ_HOST_VRAM); cb->x = 0; cb->y = 0; cb->z = 0; cb->srcx = 0; cb->srcy = 0; cb->srcz = 0; cb->w = cur_size->width; cb->h = cur_size->height; cb->d = cur_size->depth; suffix->suffixSize = sizeof(*suffix); suffix->maximumOffset = vmw_surface_get_image_buffer_size(desc, cur_size, body->guest.pitch); suffix->flags.discard = 0; suffix->flags.unsynchronized = 0; suffix->flags.reserved = 0; ++cmd; } }; /** * vmw_hw_surface_destroy - destroy a Device surface * * @res: Pointer to a struct vmw_resource embedded in a struct * vmw_surface. * * Destroys a the device surface associated with a struct vmw_surface if * any, and adjusts resource count accordingly. */ static void vmw_hw_surface_destroy(struct vmw_resource *res) { … } /** * vmw_legacy_srf_create - Create a device surface as part of the * resource validation process. * * @res: Pointer to a struct vmw_surface. * * If the surface doesn't have a hw id. * * Returns -EBUSY if there wasn't sufficient device resources to * complete the validation. Retry after freeing up resources. * * May return other errors if the kernel is out of guest resources. */ static int vmw_legacy_srf_create(struct vmw_resource *res) { … } /** * vmw_legacy_srf_dma - Copy backup data to or from a legacy surface. * * @res: Pointer to a struct vmw_res embedded in a struct * vmw_surface. * @val_buf: Pointer to a struct ttm_validate_buffer containing * information about the backup buffer. * @bind: Boolean wether to DMA to the surface. * * Transfer backup data to or from a legacy surface as part of the * validation process. * May return other errors if the kernel is out of guest resources. * The backup buffer will be fenced or idle upon successful completion, * and if the surface needs persistent backup storage, the backup buffer * will also be returned reserved iff @bind is true. */ static int vmw_legacy_srf_dma(struct vmw_resource *res, struct ttm_validate_buffer *val_buf, bool bind) { … } /** * vmw_legacy_srf_bind - Perform a legacy surface bind as part of the * surface validation process. * * @res: Pointer to a struct vmw_res embedded in a struct * vmw_surface. * @val_buf: Pointer to a struct ttm_validate_buffer containing * information about the backup buffer. * * This function will copy backup data to the surface if the * backup buffer is dirty. */ static int vmw_legacy_srf_bind(struct vmw_resource *res, struct ttm_validate_buffer *val_buf) { … } /** * vmw_legacy_srf_unbind - Perform a legacy surface unbind as part of the * surface eviction process. * * @res: Pointer to a struct vmw_res embedded in a struct * vmw_surface. * @readback: Readback - only true if dirty * @val_buf: Pointer to a struct ttm_validate_buffer containing * information about the backup buffer. * * This function will copy backup data from the surface. */ static int vmw_legacy_srf_unbind(struct vmw_resource *res, bool readback, struct ttm_validate_buffer *val_buf) { … } /** * vmw_legacy_srf_destroy - Destroy a device surface as part of a * resource eviction process. * * @res: Pointer to a struct vmw_res embedded in a struct * vmw_surface. */ static int vmw_legacy_srf_destroy(struct vmw_resource *res) { … } /** * vmw_surface_init - initialize a struct vmw_surface * * @dev_priv: Pointer to a device private struct. * @srf: Pointer to the struct vmw_surface to initialize. * @res_free: Pointer to a resource destructor used to free * the object. */ static int vmw_surface_init(struct vmw_private *dev_priv, struct vmw_surface *srf, void (*res_free) (struct vmw_resource *res)) { … } /** * vmw_user_surface_base_to_res - TTM base object to resource converter for * user visible surfaces * * @base: Pointer to a TTM base object * * Returns the struct vmw_resource embedded in a struct vmw_surface * for the user-visible object identified by the TTM base object @base. */ static struct vmw_resource * vmw_user_surface_base_to_res(struct ttm_base_object *base) { … } /** * vmw_user_surface_free - User visible surface resource destructor * * @res: A struct vmw_resource embedded in a struct vmw_surface. */ static void vmw_user_surface_free(struct vmw_resource *res) { … } /** * vmw_user_surface_base_release - User visible surface TTM base object destructor * * @p_base: Pointer to a pointer to a TTM base object * embedded in a struct vmw_user_surface. * * Drops the base object's reference on its resource, and the * pointer pointed to by *p_base is set to NULL. */ static void vmw_user_surface_base_release(struct ttm_base_object **p_base) { … } /** * vmw_surface_destroy_ioctl - Ioctl function implementing * the user surface destroy functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_surface_define_ioctl - Ioctl function implementing * the user surface define functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } static struct vmw_user_surface * vmw_lookup_user_surface_for_buffer(struct vmw_private *vmw, struct vmw_bo *bo, u32 handle) { … } struct vmw_surface *vmw_lookup_surface_for_buffer(struct vmw_private *vmw, struct vmw_bo *bo, u32 handle) { … } u32 vmw_lookup_surface_handle_for_buffer(struct vmw_private *vmw, struct vmw_bo *bo, u32 handle) { … } static int vmw_buffer_prime_to_surface_base(struct vmw_private *dev_priv, struct drm_file *file_priv, u32 fd, u32 *handle, struct ttm_base_object **base_p) { … } static int vmw_surface_handle_reference(struct vmw_private *dev_priv, struct drm_file *file_priv, uint32_t u_handle, enum drm_vmw_handle_type handle_type, struct ttm_base_object **base_p) { … } /** * vmw_surface_reference_ioctl - Ioctl function implementing * the user surface reference functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_gb_surface_create - Encode a surface_define command. * * @res: Pointer to a struct vmw_resource embedded in a struct * vmw_surface. */ static int vmw_gb_surface_create(struct vmw_resource *res) { … } static int vmw_gb_surface_bind(struct vmw_resource *res, struct ttm_validate_buffer *val_buf) { … } static int vmw_gb_surface_unbind(struct vmw_resource *res, bool readback, struct ttm_validate_buffer *val_buf) { … } static int vmw_gb_surface_destroy(struct vmw_resource *res) { … } /** * vmw_gb_surface_define_ioctl - Ioctl function implementing * the user surface define functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_gb_surface_reference_ioctl - Ioctl function implementing * the user surface reference functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_gb_surface_define_ext_ioctl - Ioctl function implementing * the user surface define functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_define_ext_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_gb_surface_reference_ext_ioctl - Ioctl function implementing * the user surface reference functionality. * * @dev: Pointer to a struct drm_device. * @data: Pointer to data copied from / to user-space. * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_gb_surface_define_internal - Ioctl function implementing * the user surface define functionality. * * @dev: Pointer to a struct drm_device. * @req: Request argument from user-space. * @rep: Response argument to user-space. * @file_priv: Pointer to a drm file private structure. */ static int vmw_gb_surface_define_internal(struct drm_device *dev, struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv) { … } /** * vmw_gb_surface_reference_internal - Ioctl function implementing * the user surface reference functionality. * * @dev: Pointer to a struct drm_device. * @req: Pointer to user-space request surface arg. * @rep: Pointer to response to user-space. * @file_priv: Pointer to a drm file private structure. */ static int vmw_gb_surface_reference_internal(struct drm_device *dev, struct drm_vmw_surface_arg *req, struct drm_vmw_gb_surface_ref_ext_rep *rep, struct drm_file *file_priv) { … } /** * vmw_subres_dirty_add - Add a dirty region to a subresource * @dirty: The surfaces's dirty tracker. * @loc_start: The location corresponding to the start of the region. * @loc_end: The location corresponding to the end of the region. * * As we are assuming that @loc_start and @loc_end represent a sequential * range of backing store memory, if the region spans multiple lines then * regardless of the x coordinate, the full lines are dirtied. * Correspondingly if the region spans multiple z slices, then full rather * than partial z slices are dirtied. */ static void vmw_subres_dirty_add(struct vmw_surface_dirty *dirty, const struct vmw_surface_loc *loc_start, const struct vmw_surface_loc *loc_end) { … } /** * vmw_subres_dirty_full - Mark a full subresource as dirty * @dirty: The surface's dirty tracker. * @subres: The subresource */ static void vmw_subres_dirty_full(struct vmw_surface_dirty *dirty, u32 subres) { … } /* * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for texture * surfaces. */ static void vmw_surface_tex_dirty_range_add(struct vmw_resource *res, size_t start, size_t end) { … } /* * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for buffer * surfaces. */ static void vmw_surface_buf_dirty_range_add(struct vmw_resource *res, size_t start, size_t end) { … } /* * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for surfaces */ static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, size_t end) { … } /* * vmw_surface_dirty_sync - The surface's dirty_sync callback. */ static int vmw_surface_dirty_sync(struct vmw_resource *res) { … } /* * vmw_surface_dirty_alloc - The surface's dirty_alloc callback. */ static int vmw_surface_dirty_alloc(struct vmw_resource *res) { … } /* * vmw_surface_dirty_free - The surface's dirty_free callback */ static void vmw_surface_dirty_free(struct vmw_resource *res) { … } /* * vmw_surface_clean - The surface's clean callback */ static int vmw_surface_clean(struct vmw_resource *res) { … } /* * vmw_gb_surface_define - Define a private GB surface * * @dev_priv: Pointer to a device private. * @metadata: Metadata representing the surface to create. * @user_srf_out: allocated user_srf. Set to NULL on failure. * * GB surfaces allocated by this function will not have a user mode handle, and * thus will only be visible to vmwgfx. For optimization reasons the * surface may later be given a user mode handle by another function to make * it available to user mode drivers. */ int vmw_gb_surface_define(struct vmw_private *dev_priv, const struct vmw_surface_metadata *req, struct vmw_surface **srf_out) { … } static SVGA3dSurfaceFormat vmw_format_bpp_to_svga(struct vmw_private *vmw, int bpp) { … } /** * vmw_dumb_create - Create a dumb kms buffer * * @file_priv: Pointer to a struct drm_file identifying the caller. * @dev: Pointer to the drm device. * @args: Pointer to a struct drm_mode_create_dumb structure * Return: Zero on success, negative error code on failure. * * This is a driver callback for the core drm create_dumb functionality. * Note that this is very similar to the vmw_bo_alloc ioctl, except * that the arguments have a different format. */ int vmw_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args) { … }