Add support for a Hyper-V based vGPU implementation that exposes the DirectX API to Linux userspace. Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> --- drivers/gpu/dxgkrnl/Kconfig | 10 + drivers/gpu/dxgkrnl/Makefile | 12 + drivers/gpu/dxgkrnl/d3dkmthk.h | 1635 +++++++++ drivers/gpu/dxgkrnl/dxgadapter.c | 1399 ++++++++ drivers/gpu/dxgkrnl/dxgkrnl.h | 913 ++++++ drivers/gpu/dxgkrnl/dxgmodule.c | 692 ++++ drivers/gpu/dxgkrnl/dxgprocess.c | 355 ++ drivers/gpu/dxgkrnl/dxgvmbus.c | 2955 +++++++++++++++++ drivers/gpu/dxgkrnl/dxgvmbus.h | 859 +++++ drivers/gpu/dxgkrnl/hmgr.c | 593 ++++ drivers/gpu/dxgkrnl/hmgr.h | 107 + drivers/gpu/dxgkrnl/ioctl.c | 5269 ++++++++++++++++++++++++++++++ drivers/gpu/dxgkrnl/misc.c | 280 ++ drivers/gpu/dxgkrnl/misc.h | 288 ++ 14 files changed, 15367 insertions(+) create mode 100644 drivers/gpu/dxgkrnl/Kconfig create mode 100644 drivers/gpu/dxgkrnl/Makefile create mode 100644 drivers/gpu/dxgkrnl/d3dkmthk.h create mode 100644 drivers/gpu/dxgkrnl/dxgadapter.c create mode 100644 drivers/gpu/dxgkrnl/dxgkrnl.h create mode 100644 drivers/gpu/dxgkrnl/dxgmodule.c create mode 100644 drivers/gpu/dxgkrnl/dxgprocess.c create mode 100644 drivers/gpu/dxgkrnl/dxgvmbus.c create mode 100644 drivers/gpu/dxgkrnl/dxgvmbus.h create mode 100644 drivers/gpu/dxgkrnl/hmgr.c create mode 100644 drivers/gpu/dxgkrnl/hmgr.h create mode 100644 drivers/gpu/dxgkrnl/ioctl.c create mode 100644 drivers/gpu/dxgkrnl/misc.c create mode 100644 drivers/gpu/dxgkrnl/misc.h diff --git a/drivers/gpu/dxgkrnl/Kconfig b/drivers/gpu/dxgkrnl/Kconfig new file mode 100644 index 000000000000..8bfec781b9a7 --- /dev/null +++ b/drivers/gpu/dxgkrnl/Kconfig @@ -0,0 +1,10 @@ +# +# dxgkrnl configuration +# + +config DXGKRNL + tristate "Microsoft virtual GPU support" + depends on HYPERV + help + This driver supports Microsoft virtual GPU. + diff --git a/drivers/gpu/dxgkrnl/Makefile b/drivers/gpu/dxgkrnl/Makefile new file mode 100644 index 000000000000..11505a153d9d --- /dev/null +++ b/drivers/gpu/dxgkrnl/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for the Linux video drivers. +# 5 Aug 1999, James Simmons, <mailto:jsimmons@xxxxxxxxxxxx> +# Rewritten to use lists instead of if-statements. + +# Each configuration option enables a list of files. + +# Uncomment to enable printing debug messages by default +#ccflags-y := -DDEBUG + +obj-$(CONFIG_DXGKRNL) += dxgkrnl.o +dxgkrnl-y := dxgmodule.o hmgr.o misc.o dxgadapter.o ioctl.o dxgvmbus.o dxgprocess.o diff --git a/drivers/gpu/dxgkrnl/d3dkmthk.h b/drivers/gpu/dxgkrnl/d3dkmthk.h new file mode 100644 index 000000000000..ed9a72986e57 --- /dev/null +++ b/drivers/gpu/dxgkrnl/d3dkmthk.h @@ -0,0 +1,1635 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver user mode interface + * + */ + +#ifndef _D3DKMTHK_H +#define _D3DKMTHK_H + +#include "misc.h" + +#define D3DDDI_MAX_WRITTEN_PRIMARIES 16 +#define D3DDDI_MAX_MPO_PRESENT_DIRTY_RECTS 0xFFF + +#define D3DKMT_CREATEALLOCATION_MAX 1024 +#define D3DKMT_ADAPTERS_MAX 64 +#define D3DDDI_MAX_BROADCAST_CONTEXT 64 +#define D3DDDI_MAX_OBJECT_WAITED_ON 32 +#define D3DDDI_MAX_OBJECT_SIGNALED 32 + +struct d3dkmt_adapterinfo { + d3dkmt_handle adapter_handle; + struct winluid adapter_luid; + uint num_sources; + winbool present_move_regions_preferred; +}; + +struct d3dkmt_enumadapters2 { + uint num_adapters; + struct d3dkmt_adapterinfo *adapters; +}; + +struct d3dkmt_closeadapter { + d3dkmt_handle adapter_handle; +}; + +struct d3dkmt_openadapterfromluid { + struct winluid adapter_luid; + d3dkmt_handle adapter_handle; +}; + +struct d3dddi_allocationlist { + d3dkmt_handle allocation; + union { + struct { + uint write_operation :1; + uint do_not_retire_instance :1; + uint offer_priority :3; + uint reserved :27; + }; + uint value; + }; +}; + +struct d3dddi_patchlocationlist { + uint allocation_index; + union { + struct { + uint slot_id:24; + uint reserved:8; + }; + uint value; + }; + uint driver_id; + uint allocation_offset; + uint patch_offset; + uint split_offset; +}; + +struct d3dkmt_createdeviceflags { + uint legacy_mode:1; + uint request_vSync:1; + uint disable_gpu_timeout:1; + uint reserved:29; +}; + +struct d3dkmt_createdevice { + union { + d3dkmt_handle adapter; + void *adapter_pointer; + }; + struct d3dkmt_createdeviceflags flags; + d3dkmt_handle device; + void *command_buffer; + uint command_buffer_size; + struct d3dddi_allocationlist *allocation_list; + uint allocation_list_size; + struct d3dddi_patchlocationlist *patch_location_list; + uint patch_location_list_size; +}; + +struct d3dkmt_destroydevice { + d3dkmt_handle device; +}; + +enum d3dkmt_clienthint { + D3DKMT_CLIENTHINT_UNKNOWN = 0, + D3DKMT_CLIENTHINT_OPENGL = 1, + D3DKMT_CLIENTHINT_CDD = 2, + D3DKMT_CLIENTHINT_DX7 = 7, + D3DKMT_CLIENTHINT_DX8 = 8, + D3DKMT_CLIENTHINT_DX9 = 9, + D3DKMT_CLIENTHINT_DX10 = 10, +}; + +struct d3dddi_createcontextflags { + union { + struct { + uint null_rendering:1; + uint initial_data:1; + uint disable_gpu_timeout:1; + uint synchronization_only:1; + uint hw_queue_supported:1; + uint reserved:27; + }; + uint value; + }; +}; + +struct d3dkmt_createcontext { + d3dkmt_handle device; + uint node_ordinal; + uint engine_affinity; + struct d3dddi_createcontextflags flags; + void *priv_drv_data; + uint priv_drv_data_size; + enum d3dkmt_clienthint client_hint; + d3dkmt_handle context; + void *command_buffer; + uint command_buffer_size; + struct d3dddi_allocationlist *allocation_list; + uint allocation_list_size; + struct d3dddi_patchlocationlist *patch_location_list; + uint patch_location_list_size; + d3dgpu_virtual_address obsolete; +}; + +struct d3dkmt_destroycontext { + d3dkmt_handle context; +}; + +struct d3dkmt_createcontextvirtual { + d3dkmt_handle device; + uint node_ordinal; + uint engine_affinity; + struct d3dddi_createcontextflags flags; + void *priv_drv_data; + uint priv_drv_data_size; + enum d3dkmt_clienthint client_hint; + d3dkmt_handle context; +}; + +struct d3dddi_createhwcontextflags { + union { + struct { + uint reserved:32; + }; + uint value; + }; +}; + +struct d3dddi_createhwqueueflags { + union { + struct { + uint disable_gpu_timeout:1; + uint reserved:31; + }; + uint value; + }; +}; + +enum d3dddi_pagingqueue_priority { + D3DDDI_PAGINGQUEUE_PRIORITY_BELOW_NORMAL = -1, + D3DDDI_PAGINGQUEUE_PRIORITY_NORMAL = 0, + D3DDDI_PAGINGQUEUE_PRIORITY_ABOVE_NORMAL = 1, +}; + +struct d3dkmt_createpagingqueue { + d3dkmt_handle device; + enum d3dddi_pagingqueue_priority priority; + d3dkmt_handle paging_queue; + d3dkmt_handle sync_object; + void *fence_cpu_virtual_address; + uint physical_adapter_index; +}; + +struct d3dddi_destroypagingqueue { + d3dkmt_handle paging_queue; +}; + +struct d3dkmt_renderflags { + uint resize_command_buffer:1; + uint resize_allocation_list:1; + uint resize_patch_location_list:1; + uint null_rendering:1; + uint present_redirected:1; + uint render_km:1; + uint render_km_readback:1; + uint reserved:25; +}; +struct d3dkmt_render { + union { + d3dkmt_handle device; + d3dkmt_handle context; + }; + uint command_offset; + uint command_length; + uint allocation_count; + uint patch_location_count; + void *new_command_buffer; + uint new_command_buffer_size; + struct d3dddi_allocationlist *new_allocation_list; + uint new_allocation_list_size; + struct d3dddi_patchlocationlist *new_patch_pocation_list; + uint new_patch_pocation_list_size; + struct d3dkmt_renderflags flags; + uint64_t present_history_token; + uint broadcast_context_count; + d3dkmt_handle broadcast_context[D3DDDI_MAX_BROADCAST_CONTEXT]; + uint queued_buffer_count; + d3dgpu_virtual_address obsolete; + void *priv_drv_data; + uint priv_drv_data_size; +}; + +enum d3dkmt_standardallocationtype { + D3DKMT_STANDARDALLOCATIONTYPE_EXISTINGHEAP = 1, +}; + +struct d3dkmt_standardallocation_existingheap { + uint64_t size; +}; + +struct d3dkmt_createstandardallocationflags { + union { + struct { + uint reserved:32; + }; + uint value; + }; +}; + +struct d3dkmt_createstandardallocation { + enum d3dkmt_standardallocationtype type; + struct d3dkmt_standardallocation_existingheap existing_heap_data; + struct d3dkmt_createstandardallocationflags flags; +}; + +struct d3dddi_allocationinfo2 { + d3dkmt_handle allocation; + union { + winhandle section; + const void *sysmem; + }; + void *priv_drv_data; + uint priv_drv_data_size; + uint vidpn_source_id; + union { + struct { + uint primary:1; + uint stereo:1; + uint override_priority:1; + uint reserved:29; + }; + uint value; + } flags; + d3dgpu_virtual_address gpu_virtual_address; + union { + uint priority; + uint64_t unused; + }; + uint64_t reserved[5]; +}; + +struct d3dkmt_createallocationflags { + union { + struct { + uint create_resource:1; + uint create_shared:1; + uint non_secure:1; + uint create_protected:1; + uint restrict_shared_access:1; + uint existing_sysmem:1; + uint nt_security_sharing:1; + uint read_only:1; + uint create_write_combined:1; + uint create_cached:1; + uint swap_chain_back_buffer:1; + uint cross_adapter:1; + uint open_cross_adapter:1; + uint partial_shared_creation:1; + uint zeroed:1; + uint write_watch:1; + uint standard_allocation:1; + uint existing_section:1; + uint reserved:14; + }; + uint value; + }; +}; + +struct d3dkmt_createallocation { + d3dkmt_handle device; + d3dkmt_handle resource; + d3dkmt_handle global_share; + const void *private_runtime_data; + uint private_runtime_data_size; + union { + struct d3dkmt_createstandardallocation *standard_allocation; + const void *priv_drv_data; + }; + uint priv_drv_data_size; + uint alloc_count; + struct d3dddi_allocationinfo2 *allocation_info; + struct d3dkmt_createallocationflags flags; + winhandle private_runtime_resource_handle; +}; + +struct d3dddicb_destroyallocation2flags { + union { + struct { + uint assume_not_in_use:1; + uint synchronous_destroy:1; + uint reserved:29; + uint system_use_only:1; + }; + uint value; + }; +}; + +struct d3dkmt_destroyallocation2 { + d3dkmt_handle device; + d3dkmt_handle resource; + const d3dkmt_handle *allocations; + uint alloc_count; + struct d3dddicb_destroyallocation2flags flags; +}; + +struct d3dddi_makeresident_flags { + union { + struct { + uint cant_trim_further:1; + uint must_succeed:1; + uint reserved:30; + }; + uint value; + }; +}; + +struct d3dddi_makeresident { + d3dkmt_handle paging_queue; + uint alloc_count; + const d3dkmt_handle *allocation_list; + const uint *priority_list; + struct d3dddi_makeresident_flags flags; + uint64_t paging_fence_value; + uint64_t num_bytes_to_trim; +}; + +struct d3dddi_evict_flags { + union { + struct { + uint evict_only_if_necessary:1; + uint not_written_to:1; + uint reserved:30; + }; + uint value; + }; +}; + +struct d3dkmt_evict { + d3dkmt_handle device; + uint alloc_count; + const d3dkmt_handle *allocations; + struct d3dddi_evict_flags flags; + uint64_t num_bytes_to_trim; +}; + +struct d3dddigpuva_protection_type { + union { + struct { + uint64_t write:1; + uint64_t execute:1; + uint64_t zero:1; + uint64_t no_access:1; + uint64_t system_use_only:1; + uint64_t reserved:59; + }; + uint64_t value; + }; +}; + +enum d3dddi_updategpuvirtualaddress_operation_type { + D3DDDI_UPDATEGPUVIRTUALADDRESS_MAP = 0, + D3DDDI_UPDATEGPUVIRTUALADDRESS_UNMAP = 1, + D3DDDI_UPDATEGPUVIRTUALADDRESS_COPY = 2, + D3DDDI_UPDATEGPUVIRTUALADDRESS_MAP_PROTECT = 3, +}; + +struct d3dddi_updategpuvirtualaddress_operation { + enum d3dddi_updategpuvirtualaddress_operation_type operation; + union { + struct { + d3dgpu_virtual_address base_address; + d3dgpu_size_t size; + d3dkmt_handle allocation; + d3dgpu_size_t allocation_offset; + d3dgpu_size_t allocation_size; + } map; + struct { + d3dgpu_virtual_address base_address; + d3dgpu_size_t size; + d3dkmt_handle allocation; + d3dgpu_size_t allocation_offset; + d3dgpu_size_t allocation_size; + struct d3dddigpuva_protection_type protection; + uint64_t driver_protection; + } map_protect; + struct { + d3dgpu_virtual_address base_address; + d3dgpu_size_t size; + struct d3dddigpuva_protection_type protection; + } unmap; + struct { + d3dgpu_virtual_address source_address; + d3dgpu_size_t size; + d3dgpu_virtual_address dest_address; + } copy; + }; +}; + +enum d3dddigpuva_reservation_type { + D3DDDIGPUVA_RESERVE_NO_ACCESS = 0, + D3DDDIGPUVA_RESERVE_ZERO = 1, + D3DDDIGPUVA_RESERVE_NO_COMMIT = 2 +}; + +struct d3dkmt_updategpuvirtualaddress { + d3dkmt_handle device; + d3dkmt_handle context; + d3dkmt_handle fence_object; + uint num_operations; + struct d3dddi_updategpuvirtualaddress_operation *operations; + uint reserved0; + uint64_t reserved1; + uint64_t fence_value; + union { + struct { + uint do_not_wait:1; + uint reserved:31; + }; + uint value; + } flags; +}; + +struct d3dddi_mapgpuvirtualaddress { + d3dkmt_handle paging_queue; + d3dgpu_virtual_address base_address; + d3dgpu_virtual_address minimum_address; + d3dgpu_virtual_address maximum_address; + d3dkmt_handle allocation; + d3dgpu_size_t offset_in_pages; + d3dgpu_size_t size_in_pages; + struct d3dddigpuva_protection_type protection; + uint64_t driver_protection; + uint reserved0; + uint64_t reserved1; + d3dgpu_virtual_address virtual_address; + uint64_t paging_fence_value; +}; + +struct d3dddi_reservegpuvirtualaddress { + d3dkmt_handle adapter; + d3dgpu_virtual_address base_address; + d3dgpu_virtual_address minimum_address; + d3dgpu_virtual_address maximum_address; + d3dgpu_size_t size; + enum d3dddigpuva_reservation_type reservation_type; + uint64_t driver_protection; + d3dgpu_virtual_address virtual_address; + uint64_t paging_fence_value; +}; + +struct d3dkmt_freegpuvirtualaddress { + d3dkmt_handle adapter; + d3dgpu_virtual_address base_address; + d3dgpu_size_t size; +}; + +enum d3dkmt_memory_segment_group { + D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL = 0, + D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL = 1 +}; + +struct d3dkmt_queryvideomemoryinfo { + winhandle process; + d3dkmt_handle adapter; + enum d3dkmt_memory_segment_group memory_segment_group; + uint64_t budget; + uint64_t current_usage; + uint64_t current_reservation; + uint64_t available_for_reservation; + uint physical_adapter_index; +}; + +enum qai_driverversion { + KMT_DRIVERVERSION_WDDM_1_0 = 1000, + KMT_DRIVERVERSION_WDDM_1_1_PRERELEASE = 1102, + KMT_DRIVERVERSION_WDDM_1_1 = 1105, + KMT_DRIVERVERSION_WDDM_1_2 = 1200, + KMT_DRIVERVERSION_WDDM_1_3 = 1300, + KMT_DRIVERVERSION_WDDM_2_0 = 2000, + KMT_DRIVERVERSION_WDDM_2_1 = 2100, + KMT_DRIVERVERSION_WDDM_2_2 = 2200, + KMT_DRIVERVERSION_WDDM_2_3 = 2300, + KMT_DRIVERVERSION_WDDM_2_4 = 2400, + KMT_DRIVERVERSION_WDDM_2_5 = 2500, + KMT_DRIVERVERSION_WDDM_2_6 = 2600, + KMT_DRIVERVERSION_WDDM_2_7 = 2700 +}; + +struct d3dkmt_adaptertype { + union { + struct { + uint render_supported:1; + uint display_supported:1; + uint software_device:1; + uint post_device:1; + uint hybrid_discrete:1; + uint hybrid_integrated:1; + uint indirect_display_device:1; + uint paravirtualized:1; + uint acg_supported:1; + uint support_set_timings_from_vidpn:1; + uint detachable:1; + uint compute_only:1; + uint prototype:1; + uint reserved:19; + }; + uint value; + }; +}; + +enum kmtqueryadapterinfotype { + KMTQAITYPE_UMDRIVERPRIVATE = 0, + KMTQAITYPE_UMDRIVERNAME = 1, + KMTQAITYPE_UMOPENGLINFO = 2, + KMTQAITYPE_GETSEGMENTSIZE = 3, + KMTQAITYPE_ADAPTERGUID = 4, + KMTQAITYPE_FLIPQUEUEINFO = 5, + KMTQAITYPE_ADAPTERADDRESS = 6, + KMTQAITYPE_SETWORKINGSETINFO = 7, + KMTQAITYPE_ADAPTERREGISTRYINFO = 8, + KMTQAITYPE_CURRENTDISPLAYMODE = 9, + KMTQAITYPE_MODELIST = 10, + KMTQAITYPE_CHECKDRIVERUPDATESTATUS = 11, + KMTQAITYPE_VIRTUALADDRESSINFO = 12, + KMTQAITYPE_DRIVERVERSION = 13, + KMTQAITYPE_ADAPTERTYPE = 15, + KMTQAITYPE_OUTPUTDUPLCONTEXTSCOUNT = 16, + KMTQAITYPE_WDDM_1_2_CAPS = 17, + KMTQAITYPE_UMD_DRIVER_VERSION = 18, + KMTQAITYPE_DIRECTFLIP_SUPPORT = 19, + KMTQAITYPE_MULTIPLANEOVERLAY_SUPPORT = 20, + KMTQAITYPE_DLIST_DRIVER_NAME = 21, + KMTQAITYPE_WDDM_1_3_CAPS = 22, + KMTQAITYPE_MULTIPLANEOVERLAY_HUD_SUPPORT = 23, + KMTQAITYPE_WDDM_2_0_CAPS = 24, + KMTQAITYPE_NODEMETADATA = 25, + KMTQAITYPE_CPDRIVERNAME = 26, + KMTQAITYPE_XBOX = 27, + KMTQAITYPE_INDEPENDENTFLIP_SUPPORT = 28, + KMTQAITYPE_MIRACASTCOMPANIONDRIVERNAME = 29, + KMTQAITYPE_PHYSICALADAPTERCOUNT = 30, + KMTQAITYPE_PHYSICALADAPTERDEVICEIDS = 31, + KMTQAITYPE_DRIVERCAPS_EXT = 32, + KMTQAITYPE_QUERY_MIRACAST_DRIVER_TYPE = 33, + KMTQAITYPE_QUERY_GPUMMU_CAPS = 34, + KMTQAITYPE_QUERY_MULTIPLANEOVERLAY_DECODE_SUPPORT = 35, + KMTQAITYPE_QUERY_HW_PROTECTION_TEARDOWN_COUNT = 36, + KMTQAITYPE_QUERY_ISBADDRIVERFORHWPROTECTIONDISABLED = 37, + KMTQAITYPE_MULTIPLANEOVERLAY_SECONDARY_SUPPORT = 38, + KMTQAITYPE_INDEPENDENTFLIP_SECONDARY_SUPPORT = 39, + KMTQAITYPE_PANELFITTER_SUPPORT = 40, + KMTQAITYPE_PHYSICALADAPTERPNPKEY = 41, + KMTQAITYPE_GETSEGMENTGROUPSIZE = 42, + KMTQAITYPE_MPO3DDI_SUPPORT = 43, + KMTQAITYPE_HWDRM_SUPPORT = 44, + KMTQAITYPE_MPOKERNELCAPS_SUPPORT = 45, + KMTQAITYPE_MULTIPLANEOVERLAY_STRETCH_SUPPORT = 46, + KMTQAITYPE_GET_DEVICE_VIDPN_OWNERSHIP_INFO = 47, + KMTQAITYPE_QUERYREGISTRY = 48, + KMTQAITYPE_KMD_DRIVER_VERSION = 49, + KMTQAITYPE_BLOCKLIST_KERNEL = 50, + KMTQAITYPE_BLOCKLIST_RUNTIME = 51, + KMTQAITYPE_ADAPTERGUID_RENDER = 52, + KMTQAITYPE_ADAPTERADDRESS_RENDER = 53, + KMTQAITYPE_ADAPTERREGISTRYINFO_RENDER = 54, + KMTQAITYPE_CHECKDRIVERUPDATESTATUS_RENDER = 55, + KMTQAITYPE_DRIVERVERSION_RENDER = 56, + KMTQAITYPE_ADAPTERTYPE_RENDER = 57, + KMTQAITYPE_WDDM_1_2_CAPS_RENDER = 58, + KMTQAITYPE_WDDM_1_3_CAPS_RENDER = 59, + KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID = 60, + KMTQAITYPE_NODEPERFDATA = 61, + KMTQAITYPE_ADAPTERPERFDATA = 62, + KMTQAITYPE_ADAPTERPERFDATA_CAPS = 63, + KMTQUITYPE_GPUVERSION = 64, + KMTQAITYPE_DRIVER_DESCRIPTION = 65, + KMTQAITYPE_DRIVER_DESCRIPTION_RENDER = 66, + KMTQAITYPE_SCANOUT_CAPS = 67, + KMTQAITYPE_PARAVIRTUALIZATION_RENDER = 68, +}; + +struct d3dkmt_queryadapterinfo { + d3dkmt_handle adapter; + enum kmtqueryadapterinfotype type; + void *private_data; + uint private_data_size; +}; + +enum d3dkmt_escapetype { + D3DKMT_ESCAPE_DRIVERPRIVATE = 0, + D3DKMT_ESCAPE_VIDMM = 1, + D3DKMT_ESCAPE_VIDSCH = 3, + D3DKMT_ESCAPE_DEVICE = 4, + D3DKMT_ESCAPE_DRT_TEST = 8, +}; + +enum d3dkmt_drt_test_command { + D3DKMT_DRT_TEST_COMMAND_HANDLETABLE = 39, +}; + +struct d3dkmt_drt_escape_head { + uint signature; + uint buffer_size; + enum d3dkmt_drt_test_command command; +}; + +enum d3dkmt_ht_command { + D3DKMT_HT_COMMAND_ALLOC, + D3DKMT_HT_COMMAND_FREE, + D3DKMT_HT_COMMAND_ASSIGN, + D3DKMT_HT_COMMAND_GET, + D3DKMT_HT_COMMAND_DESTROY, +}; + +struct d3dkmt_ht_desc { + struct d3dkmt_drt_escape_head head; + enum d3dkmt_ht_command command; + uint index; + d3dkmt_handle handle; + uint object_type; + void *object; +}; + +struct d3dddi_escapeflags { + union { + struct { + uint hardware_access:1; + uint device_status_query:1; + uint change_frame_latency:1; + uint no_adapter_synchronization:1; + uint reserved:1; + uint virtual_machine_data:1; + uint driver_known_escape:1; + uint driver_common_escape:1; + uint reserved2:24; + }; + uint value; + }; +}; + +struct d3dkmt_escape { + d3dkmt_handle adapter; + d3dkmt_handle device; + enum d3dkmt_escapetype type; + struct d3dddi_escapeflags flags; + void *priv_drv_data; + uint priv_drv_data_size; + d3dkmt_handle context; +}; + +enum dxgk_render_pipeline_stage { + DXGK_RENDER_PIPELINE_STAGE_UNKNOWN = 0, + DXGK_RENDER_PIPELINE_STAGE_INPUT_ASSEMBLER = 1, + DXGK_RENDER_PIPELINE_STAGE_VERTEX_SHADER = 2, + DXGK_RENDER_PIPELINE_STAGE_GEOMETRY_SHADER = 3, + DXGK_RENDER_PIPELINE_STAGE_STREAM_OUTPUT = 4, + DXGK_RENDER_PIPELINE_STAGE_RASTERIZER = 5, + DXGK_RENDER_PIPELINE_STAGE_PIXEL_SHADER = 6, + DXGK_RENDER_PIPELINE_STAGE_OUTPUT_MERGER = 7, +}; + +enum dxgk_page_fault_flags { + DXGK_PAGE_FAULT_WRITE = 0x1, + DXGK_PAGE_FAULT_FENCE_INVALID = 0x2, + DXGK_PAGE_FAULT_ADAPTER_RESET_REQUIRED = 0x4, + DXGK_PAGE_FAULT_ENGINE_RESET_REQUIRED = 0x8, + DXGK_PAGE_FAULT_FATAL_HARDWARE_ERROR = 0x10, + DXGK_PAGE_FAULT_IOMMU = 0x20, + DXGK_PAGE_FAULT_HW_CONTEXT_VALID = 0x40, + DXGK_PAGE_FAULT_PROCESS_HANDLE_VALID = 0x80, +}; + +enum dxgk_general_error_code { + DXGK_GENERAL_ERROR_PAGE_FAULT = 0, + DXGK_GENERAL_ERROR_INVALID_INSTRUCTION = 1, +}; + +struct dxgk_fault_error_code { + union { + struct { + uint is_device_specific_code:1; + enum dxgk_general_error_code general_error_code:31; + }; + struct { + uint is_device_specific_code_reserved_bit:1; + uint device_specific_code:31; + }; + }; +}; + +enum d3dkmt_deviceexecution_state { + D3DKMT_DEVICEEXECUTION_ACTIVE = 1, + D3DKMT_DEVICEEXECUTION_RESET = 2, + D3DKMT_DEVICEEXECUTION_HUNG = 3, + D3DKMT_DEVICEEXECUTION_STOPPED = 4, + D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY = 5, + D3DKMT_DEVICEEXECUTION_ERROR_DMAFAULT = 6, + D3DKMT_DEVICEEXECUTION_ERROR_DMAPAGEFAULT = 7, +}; + +struct d3dkmt_devicereset_state { + union { + struct { + uint desktop_switched:1; + uint reserved:31; + }; + uint value; + }; +}; + +struct d3dkmt_present_stats { + uint present_count; + uint present_refresh_count; + uint sync_refresh_count; + uint64_t sync_qpc_time; + uint64_t sync_gpu_time; +}; + +struct d3dkmt_devicepresent_state { + uint vidpn_source_id; + struct d3dkmt_present_stats present_stats; +}; + +struct d3dkmt_present_stats_dwm { + uint present_count; + uint present_refresh_count; + uint64_t present_qpc_time; + uint sync_refresh_count; + uint64_t sync_qpc_time; + uint custom_present_duration; +}; + +struct d3dkmt_devicepagefault_state { + uint64_t faulted_primitive_api_sequence_number; + enum dxgk_render_pipeline_stage faulted_pipeline_stage; + uint faulted_bind_table_entry; + enum dxgk_page_fault_flags page_fault_flags; + struct dxgk_fault_error_code fault_error_code; + d3dgpu_virtual_address faulted_virtual_address; +}; + +struct d3dkmt_devicepresent_state_dwm { + uint vidpn_source_id; + struct d3dkmt_present_stats_dwm present_stats; +}; + +struct d3dkmt_devicepresent_queue_state { + uint vidpn_source_id; + bool bQueuedPresentLimitReached; +}; + +enum d3dkmt_devicestate_type { + D3DKMT_DEVICESTATE_EXECUTION = 1, + D3DKMT_DEVICESTATE_PRESENT = 2, + D3DKMT_DEVICESTATE_RESET = 3, + D3DKMT_DEVICESTATE_PRESENT_DWM = 4, + D3DKMT_DEVICESTATE_PAGE_FAULT = 5, + D3DKMT_DEVICESTATE_PRESENT_QUEUE = 6, +}; + +struct d3dkmt_getdevicestate { + d3dkmt_handle device; + enum d3dkmt_devicestate_type state_type; + union { + enum d3dkmt_deviceexecution_state execution_state; + struct d3dkmt_devicepresent_state present_state; + struct d3dkmt_devicereset_state reset_state; + struct d3dkmt_devicepresent_state_dwm present_state_dwm; + struct d3dkmt_devicepagefault_state page_fault_state; + struct d3dkmt_devicepresent_queue_state present_queue_state; + }; +}; + +enum d3dkmdt_gdisurfacetype { + D3DKMDT_GDISURFACE_INVALID = 0, + D3DKMDT_GDISURFACE_TEXTURE = 1, + D3DKMDT_GDISURFACE_STAGING_CPUVISIBLE = 2, + D3DKMDT_GDISURFACE_STAGING = 3, + D3DKMDT_GDISURFACE_LOOKUPTABLE = 4, + D3DKMDT_GDISURFACE_EXISTINGSYSMEM = 5, + D3DKMDT_GDISURFACE_TEXTURE_CPUVISIBLE = 6, + D3DKMDT_GDISURFACE_TEXTURE_CROSSADAPTER = 7, + D3DKMDT_GDISURFACE_TEXTURE_CPUVISIBLE_CROSSADAPTER = 8, +}; + +struct d3dddi_rational { + uint numerator; + uint denominator; +}; + +enum d3dddiformat { + D3DDDIFMT_UNKNOWN = 0, +}; + +struct d3dkmdt_gdisurfacedata { + uint width; + uint height; + uint format; + enum d3dkmdt_gdisurfacetype type; + uint flags; + uint pitch; +}; + +struct d3dkmtd_stagingsurfacedata { + uint width; + uint height; + uint pitch; +}; + +struct d3dkmdt_sharedprimarysurfacedata { + uint width; + uint height; + enum d3dddiformat format; + struct d3dddi_rational refresh_rate; + uint vidpn_source_id; +}; + +struct d3dkmdt_shadowsurfacedata { + uint width; + uint height; + enum d3dddiformat format; + uint pitch; +}; + +enum d3dkmdt_standardallocationtype { + D3DKMDT_STANDARDALLOCATION_SHAREDPRIMARYSURFACE = 1, + D3DKMDT_STANDARDALLOCATION_SHADOWSURFACE = 2, + D3DKMDT_STANDARDALLOCATION_STAGINGSURFACE = 3, + D3DKMDT_STANDARDALLOCATION_GDISURFACE = 4, +}; + +struct d3dddi_synchronizationobject_flags { + union { + struct { + uint shared:1; + uint nt_security_sharing:1; + uint cross_adapter:1; + uint top_of_pipeline:1; + uint no_signal:1; + uint no_wait:1; + uint no_signal_max_value_on_tdr:1; + uint no_gpu_access:1; + uint reserved:23; + }; + uint value; + }; +}; + +enum d3dddi_synchronizationobject_type { + D3DDDI_SYNCHRONIZATION_MUTEX = 1, + D3DDDI_SEMAPHORE = 2, + D3DDDI_FENCE = 3, + D3DDDI_CPU_NOTIFICATION = 4, + D3DDDI_MONITORED_FENCE = 5, + D3DDDI_PERIODIC_MONITORED_FENCE = 6, + D3DDDI_SYNCHRONIZATION_TYPE_LIMIT +}; + +struct d3dddi_synchronizationobjectinfo2 { + enum d3dddi_synchronizationobject_type type; + struct d3dddi_synchronizationobject_flags flags; + union { + struct { + winbool initial_state; + } synchronization_mutex; + + struct { + uint max_count; + uint initial_count; + } semaphore; + + struct { + uint64_t fence_value; + } fence; + + struct { + winhandle event; + } cpu_notification; + + struct { + uint64_t initial_fence_value; + void *fence_cpu_virtual_address; + d3dgpu_virtual_address fence_gpu_virtual_address; + uint engine_affinity; + } monitored_fence; + + struct periodic_monitored_fence_t { + d3dkmt_handle adapter; + uint vidpn_target_id; + uint64_t time; + void *fence_cpu_virtual_address; + d3dgpu_virtual_address fence_gpu_virtual_address; + uint engine_affinity; + } periodic_monitored_fence; + + struct { + uint64_t reserved[8]; + } reserved; + }; + d3dkmt_handle shared_handle; +}; + +struct d3dkmt_createsynchronizationobject2 { + d3dkmt_handle device; + struct d3dddi_synchronizationobjectinfo2 info; + d3dkmt_handle sync_object; +}; + +struct d3dkmt_waitforsynchronizationobject2 { + d3dkmt_handle context; + uint object_count; + d3dkmt_handle object_array[D3DDDI_MAX_OBJECT_WAITED_ON]; + union { + struct { + uint64_t fence_value; + } fence; + uint64_t reserved[8]; + }; +}; + +struct d3dddicb_signalflags { + union { + struct { + uint signal_at_submission:1; + uint enqueue_cpu_event:1; + uint allow_fence_rewind:1; + uint reserved:28; + uint DXGK_SIGNAL_FLAG_INTERNAL0:1; + }; + uint value; + }; +}; + +struct d3dkmt_signalsynchronizationobject2 { + d3dkmt_handle context; + uint object_count; + d3dkmt_handle object_array[D3DDDI_MAX_OBJECT_SIGNALED]; + struct d3dddicb_signalflags flags; + uint context_count; + d3dkmt_handle contexts[D3DDDI_MAX_BROADCAST_CONTEXT]; + union { + struct { + uint64_t fence_value; + } fence; + winhandle cpu_event_handle; + uint64_t reserved[8]; + }; +}; + +struct d3dddi_waitforsynchronizationobjectfromcpu_flags { + union { + struct { + uint wait_any:1; + uint reserved:31; + }; + uint value; + }; +}; + +struct d3dkmt_waitforsynchronizationobjectfromcpu { + d3dkmt_handle device; + uint object_count; + d3dkmt_handle *objects; + uint64_t *fence_values; + winhandle async_event; + struct d3dddi_waitforsynchronizationobjectfromcpu_flags flags; +}; + +struct d3dkmt_signalsynchronizationobjectfromcpu { + d3dkmt_handle device; + uint object_count; + d3dkmt_handle *objects; + uint64_t *fence_values; + struct d3dddicb_signalflags flags; +}; + +struct d3dkmt_waitforsynchronizationobjectfromgpu { + d3dkmt_handle context; + uint object_count; + d3dkmt_handle *objects; + union { + uint64_t *monitored_fence_values; + uint64_t fence_value; + uint64_t reserved[8]; + }; +}; + +struct d3dkmt_signalsynchronizationobjectfromgpu { + d3dkmt_handle context; + uint object_count; + d3dkmt_handle *objects; + union { + uint64_t *monitored_fence_values; + uint64_t reserved[8]; + }; +}; + +struct d3dkmt_signalsynchronizationobjectfromgpu2 { + uint object_count; + d3dkmt_handle *objects; + struct d3dddicb_signalflags flags; + uint context_count; + d3dkmt_handle *contexts; + union { + uint64_t fence_value; + winhandle cpu_event_handle; + uint64_t *monitored_fence_values; + uint64_t reserved[8]; + }; +}; + +struct d3dkmt_destroysynchronizationobject { + d3dkmt_handle sync_object; +}; + +struct d3dkmt_opensynchronizationobject { + d3dkmt_handle shared_handle; + d3dkmt_handle sync_object; + uint64_t reserved[8]; +}; + +struct d3dkmt_submitcommandflags { + uint null_rendering:1; + uint present_redirected:1; + uint reserved:30; +}; + +struct d3dkmt_submitcommand { + d3dgpu_virtual_address command_buffer; + uint command_length; + struct d3dkmt_submitcommandflags flags; + uint64_t present_history_token; + uint broadcast_context_count; + d3dkmt_handle broadcast_context[D3DDDI_MAX_BROADCAST_CONTEXT]; + void *priv_drv_data; + uint priv_drv_data_size; + uint num_primaries; + d3dkmt_handle written_primaries[D3DDDI_MAX_WRITTEN_PRIMARIES]; + uint num_history_buffers; + d3dkmt_handle *history_buffer_array; +}; + +struct d3dkmt_submitcommandtohwqueue { + d3dkmt_handle hwqueue; + uint64_t hwqueue_progress_fence_id; + d3dgpu_virtual_address command_buffer; + uint command_length; + uint priv_drv_data_size; + void *priv_drv_data; + uint num_primaries; + d3dkmt_handle *written_primaries; +}; + +struct d3dkmt_setcontextschedulingpriority { + d3dkmt_handle context; + int priority; +}; + +struct d3dkmt_setcontextinprocessschedulingpriority { + d3dkmt_handle context; + int priority; +}; + +struct d3dkmt_getcontextschedulingpriority { + d3dkmt_handle context; + int priority; +}; + +struct d3dkmt_getcontextinprocessschedulingpriority { + d3dkmt_handle context; + int priority; +}; + +struct d3dkmt_setallocationpriority { + d3dkmt_handle device; + d3dkmt_handle resource; + const d3dkmt_handle *allocation_list; + uint allocation_count; + const uint *priorities; +}; + +struct d3dkmt_getallocationpriority { + d3dkmt_handle device; + d3dkmt_handle resource; + const d3dkmt_handle *allocation_list; + uint allocation_count; + uint *priorities; +}; + +enum d3dkmt_allocationresidencystatus { + D3DKMT_ALLOCATIONRESIDENCYSTATUS_RESIDENTINGPUMEMORY = 1, + D3DKMT_ALLOCATIONRESIDENCYSTATUS_RESIDENTINSHAREDMEMORY = 2, + D3DKMT_ALLOCATIONRESIDENCYSTATUS_NOTRESIDENT = 3, +}; + +struct d3dkmt_queryallocationresidency { + d3dkmt_handle device; + d3dkmt_handle resource; + d3dkmt_handle *allocations; + uint allocation_count; + enum d3dkmt_allocationresidencystatus *residency_status; +}; + +struct D3DDDICB_LOCK2FLAGS { + union { + struct { + uint reserved:32; + }; + uint value; + }; +}; + +struct d3dkmt_lock2 { + d3dkmt_handle device; + d3dkmt_handle allocation; + struct D3DDDICB_LOCK2FLAGS flags; + void *data; +}; + +struct d3dkmt_unlock2 { + d3dkmt_handle device; + d3dkmt_handle allocation; +}; + +enum D3DKMT_DEVICE_ERROR_REASON { + D3DKMT_DEVICE_ERROR_REASON_GENERIC = 0x80000000, + D3DKMT_DEVICE_ERROR_REASON_DRIVER_ERROR = 0x80000006, +}; + +struct d3dkmt_markdeviceaserror { + d3dkmt_handle device; + enum D3DKMT_DEVICE_ERROR_REASON reason; +}; + +struct D3DDDI_UPDATEALLOCPROPERTY_FLAGS { + union { + struct { + uint accessed_physically:1; + uint reserved:31; + }; + uint value; + }; +}; + +struct D3DDDI_SEGMENTPREFERENCE { + union { + struct { + uint segment_id0:5; + uint direction0:1; + uint segment_id1:5; + uint direction1:1; + uint segment_id2:5; + uint direction2:1; + uint segment_id3:5; + uint direction3:1; + uint segment_id4:5; + uint direction4:1; + uint reserved:2; + }; + uint value; + }; +}; + +struct d3dddi_updateallocproperty { + d3dkmt_handle paging_queue; + d3dkmt_handle allocation; + uint supported_segment_set; + struct D3DDDI_SEGMENTPREFERENCE preferred_segment; + struct D3DDDI_UPDATEALLOCPROPERTY_FLAGS flags; + uint64_t paging_fence_value; + union { + struct { + uint set_accessed_physically:1; + uint set_supported_segmentSet:1; + uint set_preferred_segment:1; + uint reserved:29; + }; + uint property_mask_value; + }; +}; + +enum d3dkmt_offer_priority { + D3DKMT_OFFER_PRIORITY_LOW = 1, + D3DKMT_OFFER_PRIORITY_NORMAL = 2, + D3DKMT_OFFER_PRIORITY_HIGH = 3, + D3DKMT_OFFER_PRIORITY_AUTO = 4, +}; + +struct d3dkmt_offer_flags { + union { + struct { + uint offer_immediately:1; + uint allow_decommit:1; + uint reserved:30; + }; + uint value; + }; +}; + +struct d3dkmt_offerallocations { + d3dkmt_handle device; + d3dkmt_handle *resources; + const d3dkmt_handle *allocations; + uint allocation_count; + enum d3dkmt_offer_priority priority; + struct d3dkmt_offer_flags flags; +}; + +enum d3dddi_reclaim_result { + D3DDDI_RECLAIM_RESULT_OK = 0, + D3DDDI_RECLAIM_RESULT_DISCARDED = 1, + D3DDDI_RECLAIM_RESULT_NOT_COMMITTED = 2, +}; + +struct d3dkmt_reclaimallocations2 { + d3dkmt_handle paging_queue; + uint allocation_count; + d3dkmt_handle *resources; + d3dkmt_handle *allocations; + union { + winbool *discarded; + enum d3dddi_reclaim_result *results; + }; + uint64_t paging_fence_value; +}; + +struct d3dkmt_changevideomemoryreservation { + winhandle process; + d3dkmt_handle adapter; + enum d3dkmt_memory_segment_group memory_segment_group; + uint64_t reservation; + uint physical_adapter_index; +}; + +struct d3dkmt_createhwcontext { + d3dkmt_handle device; + uint node_ordinal; + uint engine_affinity; + struct d3dddi_createhwcontextflags flags; + uint priv_drv_data_size; + void *priv_drv_data; + d3dkmt_handle context; +}; + +struct d3dkmt_destroyhwcontext { + d3dkmt_handle context; +}; + +struct d3dkmt_createhwqueue { + d3dkmt_handle context; + struct d3dddi_createhwqueueflags flags; + uint priv_drv_data_size; + void *priv_drv_data; + d3dkmt_handle queue; + d3dkmt_handle queue_progress_fence; + void *queue_progress_fence_cpu_va; + d3dgpu_virtual_address queue_progress_fence_gpu_va; +}; + +struct d3dkmt_destroyhwqueue { + d3dkmt_handle queue; +}; + +struct d3dkmt_submitwaitforsyncobjectstohwqueue { + d3dkmt_handle hwqueue; + uint object_count; + d3dkmt_handle *objects; + uint64_t *fence_values; +}; + +struct d3dkmt_submitsignalsyncobjectstohwqueue { + struct d3dddicb_signalflags flags; + uint hwqueue_count; + d3dkmt_handle *hwqueues; + uint object_count; + d3dkmt_handle *objects; + uint64_t *fence_values; +}; + +struct dxgk_gpuclockdata_flags { + union { + struct { + uint context_management_processor:1; + uint reserved:31; + }; + uint value; + }; +}; + +struct dxgk_gpuclockdata { + uint64_t gpu_frequency; + uint64_t gpu_clock_counter; + uint64_t cpu_clock_counter; + struct dxgk_gpuclockdata_flags flags; +} __packed; + +struct d3dkmt_queryclockcalibration { + d3dkmt_handle adapter; + uint node_ordinal; + uint physical_adapter_index; + struct dxgk_gpuclockdata clock_data; +}; + +struct d3dkmt_flushheaptransitions { + d3dkmt_handle adapter; +}; + +struct d3dkmt_getsharedresourceadapterluid { + d3dkmt_handle global_share; + winhandle handle; + struct winluid adapter_luid; +}; + +struct d3dkmt_invalidatecache { + d3dkmt_handle device; + d3dkmt_handle allocation; + uint64_t offset; + uint64_t length; +}; + +struct d3dddi_openallocationinfo2 { + d3dkmt_handle allocation; + void *priv_drv_data; + uint priv_drv_data_size; + d3dgpu_virtual_address gpu_va; + uint64_t reserved[6]; +}; + +struct d3dkmt_opensyncobjectfromnthandle { + winhandle nt_handle; + d3dkmt_handle sync_object; +}; + +struct d3dkmt_opensyncobjectfromnthandle2 { + winhandle nt_handle; + d3dkmt_handle device; + struct d3dddi_synchronizationobject_flags flags; + d3dkmt_handle sync_object; + union { + struct { + void *fence_value_cpu_va; + d3dgpu_virtual_address fence_value_gpu_va; + uint engine_affinity; + } monitored_fence; + uint64_t reserved[8]; + }; +}; + +struct d3dkmt_openresource { + d3dkmt_handle device; + d3dkmt_handle global_share; + uint allocation_count; + struct d3dddi_openallocationinfo2 *open_alloc_info; + void *private_runtime_data; + int private_runtime_data_size; + void *resource_priv_drv_data; + uint resource_priv_drv_data_size; + void *total_priv_drv_data; + uint total_priv_drv_data_size; + d3dkmt_handle resource; +}; + +struct d3dkmt_openresourcefromnthandle { + d3dkmt_handle device; + winhandle nt_handle; + uint allocation_count; + struct d3dddi_openallocationinfo2 *open_alloc_info; + int private_runtime_data_size; + void *private_runtime_data; + uint resource_priv_drv_data_size; + void *resource_priv_drv_data; + uint total_priv_drv_data_size; + void *total_priv_drv_data; + d3dkmt_handle resource; + d3dkmt_handle keyed_mutex; + void *keyed_mutex_private_data; + uint keyed_mutex_private_data_size; + d3dkmt_handle sync_object; +}; + +struct d3dkmt_queryresourceinfofromnthandle { + d3dkmt_handle device; + winhandle nt_handle; + void *private_runtime_data; + uint private_runtime_data_size; + uint total_priv_drv_data_size; + uint resource_priv_drv_data_size; + uint allocation_count; +}; + +struct d3dkmt_queryresourceinfo { + d3dkmt_handle device; + d3dkmt_handle global_share; + void *private_runtime_data; + uint private_runtime_data_size; + uint total_priv_drv_data_size; + uint resource_priv_drv_data_size; + uint allocation_count; +}; + +struct d3dkmt_shareobjects { + uint object_count; + const d3dkmt_handle *objects; /* per-process DXG handle */ + void *object_attr; /* security attributes */ + uint desired_access; + winhandle *shared_handle; /* output file descriptor */ +}; + +union d3dkmt_enumadapters_filter { + struct { + uint64_t include_compute_only:1; + uint64_t include_display_only:1; + uint64_t reserved:62; + }; + uint64_t value; +}; + +struct d3dkmt_enumadapters3 { + union d3dkmt_enumadapters_filter filter; + uint adapter_count; + struct d3dkmt_adapterinfo *adapters; +}; + +/* + * Dxgkrnl Graphics Port Driver ioctl definitions + * + */ + +#define LX_IOCTL_DIR_WRITE 0x1 +#define LX_IOCTL_DIR_READ 0x2 + +#define LX_IOCTL_DIR(_ioctl) (((_ioctl) >> 30) & 0x3) +#define LX_IOCTL_SIZE(_ioctl) (((_ioctl) >> 16) & 0x3FFF) +#define LX_IOCTL_TYPE(_ioctl) (((_ioctl) >> 8) & 0xFF) +#define LX_IOCTL_CODE(_ioctl) (((_ioctl) >> 0) & 0xFF) + +#define LX_IOCTL(_dir, _size, _type, _code) ( \ + (((uint)(_dir) & 0x3) << 30) | \ + (((uint)(_size) & 0x3FFF) << 16) | \ + (((uint)(_type) & 0xFF) << 8) | \ + (((uint)(_code) & 0xFF) << 0)) + +#define LX_IO(_type, _code) LX_IOCTL(0, 0, (_type), (_code)) +#define LX_IOR(_type, _code, _size) \ + LX_IOCTL(LX_IOCTL_DIR_READ, (_size), (_type), (_code)) +#define LX_IOW(_type, _code, _size) \ + LX_IOCTL(LX_IOCTL_DIR_WRITE, (_size), (_type), (_code)) +#define LX_IOWR(_type, _code, _size) \ + LX_IOCTL(LX_IOCTL_DIR_WRITE | \ + LX_IOCTL_DIR_READ, (_size), (_type), (_code)) + +#define LX_DXOPENADAPTERFROMLUID \ + LX_IOWR(0x47, 0x01, sizeof(struct d3dkmt_openadapterfromluid)) +#define LX_DXCREATEDEVICE \ + LX_IOWR(0x47, 0x02, sizeof(struct d3dkmt_createdevice)) +#define LX_DXCREATECONTEXT \ + LX_IOWR(0x47, 0x03, sizeof(struct d3dkmt_createcontext)) +#define LX_DXCREATECONTEXTVIRTUAL \ + LX_IOWR(0x47, 0x04, sizeof(struct d3dkmt_createcontextvirtual)) +#define LX_DXDESTROYCONTEXT \ + LX_IOWR(0x47, 0x05, sizeof(struct d3dkmt_destroycontext)) +#define LX_DXCREATEALLOCATION \ + LX_IOWR(0x47, 0x06, sizeof(struct d3dkmt_createallocation)) +#define LX_DXCREATEPAGINGQUEUE \ + LX_IOWR(0x47, 0x07, sizeof(struct d3dkmt_createpagingqueue)) +#define LX_DXRESERVEGPUVIRTUALADDRESS \ + LX_IOWR(0x47, 0x08, sizeof(struct d3dddi_reservegpuvirtualaddress)) +#define LX_DXQUERYADAPTERINFO \ + LX_IOWR(0x47, 0x09, sizeof(struct d3dkmt_queryadapterinfo)) +#define LX_DXQUERYVIDEOMEMORYINFO \ + LX_IOWR(0x47, 0x0a, sizeof(struct d3dkmt_queryvideomemoryinfo)) +#define LX_DXMAKERESIDENT \ + LX_IOWR(0x47, 0x0b, sizeof(struct d3dddi_makeresident)) +#define LX_DXMAPGPUVIRTUALADDRESS \ + LX_IOWR(0x47, 0x0c, sizeof(struct d3dddi_mapgpuvirtualaddress)) +#define LX_DXESCAPE \ + LX_IOWR(0x47, 0x0d, sizeof(struct d3dkmt_escape)) +#define LX_DXGETDEVICESTATE \ + LX_IOWR(0x47, 0x0e, sizeof(struct d3dkmt_getdevicestate)) +#define LX_DXSUBMITCOMMAND \ + LX_IOWR(0x47, 0x0f, sizeof(struct d3dkmt_submitcommand)) +#define LX_DXCREATESYNCHRONIZATIONOBJECT \ + LX_IOWR(0x47, 0x10, sizeof(struct d3dkmt_createsynchronizationobject2)) +#define LX_DXSIGNALSYNCHRONIZATIONOBJECT \ + LX_IOWR(0x47, 0x11, sizeof(struct d3dkmt_signalsynchronizationobject2)) +#define LX_DXWAITFORSYNCHRONIZATIONOBJECT \ + LX_IOWR(0x47, 0x12, sizeof(struct d3dkmt_waitforsynchronizationobject2)) +#define LX_DXDESTROYALLOCATION2 \ + LX_IOWR(0x47, 0x13, sizeof(struct d3dkmt_destroyallocation2)) +#define LX_DXENUMADAPTERS2 \ + LX_IOWR(0x47, 0x14, sizeof(struct d3dkmt_enumadapters2)) +#define LX_DXCLOSEADAPTER \ + LX_IOWR(0x47, 0x15, sizeof(struct d3dkmt_closeadapter)) +#define LX_DXCHANGEVIDEOMEMORYRESERVATION \ + LX_IOWR(0x47, 0x16, sizeof(struct d3dkmt_changevideomemoryreservation)) +#define LX_DXCREATEHWCONTEXT \ + LX_IOWR(0x47, 0x17, sizeof(struct d3dkmt_createhwcontext)) +#define LX_DXCREATEHWQUEUE \ + LX_IOWR(0x47, 0x18, sizeof(struct d3dkmt_createhwqueue)) +#define LX_DXDESTROYDEVICE \ + LX_IOWR(0x47, 0x19, sizeof(struct d3dkmt_destroydevice)) +#define LX_DXDESTROYHWCONTEXT \ + LX_IOWR(0x47, 0x1a, sizeof(struct d3dkmt_destroyhwcontext)) +#define LX_DXDESTROYHWQUEUE \ + LX_IOWR(0x47, 0x1b, sizeof(struct d3dkmt_destroyhwqueue)) +#define LX_DXDESTROYPAGINGQUEUE \ + LX_IOWR(0x47, 0x1c, sizeof(struct d3dddi_destroypagingqueue)) +#define LX_DXDESTROYSYNCHRONIZATIONOBJECT \ + LX_IOWR(0x47, 0x1d, sizeof(struct d3dkmt_destroysynchronizationobject)) +#define LX_DXEVICT \ + LX_IOWR(0x47, 0x1e, sizeof(struct d3dkmt_evict)) +#define LX_DXFLUSHHEAPTRANSITIONS \ + LX_IOWR(0x47, 0x1f, sizeof(struct d3dkmt_flushheaptransitions)) +#define LX_DXFREEGPUVIRTUALADDRESS \ + LX_IOWR(0x47, 0x20, sizeof(struct d3dkmt_freegpuvirtualaddress)) +#define LX_DXGETCONTEXTINPROCESSSCHEDULINGPRIORITY \ + LX_IOWR(0x47, 0x21, \ + sizeof(struct d3dkmt_getcontextinprocessschedulingpriority)) +#define LX_DXGETCONTEXTSCHEDULINGPRIORITY \ + LX_IOWR(0x47, 0x22, sizeof(struct d3dkmt_getcontextschedulingpriority)) +#define LX_DXGETSHAREDRESOURCEADAPTERLUID \ + LX_IOWR(0x47, 0x23, sizeof(struct d3dkmt_getsharedresourceadapterluid)) +#define LX_DXINVALIDATECACHE \ + LX_IOWR(0x47, 0x24, sizeof(struct d3dkmt_invalidatecache)) +#define LX_DXLOCK2 \ + LX_IOWR(0x47, 0x25, sizeof(struct d3dkmt_lock2)) +#define LX_DXMARKDEVICEASERROR \ + LX_IOWR(0x47, 0x26, sizeof(struct d3dkmt_markdeviceaserror)) +#define LX_DXOFFERALLOCATIONS \ + LX_IOWR(0x47, 0x27, sizeof(struct d3dkmt_offerallocations)) +#define LX_DXOPENRESOURCE \ + LX_IOWR(0x47, 0x28, sizeof(struct d3dkmt_openresource)) +#define LX_DXOPENSYNCHRONIZATIONOBJECT \ + LX_IOWR(0x47, 0x29, sizeof(struct d3dkmt_opensynchronizationobject)) +#define LX_DXQUERYALLOCATIONRESIDENCY \ + LX_IOWR(0x47, 0x2a, sizeof(struct d3dkmt_queryallocationresidency)) +#define LX_DXQUERYRESOURCEINFO \ + LX_IOWR(0x47, 0x2b, sizeof(struct d3dkmt_queryresourceinfo)) +#define LX_DXRECLAIMALLOCATIONS2 \ + LX_IOWR(0x47, 0x2c, sizeof(struct d3dkmt_reclaimallocations2)) +#define LX_DXRENDER \ + LX_IOWR(0x47, 0x2d, sizeof(struct d3dkmt_render)) +#define LX_DXSETALLOCATIONPRIORITY \ + LX_IOWR(0x47, 0x2e, sizeof(struct d3dkmt_setallocationpriority)) +#define LX_DXSETCONTEXTINPROCESSSCHEDULINGPRIORITY \ + LX_IOWR(0x47, 0x2f, \ + sizeof(struct d3dkmt_setcontextinprocessschedulingpriority)) +#define LX_DXSETCONTEXTSCHEDULINGPRIORITY \ + LX_IOWR(0x47, 0x30, sizeof(struct d3dkmt_setcontextschedulingpriority)) +#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU \ + LX_IOWR(0x47, 0x31, \ + sizeof(struct d3dkmt_signalsynchronizationobjectfromcpu)) +#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU \ + LX_IOWR(0x47, 0x32, \ + sizeof(struct d3dkmt_signalsynchronizationobjectfromgpu)) +#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2 \ + LX_IOWR(0x47, 0x33, \ + sizeof(struct d3dkmt_signalsynchronizationobjectfromgpu2)) +#define LX_DXSUBMITCOMMANDTOHWQUEUE \ + LX_IOWR(0x47, 0x34, sizeof(struct d3dkmt_submitcommandtohwqueue)) +#define LX_DXSUBMITSIGNALSYNCOBJECTSTOHWQUEUE \ + LX_IOWR(0x47, 0x35, \ + sizeof(struct d3dkmt_submitsignalsyncobjectstohwqueue)) +#define LX_DXSUBMITWAITFORSYNCOBJECTSTOHWQUEUE \ + LX_IOWR(0x47, 0x36, \ + sizeof(struct d3dkmt_submitwaitforsyncobjectstohwqueue)) +#define LX_DXUNLOCK2 \ + LX_IOWR(0x47, 0x37, sizeof(struct d3dkmt_unlock2)) +#define LX_DXUPDATEALLOCPROPERTY \ + LX_IOWR(0x47, 0x38, sizeof(struct d3dddi_updateallocproperty)) +#define LX_DXUPDATEGPUVIRTUALADDRESS \ + LX_IOWR(0x47, 0x39, sizeof(struct d3dkmt_updategpuvirtualaddress)) +#define LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMCPU \ + LX_IOWR(0x47, 0x3a, \ + sizeof(struct d3dkmt_waitforsynchronizationobjectfromcpu)) +#define LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU \ + LX_IOWR(0x47, 0x3b, \ + sizeof(struct d3dkmt_waitforsynchronizationobjectfromgpu)) +#define LX_DXGETALLOCATIONPRIORITY \ + LX_IOWR(0x47, 0x3c, sizeof(struct d3dkmt_getallocationpriority)) +#define LX_DXQUERYCLOCKCALIBRATION \ + LX_IOWR(0x47, 0x3d, sizeof(struct d3dkmt_queryclockcalibration)) +#define LX_DXENUMADAPTERS3 \ + LX_IOWR(0x47, 0x3e, sizeof(struct d3dkmt_enumadapters3)) +#define LX_DXSHAREOBJECTS \ + LX_IOWR(0x47, 0x3f, sizeof(struct d3dkmt_shareobjects)) +#define LX_DXOPENSYNCOBJECTFROMNTHANDLE2 \ + LX_IOWR(0x47, 0x40, sizeof(struct d3dkmt_opensyncobjectfromnthandle2)) +#define LX_DXQUERYRESOURCEINFOFROMNTHANDLE \ + LX_IOWR(0x47, 0x41, sizeof(struct d3dkmt_queryresourceinfofromnthandle)) +#define LX_DXOPENRESOURCEFROMNTHANDLE \ + LX_IOWR(0x47, 0x42, sizeof(struct d3dkmt_openresourcefromnthandle)) + +#define LX_IO_MAX 0x42 + +#endif /* _D3DKMTHK_H */ diff --git a/drivers/gpu/dxgkrnl/dxgadapter.c b/drivers/gpu/dxgkrnl/dxgadapter.c new file mode 100644 index 000000000000..aa50aa5335b3 --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgadapter.c @@ -0,0 +1,1399 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver + * dxgadapter implementation + * + */ + +#include <linux/module.h> +#include <linux/hyperv.h> +#include <linux/pagemap.h> +#include <linux/eventfd.h> + +#include "dxgkrnl.h" + +int dxgadapter_init(struct dxgadapter *adapter, struct hv_device *hdev) +{ + int ret = 0; + char s[80]; + + UNUSED(s); + guid_to_luid(&hdev->channel->offermsg.offer.if_instance, + &adapter->luid); + TRACE_DEBUG(1, "%s: %x:%x %p %pUb\n", + __func__, adapter->luid.b, adapter->luid.a, hdev->channel, + &hdev->channel->offermsg.offer.if_instance); + + adapter->adapter_state = DXGADAPTER_STATE_STOPPED; + refcount_set(&adapter->refcount, 1); + init_rwsem(&adapter->core_lock); + + INIT_LIST_HEAD(&adapter->adapter_process_list_head); + INIT_LIST_HEAD(&adapter->shared_resource_list_head); + INIT_LIST_HEAD(&adapter->adapter_shared_syncobj_list_head); + INIT_LIST_HEAD(&adapter->syncobj_list_head); + init_rwsem(&adapter->shared_resource_list_lock); + + ret = dxgvmbuschannel_init(&adapter->channel, hdev); + if (ret) + goto cleanup; + + adapter->channel.adapter = adapter; + + ret = dxgvmb_send_open_adapter(adapter); + if (ret) { + pr_err("dxgvmb_send_open_adapter failed: %d\n", ret); + goto cleanup; + } + + adapter->adapter_state = DXGADAPTER_STATE_ACTIVE; + + ret = dxgvmb_send_get_internal_adapter_info(adapter); + if (ret) { + pr_err("get_internal_adapter_info failed: %d\n", ret); + goto cleanup; + } + +cleanup: + + return ret; +} + +void dxgadapter_stop(struct dxgadapter *adapter) +{ + struct dxgprocess_adapter *entry; + + dxgglobal_acquire_process_adapter_lock(); + + list_for_each_entry(entry, &adapter->adapter_process_list_head, + adapter_process_list_entry) { + dxgprocess_adapter_stop(entry); + } + + dxgglobal_release_process_adapter_lock(); + + if (dxgadapter_acquire_lock_exclusive(adapter) == 0) { + dxgvmb_send_close_adapter(adapter); + dxgadapter_release_lock_exclusive(adapter); + } + dxgvmbuschannel_destroy(&adapter->channel); +} + +void dxgadapter_destroy(struct dxgadapter *adapter) +{ + TRACE_DEBUG(1, "%s %p\n", __func__, adapter); + dxgmem_free(NULL, DXGMEM_ADAPTER, adapter); +} + +bool dxgadapter_acquire_reference(struct dxgadapter *adapter) +{ + return refcount_inc_not_zero(&adapter->refcount); +} + +void dxgadapter_release_reference(struct dxgadapter *adapter) +{ + if (refcount_dec_and_test(&adapter->refcount)) + dxgadapter_destroy(adapter); +} + +bool dxgadapter_is_active(struct dxgadapter *adapter) +{ + return adapter->adapter_state == DXGADAPTER_STATE_ACTIVE; +} + +/* Protected by dxgglobal_acquire_process_adapter_lock */ +void dxgadapter_add_process(struct dxgadapter *adapter, + struct dxgprocess_adapter *process_info) +{ + TRACE_DEBUG(1, "%s %p %p", __func__, adapter, process_info); + list_add_tail(&process_info->adapter_process_list_entry, + &adapter->adapter_process_list_head); +} + +void dxgadapter_remove_process(struct dxgprocess_adapter *process_info) +{ + TRACE_DEBUG(1, "%s %p %p", __func__, + process_info->adapter, process_info); + list_del(&process_info->adapter_process_list_entry); + process_info->adapter_process_list_entry.next = NULL; + process_info->adapter_process_list_entry.prev = NULL; +} + +void dxgadapter_add_shared_resource(struct dxgadapter *adapter, + struct dxgsharedresource *object) +{ + /* Adapter lock is held */ + down_write(&adapter->shared_resource_list_lock); + list_add_tail(&object->shared_resource_list_entry, + &adapter->shared_resource_list_head); + up_write(&adapter->shared_resource_list_lock); +} + +void dxgadapter_remove_shared_resource(struct dxgadapter *adapter, + struct dxgsharedresource *object) +{ + down_write(&adapter->shared_resource_list_lock); + if (object->shared_resource_list_entry.next) { + list_del(&object->shared_resource_list_entry); + object->shared_resource_list_entry.next = NULL; + } + up_write(&adapter->shared_resource_list_lock); +} + +void dxgadapter_add_shared_syncobj(struct dxgadapter *adapter, + struct dxgsharedsyncobject *object) +{ + down_write(&adapter->shared_resource_list_lock); + list_add_tail(&object->adapter_shared_syncobj_list_entry, + &adapter->adapter_shared_syncobj_list_head); + up_write(&adapter->shared_resource_list_lock); +} + +void dxgadapter_remove_shared_syncobj(struct dxgadapter *adapter, + struct dxgsharedsyncobject *object) +{ + down_write(&adapter->shared_resource_list_lock); + if (object->adapter_shared_syncobj_list_entry.next) { + list_del(&object->adapter_shared_syncobj_list_entry); + object->adapter_shared_syncobj_list_entry.next = NULL; + } + up_write(&adapter->shared_resource_list_lock); +} + +void dxgadapter_add_syncobj(struct dxgadapter *adapter, + struct dxgsyncobject *object) +{ + down_write(&adapter->shared_resource_list_lock); + list_add_tail(&object->syncobj_list_entry, &adapter->syncobj_list_head); + up_write(&adapter->shared_resource_list_lock); +} + +void dxgadapter_remove_syncobj(struct dxgsyncobject *object) +{ + down_write(&object->adapter->shared_resource_list_lock); + if (object->syncobj_list_entry.next) { + list_del(&object->syncobj_list_entry); + object->syncobj_list_entry.next = NULL; + } + up_write(&object->adapter->shared_resource_list_lock); +} + +int dxgadapter_acquire_lock_exclusive(struct dxgadapter *adapter) +{ + TRACE_DEBUG(1, "%s", __func__); + dxglockorder_acquire(DXGLOCK_ADAPTER); + down_write(&adapter->core_lock); + if (adapter->adapter_state != DXGADAPTER_STATE_ACTIVE) { + dxgadapter_release_lock_exclusive(adapter); + return STATUS_DEVICE_REMOVED; + } + return 0; +} + +void dxgadapter_acquire_lock_forced(struct dxgadapter *adapter) +{ + dxglockorder_acquire(DXGLOCK_ADAPTER); + down_write(&adapter->core_lock); +} + +void dxgadapter_release_lock_exclusive(struct dxgadapter *adapter) +{ + TRACE_DEBUG(1, "%s", __func__); + up_write(&adapter->core_lock); + dxglockorder_release(DXGLOCK_ADAPTER); +} + +int dxgadapter_acquire_lock_shared(struct dxgadapter *adapter) +{ + TRACE_DEBUG(1, "%s", __func__); + dxglockorder_acquire(DXGLOCK_ADAPTER); + down_read(&adapter->core_lock); + if (adapter->adapter_state == DXGADAPTER_STATE_ACTIVE) + return 0; + dxgadapter_release_lock_shared(adapter); + return STATUS_DEVICE_REMOVED; +} + +void dxgadapter_release_lock_shared(struct dxgadapter *adapter) +{ + TRACE_DEBUG(1, "dxgadapter_release_lock\n"); + up_read(&adapter->core_lock); + dxglockorder_release(DXGLOCK_ADAPTER); +} + +struct dxgdevice *dxgdevice_create(struct dxgadapter *adapter, + struct dxgprocess *process) +{ + struct dxgdevice *device = dxgmem_alloc(process, DXGMEM_DEVICE, + sizeof(struct dxgdevice)); + if (device) { + refcount_set(&device->refcount, 1); + device->adapter = adapter; + device->process = process; + dxgadapter_acquire_reference(adapter); + INIT_LIST_HEAD(&device->context_list_head); + INIT_LIST_HEAD(&device->alloc_list_head); + INIT_LIST_HEAD(&device->resource_list_head); + init_rwsem(&device->device_lock); + init_rwsem(&device->context_list_lock); + init_rwsem(&device->alloc_list_lock); + INIT_LIST_HEAD(&device->pqueue_list_head); + INIT_LIST_HEAD(&device->syncobj_list_head); + device->object_state = DXGOBJECTSTATE_CREATED; + + dxgprocess_adapter_add_device(process, adapter, device); + } + return device; +} + +void dxgdevice_stop(struct dxgdevice *device) +{ + struct dxgallocation *alloc; + struct dxgpagingqueue *pqueue; + struct dxgsyncobject *syncobj; + + TRACE_DEBUG(1, "%s: DXGKDEBUG %p", __func__, device); + dxgdevice_acquire_alloc_list_lock(device); + list_for_each_entry(alloc, &device->alloc_list_head, alloc_list_entry) { + dxgallocation_stop(alloc); + } + dxgdevice_release_alloc_list_lock(device); + + hmgrtable_lock(&device->process->handle_table, DXGLOCK_EXCL); + list_for_each_entry(pqueue, &device->pqueue_list_head, + pqueue_list_entry) { + dxgpagingqueue_stop(pqueue); + } + list_for_each_entry(syncobj, &device->syncobj_list_head, + syncobj_list_entry) { + dxgsyncobject_stop(syncobj); + } + hmgrtable_unlock(&device->process->handle_table, DXGLOCK_EXCL); + TRACE_DEBUG(1, "%s: end %p\n", __func__, device); +} + +void dxgdevice_mark_destroyed(struct dxgdevice *device) +{ + down_write(&device->device_lock); + device->object_state = DXGOBJECTSTATE_DESTROYED; + up_write(&device->device_lock); +} + +void dxgdevice_destroy(struct dxgdevice *device) +{ + struct dxgprocess *process = device->process; + struct dxgadapter *adapter = device->adapter; + d3dkmt_handle device_handle = 0; + + TRACE_DEBUG(1, "%s: %p\n", __func__, device); + + down_write(&device->device_lock); + + if (device->object_state != DXGOBJECTSTATE_ACTIVE) + goto cleanup; + + device->object_state = DXGOBJECTSTATE_DESTROYED; + + dxgdevice_stop(device); + + dxgdevice_acquire_alloc_list_lock(device); + + while (!list_empty(&device->syncobj_list_head)) { + struct dxgsyncobject *syncobj = + list_first_entry(&device->syncobj_list_head, + struct dxgsyncobject, + syncobj_list_entry); + list_del(&syncobj->syncobj_list_entry); + syncobj->syncobj_list_entry.next = NULL; + dxgdevice_release_alloc_list_lock(device); + + dxgsyncobject_destroy(process, syncobj); + + dxgdevice_acquire_alloc_list_lock(device); + } + + { + struct dxgallocation *alloc; + struct dxgallocation *tmp; + + TRACE_DEBUG(1, "destroying allocations\n"); + list_for_each_entry_safe(alloc, tmp, &device->alloc_list_head, + alloc_list_entry) { + dxgallocation_destroy(alloc); + } + } + + { + struct dxgresource *resource; + struct dxgresource *tmp; + + TRACE_DEBUG(1, "destroying resources\n"); + list_for_each_entry_safe(resource, tmp, + &device->resource_list_head, + resource_list_entry) { + dxgresource_destroy(resource); + } + } + + dxgdevice_release_alloc_list_lock(device); + + { + struct dxgcontext *context; + struct dxgcontext *tmp; + + TRACE_DEBUG(1, "destroying contexts\n"); + dxgdevice_acquire_context_list_lock(device); + list_for_each_entry_safe(context, tmp, + &device->context_list_head, + context_list_entry) { + dxgcontext_destroy(process, context); + } + dxgdevice_release_context_list_lock(device); + } + + { + struct dxgpagingqueue *tmp; + struct dxgpagingqueue *pqueue; + + TRACE_DEBUG(1, "destroying paging queues\n"); + list_for_each_entry_safe(pqueue, tmp, &device->pqueue_list_head, + pqueue_list_entry) { + dxgpagingqueue_destroy(pqueue); + } + } + + /* Guest handles need to be released before the host handles */ + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + if (device->handle_valid) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGDEVICE, device->handle); + device_handle = device->handle; + device->handle_valid = 0; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (device_handle) { + up_write(&device->device_lock); + if (dxgadapter_acquire_lock_shared(adapter) == 0) { + dxgvmb_send_destroy_device(adapter, process, + device_handle); + dxgadapter_release_lock_shared(adapter); + } + down_write(&device->device_lock); + } + +cleanup: + + if (device->adapter) { + dxgprocess_adapter_remove_device(device); + dxgadapter_release_reference(device->adapter); + } + + up_write(&device->device_lock); + + dxgdevice_release_reference(device); + TRACE_DEBUG(1, "dxgdevice_destroy_end\n"); +} + +int dxgdevice_acquire_lock_shared(struct dxgdevice *device) +{ + down_read(&device->device_lock); + if (!dxgdevice_is_active(device)) { + up_read(&device->device_lock); + return STATUS_DEVICE_REMOVED; + } + return 0; +} + +void dxgdevice_release_lock_shared(struct dxgdevice *device) +{ + up_read(&device->device_lock); +} + +bool dxgdevice_is_active(struct dxgdevice *device) +{ + return device->object_state == DXGOBJECTSTATE_ACTIVE; +} + +void dxgdevice_acquire_context_list_lock(struct dxgdevice *device) +{ + dxglockorder_acquire(DXGLOCK_DEVICE_CONTEXTLIST); + down_write(&device->context_list_lock); +} + +void dxgdevice_release_context_list_lock(struct dxgdevice *device) +{ + up_write(&device->context_list_lock); + dxglockorder_release(DXGLOCK_DEVICE_CONTEXTLIST); +} + +void dxgdevice_acquire_alloc_list_lock(struct dxgdevice *device) +{ + dxglockorder_acquire(DXGLOCK_DEVICE_ALLOCLIST); + down_write(&device->alloc_list_lock); +} + +void dxgdevice_release_alloc_list_lock(struct dxgdevice *device) +{ + dxglockorder_release(DXGLOCK_DEVICE_ALLOCLIST); + up_write(&device->alloc_list_lock); +} + +void dxgdevice_acquire_alloc_list_lock_shared(struct dxgdevice *device) +{ + dxglockorder_acquire(DXGLOCK_DEVICE_ALLOCLIST); + down_read(&device->alloc_list_lock); +} + +void dxgdevice_release_alloc_list_lock_shared(struct dxgdevice *device) +{ + dxglockorder_release(DXGLOCK_DEVICE_ALLOCLIST); + up_read(&device->alloc_list_lock); +} + +void dxgdevice_add_context(struct dxgdevice *device, struct dxgcontext *context) +{ + down_write(&device->context_list_lock); + list_add_tail(&context->context_list_entry, &device->context_list_head); + up_write(&device->context_list_lock); +} + +void dxgdevice_remove_context(struct dxgdevice *device, + struct dxgcontext *context) +{ + if (context->context_list_entry.next) { + list_del(&context->context_list_entry); + context->context_list_entry.next = NULL; + } +} + +void dxgdevice_add_alloc(struct dxgdevice *device, struct dxgallocation *alloc) +{ + dxgdevice_acquire_alloc_list_lock(device); + list_add_tail(&alloc->alloc_list_entry, &device->alloc_list_head); + dxgdevice_acquire_reference(device); + alloc->owner.device = device; + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_remove_alloc(struct dxgdevice *device, + struct dxgallocation *alloc) +{ + if (alloc->alloc_list_entry.next) { + list_del(&alloc->alloc_list_entry); + alloc->alloc_list_entry.next = NULL; + dxgdevice_release_reference(device); + } +} + +void dxgdevice_remove_alloc_safe(struct dxgdevice *device, + struct dxgallocation *alloc) +{ + dxgdevice_acquire_alloc_list_lock(device); + dxgdevice_remove_alloc(device, alloc); + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_add_resource(struct dxgdevice *device, struct dxgresource *res) +{ + dxgdevice_acquire_alloc_list_lock(device); + list_add_tail(&res->resource_list_entry, &device->resource_list_head); + dxgdevice_acquire_reference(device); + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_remove_resource(struct dxgdevice *device, + struct dxgresource *res) +{ + if (res->resource_list_entry.next) { + list_del(&res->resource_list_entry); + res->resource_list_entry.next = NULL; + dxgdevice_release_reference(device); + } +} + +struct dxgsharedresource *dxgsharedresource_create(struct dxgadapter *adapter) +{ + struct dxgsharedresource *resource = dxgmem_alloc(NULL, + DXGMEM_SHAREDRESOURCE, + sizeof(struct + dxgsharedresource)); + if (resource) { + INIT_LIST_HEAD(&resource->resource_list_head); + refcount_set(&resource->refcount, 1); + dxgmutex_init(&resource->fd_mutex, DXGLOCK_FDMUTEX); + resource->adapter = adapter; + } + return resource; +} + +bool dxgsharedresource_acquire_reference(struct dxgsharedresource *resource) +{ + return refcount_inc_not_zero(&resource->refcount); +} + +void dxgsharedresource_release_reference(struct dxgsharedresource *resource) +{ + if (!refcount_dec_and_test(&resource->refcount)) + return; + if (resource->global_handle) + hmgrtable_free_handle_safe(&dxgglobal->handle_table, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE, + resource->global_handle); + if (resource->runtime_private_data) + dxgmem_free(NULL, DXGMEM_RUNTIMEPRIVATE, + resource->runtime_private_data); + if (resource->resource_private_data) + dxgmem_free(NULL, DXGMEM_RESOURCEPRIVATE, + resource->resource_private_data); + if (resource->alloc_private_data_sizes) + dxgmem_free(NULL, DXGMEM_ALLOCPRIVATE, + resource->alloc_private_data_sizes); + if (resource->alloc_private_data) + dxgmem_free(NULL, DXGMEM_ALLOCPRIVATE, + resource->alloc_private_data); + dxgmem_free(NULL, DXGMEM_SHAREDRESOURCE, resource); +} + +void dxgsharedresource_add_resource(struct dxgsharedresource *shared_resource, + struct dxgresource *resource) +{ + down_write(&shared_resource->adapter->shared_resource_list_lock); + TRACE_DEBUG(1, "%s: %p %p", __func__, shared_resource, resource); + list_add_tail(&resource->shared_resource_list_entry, + &shared_resource->resource_list_head); + dxgsharedresource_acquire_reference(shared_resource); + dxgresource_acquire_reference(resource); + resource->shared_owner = shared_resource; + up_write(&shared_resource->adapter->shared_resource_list_lock); +} + +void dxgsharedresource_remove_resource(struct dxgsharedresource + *shared_resource, + struct dxgresource *resource) +{ + down_write(&shared_resource->adapter->shared_resource_list_lock); + TRACE_DEBUG(1, "%s: %p %p", __func__, shared_resource, resource); + if (resource->shared_resource_list_entry.next) { + list_del(&resource->shared_resource_list_entry); + resource->shared_resource_list_entry.next = NULL; + dxgsharedresource_release_reference(shared_resource); + resource->shared_owner = NULL; + dxgresource_release_reference(resource); + } + up_write(&shared_resource->adapter->shared_resource_list_lock); +} + +struct dxgresource *dxgresource_create(struct dxgdevice *device) +{ + struct dxgresource *resource = dxgmem_alloc(device->process, + DXGMEM_RESOURCE, + sizeof(struct dxgresource)); + if (resource) { + refcount_set(&resource->refcount, 1); + resource->device = device; + resource->process = device->process; + resource->object_state = DXGOBJECTSTATE_ACTIVE; + dxgmutex_init(&resource->resource_mutex, DXGLOCK_RESOURCE); + INIT_LIST_HEAD(&resource->alloc_list_head); + dxgdevice_add_resource(device, resource); + } + return resource; +} + +void dxgresource_free_handle(struct dxgresource *resource) +{ + struct dxgallocation *alloc; + + if (resource->handle_valid) { + hmgrtable_free_handle_safe(&resource->device->process-> + handle_table, + HMGRENTRY_TYPE_DXGRESOURCE, + resource->handle); + resource->handle_valid = 0; + } + list_for_each_entry(alloc, &resource->alloc_list_head, alloc_list_entry) { + dxgallocation_free_handle(alloc); + } +} + +void dxgresource_destroy(struct dxgresource *resource) +{ + /* device->alloc_list_lock is held */ + struct dxgallocation *alloc; + struct dxgallocation *tmp; + struct d3dkmt_destroyallocation2 args = { }; + int destroyed = test_and_set_bit(0, &resource->flags); + struct dxgdevice *device = resource->device; + + if (!destroyed) { + dxgresource_free_handle(resource); + if (resource->handle) { + args.device = device->handle; + args.resource = resource->handle; + args.flags.assume_not_in_use = 1; + dxgvmb_send_destroy_allocation(device->process, + device, + &device->adapter-> + channel, &args, NULL); + resource->handle = 0; + } + list_for_each_entry_safe(alloc, tmp, &resource->alloc_list_head, + alloc_list_entry) { + dxgallocation_destroy(alloc); + } + dxgdevice_remove_resource(device, resource); + if (resource->shared_owner) { + dxgsharedresource_remove_resource(resource-> + shared_owner, + resource); + resource->shared_owner = NULL; + } + } + dxgresource_release_reference(resource); +} + +void dxgresource_acquire_reference(struct dxgresource *resource) +{ + refcount_inc_not_zero(&resource->refcount); +} + +void dxgresource_release_reference(struct dxgresource *resource) +{ + if (refcount_dec_and_test(&resource->refcount)) + dxgmem_free(resource->process, DXGMEM_RESOURCE, resource); +} + +bool dxgresource_is_active(struct dxgresource *resource) +{ + return resource->object_state == DXGOBJECTSTATE_ACTIVE; +} + +int dxgresource_add_alloc(struct dxgresource *resource, + struct dxgallocation *alloc) +{ + int ret = STATUS_DEVICE_REMOVED; + struct dxgdevice *device = resource->device; + + dxgdevice_acquire_alloc_list_lock(device); + if (dxgresource_is_active(resource)) { + list_add_tail(&alloc->alloc_list_entry, + &resource->alloc_list_head); + alloc->owner.resource = resource; + ret = 0; + } + alloc->resource_owner = 1; + dxgdevice_release_alloc_list_lock(device); + return ret; +} + +void dxgresource_remove_alloc(struct dxgresource *resource, + struct dxgallocation *alloc) +{ + if (alloc->alloc_list_entry.next) { + list_del(&alloc->alloc_list_entry); + alloc->alloc_list_entry.next = NULL; + } +} + +void dxgresource_remove_alloc_safe(struct dxgresource *resource, + struct dxgallocation *alloc) +{ + dxgdevice_acquire_alloc_list_lock(resource->device); + dxgresource_remove_alloc(resource, alloc); + dxgdevice_release_alloc_list_lock(resource->device); +} + +bool dxgdevice_acquire_reference(struct dxgdevice *device) +{ + return refcount_inc_not_zero(&device->refcount); +} + +void dxgdevice_release_reference(struct dxgdevice *device) +{ + if (refcount_dec_and_test(&device->refcount)) + dxgmem_free(device->process, DXGMEM_DEVICE, device); +} + +void dxgdevice_add_paging_queue(struct dxgdevice *device, + struct dxgpagingqueue *entry) +{ + dxgdevice_acquire_alloc_list_lock(device); + list_add_tail(&entry->pqueue_list_entry, &device->pqueue_list_head); + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_remove_paging_queue(struct dxgpagingqueue *pqueue) +{ + struct dxgdevice *device = pqueue->device; + + dxgdevice_acquire_alloc_list_lock(device); + if (pqueue->pqueue_list_entry.next) { + list_del(&pqueue->pqueue_list_entry); + pqueue->pqueue_list_entry.next = NULL; + } + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_add_syncobj(struct dxgdevice *device, + struct dxgsyncobject *syncobj) +{ + dxgdevice_acquire_alloc_list_lock(device); + list_add_tail(&syncobj->syncobj_list_entry, &device->syncobj_list_head); + dxgsyncobject_acquire_reference(syncobj); + dxgdevice_release_alloc_list_lock(device); +} + +void dxgdevice_remove_syncobj(struct dxgsyncobject *entry) +{ + struct dxgdevice *device = entry->device; + + dxgdevice_acquire_alloc_list_lock(device); + if (entry->syncobj_list_entry.next) { + list_del(&entry->syncobj_list_entry); + entry->syncobj_list_entry.next = NULL; + dxgsyncobject_release_reference(entry); + } + dxgdevice_release_alloc_list_lock(device); + dxgdevice_release_reference(device); + entry->device = NULL; +} + +struct dxgcontext *dxgcontext_create(struct dxgdevice *device) +{ + struct dxgcontext *context = dxgmem_alloc(device->process, + DXGMEM_CONTEXT, + sizeof(struct dxgcontext)); + if (context) { + refcount_set(&context->refcount, 1); + context->device = device; + context->process = device->process; + context->device_handle = device->handle; + dxgdevice_acquire_reference(device); + INIT_LIST_HEAD(&context->hwqueue_list_head); + init_rwsem(&context->hwqueue_list_lock); + dxgdevice_add_context(device, context); + context->object_state = DXGOBJECTSTATE_ACTIVE; + } + return context; +} + +/* + * Called when the device context list lock is held + */ +void dxgcontext_destroy(struct dxgprocess *process, struct dxgcontext *context) +{ + struct dxghwqueue *hwqueue; + struct dxghwqueue *tmp; + + TRACE_DEBUG(1, "%s %p\n", __func__, context); + context->object_state = DXGOBJECTSTATE_DESTROYED; + if (context->device) { + if (context->handle) { + hmgrtable_free_handle_safe(&context->process-> + handle_table, + HMGRENTRY_TYPE_DXGCONTEXT, + context->handle); + } + dxgdevice_remove_context(context->device, context); + dxgdevice_release_reference(context->device); + } + list_for_each_entry_safe(hwqueue, tmp, &context->hwqueue_list_head, + hwqueue_list_entry) { + dxghwqueue_destroy(process, hwqueue); + } + dxgcontext_release_reference(context); +} + +void dxgcontext_destroy_safe(struct dxgprocess *process, + struct dxgcontext *context) +{ + struct dxgdevice *device = context->device; + + dxgdevice_acquire_context_list_lock(device); + dxgcontext_destroy(process, context); + dxgdevice_release_context_list_lock(device); +} + +bool dxgcontext_is_active(struct dxgcontext *context) +{ + return context->object_state == DXGOBJECTSTATE_ACTIVE; +} + +bool dxgcontext_acquire_reference(struct dxgcontext *context) +{ + return refcount_inc_not_zero(&context->refcount); +} + +void dxgcontext_release_reference(struct dxgcontext *context) +{ + if (refcount_dec_and_test(&context->refcount)) + dxgmem_free(context->process, DXGMEM_CONTEXT, context); +} + +int dxgcontext_add_hwqueue(struct dxgcontext *context, + struct dxghwqueue *hwqueue) +{ + int ret = 0; + + down_write(&context->hwqueue_list_lock); + if (dxgcontext_is_active(context)) + list_add_tail(&hwqueue->hwqueue_list_entry, + &context->hwqueue_list_head); + else + ret = STATUS_DEVICE_REMOVED; + up_write(&context->hwqueue_list_lock); + return ret; +} + +void dxgcontext_remove_hwqueue(struct dxgcontext *context, + struct dxghwqueue *hwqueue) +{ + if (hwqueue->hwqueue_list_entry.next) { + list_del(&hwqueue->hwqueue_list_entry); + hwqueue->hwqueue_list_entry.next = NULL; + } +} + +void dxgcontext_remove_hwqueue_safe(struct dxgcontext *context, + struct dxghwqueue *hwqueue) +{ + down_write(&context->hwqueue_list_lock); + dxgcontext_remove_hwqueue(context, hwqueue); + up_write(&context->hwqueue_list_lock); +} + +struct dxgallocation *dxgallocation_create(struct dxgprocess *process) +{ + struct dxgallocation *alloc = dxgmem_alloc(process, DXGMEM_ALLOCATION, + sizeof(struct + dxgallocation)); + if (alloc) + alloc->process = process; + return alloc; +} + +void dxgallocation_stop(struct dxgallocation *alloc) +{ + if (alloc->pages) { + release_pages(alloc->pages, alloc->num_pages); + dxgmem_free(alloc->process, DXGMEM_ALLOCATION, alloc->pages); + alloc->pages = NULL; + } + dxgprocess_ht_lock_exclusive_down(alloc->process); + if (alloc->cpu_address_mapped) { + dxg_unmap_iospace(alloc->cpu_address, + alloc->num_pages << PAGE_SHIFT); + alloc->cpu_address_mapped = false; + alloc->cpu_address = NULL; + } + dxgprocess_ht_lock_exclusive_up(alloc->process); +} + +void dxgallocation_free_handle(struct dxgallocation *alloc) +{ + dxgprocess_ht_lock_exclusive_down(alloc->process); + if (alloc->handle_valid) { + hmgrtable_free_handle(&alloc->process->handle_table, + HMGRENTRY_TYPE_DXGALLOCATION, + alloc->alloc_handle); + alloc->handle_valid = 0; + } + dxgprocess_ht_lock_exclusive_up(alloc->process); +} + +void dxgallocation_destroy(struct dxgallocation *alloc) +{ + struct dxgprocess *process = alloc->process; + struct d3dkmt_destroyallocation2 args = { }; + + dxgallocation_stop(alloc); + if (alloc->resource_owner) + dxgresource_remove_alloc(alloc->owner.resource, alloc); + else if (alloc->owner.device) + dxgdevice_remove_alloc(alloc->owner.device, alloc); + dxgallocation_free_handle(alloc); + if (alloc->alloc_handle && !alloc->resource_owner) { + args.device = alloc->owner.device->handle; + args.alloc_count = 1; + args.flags.assume_not_in_use = 1; + dxgvmb_send_destroy_allocation(process, + alloc->owner.device, + &alloc->owner.device->adapter-> + channel, &args, + &alloc->alloc_handle); + } + if (alloc->gpadl) { + TRACE_DEBUG(1, "Teardown gpadl %d", alloc->gpadl); + vmbus_teardown_gpadl(dxgglobal_get_vmbus(), alloc->gpadl); + TRACE_DEBUG(1, "Teardown gpadl end"); + alloc->gpadl = 0; + } + if (alloc->priv_drv_data) + dxgmem_free(alloc->process, DXGMEM_ALLOCPRIVATE, + alloc->priv_drv_data); + if (alloc->cpu_address_mapped) + pr_err("Alloc IO space is mapped: %p", alloc); + dxgmem_free(alloc->process, DXGMEM_ALLOCATION, alloc); +} + +struct dxgpagingqueue *dxgpagingqueue_create(struct dxgdevice *device) +{ + struct dxgpagingqueue *pqueue; + + pqueue = dxgmem_alloc(device->process, DXGMEM_PQUEUE, sizeof(*pqueue)); + if (pqueue) { + pqueue->device = device; + pqueue->process = device->process; + pqueue->device_handle = device->handle; + dxgdevice_add_paging_queue(device, pqueue); + } + return pqueue; +} + +void dxgpagingqueue_stop(struct dxgpagingqueue *pqueue) +{ + if (pqueue->mapped_address) { + int ret = dxg_unmap_iospace(pqueue->mapped_address, PAGE_SIZE); + + UNUSED(ret); + TRACE_DEBUG(1, "fence is unmapped %d %p", + ret, pqueue->mapped_address); + pqueue->mapped_address = NULL; + } +} + +void dxgpagingqueue_destroy(struct dxgpagingqueue *pqueue) +{ + struct dxgprocess *process = pqueue->process; + + TRACE_DEBUG(1, "%s %p %x\n", __func__, pqueue, pqueue->handle); + + dxgpagingqueue_stop(pqueue); + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + if (pqueue->handle) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + pqueue->handle); + pqueue->handle = 0; + } + if (pqueue->syncobj_handle) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_MONITOREDFENCE, + pqueue->syncobj_handle); + pqueue->syncobj_handle = 0; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + if (pqueue->device) + dxgdevice_remove_paging_queue(pqueue); + dxgmem_free(process, DXGMEM_PQUEUE, pqueue); +} + +struct dxgprocess_adapter *dxgprocess_adapter_create(struct dxgprocess *process, + struct dxgadapter *adapter) +{ + struct dxgprocess_adapter *adapter_info = dxgmem_alloc(process, + DXGMEM_PROCESS_ADAPTER, + sizeof + (*adapter_info)); + if (adapter_info) { + if (!dxgadapter_acquire_reference(adapter)) { + pr_err("failed to acquire adapter reference"); + goto cleanup; + } + adapter_info->adapter = adapter; + adapter_info->process = process; + adapter_info->refcount = 1; + dxgmutex_init(&adapter_info->device_list_mutex, + DXGLOCK_PROCESSADAPTERDEVICELIST); + INIT_LIST_HEAD(&adapter_info->device_list_head); + list_add_tail(&adapter_info->process_adapter_list_entry, + &process->process_adapter_list_head); + dxgadapter_add_process(adapter, adapter_info); + } + return adapter_info; +cleanup: + if (adapter_info) + dxgmem_free(process, DXGMEM_PROCESS_ADAPTER, adapter_info); + return NULL; +} + +void dxgprocess_adapter_stop(struct dxgprocess_adapter *adapter_info) +{ + struct dxgdevice *device; + + dxgmutex_lock(&adapter_info->device_list_mutex); + list_for_each_entry(device, &adapter_info->device_list_head, + device_list_entry) { + dxgdevice_stop(device); + } + dxgmutex_unlock(&adapter_info->device_list_mutex); +} + +void dxgprocess_adapter_destroy(struct dxgprocess_adapter *adapter_info) +{ + struct dxgdevice *device; + + dxgmutex_lock(&adapter_info->device_list_mutex); + while (!list_empty(&adapter_info->device_list_head)) { + device = list_first_entry(&adapter_info->device_list_head, + struct dxgdevice, device_list_entry); + list_del(&device->device_list_entry); + device->device_list_entry.next = NULL; + dxgmutex_unlock(&adapter_info->device_list_mutex); + dxgdevice_destroy(device); + dxgmutex_lock(&adapter_info->device_list_mutex); + } + dxgmutex_unlock(&adapter_info->device_list_mutex); + + dxgadapter_remove_process(adapter_info); + dxgadapter_release_reference(adapter_info->adapter); + list_del(&adapter_info->process_adapter_list_entry); + dxgmem_free(adapter_info->process, DXGMEM_PROCESS_ADAPTER, + adapter_info); +} + +/* + * Must be called when dxgglobal::process_adapter_mutex is held + */ +void dxgprocess_adapter_release(struct dxgprocess_adapter *adapter_info) +{ + TRACE_DEBUG(1, "%s %p %d", + __func__, adapter_info, adapter_info->refcount); + adapter_info->refcount--; + if (adapter_info->refcount == 0) + dxgprocess_adapter_destroy(adapter_info); +} + +int dxgprocess_adapter_add_device(struct dxgprocess *process, + struct dxgadapter *adapter, + struct dxgdevice *device) +{ + struct dxgprocess_adapter *entry; + struct dxgprocess_adapter *adapter_info = NULL; + int ret = 0; + + dxgglobal_acquire_process_adapter_lock(); + + list_for_each_entry(entry, &process->process_adapter_list_head, + process_adapter_list_entry) { + if (entry->adapter == adapter) { + adapter_info = entry; + break; + } + } + if (adapter_info == NULL) { + pr_err("failed to find process adapter info\n"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + dxgmutex_lock(&adapter_info->device_list_mutex); + list_add_tail(&device->device_list_entry, + &adapter_info->device_list_head); + device->adapter_info = adapter_info; + dxgmutex_unlock(&adapter_info->device_list_mutex); + +cleanup: + + dxgglobal_release_process_adapter_lock(); + return ret; +} + +void dxgprocess_adapter_remove_device(struct dxgdevice *device) +{ + TRACE_DEBUG(1, "%s %p\n", __func__, device); + dxgmutex_lock(&device->adapter_info->device_list_mutex); + if (device->device_list_entry.next) { + list_del(&device->device_list_entry); + device->device_list_entry.next = NULL; + } + dxgmutex_unlock(&device->adapter_info->device_list_mutex); +} + +struct dxgsharedsyncobject *dxgsharedsyncobj_create(struct dxgadapter *adapter, + struct dxgsyncobject *so) +{ + struct dxgsharedsyncobject *syncobj; + + syncobj = dxgmem_alloc(NULL, DXGMEM_SHAREDSYNCOBJ, sizeof(*syncobj)); + if (syncobj) { + refcount_set(&syncobj->refcount, 1); + INIT_LIST_HEAD(&syncobj->shared_syncobj_list_head); + syncobj->adapter = adapter; + syncobj->type = so->type; + syncobj->monitored_fence = so->monitored_fence; + dxgadapter_add_shared_syncobj(adapter, syncobj); + dxgadapter_acquire_reference(adapter); + init_rwsem(&syncobj->syncobj_list_lock); + dxgmutex_init(&syncobj->fd_mutex, DXGLOCK_FDMUTEX); + } + return syncobj; +} + +bool dxgsharedsyncobj_acquire_reference(struct dxgsharedsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p %d", __func__, syncobj, + refcount_read(&syncobj->refcount)); + return refcount_inc_not_zero(&syncobj->refcount); +} + +void dxgsharedsyncobj_release_reference(struct dxgsharedsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p %d", __func__, syncobj, + refcount_read(&syncobj->refcount)); + if (refcount_dec_and_test(&syncobj->refcount)) { + TRACE_DEBUG(1, "Destroying"); + if (syncobj->global_shared_handle) { + hmgrtable_lock(&dxgglobal->handle_table, DXGLOCK_EXCL); + hmgrtable_free_handle(&dxgglobal->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + syncobj->global_shared_handle); + hmgrtable_unlock(&dxgglobal->handle_table, + DXGLOCK_EXCL); + } + if (syncobj->adapter) { + dxgadapter_remove_shared_syncobj(syncobj->adapter, + syncobj); + dxgadapter_release_reference(syncobj->adapter); + } + dxgmem_free(NULL, DXGMEM_SHAREDSYNCOBJ, syncobj); + } + TRACE_DEBUG(1, "%s end", __func__); +} + +void dxgsharedsyncobj_add_syncobj(struct dxgsharedsyncobject *shared, + struct dxgsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p 0x%p", __func__, shared, syncobj); + dxgsharedsyncobj_acquire_reference(shared); + down_write(&shared->syncobj_list_lock); + list_add(&syncobj->shared_syncobj_list_entry, + &shared->shared_syncobj_list_head); + syncobj->shared_owner = shared; + up_write(&shared->syncobj_list_lock); +} + +void dxgsharedsyncobj_remove_syncobj(struct dxgsharedsyncobject *shared, + struct dxgsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p", __func__, shared); + down_write(&shared->syncobj_list_lock); + list_del(&syncobj->shared_syncobj_list_entry); + up_write(&shared->syncobj_list_lock); +} + +struct dxgsyncobject *dxgsyncobject_create(struct dxgprocess *process, + struct dxgdevice *device, + struct dxgadapter *adapter, + enum + d3dddi_synchronizationobject_type + type, + struct + d3dddi_synchronizationobject_flags + flags) +{ + struct dxgsyncobject *syncobj; + + syncobj = dxgmem_alloc(process, DXGMEM_SYNCOBJ, sizeof(*syncobj)); + if (syncobj == NULL) + goto cleanup; + syncobj->type = type; + syncobj->process = process; + switch (type) { + case D3DDDI_MONITORED_FENCE: + case D3DDDI_PERIODIC_MONITORED_FENCE: + syncobj->monitored_fence = 1; + break; + case D3DDDI_CPU_NOTIFICATION: + syncobj->cpu_event = 1; + syncobj->host_event = dxgmem_alloc(process, DXGMEM_HOSTEVENT, + sizeof(struct dxghostevent)); + if (syncobj->host_event == NULL) + goto cleanup; + break; + default: + break; + } + if (flags.shared) { + syncobj->shared = 1; + if (flags.nt_security_sharing) + syncobj->shared_nt = 1; + } + + refcount_set(&syncobj->refcount, 1); + + if (syncobj->monitored_fence) { + syncobj->device = device; + syncobj->device_handle = device->handle; + dxgdevice_acquire_reference(device); + dxgdevice_add_syncobj(device, syncobj); + } else { + dxgadapter_add_syncobj(adapter, syncobj); + } + syncobj->adapter = adapter; + dxgadapter_acquire_reference(adapter); + + TRACE_DEBUG(1, "%s 0x%p\n", __func__, syncobj); + return syncobj; +cleanup: + if (syncobj->host_event) + dxgmem_free(process, DXGMEM_HOSTEVENT, syncobj->host_event); + if (syncobj) + dxgmem_free(process, DXGMEM_SYNCOBJ, syncobj); + return NULL; +} + +void dxgsyncobject_destroy(struct dxgprocess *process, + struct dxgsyncobject *syncobj) +{ + int destroyed; + + TRACE_DEBUG(1, "%s 0x%p", __func__, syncobj); + + dxgsyncobject_stop(syncobj); + + destroyed = test_and_set_bit(0, &syncobj->flags); + if (!destroyed) { + TRACE_DEBUG(1, "Deleting handle: %x", syncobj->handle); + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + if (syncobj->handle) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + syncobj->handle); + syncobj->handle = 0; + dxgsyncobject_release_reference(syncobj); + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (syncobj->cpu_event) { + if (syncobj->host_event->cpu_event) { + eventfd_ctx_put(syncobj->host_event->cpu_event); + if (syncobj->host_event->event_id) { + dxgglobal_remove_host_event(syncobj-> + host_event); + } + syncobj->host_event->cpu_event = NULL; + } + } + if (syncobj->monitored_fence) + dxgdevice_remove_syncobj(syncobj); + else + dxgadapter_remove_syncobj(syncobj); + if (syncobj->adapter) { + dxgadapter_release_reference(syncobj->adapter); + syncobj->adapter = NULL; + } + } + dxgsyncobject_release_reference(syncobj); + + TRACE_DEBUG(1, "%s end", __func__); +} + +void dxgsyncobject_stop(struct dxgsyncobject *syncobj) +{ + int stopped = test_and_set_bit(1, &syncobj->flags); + + TRACE_DEBUG(1, "%s", __func__); + if (!stopped) { + TRACE_DEBUG(1, "stopping"); + if (syncobj->monitored_fence) { + if (syncobj->mapped_address) { + int ret = + dxg_unmap_iospace(syncobj->mapped_address, + PAGE_SIZE); + + (void)ret; + TRACE_DEBUG(1, "fence is unmapped %d %p\n", + ret, syncobj->mapped_address); + syncobj->mapped_address = NULL; + } + } + } + TRACE_DEBUG(1, "%s end", __func__); +} + +void dxgsyncobject_acquire_reference(struct dxgsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p %d", + __func__, syncobj, refcount_read(&syncobj->refcount)); + refcount_inc_not_zero(&syncobj->refcount); +} + +void dxgsyncobject_release_reference(struct dxgsyncobject *syncobj) +{ + TRACE_DEBUG(1, "%s 0x%p %d", + __func__, syncobj, refcount_read(&syncobj->refcount)); + if (refcount_dec_and_test(&syncobj->refcount)) { + if (syncobj->shared_owner) { + dxgsharedsyncobj_remove_syncobj(syncobj->shared_owner, + syncobj); + dxgsharedsyncobj_release_reference(syncobj-> + shared_owner); + } + if (syncobj->host_event) + dxgmem_free(syncobj->process, DXGMEM_HOSTEVENT, + syncobj->host_event); + dxgmem_free(syncobj->process, DXGMEM_SYNCOBJ, syncobj); + } +} + +struct dxghwqueue *dxghwqueue_create(struct dxgcontext *context) +{ + struct dxgprocess *process = context->device->process; + struct dxghwqueue *hwqueue = + dxgmem_alloc(process, DXGMEM_HWQUEUE, sizeof(*hwqueue)); + if (hwqueue) { + refcount_set(&hwqueue->refcount, 1); + hwqueue->context = context; + hwqueue->process = process; + hwqueue->device_handle = context->device->handle; + if (dxgcontext_add_hwqueue(context, hwqueue)) { + dxghwqueue_release_reference(hwqueue); + hwqueue = NULL; + } else { + dxgcontext_acquire_reference(context); + } + } + return hwqueue; +} + +void dxghwqueue_destroy(struct dxgprocess *process, struct dxghwqueue *hwqueue) +{ + TRACE_DEBUG(1, "%s %p\n", __func__, hwqueue); + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + if (hwqueue->handle) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGHWQUEUE, + hwqueue->handle); + hwqueue->handle = 0; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (hwqueue->progress_fence_mapped_address) { + dxg_unmap_iospace(hwqueue->progress_fence_mapped_address, + PAGE_SIZE); + hwqueue->progress_fence_mapped_address = NULL; + } + dxgcontext_remove_hwqueue_safe(hwqueue->context, hwqueue); + + dxgcontext_release_reference(hwqueue->context); + dxghwqueue_release_reference(hwqueue); +} + +bool dxghwqueue_acquire_reference(struct dxghwqueue *hwqueue) +{ + return refcount_inc_not_zero(&hwqueue->refcount); +} + +void dxghwqueue_release_reference(struct dxghwqueue *hwqueue) +{ + if (refcount_dec_and_test(&hwqueue->refcount)) + dxgmem_free(hwqueue->process, DXGMEM_HWQUEUE, hwqueue); +} diff --git a/drivers/gpu/dxgkrnl/dxgkrnl.h b/drivers/gpu/dxgkrnl/dxgkrnl.h new file mode 100644 index 000000000000..20be1b5053c7 --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgkrnl.h @@ -0,0 +1,913 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver headers + * + */ + +#ifndef _DXGKRNL_H +#define _DXGKRNL_H + +#include <linux/uuid.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/semaphore.h> +#include <linux/refcount.h> +#include <linux/rwsem.h> +#include <linux/atomic.h> +#include <linux/spinlock.h> +#include <linux/gfp.h> +#include <linux/cdev.h> + +struct dxgprocess; +struct dxgadapter; +struct dxgdevice; +struct dxgcontext; +struct dxgallocation; +struct dxgresource; +struct dxgsharedresource; +struct dxgsyncobject; +struct dxgsharedsyncobject; +struct dxghwqueue; + +#include "misc.h" +#include "hmgr.h" +#include "d3dkmthk.h" + +struct dxgk_device_types { + uint post_device:1; + uint post_device_certain:1; + uint software_device:1; + uint soft_gpu_device:1; + uint warp_device:1; + uint bdd_device:1; + uint support_miracast:1; + uint mismatched_lda:1; + uint indirect_display_device:1; + uint xbox_one_device:1; + uint child_id_support_dwm_clone:1; + uint child_id_support_dwm_clone2:1; + uint has_internal_panel:1; + uint rfx_vgpu_device:1; + uint virtual_render_device:1; + uint support_preserve_boot_display:1; + uint is_uefi_frame_buffer:1; + uint removable_device:1; + uint virtual_monitor_device:1; +}; + +enum dxgobjectstate { + DXGOBJECTSTATE_CREATED, + DXGOBJECTSTATE_ACTIVE, + DXGOBJECTSTATE_STOPPED, + DXGOBJECTSTATE_DESTROYED, +}; + +struct dxgvmbuschannel { + struct vmbus_channel *channel; + struct hv_device *hdev; + struct dxgadapter *adapter; + spinlock_t packet_list_mutex; + struct list_head packet_list_head; + struct kmem_cache *packet_cache; + atomic64_t packet_request_id; +}; + +int dxgvmbuschannel_init(struct dxgvmbuschannel *ch, struct hv_device *hdev); +void dxgvmbuschannel_destroy(struct dxgvmbuschannel *ch); +void dxgvmbuschannel_receive(void *ctx); + +struct dxgpagingqueue { + struct dxgdevice *device; + struct dxgprocess *process; + struct list_head pqueue_list_entry; + d3dkmt_handle device_handle; + d3dkmt_handle handle; + d3dkmt_handle syncobj_handle; + void *mapped_address; +}; + +/* + * The structure describes an event, which will be signaled by + * a message from host. + */ +struct dxghostevent { + struct list_head host_event_list_entry; + u64 event_id; + struct dxgprocess *process; + struct eventfd_ctx *cpu_event; + struct completion *completion_event; + bool destroy_after_signal; + bool remove_from_list; +}; + +struct dxgpagingqueue *dxgpagingqueue_create(struct dxgdevice *device); +void dxgpagingqueue_destroy(struct dxgpagingqueue *pqueue); +void dxgpagingqueue_stop(struct dxgpagingqueue *pqueue); + +/* + * This is GPU synchronization object, which is used to synchronize execution + * between GPU contextx/hardware queues or for tracking GPU execution progress. + * A dxgsyncobject is created when somebody creates a syncobject or opens a + * shared syncobject. + * A syncobject belongs to an adapter, unless it is a cross-adapter object. + * Cross adapter syncobjects are currently not implemented. + * + * D3DDDI_MONITORED_FENCE and D3DDDI_PERIODIC_MONITORED_FENCE are called + * "device" syncobject, because the belong to a device (dxgdevice). + * Device syncobjects are inserted to a list in dxgdevice. + * + * A syncobject can be "shared", meaning that it could be opened by many + * processes. + * + * Shared syncobjects are inserted to a list in its owner + * (dxgsharedsyncobject). + * A syncobject can be shared by using a global handle or by using + * "NT security handle". + * When global handle sharing is used, the handle is created durinig object + * creation. + * When "NT security" is used, the handle for sharing is create be calling + * dxgk_share_objects. On Linux "NT handle" is represented by a file + * descriptor. FD points to dxgsharedsyncobject. + */ +struct dxgsyncobject { + refcount_t refcount; + enum d3dddi_synchronizationobject_type type; + /* + * List entry in dxgdevice for device sync objects. + * List entry in dxgadapter for other objects + */ + struct list_head syncobj_list_entry; + /* List entry in the dxgsharedsyncobject object for shared synobjects */ + struct list_head shared_syncobj_list_entry; + /* Adapter, the syncobject belongs to. NULL for stopped sync obejcts. */ + struct dxgadapter *adapter; + /* + * Pointer to the device, which was used to create the object. + * This is NULL for non-device syncbjects + */ + struct dxgdevice *device; + struct dxgprocess *process; + /* Used by D3DDDI_CPU_NOTIFICATION objects */ + struct dxghostevent *host_event; + /* Owner object for shared syncobjects */ + struct dxgsharedsyncobject *shared_owner; + /* CPU virtual address of the fence value for "device" syncobjects */ + void *mapped_address; + /* Handle in the process handle table */ + d3dkmt_handle handle; + /* Cached handle of the device. Used to avoid device dereference. */ + d3dkmt_handle device_handle; + union { + struct { + /* Must be the first bit */ + uint destroyed:1; + /* Must be the second bit */ + uint stopped:1; + /* device syncobject */ + uint monitored_fence:1; + uint cpu_event:1; + uint shared:1; + /* shared using file descriptor */ + uint shared_nt:1; + uint reserved:26; + }; + long flags; + }; +}; + +/* + * The object is used as parent of all sync objects, created for a shared + * syncobject. When a shared syncobject is created without NT security, the + * handle in the global handle table will point to this object. + */ +struct dxgsharedsyncobject { + refcount_t refcount; + /* Referenced by file descriptors */ + int host_shared_handle_nt_reference; + /* + * Handle in the global handle table. It is zero for NT + * security syncobjects + */ + d3dkmt_handle global_shared_handle; + /* Corresponding handle in the host global handle table */ + d3dkmt_handle host_shared_handle; + /* + * When the sync object is shared by NT handle, this is the + * corresponding handle in the host + */ + d3dkmt_handle host_shared_handle_nt; + /* Protects access to host_shared_handle_nt */ + struct dxgmutex fd_mutex; + struct rw_semaphore syncobj_list_lock; + struct list_head shared_syncobj_list_head; + struct list_head adapter_shared_syncobj_list_entry; + struct dxgadapter *adapter; + enum d3dddi_synchronizationobject_type type; + uint monitored_fence:1; +}; + +struct dxgsharedsyncobject *dxgsharedsyncobj_create(struct dxgadapter *adapter, + struct dxgsyncobject + *syncobj); +bool dxgsharedsyncobj_acquire_reference(struct dxgsharedsyncobject *syncobj); +void dxgsharedsyncobj_release_reference(struct dxgsharedsyncobject *syncobj); +void dxgsharedsyncobj_add_syncobj(struct dxgsharedsyncobject *sharedsyncobj, + struct dxgsyncobject *syncobj); +void dxgsharedsyncobj_remove_syncobj(struct dxgsharedsyncobject *sharedsyncobj, + struct dxgsyncobject *syncobj); + +struct dxgsyncobject *dxgsyncobject_create(struct dxgprocess *process, + struct dxgdevice *device, + struct dxgadapter *adapter, + enum + d3dddi_synchronizationobject_type + type, + struct + d3dddi_synchronizationobject_flags + flags); +void dxgsyncobject_destroy(struct dxgprocess *process, + struct dxgsyncobject *syncobj); +void dxgsyncobject_stop(struct dxgsyncobject *syncobj); +void dxgsyncobject_acquire_reference(struct dxgsyncobject *syncobj); +void dxgsyncobject_release_reference(struct dxgsyncobject *syncobj); + +extern struct device *dxgglobaldev; + +struct dxgglobal { + struct dxgvmbuschannel channel; + struct delayed_work dwork; + struct hv_device *hdev; + u32 num_adapters; + struct resource *mem; + u64 mmiospace_base; + u64 mmiospace_size; + dev_t device_devt; + struct class *device_class; + struct device *device; + struct cdev device_cdev; + struct dxgmutex device_mutex; + + /* list of created processes */ + struct list_head plisthead; + struct dxgmutex plistmutex; + + /* list of created adapters */ + struct list_head adapter_list_head; + struct rw_semaphore adapter_list_lock; + + /* List of all current threads for lock order tracking. */ + struct mutex thread_info_mutex; + struct list_head thread_info_list_head; + + /* protects acces to the global VM bus channel */ + struct rw_semaphore channel_lock; + + /* protects the dxgprocess_adapter lists */ + struct dxgmutex process_adapter_mutex; + + /* list of events, waiting to be signaled by the host */ + struct list_head host_event_list_head; + spinlock_t host_event_list_mutex; + atomic64_t host_event_id; + + /* Handle table for shared objects */ + struct hmgrtable handle_table; + + bool cdev_initialized; + bool devt_initialized; + bool vmbus_registered; +}; + +extern struct dxgglobal *dxgglobal; + +void dxgglobal_acquire_adapter_list_lock(enum dxglockstate state); +void dxgglobal_release_adapter_list_lock(enum dxglockstate state); +struct vmbus_channel *dxgglobal_get_vmbus(void); +struct dxgvmbuschannel *dxgglobal_get_dxgvmbuschannel(void); +void dxgglobal_acquire_process_adapter_lock(void); +void dxgglobal_release_process_adapter_lock(void); +void dxgglobal_add_host_event(struct dxghostevent *hostevent); +void dxgglobal_remove_host_event(struct dxghostevent *hostevent); +u64 dxgglobal_new_host_event_id(void); +void dxgglobal_signal_host_event(u64 event_id); +struct dxghostevent *dxgglobal_get_host_event(u64 event_id); +int dxgglobal_acquire_channel_lock(void); +void dxgglobal_release_channel_lock(void); + +/* + * Describes adapter information for each process + */ +struct dxgprocess_adapter { + /* Entry in dxgadapter::adapter_process_list_head */ + struct list_head adapter_process_list_entry; + /* Entry in dxgprocess::process_adapter_list_head */ + struct list_head process_adapter_list_entry; + /* List of all dxgdevice objects created for the process on adapter */ + struct list_head device_list_head; + struct dxgmutex device_list_mutex; + struct dxgadapter *adapter; + struct dxgprocess *process; + int refcount; +}; + +struct dxgprocess_adapter *dxgprocess_adapter_create(struct dxgprocess *process, + struct dxgadapter + *adapter); +void dxgprocess_adapter_release(struct dxgprocess_adapter *adapter); +int dxgprocess_adapter_add_device(struct dxgprocess *process, + struct dxgadapter *adapter, + struct dxgdevice *device); +void dxgprocess_adapter_remove_device(struct dxgdevice *device); +void dxgprocess_adapter_stop(struct dxgprocess_adapter *adapter_info); +void dxgprocess_adapter_destroy(struct dxgprocess_adapter *adapter_info); + +struct dxgprocess { + /* + * Process list entry in dxgglobal. + * Protected by the dxgglobal->plistmutex. + */ + struct list_head plistentry; + struct task_struct *process; + pid_t pid; + pid_t tgid; + /* how many time the process was opened */ + int refcount; + /* + * This handle table is used for all objects except dxgadapter + * The handle table lock order is higher than the local_handle_table + * lock + */ + struct hmgrtable handle_table; + /* + * This handle table is used for dxgadapter objects. + * The handle table lock order is lowest. + */ + struct hmgrtable local_handle_table; + d3dkmt_handle host_handle; + + /* List of opened adapters (dxgprocess_adapter) */ + struct list_head process_adapter_list_head; + + struct hmgrtable *test_handle_table[2]; + atomic_t dxg_memory[DXGMEM_LAST]; + struct dxgmutex process_mutex; +}; + +struct dxgprocess *dxgprocess_create(void); +void dxgprocess_destroy(struct dxgprocess *process); +void dxgprocess_release_reference(struct dxgprocess *process); +int dxgprocess_open_adapter(struct dxgprocess *process, + struct dxgadapter *adapter, d3dkmt_handle *handle); +int dxgprocess_close_adapter(struct dxgprocess *process, d3dkmt_handle handle); +struct dxgadapter *dxgprocess_get_adapter(struct dxgprocess *process, + d3dkmt_handle handle); +struct dxgadapter *dxgprocess_adapter_by_handle(struct dxgprocess *process, + d3dkmt_handle handle); +struct dxgdevice *dxgprocess_device_by_handle(struct dxgprocess *process, + d3dkmt_handle handle); +struct dxgdevice *dxgprocess_device_by_object_handle(struct dxgprocess *process, + enum hmgrentry_type t, + d3dkmt_handle handle); +void dxgprocess_ht_lock_shared_down(struct dxgprocess *process); +void dxgprocess_ht_lock_shared_up(struct dxgprocess *process); +void dxgprocess_ht_lock_exclusive_down(struct dxgprocess *process); +void dxgprocess_ht_lock_exclusive_up(struct dxgprocess *process); +struct dxgprocess_adapter *dxgprocess_get_adapter_info(struct dxgprocess + *process, + struct dxgadapter + *adapter); + +enum dxgadapter_state { + DXGADAPTER_STATE_ACTIVE = 0, + DXGADAPTER_STATE_STOPPED = 1, +}; + +/* + * This object represents the grapchis adapter. + * Objects, which take reference on the adapter: + * - dxgglobal + * - dxgdevice + * - adapter handle (d3dkmt_handle) + */ +struct dxgadapter { + struct rw_semaphore core_lock; + struct rw_semaphore adapter_process_list_lock; + refcount_t refcount; + /* Entry in the list of adapters in dxgglobal */ + struct list_head adapter_list_entry; + /* The list of dxgprocess_adapter entries */ + struct list_head adapter_process_list_head; + /* List of all dxgsharedresource objects */ + struct list_head shared_resource_list_head; + /* List of all dxgsharedsyncobject objects */ + struct list_head adapter_shared_syncobj_list_head; + /* List of all non-device dxgsyncobject objects */ + struct list_head syncobj_list_head; + /* This lock protects shared resource and syncobject lists */ + struct rw_semaphore shared_resource_list_lock; + struct winluid luid; + struct dxgvmbuschannel channel; + d3dkmt_handle host_handle; + enum dxgadapter_state adapter_state; + struct winluid host_adapter_luid; + winwchar device_description[80]; + winwchar device_instance_id[W_MAX_PATH]; +}; + +int dxgadapter_init(struct dxgadapter *adapter, struct hv_device *hdev); +bool dxgadapter_is_active(struct dxgadapter *adapter); +void dxgadapter_stop(struct dxgadapter *adapter); +void dxgadapter_destroy(struct dxgadapter *adapter); +bool dxgadapter_acquire_reference(struct dxgadapter *adapter); +void dxgadapter_release_reference(struct dxgadapter *adapter); +int dxgadapter_acquire_lock_shared(struct dxgadapter *adapter); +void dxgadapter_release_lock_shared(struct dxgadapter *adapter); +int dxgadapter_acquire_lock_exclusive(struct dxgadapter *adapter); +void dxgadapter_acquire_lock_forced(struct dxgadapter *adapter); +void dxgadapter_release_lock_exclusive(struct dxgadapter *adapter); +void dxgadapter_add_shared_resource(struct dxgadapter *adapter, + struct dxgsharedresource *res); +void dxgadapter_remove_shared_resource(struct dxgadapter *adapter, + struct dxgsharedresource *res); +void dxgadapter_add_shared_syncobj(struct dxgadapter *adapter, + struct dxgsharedsyncobject *so); +void dxgadapter_remove_shared_syncobj(struct dxgadapter *adapter, + struct dxgsharedsyncobject *so); +void dxgadapter_add_syncobj(struct dxgadapter *adapter, + struct dxgsyncobject *so); +void dxgadapter_remove_syncobj(struct dxgsyncobject *so); +void dxgadapter_add_process(struct dxgadapter *adapter, + struct dxgprocess_adapter *process_info); +void dxgadapter_remove_process(struct dxgprocess_adapter *process_info); + +/* + * The object represent the device object. + * The following objects take reference on the device + * - dxgcontext + * - device handle (d3dkmt_handle) + */ +struct dxgdevice { + enum dxgobjectstate object_state; + /* Device takes reference on the adapter */ + struct dxgadapter *adapter; + struct dxgprocess_adapter *adapter_info; + struct dxgprocess *process; + /* Entry in the DGXPROCESS_ADAPTER device list */ + struct list_head device_list_entry; + refcount_t refcount; + /* Protects destcruction of the device object */ + struct rw_semaphore device_lock; + struct rw_semaphore context_list_lock; + struct list_head context_list_head; + /* List of device allocations */ + struct rw_semaphore alloc_list_lock; + struct list_head alloc_list_head; + struct list_head resource_list_head; + /* List of paging queues. Protected by process handle table lock. */ + struct list_head pqueue_list_head; + struct list_head syncobj_list_head; + d3dkmt_handle handle; + uint handle_valid; +}; + +struct dxgdevice *dxgdevice_create(struct dxgadapter *a, struct dxgprocess *p); +void dxgdevice_destroy(struct dxgdevice *device); +void dxgdevice_stop(struct dxgdevice *device); +int dxgdevice_acquire_lock_shared(struct dxgdevice *dev); +void dxgdevice_release_lock_shared(struct dxgdevice *dev); +bool dxgdevice_acquire_reference(struct dxgdevice *dev); +void dxgdevice_release_reference(struct dxgdevice *dev); +void dxgdevice_add_context(struct dxgdevice *dev, struct dxgcontext *ctx); +void dxgdevice_remove_context(struct dxgdevice *dev, struct dxgcontext *ctx); +void dxgdevice_add_alloc(struct dxgdevice *dev, struct dxgallocation *a); +void dxgdevice_remove_alloc(struct dxgdevice *dev, struct dxgallocation *a); +void dxgdevice_remove_alloc_safe(struct dxgdevice *dev, + struct dxgallocation *a); +void dxgdevice_add_resource(struct dxgdevice *dev, struct dxgresource *res); +void dxgdevice_remove_resource(struct dxgdevice *dev, struct dxgresource *res); +void dxgdevice_add_paging_queue(struct dxgdevice *dev, + struct dxgpagingqueue *pqueue); +void dxgdevice_remove_paging_queue(struct dxgpagingqueue *pqueue); +void dxgdevice_add_syncobj(struct dxgdevice *dev, struct dxgsyncobject *so); +void dxgdevice_remove_syncobj(struct dxgsyncobject *so); +bool dxgdevice_is_active(struct dxgdevice *dev); +void dxgdevice_acquire_context_list_lock(struct dxgdevice *dev); +void dxgdevice_release_context_list_lock(struct dxgdevice *dev); +void dxgdevice_acquire_alloc_list_lock(struct dxgdevice *dev); +void dxgdevice_release_alloc_list_lock(struct dxgdevice *dev); +void dxgdevice_acquire_alloc_list_lock_shared(struct dxgdevice *dev); +void dxgdevice_release_alloc_list_lock_shared(struct dxgdevice *dev); + +/* + * The object represent the execution context of a device. + */ +struct dxgcontext { + enum dxgobjectstate object_state; + struct dxgdevice *device; + struct dxgprocess *process; + /* entry in the device context list */ + struct list_head context_list_entry; + struct list_head hwqueue_list_head; + struct rw_semaphore hwqueue_list_lock; + refcount_t refcount; + d3dkmt_handle handle; + d3dkmt_handle device_handle; +}; + +struct dxgcontext *dxgcontext_create(struct dxgdevice *dev); +void dxgcontext_destroy(struct dxgprocess *pr, struct dxgcontext *ctx); +void dxgcontext_destroy_safe(struct dxgprocess *pr, struct dxgcontext *ctx); +bool dxgcontext_acquire_reference(struct dxgcontext *ctx); +void dxgcontext_release_reference(struct dxgcontext *ctx); +int dxgcontext_add_hwqueue(struct dxgcontext *ctx, struct dxghwqueue *hq); +void dxgcontext_remove_hwqueue(struct dxgcontext *ctx, struct dxghwqueue *hq); +void dxgcontext_remove_hwqueue_safe(struct dxgcontext *ctx, + struct dxghwqueue *hq); +bool dxgcontext_is_active(struct dxgcontext *ctx); + +/* + * The object represent the execution hardware queue of a device. + */ +struct dxghwqueue { + /* entry in the context hw queue list */ + struct list_head hwqueue_list_entry; + refcount_t refcount; + struct dxgcontext *context; + struct dxgprocess *process; + d3dkmt_handle progress_fence_sync_object; + d3dkmt_handle handle; + d3dkmt_handle device_handle; + void *progress_fence_mapped_address; +}; + +struct dxghwqueue *dxghwqueue_create(struct dxgcontext *ctx); +void dxghwqueue_destroy(struct dxgprocess *pr, struct dxghwqueue *hq); +bool dxghwqueue_acquire_reference(struct dxghwqueue *hq); +void dxghwqueue_release_reference(struct dxghwqueue *hq); + +/* + * A shared resource object is created to track the list of dxgresource objects, + * which are opened for the same underlying shared resource. + * There are two types of sharing resource objects: + * - sharing by using a global handle (nt_security is false). + * The global handle is a handle in the handle table of dxgglobal. It points + * to a dxgsharedresource object. dxgk_open_resource() creates a dxgresource + * object using dxgsharedresource. + * - shariing by using a file descriptor handle (nt_security is true). + * FD is created by calling dxgk_share_objects and providing shandle to + * dxgsharedresource. The FD points to a dxgresource object, which is created + * by calling dxgk_open_resource_nt. dxgresource object is referenced by the + * FD. + * + * The object is referenced by every dxgresource in its list. + * + */ +struct dxgsharedresource { + /* Every dxgresource object in the resource list takes a reference */ + refcount_t refcount; + struct dxgadapter *adapter; + /* List of dxgresource objects, opened for the shared resource. */ + /* Protected by dxgadapter::shared_resource_list_lock */ + struct list_head resource_list_head; + /* Entry in the list of dxgsharedresource in dxgadapter */ + /* Protected by dxgadapter::shared_resource_list_lock */ + struct list_head shared_resource_list_entry; + struct dxgmutex fd_mutex; + /* Referenced by file descriptors */ + int host_shared_handle_nt_reference; + /* Handle in the dxgglobal handle table, when nt_security is not used */ + d3dkmt_handle global_handle; + /* Corresponding global handle in the host */ + d3dkmt_handle host_shared_handle; + /* + * When the sync object is shared by NT handle, this is the + * corresponding handle in the host + */ + d3dkmt_handle host_shared_handle_nt; + /* Values below are computed when the resource is sealed */ + uint runtime_private_data_size; + uint alloc_private_data_size; + uint resource_private_data_size; + uint allocation_count; + union { + struct { + /* Referenced by file descriptor */ + uint nt_security:1; + /* Cannot add new allocations */ + uint sealed:1; + uint reserved:30; + }; + long flags; + }; + uint *alloc_private_data_sizes; + uint8_t *alloc_private_data; + uint8_t *runtime_private_data; + uint8_t *resource_private_data; +}; + +struct dxgsharedresource *dxgsharedresource_create(struct dxgadapter *adapter); +bool dxgsharedresource_acquire_reference(struct dxgsharedresource *res); +void dxgsharedresource_release_reference(struct dxgsharedresource *res); +void dxgsharedresource_add_resource(struct dxgsharedresource *sres, + struct dxgresource *res); +void dxgsharedresource_remove_resource(struct dxgsharedresource *sres, + struct dxgresource *res); + +struct dxgresource { + refcount_t refcount; + enum dxgobjectstate object_state; + d3dkmt_handle handle; + struct list_head alloc_list_head; + struct list_head resource_list_entry; + struct list_head shared_resource_list_entry; + struct dxgdevice *device; + struct dxgprocess *process; + /* Protects adding allocations to resource and resource destruction */ + struct dxgmutex resource_mutex; + winhandle private_runtime_handle; + union { + struct { + uint destroyed:1; /* Must be the first */ + uint handle_valid:1; + uint reserved:30; + }; + long flags; + }; + /* Owner of the shared resource */ + struct dxgsharedresource *shared_owner; +}; + +struct dxgresource *dxgresource_create(struct dxgdevice *dev); +void dxgresource_destroy(struct dxgresource *res); +void dxgresource_free_handle(struct dxgresource *res); +void dxgresource_acquire_reference(struct dxgresource *res); +void dxgresource_release_reference(struct dxgresource *res); +int dxgresource_add_alloc(struct dxgresource *res, struct dxgallocation *a); +void dxgresource_remove_alloc(struct dxgresource *res, struct dxgallocation *a); +void dxgresource_remove_alloc_safe(struct dxgresource *res, + struct dxgallocation *a); +bool dxgresource_is_active(struct dxgresource *res); + +struct privdata { + uint data_size; + uint8_t data[1]; +}; + +struct dxgallocation { + /* Entry in the device list or resource list (when resource exists) */ + struct list_head alloc_list_entry; + /* Allocation owner */ + union { + struct dxgdevice *device; + struct dxgresource *resource; + } owner; + struct dxgprocess *process; + /* Pointer to private driver data desc. Used for shared resources */ + struct privdata *priv_drv_data; + d3dkmt_handle alloc_handle; + /* Set to 1 when allocation belongs to resource. */ + uint resource_owner:1; + /* Set to 1 when 'cpu_address' is mapped to the IO space. */ + uint cpu_address_mapped:1; + /* Set to 1 when the allocatio is mapped as cached */ + uint cached:1; + uint handle_valid:1; + /* GPADL address list for existing sysmem allocations */ + uint gpadl; + /* Number of pages in the 'pages' array */ + uint num_pages; + /* + * How many times dxgk_lock2 is called to allocation, which is mapped + * to IO space. + */ + uint cpu_address_refcount; + /* + * CPU address from the existing sysmem allocation, or + * mapped to the CPU visible backing store in the IO space + */ + void *cpu_address; + /* Describes pages for the existing sysmem allocation */ + struct page **pages; +}; + +struct dxgallocation *dxgallocation_create(struct dxgprocess *process); +void dxgallocation_stop(struct dxgallocation *a); +void dxgallocation_destroy(struct dxgallocation *a); +void dxgallocation_free_handle(struct dxgallocation *a); + +void ioctl_desc_init(void); +long dxgk_compat_ioctl(struct file *f, unsigned int p1, unsigned long p2); +long dxgk_unlocked_ioctl(struct file *f, unsigned int p1, unsigned long p2); + +int dxg_unmap_iospace(void *va, uint size); +int dxg_copy_from_user(void *to, const void __user *from, unsigned long len); +int dxg_copy_to_user(void *to, const void __user *from, unsigned long len); +static inline void guid_to_luid(guid_t *guid, struct winluid *luid) +{ + *luid = *(struct winluid *)&guid->b[0]; +} + +/* + * VM bus interface + * + */ +int dxgvmb_send_set_iospace_region(u64 start, u64 len, u32 shared_mem_gpadl); +int dxgvmb_send_create_process(struct dxgprocess *process); +int dxgvmb_send_destroy_process(d3dkmt_handle process); +int dxgvmb_send_open_adapter(struct dxgadapter *adapter); +int dxgvmb_send_close_adapter(struct dxgadapter *adapter); +int dxgvmb_send_get_internal_adapter_info(struct dxgadapter *adapter); +d3dkmt_handle dxgvmb_send_create_device(struct dxgadapter *adapter, + struct dxgprocess *process, + struct d3dkmt_createdevice *args); +int dxgvmb_send_destroy_device(struct dxgadapter *adapter, + struct dxgprocess *process, d3dkmt_handle h); +d3dkmt_handle dxgvmb_send_create_context(struct dxgadapter *adapter, + struct dxgprocess *process, + struct d3dkmt_createcontextvirtual + *args); +int dxgvmb_send_destroy_context(struct dxgadapter *adapter, + struct dxgprocess *process, d3dkmt_handle h); +int dxgvmb_send_create_paging_queue(struct dxgprocess *pr, + struct dxgvmbuschannel *ch, + struct dxgdevice *dev, + struct d3dkmt_createpagingqueue *args, + struct dxgpagingqueue *pq); +int dxgvmb_send_destroy_paging_queue(struct dxgprocess *process, + struct dxgvmbuschannel *ch, + d3dkmt_handle h); +int dxgvmb_send_create_allocation(struct dxgprocess *pr, struct dxgdevice *dev, + struct d3dkmt_createallocation *args, + struct d3dkmt_createallocation *__user + input_args, struct dxgresource *res, + struct dxgallocation **allocs, + struct d3dddi_allocationinfo2 *alloc_info, + struct d3dkmt_createstandardallocation + *standard_alloc); +int dxgvmb_send_destroy_allocation(struct dxgprocess *pr, struct dxgdevice *dev, + struct dxgvmbuschannel *ch, + struct d3dkmt_destroyallocation2 *args, + d3dkmt_handle *alloc_handles); +int dxgvmb_send_make_resident(struct dxgprocess *pr, struct dxgdevice *dev, + struct dxgvmbuschannel *ch, + struct d3dddi_makeresident *args); +int dxgvmb_send_evict(struct dxgprocess *pr, struct dxgvmbuschannel *ch, + struct d3dkmt_evict *args); +int dxgvmb_send_submit_command(struct dxgprocess *pr, + struct dxgvmbuschannel *ch, + struct d3dkmt_submitcommand *args); +int dxgvmb_send_map_gpu_va(struct dxgprocess *pr, d3dkmt_handle h, + struct dxgvmbuschannel *ch, + struct d3dddi_mapgpuvirtualaddress *args); +int dxgvmb_send_reserve_gpu_va(struct dxgprocess *pr, + struct dxgvmbuschannel *ch, + struct d3dddi_reservegpuvirtualaddress *args); +int dxgvmb_send_free_gpu_va(struct dxgprocess *pr, struct dxgvmbuschannel *ch, + struct d3dkmt_freegpuvirtualaddress *args); +int dxgvmb_send_update_gpu_va(struct dxgprocess *pr, struct dxgvmbuschannel *ch, + struct d3dkmt_updategpuvirtualaddress *args); +int dxgvmb_send_create_sync_object(struct dxgprocess *pr, + struct dxgvmbuschannel *ch, + struct d3dkmt_createsynchronizationobject2 + *args, struct dxgsyncobject *so); +int dxgvmb_send_destroy_sync_object(struct dxgprocess *pr, d3dkmt_handle h); +int dxgvmb_send_signal_sync_object(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dddicb_signalflags flags, + uint64_t legacy_fence_value, + d3dkmt_handle context, + uint object_count, d3dkmt_handle *object, + uint context_count, d3dkmt_handle *contexts, + uint fence_count, uint64_t *fences, + struct eventfd_ctx *cpu_event, + d3dkmt_handle device); +int dxgvmb_send_wait_sync_object_gpu(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, uint object_count, + d3dkmt_handle *objects, uint64_t *fences, + bool legacy_fence); +int dxgvmb_send_wait_sync_object_cpu(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct + d3dkmt_waitforsynchronizationobjectfromcpu + *args, u64 cpu_event); +int dxgvmb_send_lock2(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_lock2 *args, + struct d3dkmt_lock2 *__user outargs); +int dxgvmb_send_unlock2(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_unlock2 *args); +int dxgvmb_send_update_alloc_property(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dddi_updateallocproperty *args, + struct d3dddi_updateallocproperty *__user + inargs); +int dxgvmb_send_mark_device_as_error(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_markdeviceaserror *args); +int dxgvmb_send_set_allocation_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_setallocationpriority + *args); +int dxgvmb_send_get_allocation_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_getallocationpriority + *args); +int dxgvmb_send_set_context_scheduling_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, + int priority, bool in_process); +int dxgvmb_send_get_context_scheduling_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, + int *priority, bool in_process); +int dxgvmb_send_offer_allocations(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_offerallocations *args); +int dxgvmb_send_reclaim_allocations(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle device, + struct d3dkmt_reclaimallocations2 *args, + uint64_t * __user paging_fence_value); +int dxgvmb_send_change_vidmem_reservation(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle other_process, + struct + d3dkmt_changevideomemoryreservation + *args); +int dxgvmb_send_create_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_createhwqueue *args, + struct d3dkmt_createhwqueue *__user inargs, + struct dxghwqueue *hq); +int dxgvmb_send_destroy_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle handle); +int dxgvmb_send_query_adapter_info(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryadapterinfo *args); +int dxgvmb_send_submit_command_to_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_submitcommandtohwqueue + *args); +int dxgvmb_send_query_clock_calibration(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryclockcalibration + *args, + struct d3dkmt_queryclockcalibration + *__user inargs); +int dxgvmb_send_flush_heap_transitions(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_flushheaptransitions + *args); +int dxgvmb_send_open_sync_object(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle h, d3dkmt_handle *ph); +int dxgvmb_send_open_sync_object_nt(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_opensyncobjectfromnthandle2 + *args, struct dxgsyncobject *syncobj); +int dxgvmb_send_query_alloc_residency(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryallocationresidency + *args); +int dxgvmb_send_escape(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_escape *args); +int dxgvmb_send_query_vidmem_info(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryvideomemoryinfo *args, + struct d3dkmt_queryvideomemoryinfo *__user + inargs); +int dxgvmb_send_get_device_state(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_getdevicestate *args, + struct d3dkmt_getdevicestate *__user inargs); +int dxgvmb_send_create_nt_shared_object(struct dxgprocess *process, + d3dkmt_handle object, + d3dkmt_handle *shared_handle); +int dxgvmb_send_destroy_nt_shared_object(d3dkmt_handle shared_handle); +int dxgvmb_send_open_resource(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle device, bool nt_security_sharing, + d3dkmt_handle global_share, uint allocation_count, + uint total_priv_drv_data_size, + d3dkmt_handle *resource_handle, + d3dkmt_handle *alloc_handles); +int dxgvmb_send_get_standard_alloc_priv_data(struct dxgdevice *device, + enum d3dkmdt_standardallocationtype + alloc_type, + struct d3dkmdt_gdisurfacedata + *alloc_data, + uint physical_adapter_index, + uint *alloc_priv_driver_size, + void *prive_alloc_data); + +#endif diff --git a/drivers/gpu/dxgkrnl/dxgmodule.c b/drivers/gpu/dxgkrnl/dxgmodule.c new file mode 100644 index 000000000000..cae13d22801a --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgmodule.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Driver + * Interface with Linux kernel and the VM bus driver + * + */ + +#include <linux/module.h> +#include <linux/eventfd.h> +#include <linux/hyperv.h> + +#include "dxgkrnl.h" + +struct dxgglobal *dxgglobal; +struct device *dxgglobaldev; + +#define DXGKRNL_VERSION 0x0001 +#define PCI_VENDOR_ID_MICROSOFT 0x1414 +#define PCI_DEVICE_ID_VIRTUAL_RENDER 0x008E + +// +// Interface from dxgglobal +// + +struct vmbus_channel *dxgglobal_get_vmbus(void) +{ + return dxgglobal->channel.channel; +} + +struct dxgvmbuschannel *dxgglobal_get_dxgvmbuschannel(void) +{ + return &dxgglobal->channel; +} + +int dxgglobal_acquire_channel_lock(void) +{ + dxglockorder_acquire(DXGLOCK_GLOBAL_CHANNEL); + down_read(&dxgglobal->channel_lock); + if (dxgglobal->channel.channel == NULL) { + pr_err("Failed to acquire global channel lock"); + return STATUS_DEVICE_REMOVED; + } else { + return 0; + } +} + +void dxgglobal_release_channel_lock(void) +{ + up_read(&dxgglobal->channel_lock); + dxglockorder_release(DXGLOCK_GLOBAL_CHANNEL); +} + +void dxgglobal_acquire_adapter_list_lock(enum dxglockstate state) +{ + TRACE_DEBUG(1, "%s", __func__); + dxglockorder_acquire(DXGLOCK_GLOBAL_ADAPTERLIST); + if (state == DXGLOCK_EXCL) + down_write(&dxgglobal->adapter_list_lock); + else + down_read(&dxgglobal->adapter_list_lock); +} + +void dxgglobal_release_adapter_list_lock(enum dxglockstate state) +{ + TRACE_DEBUG(1, "%s", __func__); + if (state == DXGLOCK_EXCL) + up_write(&dxgglobal->adapter_list_lock); + else + up_read(&dxgglobal->adapter_list_lock); + dxglockorder_release(DXGLOCK_GLOBAL_ADAPTERLIST); +} + +void dxgglobal_add_host_event(struct dxghostevent *event) +{ + spin_lock_irq(&dxgglobal->host_event_list_mutex); + list_add_tail(&event->host_event_list_entry, + &dxgglobal->host_event_list_head); + spin_unlock_irq(&dxgglobal->host_event_list_mutex); +} + +void dxgglobal_remove_host_event(struct dxghostevent *event) +{ + spin_lock_irq(&dxgglobal->host_event_list_mutex); + if (event->host_event_list_entry.next != NULL) { + list_del(&event->host_event_list_entry); + event->host_event_list_entry.next = NULL; + } + spin_unlock_irq(&dxgglobal->host_event_list_mutex); +} + +void dxgglobal_signal_host_event(u64 event_id) +{ + struct dxghostevent *event; + unsigned long flags; + + TRACE_DEBUG(1, "%s %lld\n", __func__, event_id); + + spin_lock_irqsave(&dxgglobal->host_event_list_mutex, flags); + list_for_each_entry(event, &dxgglobal->host_event_list_head, + host_event_list_entry) { + if (event->event_id == event_id) { + TRACE_DEBUG(1, "found event to signal %lld\n", + event_id); + if (event->remove_from_list || + event->destroy_after_signal) { + list_del(&event->host_event_list_entry); + event->host_event_list_entry.next = NULL; + TRACE_DEBUG(1, "removing event from list\n"); + } + if (event->cpu_event) { + TRACE_DEBUG(1, "signal cpu event\n"); + eventfd_signal(event->cpu_event, 1); + if (event->destroy_after_signal) + eventfd_ctx_put(event->cpu_event); + } else { + TRACE_DEBUG(1, "signal completion\n"); + complete(event->completion_event); + } + if (event->destroy_after_signal) { + TRACE_DEBUG(1, "destroying event %p\n", event); + dxgmem_free(event->process, + DXGMEM_EVENT, event); + } + break; + } + } + spin_unlock_irqrestore(&dxgglobal->host_event_list_mutex, flags); + TRACE_DEBUG(1, "dxgglobal_signal_host_event_end %lld\n", event_id); +} + +struct dxghostevent *dxgglobal_get_host_event(u64 event_id) +{ + struct dxghostevent *entry; + struct dxghostevent *event = NULL; + + spin_lock_irq(&dxgglobal->host_event_list_mutex); + list_for_each_entry(entry, &dxgglobal->host_event_list_head, + host_event_list_entry) { + if (entry->event_id == event_id) { + list_del(&entry->host_event_list_entry); + entry->host_event_list_entry.next = NULL; + event = entry; + break; + } + } + spin_unlock_irq(&dxgglobal->host_event_list_mutex); + return event; +} + +u64 dxgglobal_new_host_event_id(void) +{ + return atomic64_inc_return(&dxgglobal->host_event_id); +} + +void dxgglobal_acquire_process_adapter_lock(void) +{ + dxgmutex_lock(&dxgglobal->process_adapter_mutex); +} + +void dxgglobal_release_process_adapter_lock(void) +{ + dxgmutex_unlock(&dxgglobal->process_adapter_mutex); +} + +/* + * File operations + */ + +static struct dxgprocess *dxgglobal_get_current_process(void) +{ + /* + * Find the DXG process for the current process. + * A new process is created if necessary. + */ + struct dxgprocess *process = NULL; + struct dxgprocess *entry = NULL; + + dxgmutex_lock(&dxgglobal->plistmutex); + list_for_each_entry(entry, &dxgglobal->plisthead, plistentry) { + /* All threads of a process have the same thread group ID */ + if (entry->process->tgid == current->tgid) { + entry->refcount++; + process = entry; + TRACE_DEBUG(1, "found dxgprocess entry\n"); + break; + } + } + dxgmutex_unlock(&dxgglobal->plistmutex); + + if (process == NULL) + process = dxgprocess_create(); + + return process; +} + +static int dxgk_open(struct inode *n, struct file *f) +{ + int ret = 0; + struct dxgprocess *process; + struct dxgthreadinfo *thread; + + TRACE_DEBUG2(1, 0, "%s %p %d %d", + __func__, f, current->pid, current->tgid); + + thread = dxglockorder_get_thread(); + + /* Find/create a dxgprocess structure for this process */ + process = dxgglobal_get_current_process(); + + if (process) { + f->private_data = process; + } else { + TRACE_DEBUG(1, "cannot create dxgprocess for open\n"); + ret = -EBADF; + } + + dxglockorder_put_thread(thread); + TRACE_DEBUG2(1, 0, "%s end %x", __func__, ret); + return ret; +} + +static int dxgk_release(struct inode *n, struct file *f) +{ + struct dxgthreadinfo *thread; + struct dxgprocess *process; + + process = (struct dxgprocess *)f->private_data; + TRACE_DEBUG2(1, 0, "%s %p, %p", __func__, f, process); + + if (process == NULL) + return -EINVAL; + + thread = dxglockorder_get_thread(); + + dxgprocess_release_reference(process); + + dxglockorder_check_empty(thread); + dxglockorder_put_thread(thread); + + f->private_data = NULL; + TRACE_DEBUG2(1, 0, "%s end", __func__); + return 0; +} + +static ssize_t dxgk_read(struct file *f, char __user *s, size_t len, + loff_t *o) +{ + TRACE_DEBUG(1, "file read\n"); + return 0; +} + +static ssize_t dxgk_write(struct file *f, const char __user *s, size_t len, + loff_t *o) +{ + TRACE_DEBUG(1, "file write\n"); + return len; +} + +const struct file_operations dxgk_fops = { + .owner = THIS_MODULE, + .open = dxgk_open, + .release = dxgk_release, + .compat_ioctl = dxgk_compat_ioctl, + .unlocked_ioctl = dxgk_unlocked_ioctl, + .write = dxgk_write, + .read = dxgk_read, +}; + +/* + * Interface with the VM bus driver + */ + +static int dxgglobal_getiospace(struct dxgglobal *dxgglobal) +{ + /* Get mmio space for the global channel */ + struct hv_device *hdev = dxgglobal->hdev; + struct vmbus_channel *channel = hdev->channel; + resource_size_t pot_start = 0; + resource_size_t pot_end = -1; + int ret; + + dxgglobal->mmiospace_size = channel->offermsg.offer.mmio_megabytes; + if (dxgglobal->mmiospace_size == 0) { + TRACE_DEBUG(1, "zero mmio space is offered\n"); + return STATUS_NO_MEMORY; + } + dxgglobal->mmiospace_size <<= 20; + TRACE_DEBUG(1, "mmio offered: %llx\n", dxgglobal->mmiospace_size); + + ret = vmbus_allocate_mmio(&dxgglobal->mem, hdev, pot_start, pot_end, + dxgglobal->mmiospace_size, 0x10000, false); + if (ret) { + pr_err("Unable to allocate mmio memory: %d\n", ret); + return ret; + } + dxgglobal->mmiospace_size = dxgglobal->mem->end - + dxgglobal->mem->start + 1; + dxgglobal->mmiospace_base = dxgglobal->mem->start; + TRACE_DEBUG(1, "mmio allocated %llx %llx %llx %llx\n", + dxgglobal->mmiospace_base, + dxgglobal->mmiospace_size, + dxgglobal->mem->start, dxgglobal->mem->end); + + return 0; +} + +static char *dxg_devnode(struct device *dev, umode_t *mode) +{ + /* Set read/write permissions to the DXG device */ + if (mode) + *mode = 0666; + return NULL; +} + +static int dxgglobal_init_global_channel(struct hv_device *hdev) +{ + int ret = 0; + + TRACE_DEBUG(1, "%s %x %x", __func__, hdev->vendor_id, hdev->device_id); + { + TRACE_DEBUG(1, "device type : %pUb\n", &hdev->dev_type); + TRACE_DEBUG(1, "device channel: %pUb %p primary: %p\n", + &hdev->channel->offermsg.offer.if_type, + hdev->channel, hdev->channel->primary_channel); + } + + if (dxgglobal->hdev) { + /* This device should appear only once */ + pr_err("dxgglobal already initialized\n"); + ret = -EBADE; + goto error; + } + + dxgglobal->hdev = hdev; + + ret = dxgvmbuschannel_init(&dxgglobal->channel, hdev); + if (ret) { + pr_err("dxgvmbuschannel_init failed: %d\n", ret); + goto error; + } + + ret = dxgglobal_getiospace(dxgglobal); + if (ret) { + pr_err("getiospace failed: %d\n", ret); + goto error; + } + + ret = dxgvmb_send_set_iospace_region(dxgglobal->mmiospace_base, + dxgglobal->mmiospace_size, 0); + if (ret) { + pr_err("send_set_iospace_region failed\n"); + goto error; + } + + hv_set_drvdata(hdev, dxgglobal); + + if (alloc_chrdev_region(&dxgglobal->device_devt, 0, 1, "dxgkrnl") < 0) { + pr_err("alloc_chrdev_region failed\n"); + ret = -ENODEV; + goto error; + } + dxgglobal->devt_initialized = true; + dxgglobal->device_class = class_create(THIS_MODULE, "dxgkdrv"); + if (dxgglobal->device_class == NULL) { + pr_err("class_create failed\n"); + ret = -ENODEV; + goto error; + } + dxgglobal->device_class->devnode = dxg_devnode; + dxgglobal->device = device_create(dxgglobal->device_class, NULL, + dxgglobal->device_devt, NULL, "dxg"); + if (dxgglobal->device == NULL) { + pr_err("device_create failed\n"); + ret = -ENODEV; + goto error; + } + dxgglobaldev = dxgglobal->device; + cdev_init(&dxgglobal->device_cdev, &dxgk_fops); + ret = cdev_add(&dxgglobal->device_cdev, dxgglobal->device_devt, 1); + if (ret < 0) { + pr_err("cdev_add failed: %d\n", ret); + goto error; + } + dxgglobal->cdev_initialized = true; + +error: + return ret; +} + +static void dxgglobal_destroy_global_channel(void) +{ + dxglockorder_acquire(DXGLOCK_GLOBAL_CHANNEL); + down_write(&dxgglobal->channel_lock); + + TRACE_DEBUG(1, "%s", __func__); + + if (dxgglobal->devt_initialized) { + unregister_chrdev_region(dxgglobal->device_devt, 1); + dxgglobal->devt_initialized = false; + } + if (dxgglobal->cdev_initialized) { + cdev_del(&dxgglobal->device_cdev); + dxgglobal->cdev_initialized = false; + } + if (dxgglobal->device) { + device_destroy(dxgglobal->device_class, dxgglobal->device_devt); + dxgglobal->device = NULL; + dxgglobaldev = NULL; + } + if (dxgglobal->device_class) { + class_destroy(dxgglobal->device_class); + dxgglobal->device_class = NULL; + } + if (dxgglobal->mem) { + vmbus_free_mmio(dxgglobal->mmiospace_base, + dxgglobal->mmiospace_size); + dxgglobal->mem = NULL; + } + + dxgvmbuschannel_destroy(&dxgglobal->channel); + + if (dxgglobal->hdev) { + hv_set_drvdata(dxgglobal->hdev, NULL); + dxgglobal->hdev = NULL; + } + + TRACE_DEBUG(1, "%s end\n", __func__); + + up_write(&dxgglobal->channel_lock); + dxglockorder_release(DXGLOCK_GLOBAL_CHANNEL); +} + +static int dxgglobal_create_adapter(struct hv_device *hdev) +{ + struct dxgadapter *adapter; + int ret; + + TRACE_DEBUG(1, "%s", __func__); + + adapter = dxgmem_alloc(NULL, DXGMEM_ADAPTER, sizeof(struct dxgadapter)); + if (adapter == NULL) { + pr_err("failed to allocated dxgadapter\n"); + return STATUS_NO_MEMORY; + } + + ret = dxgadapter_init(adapter, hdev); + if (ret) { + dxgadapter_stop(adapter); + dxgadapter_release_reference(adapter); + } else { + dxgglobal_acquire_adapter_list_lock(DXGLOCK_EXCL); + + TRACE_DEBUG(1, "new adapter added %p\n", adapter); + + list_add_tail(&adapter->adapter_list_entry, + &dxgglobal->adapter_list_head); + dxgglobal->num_adapters++; + + dxgglobal_release_adapter_list_lock(DXGLOCK_EXCL); + } + + TRACE_DEBUG(1, "%s end: %d\n", __func__, ret); + return ret; +} + +static void dxgglobal_stop_adapter(struct hv_device *hdev) +{ + struct dxgadapter *adapter = NULL; + struct dxgadapter *entry; + struct winluid luid; + + guid_to_luid(&hdev->channel->offermsg.offer.if_instance, &luid); + + TRACE_DEBUG(1, "%s: %x:%x\n", __func__, luid.b, luid.a); + + dxgglobal_acquire_adapter_list_lock(DXGLOCK_EXCL); + + list_for_each_entry(entry, &dxgglobal->adapter_list_head, + adapter_list_entry) { + if (*(u64 *) &luid == *(u64 *) &entry->luid) { + adapter = entry; + break; + } + } + + if (adapter) + list_del(&adapter->adapter_list_entry); + + dxgglobal_release_adapter_list_lock(DXGLOCK_EXCL); + + if (adapter) { + dxgadapter_stop(adapter); + dxgadapter_release_reference(adapter); + } else { + pr_err("Adapter is not found\n"); + } + + TRACE_DEBUG(1, "%s end", __func__); +} + +static const struct hv_vmbus_device_id id_table[] = { + /* Per GPU Device GUID */ + { HV_GPUP_DXGK_VGPU_GUID }, + /* Global Dxgkgnl channel for the virtual machine */ + { HV_GPUP_DXGK_GLOBAL_GUID }, + { } +}; + +static int dxg_probe_device(struct hv_device *hdev, + const struct hv_vmbus_device_id *dev_id) +{ + int ret = 0; + struct dxgthreadinfo *thread = dxglockorder_get_thread(); + + dxgmutex_lock(&dxgglobal->device_mutex); + + TRACE_DEBUG(1, "probe_device\n"); + + if (uuid_le_cmp(hdev->dev_type, id_table[0].guid) == 0) { + /* This is a new virtual GPU channel */ + ret = dxgglobal_create_adapter(hdev); + } else if (uuid_le_cmp(hdev->dev_type, id_table[1].guid) == 0) { + /* This is the global Dxgkgnl channel */ + ret = dxgglobal_init_global_channel(hdev); + if (ret) { + dxgglobal_destroy_global_channel(); + goto error; + } + } else { + /* Unknown device type */ + pr_err("probe: unknown device type\n"); + ret = -EBADE; + goto error; + } + +error: + + TRACE_DEBUG(1, "probe_device end\n"); + + dxgmutex_unlock(&dxgglobal->device_mutex); + + dxglockorder_put_thread(thread); + + return ret; +} + +static int dxg_remove_device(struct hv_device *hdev) +{ + int ret = 0; + struct dxgthreadinfo *thread; + + TRACE_DEBUG(1, "%s\n", __func__); + + thread = dxglockorder_get_thread(); + dxgmutex_lock(&dxgglobal->device_mutex); + + if (uuid_le_cmp(hdev->dev_type, id_table[0].guid) == 0) { + TRACE_DEBUG(1, "Remove virtual GPU\n"); + dxgglobal_stop_adapter(hdev); + } else if (uuid_le_cmp(hdev->dev_type, id_table[1].guid) == 0) { + TRACE_DEBUG(1, "Remove global channel device\n"); + dxgglobal_destroy_global_channel(); + } else { + /* Unknown device type */ + pr_err("remove: unknown device type\n"); + ret = -EBADE; + } + + dxgmutex_unlock(&dxgglobal->device_mutex); + dxglockorder_put_thread(thread); + return ret; +} + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct hv_driver dxg_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = dxg_probe_device, + .remove = dxg_remove_device, + .driver = { + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +/* + * Interface with Linux kernel + */ + +static int dxgglobal_create(void) +{ + int ret = 0; + + TRACE_DEBUG(1, "%s", __func__); + + dxgglobal = dxgmem_alloc(NULL, DXGMEM_GLOBAL, sizeof(struct dxgglobal)); + if (!dxgglobal) { + pr_err("no memory for dxgglobal\n"); + return STATUS_NO_MEMORY; + } + + INIT_LIST_HEAD(&dxgglobal->plisthead); + dxgmutex_init(&dxgglobal->plistmutex, DXGLOCK_PROCESSLIST); + dxgmutex_init(&dxgglobal->device_mutex, DXGLOCK_GLOBAL_DEVICE); + dxgmutex_init(&dxgglobal->process_adapter_mutex, + DXGLOCK_PROCESSADAPTER); + + INIT_LIST_HEAD(&dxgglobal->thread_info_list_head); + mutex_init(&dxgglobal->thread_info_mutex); + + INIT_LIST_HEAD(&dxgglobal->adapter_list_head); + init_rwsem(&dxgglobal->adapter_list_lock); + + init_rwsem(&dxgglobal->channel_lock); + + INIT_LIST_HEAD(&dxgglobal->host_event_list_head); + spin_lock_init(&dxgglobal->host_event_list_mutex); + atomic64_set(&dxgglobal->host_event_id, 1); + + hmgrtable_init(&dxgglobal->handle_table, NULL); + + TRACE_DEBUG(1, "dxgglobal_init end\n"); + return ret; +} + +static void dxgglobal_destroy(void) +{ + if (dxgglobal) { + TRACE_DEBUG(1, "%s\n", __func__); + + if (dxgglobal->vmbus_registered) + vmbus_driver_unregister(&dxg_drv); + + dxgglobal_destroy_global_channel(); + hmgrtable_destroy(&dxgglobal->handle_table); + + dxgmem_free(NULL, DXGMEM_GLOBAL, dxgglobal); + dxgglobal = NULL; + TRACE_DEBUG(1, "%s end\n", __func__); + } +} + +static int __init dxg_drv_init(void) +{ + int ret; + + pr_err("%s Version: %x", __func__, DXGKRNL_VERSION); + + ret = dxgglobal_create(); + if (ret) { + pr_err("dxgglobal_init failed"); + return STATUS_NO_MEMORY; + } + + ret = vmbus_driver_register(&dxg_drv); + if (ret) { + pr_err("vmbus_driver_register failed: %d", ret); + return ret; + } + dxgglobal->vmbus_registered = true; + + ioctl_desc_init(); + + return 0; +} + +static void __exit dxg_drv_exit(void) +{ + struct dxgthreadinfo *thread; + + TRACE_DEBUG(1, "%s\n", __func__); + + thread = dxglockorder_get_thread(); + dxgglobal_destroy(); + thread->lock_held = true; /* No need to acquire internal locks */ + dxglockorder_put_thread(thread); + dxgmem_check(NULL, DXGMEM_GLOBAL); + + TRACE_DEBUG(1, "%s end\n", __func__); +} + +module_init(dxg_drv_init); +module_exit(dxg_drv_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Microsoft Dxgkrnl virtual GPU Driver"); diff --git a/drivers/gpu/dxgkrnl/dxgprocess.c b/drivers/gpu/dxgkrnl/dxgprocess.c new file mode 100644 index 000000000000..fae2076631fd --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgprocess.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver + * DXGPROCSS implementation + * + */ + +#include "dxgkrnl.h" + +/* + * Creates a new dxgprocess object + * Must be called when dxgglobal->plistmutex is held + */ +struct dxgprocess *dxgprocess_create(void) +{ + struct dxgprocess *process; + int ret; + + TRACE_DEBUG(1, "%s", __func__); + + process = dxgmem_alloc(NULL, DXGMEM_PROCESS, sizeof(struct dxgprocess)); + if (process == NULL) { + pr_err("failed to allocate dxgprocess\n"); + } else { + TRACE_DEBUG(1, "new dxgprocess created\n"); + process->process = current; + process->pid = current->pid; + process->tgid = current->tgid; + dxgmutex_init(&process->process_mutex, DXGLOCK_PROCESSMUTEX); + ret = dxgvmb_send_create_process(process); + if (ret) { + TRACE_DEBUG(1, "dxgvmb_send_create_process failed\n"); + dxgmem_free(NULL, DXGMEM_PROCESS, process); + process = NULL; + } else { + INIT_LIST_HEAD(&process->plistentry); + process->refcount = 1; + + dxgmutex_lock(&dxgglobal->plistmutex); + list_add_tail(&process->plistentry, + &dxgglobal->plisthead); + dxgmutex_unlock(&dxgglobal->plistmutex); + + hmgrtable_init(&process->handle_table, process); + hmgrtable_init(&process->local_handle_table, process); + INIT_LIST_HEAD(&process->process_adapter_list_head); + } + } + return process; +} + +void dxgprocess_destroy(struct dxgprocess *process) +{ + int i; + enum hmgrentry_type t; + d3dkmt_handle h; + void *o; + struct dxgsyncobject *syncobj; + struct dxgprocess_adapter *entry; + struct dxgprocess_adapter *tmp; + struct dxgadapter *adapter; + + TRACE_DEBUG(1, "%s", __func__); + + /* Destroy all adapter state */ + dxgglobal_acquire_process_adapter_lock(); + list_for_each_entry_safe(entry, tmp, + &process->process_adapter_list_head, + process_adapter_list_entry) { + adapter = entry->adapter; + dxgprocess_adapter_destroy(entry); + } + dxgglobal_release_process_adapter_lock(); + + i = 0; + while (hmgrtable_next_entry(&process->local_handle_table, + &i, &t, &h, &o)) { + switch (t) { + case HMGRENTRY_TYPE_DXGADAPTER: + dxgprocess_close_adapter(process, h); + break; + default: + pr_err("invalid entry in local handle table %d", t); + break; + } + } + + i = 0; + while (hmgrtable_next_entry(&process->handle_table, &i, &t, &h, &o)) { + switch (t) { + case HMGRENTRY_TYPE_DXGSYNCOBJECT: + TRACE_DEBUG(1, "Destroy syncobj: %p %d", o, i); + syncobj = o; + syncobj->handle = 0; + dxgsyncobject_destroy(process, syncobj); + break; + default: + pr_err("invalid entry in handle table %d", t); + break; + } + } + + hmgrtable_destroy(&process->handle_table); + hmgrtable_destroy(&process->local_handle_table); + + for (i = 0; i < 2; i++) { + if (process->test_handle_table[i]) { + hmgrtable_destroy(process->test_handle_table[i]); + dxgmem_free(process, DXGMEM_HANDLE_TABLE, + process->test_handle_table[i]); + process->test_handle_table[i] = NULL; + } + } + + TRACE_DEBUG(1, "%s end", __func__); +} + +/* + * Release reference count on a process object + */ +void dxgprocess_release_reference(struct dxgprocess *process) +{ + TRACE_DEBUG(1, "%s %d", __func__, process->refcount); + dxgmutex_lock(&dxgglobal->plistmutex); + process->refcount--; + if (process->refcount == 0) { + list_del(&process->plistentry); + process->plistentry.next = NULL; + dxgmutex_unlock(&dxgglobal->plistmutex); + + dxgprocess_destroy(process); + + if (process->host_handle) + dxgvmb_send_destroy_process(process->host_handle); + dxgmem_check(process, DXGMEM_PROCESS); + dxgmem_free(NULL, DXGMEM_PROCESS, process); + } else { + dxgmutex_unlock(&dxgglobal->plistmutex); + } +} + +struct dxgprocess_adapter *dxgprocess_get_adapter_info(struct dxgprocess + *process, + struct dxgadapter + *adapter) +{ + struct dxgprocess_adapter *entry; + + list_for_each_entry(entry, &process->process_adapter_list_head, + process_adapter_list_entry) { + if (adapter == entry->adapter) { + TRACE_DEBUG(1, "found process adapter info %p", entry); + return entry; + } + } + return NULL; +} + +/* + * Dxgprocess takes references on dxgadapter and dxgprocess_adapter. + */ +int dxgprocess_open_adapter(struct dxgprocess *process, + struct dxgadapter *adapter, d3dkmt_handle *h) +{ + int ret = 0; + struct dxgprocess_adapter *adapter_info; + d3dkmt_handle handle; + + *h = 0; + adapter_info = dxgprocess_get_adapter_info(process, adapter); + if (adapter_info == NULL) { + TRACE_DEBUG(1, "creating new process adapter info\n"); + adapter_info = dxgprocess_adapter_create(process, adapter); + if (adapter_info == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + } else { + adapter_info->refcount++; + } + + handle = hmgrtable_alloc_handle_safe(&process->local_handle_table, + adapter, HMGRENTRY_TYPE_DXGADAPTER, + true); + if (handle) { + *h = handle; + } else { + pr_err("failed to create adapter handle\n"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + +cleanup: + + if (ret) { + if (adapter_info) { + dxgglobal_acquire_process_adapter_lock(); + dxgprocess_adapter_release(adapter_info); + dxgglobal_release_process_adapter_lock(); + } + } + + return ret; +} + +int dxgprocess_close_adapter(struct dxgprocess *process, d3dkmt_handle handle) +{ + struct dxgadapter *adapter; + struct dxgprocess_adapter *adapter_info; + int ret = 0; + + if (handle == 0) + return 0; + + hmgrtable_lock(&process->local_handle_table, DXGLOCK_EXCL); + adapter = dxgprocess_get_adapter(process, handle); + if (adapter) + hmgrtable_free_handle(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, handle); + hmgrtable_unlock(&process->local_handle_table, DXGLOCK_EXCL); + + if (adapter) { + adapter_info = dxgprocess_get_adapter_info(process, adapter); + if (adapter_info) { + dxgglobal_acquire_process_adapter_lock(); + dxgprocess_adapter_release(adapter_info); + dxgglobal_release_process_adapter_lock(); + } else { + ret = STATUS_INVALID_PARAMETER; + } + } else { + pr_err("%s failed %x", __func__, handle); + ret = STATUS_INVALID_PARAMETER; + } + + return ret; +} + +struct dxgadapter *dxgprocess_get_adapter(struct dxgprocess *process, + d3dkmt_handle handle) +{ + struct dxgadapter *adapter; + + adapter = hmgrtable_get_object_by_type(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, + handle); + if (adapter == NULL) + pr_err("%s failed %x\n", __func__, handle); + return adapter; +} + +/* + * Gets the adapter object from the process handle table. + * The adapter object is referenced. + * The function acquired the handle table lock shared. + */ +struct dxgadapter *dxgprocess_adapter_by_handle(struct dxgprocess *process, + d3dkmt_handle handle) +{ + struct dxgadapter *adapter; + + hmgrtable_lock(&process->local_handle_table, DXGLOCK_SHARED); + adapter = hmgrtable_get_object_by_type(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, + handle); + if (adapter == NULL) + pr_err("adapter_by_handle failed %x\n", handle); + else if (!dxgadapter_acquire_reference(adapter)) { + pr_err("failed to acquire adapter reference\n"); + adapter = NULL; + } + hmgrtable_unlock(&process->local_handle_table, DXGLOCK_SHARED); + return adapter; +} + +struct dxgdevice *dxgprocess_device_by_object_handle(struct dxgprocess *process, + enum hmgrentry_type t, + d3dkmt_handle handle) +{ + struct dxgdevice *device = NULL; + void *obj; + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + obj = hmgrtable_get_object_by_type(&process->handle_table, t, handle); + if (obj) { + d3dkmt_handle device_handle = 0; + + switch (t) { + case HMGRENTRY_TYPE_DXGDEVICE: + device = obj; + break; + case HMGRENTRY_TYPE_DXGCONTEXT: + device_handle = + ((struct dxgcontext *)obj)->device_handle; + break; + case HMGRENTRY_TYPE_DXGPAGINGQUEUE: + device_handle = + ((struct dxgpagingqueue *)obj)->device_handle; + break; + case HMGRENTRY_TYPE_DXGHWQUEUE: + device_handle = + ((struct dxghwqueue *)obj)->device_handle; + break; + default: + pr_err("invalid handle type: %d\n", t); + break; + } + if (device == NULL) + device = hmgrtable_get_object_by_type( + &process->handle_table, + HMGRENTRY_TYPE_DXGDEVICE, + device_handle); + if (device) + if (!dxgdevice_acquire_reference(device)) + device = NULL; + } + if (device == NULL) + pr_err("device_by_handle failed: %d %x\n", t, handle); + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + return device; +} + +struct dxgdevice *dxgprocess_device_by_handle(struct dxgprocess *process, + d3dkmt_handle handle) +{ + return dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGDEVICE, + handle); +} + +void dxgprocess_ht_lock_shared_down(struct dxgprocess *process) +{ + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); +} + +void dxgprocess_ht_lock_shared_up(struct dxgprocess *process) +{ + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); +} + +void dxgprocess_ht_lock_exclusive_down(struct dxgprocess *process) +{ + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); +} + +void dxgprocess_ht_lock_exclusive_up(struct dxgprocess *process) +{ + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); +} diff --git a/drivers/gpu/dxgkrnl/dxgvmbus.c b/drivers/gpu/dxgkrnl/dxgvmbus.c new file mode 100644 index 000000000000..f903d659c237 --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgvmbus.c @@ -0,0 +1,2955 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver + * VM bus interface implementation + * + */ + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/slab.h> +#include <linux/eventfd.h> +#include <linux/hyperv.h> +#include <linux/mman.h> + +#include "dxgkrnl.h" +#include "dxgvmbus.h" + +/* + * The interface version is used to ensure that the host and the guest use the + * same VM bus protocol. It needs to be incremented every time the VM bus + * interface changes. DXGK_VMBUS_LAST_COMPATIBLE_INTERFACE_VERSION is + * incremented each time the earlier versions of the interface are no longer + * compatible with the current version. + */ +const uint DXGK_VMBUS_INTERFACE_VERSION = 27; +const uint DXGK_VMBUS_LAST_COMPATIBLE_INTERFACE_VERSION = 16; + +#define RING_BUFSIZE (256 * 1024) + +struct dxgvmbuspacket { + struct list_head packet_list_entry; + u64 request_id; + struct completion wait; + void *buffer; + u32 buffer_length; + int status; +}; + +int dxgvmbuschannel_init(struct dxgvmbuschannel *ch, struct hv_device *hdev) +{ + int ret; + + ch->hdev = hdev; + spin_lock_init(&ch->packet_list_mutex); + INIT_LIST_HEAD(&ch->packet_list_head); + atomic64_set(&ch->packet_request_id, 0); + + ch->packet_cache = kmem_cache_create("DXGK packet cache", + sizeof(struct dxgvmbuspacket), 0, + 0, NULL); + if (ch->packet_cache == NULL) { + pr_err("packet_cache alloc failed"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE, + NULL, 0, dxgvmbuschannel_receive, ch); + if (ret) { + pr_err("vmbus_open failed: %d", ret); + goto cleanup; + } + + ch->channel = hdev->channel; + +cleanup: + + return ret; +} + +void dxgvmbuschannel_destroy(struct dxgvmbuschannel *ch) +{ + kmem_cache_destroy(ch->packet_cache); + ch->packet_cache = NULL; + + if (ch->channel) { + vmbus_close(ch->channel); + ch->channel = NULL; + } +} + +static inline void command_vm_to_host_init0(struct dxgkvmb_command_vm_to_host + *command) +{ + command->command_type = DXGK_VMBCOMMAND_INVALID_VM_TO_HOST; + command->process = 0; + command->command_id = 0; + command->channel_type = DXGKVMB_VM_TO_HOST; +} + +static inline void command_vm_to_host_init1(struct dxgkvmb_command_vm_to_host + *command, + enum dxgkvmb_commandtype_global + type) +{ + command->command_type = type; + command->process = 0; + command->command_id = 0; + command->channel_type = DXGKVMB_VM_TO_HOST; +} + +void signal_guest_event(struct dxgkvmb_command_host_to_vm *packet, + u32 packet_length) +{ + struct dxgkvmb_command_signalguestevent *command = (void *)packet; + + TRACE_DEBUG(1, "%s global packet", __func__); + + if (packet_length < sizeof(struct dxgkvmb_command_signalguestevent)) { + pr_err("invalid packet size"); + return; + } + if (command->event == 0) { + pr_err("invalid event pointer"); + return; + } + dxgglobal_signal_host_event(command->event); +} + +void process_inband_packet(struct dxgvmbuschannel *channel, + struct vmpacket_descriptor *desc) +{ + u32 packet_length = hv_pkt_datalen(desc); + + if (channel->adapter == NULL) { + if (packet_length < sizeof(struct dxgkvmb_command_host_to_vm)) { + pr_err("Invalid global packet"); + } else { + struct dxgkvmb_command_host_to_vm *packet = + hv_pkt_data(desc); + TRACE_DEBUG(1, "global packet %d", + packet->command_type); + switch (packet->command_type) { + case DXGK_VMBCOMMAND_SETGUESTDATA: + break; + case DXGK_VMBCOMMAND_SIGNALGUESTEVENT: + case DXGK_VMBCOMMAND_SIGNALGUESTEVENTPASSIVE: + signal_guest_event(packet, packet_length); + break; + case DXGK_VMBCOMMAND_SENDWNFNOTIFICATION: + break; + default: + pr_err("unexpected host message %d", + packet->command_type); + } + } + } else { + pr_err("Unexpected packet for adapter channel"); + } +} + +void process_completion_packet(struct dxgvmbuschannel *channel, + struct vmpacket_descriptor *desc) +{ + struct dxgvmbuspacket *packet = NULL; + struct dxgvmbuspacket *entry; + u32 packet_length = hv_pkt_datalen(desc); + unsigned long flags; + + spin_lock_irqsave(&channel->packet_list_mutex, flags); + list_for_each_entry(entry, &channel->packet_list_head, + packet_list_entry) { + if (desc->trans_id == entry->request_id) { + packet = entry; + list_del(&packet->packet_list_entry); + break; + } + } + spin_unlock_irqrestore(&channel->packet_list_mutex, flags); + + if (packet) { + if (packet->buffer_length) { + if (packet_length < packet->buffer_length) { + TRACE_DEBUG(1, "invalid size %d Expected:%d", + packet_length, + packet->buffer_length); + packet->status = STATUS_BUFFER_OVERFLOW; + } else { + memcpy(packet->buffer, hv_pkt_data(desc), + packet->buffer_length); + } + } + complete(&packet->wait); + } else { + pr_err("did not find packet to complete"); + } +} + +/* Receive callback for messages from the host */ +void dxgvmbuschannel_receive(void *ctx) +{ + struct dxgvmbuschannel *channel = ctx; + struct vmpacket_descriptor *desc; + + TRACE_DEBUG(1, "%s %p", __func__, channel->adapter); + foreach_vmbus_pkt(desc, channel->channel) { + TRACE_DEFINE(u32 packet_length = hv_pkt_datalen(desc); + ) + TRACE_DEBUG(1, "next packet (id, size, type): %llu %d %d", + desc->trans_id, packet_length, desc->type); + if (desc->type == VM_PKT_COMP) { + process_completion_packet(channel, desc); + } else { + if (desc->type != VM_PKT_DATA_INBAND) + pr_err("unexpected packet type"); + process_inband_packet(channel, desc); + } + } +} + +int dxgvmb_send_sync_msg(struct dxgvmbuschannel *channel, + void *command, + u32 cmd_size, void *result, u32 result_size) +{ + int ret = 0; + unsigned long t; + struct dxgvmbuspacket *packet = NULL; + + if (cmd_size > DXG_MAX_VM_BUS_PACKET_SIZE || + result_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("%s invalid data size", __func__); + return STATUS_INVALID_PARAMETER; + } + + packet = kmem_cache_alloc(channel->packet_cache, 0); + if (packet == NULL) { + pr_err("kmem_cache_alloc failed"); + return STATUS_NO_MEMORY; + } + + if (channel->adapter == NULL) { + TRACE_DEFINE(struct dxgkvmb_command_vm_to_host *cmd = command; + ) + TRACE_DEBUG(1, "send_sync_msg global: %d %p %d %d", + cmd->command_type, command, cmd_size, + result_size); + } else { + TRACE_DEFINE(struct dxgkvmb_command_vgpu_to_host *cmd = command; + ) + TRACE_DEBUG(1, "send_sync_msg adapter: %d %p %d %d", + cmd->command_type, command, cmd_size, + result_size); + } + + packet->request_id = atomic64_inc_return(&channel->packet_request_id); + init_completion(&packet->wait); + packet->buffer = result; + packet->buffer_length = result_size; + packet->status = 0; + spin_lock_irq(&channel->packet_list_mutex); + list_add_tail(&packet->packet_list_entry, &channel->packet_list_head); + spin_unlock_irq(&channel->packet_list_mutex); + + ret = vmbus_sendpacket(channel->channel, command, cmd_size, + packet->request_id, VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) { + pr_err("vmbus_sendpacket failed"); + goto cleanup; + } + + TRACE_DEBUG(1, "waiting for completion: %llu", packet->request_id); + t = wait_for_completion_timeout(&packet->wait, (1000 * HZ)); + if (!t) { + TRACE_DEBUG(1, "timeout waiting for completion"); + ret = STATUS_TIMEOUT; + } else { + TRACE_DEBUG(1, "completion done: %llu %x", + packet->request_id, packet->status); + if (!NT_SUCCESS(packet->status)) + ret = packet->status; + } + +cleanup: + + kmem_cache_free(channel->packet_cache, packet); + return ret; +} + +static ntstatus dxgvmb_send_sync_msg_ntstatus(struct dxgvmbuschannel *channel, + void *command, u32 cmd_size) +{ + ntstatus status = STATUS_SUCCESS; + int ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + &status, sizeof(ntstatus)); + if (ret) + status = STATUS_UNSUCCESSFUL; + return status; +} + +static int check_iospace_address(unsigned long address, uint size) +{ + if (address < dxgglobal->mmiospace_base || + size > dxgglobal->mmiospace_size || + address >= (dxgglobal->mmiospace_base + + dxgglobal->mmiospace_size - size)) { + pr_err("invalid iospace address %lx", address); + return STATUS_INVALID_PARAMETER; + } + return 0; +} + +int dxg_unmap_iospace(void *va, uint size) +{ + int ret = 0; + + TRACE_DEBUG(1, "%s %p %x", __func__, va, size); + + /* + * When an app calls exit(), dxgkrnl is called to close the device + * with current->mm equal to NULL. + */ + if (current->mm) { + ret = vm_munmap((unsigned long)va, size); + if (ret) + pr_err("vm_munmap failed %d", ret); + } + return ret; +} + +static uint8_t *dxg_map_iospace(uint64_t iospace_address, uint size, + unsigned long protection, bool cached) +{ + struct vm_area_struct *vma; + unsigned long va; + int ret = 0; + + TRACE_DEBUG(1, "%s: %llx %x %lx", + __func__, iospace_address, size, protection); + if (check_iospace_address(iospace_address, size)) { + pr_err("%s: invalid address", __func__); + return NULL; + } + + va = vm_mmap(NULL, 0, size, protection, MAP_SHARED | MAP_ANONYMOUS, 0); + if ((long)va <= 0) { + pr_err("vm_mmap failed %lx %d", va, size); + return NULL; + } + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, (unsigned long)va); + if (vma) { + pgprot_t prot = vma->vm_page_prot; + + if (!cached) + prot = pgprot_writecombine(prot); + TRACE_DEBUG(1, "vma: %lx %lx %lx", + vma->vm_start, vma->vm_end, va); + vma->vm_pgoff = iospace_address >> PAGE_SHIFT; + ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + size, prot); + if (ret) + pr_err("io_remap_pfn_range failed: %d", ret); + } else { + pr_err("failed to find vma: %p %lx", vma, va); + ret = STATUS_NO_MEMORY; + } + up_read(¤t->mm->mmap_sem); + + if (ret) { + dxg_unmap_iospace((void *)va, size); + return NULL; + } + TRACE_DEBUG(1, "%s end: %lx", __func__, va); + return (uint8_t *) va; +} + +/* + * Messages to the host + */ + +int dxgvmb_send_set_iospace_region(u64 start, u64 len, u32 shared_mem_gpadl) +{ + ntstatus status; + struct dxgkvmb_command_setiospaceregion command = { }; + int ret = dxgglobal_acquire_channel_lock(); + + if (ret) + goto cleanup; + + command_vm_to_host_init1(&command.hdr, + DXGK_VMBCOMMAND_SETIOSPACEREGION); + command.start = start; + command.length = len; + command.shared_page_gpadl = shared_mem_gpadl; + status = dxgvmb_send_sync_msg_ntstatus(&dxgglobal->channel, &command, + sizeof(command)); + if (!NT_SUCCESS(status)) { + pr_err("send_set_iospace_region failed %x", status); + ret = status; + } + + dxgglobal_release_channel_lock(); +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_create_process(struct dxgprocess *process) +{ + int ret = 0; + struct dxgkvmb_command_createprocess command = { 0 }; + struct dxgkvmb_command_createprocess_return result = { 0 }; + char s[W_MAX_PATH]; + int i; + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + TRACE_DEBUG(1, "%s", __func__); + command_vm_to_host_init1(&command.hdr, DXGK_VMBCOMMAND_CREATEPROCESS); + command.process = process; + command.process_id = process->process->pid; + command.linux_process = 1; + s[0] = 0; + __get_task_comm(s, W_MAX_PATH, process->process); + for (i = 0; i < W_MAX_PATH; i++) { + command.process_name[i] = s[i]; + if (s[i] == 0) + break; + } + + TRACE_DEBUG(1, "create_process msg %d %d", + command.hdr.command_type, (u32) sizeof(command)); + ret = dxgvmb_send_sync_msg(&dxgglobal->channel, &command, + sizeof(command), &result, sizeof(result)); + if (ret) { + pr_err("create_process failed %d", ret); + } else if (result.hprocess == 0) { + pr_err("create_process returned 0 handle"); + ret = STATUS_INTERNAL_ERROR; + } else { + process->host_handle = result.hprocess; + TRACE_DEBUG(1, "create_process returned %x", + process->host_handle); + } + + dxgglobal_release_channel_lock(); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_destroy_process(d3dkmt_handle process) +{ + ntstatus status; + struct dxgkvmb_command_destroyprocess command = { 0 }; + + status = dxgglobal_acquire_channel_lock(); + if (!NT_SUCCESS(status)) + goto cleanup; + command_vm_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_DESTROYPROCESS, + process); + status = dxgvmb_send_sync_msg_ntstatus(&dxgglobal->channel, + &command, sizeof(command)); + dxgglobal_release_channel_lock(); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_open_adapter(struct dxgadapter *adapter) +{ + int ret; + struct dxgkvmb_command_openadapter command = { }; + struct dxgkvmb_command_openadapter_return result = { }; + + command_vgpu_to_host_init1(&command.hdr, DXGK_VMBCOMMAND_OPENADAPTER); + command.vmbus_interface_version = DXGK_VMBUS_INTERFACE_VERSION, + command.vmbus_last_compatible_interface_version = + DXGK_VMBUS_LAST_COMPATIBLE_INTERFACE_VERSION; + + ret = dxgvmb_send_sync_msg(&adapter->channel, &command, sizeof(command), + &result, sizeof(result)); + if (ret) + goto cleanup; + + ret = result.status; + adapter->host_handle = result.host_adapter_handle; + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_close_adapter(struct dxgadapter *adapter) +{ + int ret = 0; + struct dxgkvmb_command_closeadapter command; + + command_vgpu_to_host_init1(&command.hdr, DXGK_VMBCOMMAND_CLOSEADAPTER); + command.host_handle = adapter->host_handle; + + ret = dxgvmb_send_sync_msg(&adapter->channel, &command, sizeof(command), + NULL, 0); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_get_internal_adapter_info(struct dxgadapter *adapter) +{ + int ret = 0; + struct dxgkvmb_command_getinternaladapterinfo command = { }; + struct dxgkvmb_command_getinternaladapterinfo_return result = { }; + + command_vgpu_to_host_init1(&command.hdr, + DXGK_VMBCOMMAND_GETINTERNALADAPTERINFO); + + ret = dxgvmb_send_sync_msg(&adapter->channel, &command, sizeof(command), + &result, sizeof(result)); + if (NT_SUCCESS(ret)) { + adapter->host_adapter_luid = result.host_adapter_luid; + wcsncpy(adapter->device_description, result.device_description, + sizeof(adapter->device_description) / sizeof(winwchar)); + wcsncpy(adapter->device_instance_id, result.device_instance_id, + sizeof(adapter->device_instance_id) / sizeof(winwchar)); + } + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +d3dkmt_handle dxgvmb_send_create_device(struct dxgadapter *adapter, + struct dxgprocess *process, + struct d3dkmt_createdevice *args) +{ + int ret; + struct dxgkvmb_command_createdevice command = { }; + uint cmd_size = sizeof(command); + struct dxgkvmb_command_createdevice_return result = { }; + + command_vgpu_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_CREATEDEVICE, + process->host_handle); + command.flags = args->flags; + ret = dxgvmb_send_sync_msg(&adapter->channel, &command, cmd_size, + &result, sizeof(result)); + if (ret) + result.device = 0; + TRACE_FUNC_EXIT_ERR(__func__, ret); + return result.device; +} + +int dxgvmb_send_destroy_device(struct dxgadapter *adapter, + struct dxgprocess *process, d3dkmt_handle h) +{ + ntstatus status; + struct dxgkvmb_command_destroydevice command = { }; + uint cmd_size = sizeof(command); + + command_vgpu_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_DESTROYDEVICE, + process->host_handle); + command.device = h; + + status = dxgvmb_send_sync_msg_ntstatus(&adapter->channel, &command, + cmd_size); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +d3dkmt_handle dxgvmb_send_create_context(struct dxgadapter *adapter, + struct dxgprocess *process, + struct d3dkmt_createcontextvirtual + *args) +{ + struct dxgkvmb_command_createcontextvirtual *command = NULL; + uint cmd_size; + int ret; + d3dkmt_handle context = 0; + + if (args->priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("PrivateDriverDataSize is invalid"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + cmd_size = sizeof(struct dxgkvmb_command_createcontextvirtual) + + args->priv_drv_data_size - 1; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + pr_err("failed to allocate memory for command"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_CREATECONTEXTVIRTUAL, + process->host_handle); + command->device = args->device; + command->node_ordinal = args->node_ordinal; + command->engine_affinity = args->engine_affinity; + command->flags = args->flags; + command->client_hint = args->client_hint; + command->priv_drv_data_size = args->priv_drv_data_size; + if (args->priv_drv_data_size) { + ret = dxg_copy_from_user(command->priv_drv_data, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + } + /* Input command is returned back as output */ + ret = dxgvmb_send_sync_msg(&adapter->channel, command, cmd_size, + command, cmd_size); + if (ret) { + goto cleanup; + } else { + context = command->context; + if (args->priv_drv_data_size) { + ret = dxg_copy_to_user(args->priv_drv_data, + command->priv_drv_data, + args->priv_drv_data_size); + if (ret) { + dxgvmb_send_destroy_context(adapter, process, + context); + context = 0; + } + } + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + return context; +} + +int dxgvmb_send_destroy_context(struct dxgadapter *adapter, + struct dxgprocess *process, d3dkmt_handle h) +{ + ntstatus status; + struct dxgkvmb_command_destroycontext command = { }; + uint cmd_size = sizeof(command); + + command_vgpu_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_DESTROYCONTEXT, + process->host_handle); + command.context = h; + + status = dxgvmb_send_sync_msg_ntstatus(&adapter->channel, &command, + cmd_size); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_create_paging_queue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct dxgdevice *device, + struct d3dkmt_createpagingqueue *args, + struct dxgpagingqueue *pqueue) +{ + struct dxgkvmb_command_createpagingqueue_return result; + struct dxgkvmb_command_createpagingqueue command; + int ret; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_CREATEPAGINGQUEUE, + process->host_handle); + command.args = *args; + args->paging_queue = 0; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), &result, + sizeof(result)); + if (ret) { + pr_err("send_create_paging_queue failed %x", ret); + goto cleanup; + } + + args->paging_queue = result.paging_queue; + args->sync_object = result.sync_object; + args->fence_cpu_virtual_address = + dxg_map_iospace(result.fence_storage_physical_address, PAGE_SIZE, + PROT_READ | PROT_WRITE, true); + if (args->fence_cpu_virtual_address == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + pqueue->mapped_address = args->fence_cpu_virtual_address; + pqueue->handle = args->paging_queue; + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_destroy_paging_queue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle h) +{ + int ret; + struct dxgkvmb_command_destroypagingqueue command; + uint cmd_size = sizeof(command); + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_DESTROYPAGINGQUEUE, + process->host_handle); + command.paging_queue = h; + + ret = dxgvmb_send_sync_msg(channel, &command, cmd_size, NULL, 0); + + dxgglobal_release_channel_lock(); + +cleanup: + + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +static int copy_private_data(struct d3dkmt_createallocation *args, + struct dxgkvmb_command_createallocation *command, + struct d3dddi_allocationinfo2 *input_alloc_info, + struct d3dkmt_createstandardallocation + *standard_alloc) +{ + struct dxgkvmb_command_createallocation_allocinfo *alloc_info; + struct d3dddi_allocationinfo2 *input_alloc; + int ret = 0; + int i; + uint8_t *private_data_dest = (uint8_t *) &command[1] + + (args->alloc_count * + sizeof(struct dxgkvmb_command_createallocation_allocinfo)); + + if (args->private_runtime_data_size) { + ret = dxg_copy_from_user(private_data_dest, + args->private_runtime_data, + args->private_runtime_data_size); + if (ret) + goto cleanup; + private_data_dest += args->private_runtime_data_size; + } + + if (args->flags.standard_allocation) { + TRACE_DEBUG2(1, 1, "private data offset %d", + (uint) (private_data_dest - (uint8_t *) command)); + + args->priv_drv_data_size = sizeof(*args->standard_allocation); + memcpy(private_data_dest, standard_alloc, + sizeof(*standard_alloc)); + private_data_dest += args->priv_drv_data_size; + } else if (args->priv_drv_data_size) { + ret = dxg_copy_from_user(private_data_dest, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + private_data_dest += args->priv_drv_data_size; + } + + alloc_info = (void *)&command[1]; + input_alloc = input_alloc_info; + if (input_alloc_info[0].sysmem) + command->flags.existing_sysmem = 1; + for (i = 0; i < args->alloc_count; i++) { + alloc_info->flags = input_alloc->flags.value; + alloc_info->vidpn_source_id = input_alloc->vidpn_source_id; + alloc_info->priv_drv_data_size = + input_alloc->priv_drv_data_size; + if (input_alloc->priv_drv_data_size) { + ret = dxg_copy_from_user(private_data_dest, + input_alloc->priv_drv_data, + input_alloc-> + priv_drv_data_size); + if (ret) + goto cleanup; + private_data_dest += input_alloc->priv_drv_data_size; + } + alloc_info++; + input_alloc++; + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int create_existing_sysmem(struct dxgdevice *device, + struct dxgkvmb_command_allocinfo_return *host_alloc, + struct dxgallocation *dxgalloc, + bool read_only, + const void *sysmem) +{ + int ret = 0; + void *kmem = NULL; + struct dxgkvmb_command_setexistingsysmemstore set_store_command = { }; + u64 alloc_size = host_alloc->allocation_size; + uint npages = alloc_size >> PAGE_SHIFT; + struct dxgvmbuschannel *channel = &device->adapter->channel; + + /* + * Create a guest physical address list and set it as the allocation + * backing store in the host. This is done after creating the host + * allocation, because only now the allocation size is known. + */ + + TRACE_DEBUG(2, "alloc size: %lld", alloc_size); + + dxgalloc->cpu_address = (void *)sysmem; + dxgalloc->pages = dxgmem_alloc(dxgalloc->process, DXGMEM_ALLOCATION, + npages * sizeof(void *)); + if (dxgalloc->pages == NULL) { + pr_err("failed to allocate pages"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = get_user_pages_fast((unsigned long)sysmem, npages, !read_only, + dxgalloc->pages); + if (ret != npages) { + pr_err("get_user_pages_fast failed: %d", ret); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + kmem = vmap(dxgalloc->pages, npages, VM_MAP, PAGE_KERNEL); + if (kmem == NULL) { + pr_err("vmap failed"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = vmbus_establish_gpadl(dxgglobal_get_vmbus(), kmem, + alloc_size, &dxgalloc->gpadl); + if (ret) { + pr_err("establish_gpadl failed: %d", ret); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + TRACE_DEBUG(1, "New gpadl %d", dxgalloc->gpadl); + + command_vgpu_to_host_init2(&set_store_command.hdr, + DXGK_VMBCOMMAND_SETEXISTINGSYSMEMSTORE, + device->process->host_handle); + set_store_command.device = device->handle; + set_store_command.allocation = host_alloc->allocation; + set_store_command.gpadl = dxgalloc->gpadl; + ret = dxgvmb_send_sync_msg_ntstatus(channel, &set_store_command, + sizeof(set_store_command)); + if (!NT_SUCCESS(ret)) { + pr_err("failed to set existing store: %x", ret); + goto cleanup; + } + +cleanup: + if (kmem) + vunmap(kmem); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +static int process_allocation_handles(struct dxgprocess *process, + struct dxgdevice *device, + struct d3dkmt_createallocation *args, + struct + dxgkvmb_command_createallocation_return + *result, struct dxgallocation **dxgalloc, + struct dxgresource *resource) +{ + int ret = 0, i; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + if (args->flags.create_resource) { + ret = hmgrtable_assign_handle(&process->handle_table, resource, + HMGRENTRY_TYPE_DXGRESOURCE, + result->resource); + if (ret) { + pr_err("failed to assign resource handle %x", + result->resource); + } else { + resource->handle = result->resource; + resource->handle_valid = 1; + } + } + for (i = 0; i < args->alloc_count; i++) { + struct dxgkvmb_command_allocinfo_return *host_alloc; + + host_alloc = &result->allocation_info[i]; + ret = hmgrtable_assign_handle(&process->handle_table, + dxgalloc[i], + HMGRENTRY_TYPE_DXGALLOCATION, + host_alloc->allocation); + if (ret) { + pr_err("failed to assign alloc handle %x %d %d", + host_alloc->allocation, + args->alloc_count, i); + break; + } + dxgalloc[i]->alloc_handle = host_alloc->allocation; + dxgalloc[i]->handle_valid = 1; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (ret) + goto cleanup; + + if (args->flags.create_shared && !args->flags.nt_security_sharing) { + struct dxgsharedresource *shared_resource = + resource->shared_owner; + shared_resource->host_shared_handle = result->global_share; + shared_resource->global_handle = + hmgrtable_alloc_handle_safe(&dxgglobal->handle_table, + shared_resource, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE, + true); + if (shared_resource->global_handle == 0) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + args->global_share = shared_resource->global_handle; + TRACE_DEBUG(1, "Shared resource global handles: %x %x", + shared_resource->global_handle, + shared_resource->host_shared_handle); + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +static int create_local_allocations(struct dxgprocess *process, + struct dxgdevice *device, + struct d3dkmt_createallocation *args, + struct d3dkmt_createallocation *__user + input_args, + struct d3dddi_allocationinfo2 *alloc_info, + struct + dxgkvmb_command_createallocation_return + *result, struct dxgresource *resource, + struct dxgallocation **dxgalloc, + struct dxgkvmb_command_destroyallocation + *destroy_buffer, uint destroy_buffer_size) +{ + int i; + int alloc_count = args->alloc_count; + uint8_t *alloc_private_data = NULL; + int ret = 0; + ntstatus status = STATUS_SUCCESS; + struct dxgvmbuschannel *channel = &device->adapter->channel; + + /* Prepare the command to destroy allocation in case of failure */ + command_vgpu_to_host_init2(&destroy_buffer->hdr, + DXGK_VMBCOMMAND_DESTROYALLOCATION, + process->host_handle); + destroy_buffer->device = args->device; + destroy_buffer->resource = args->resource; + destroy_buffer->alloc_count = alloc_count; + destroy_buffer->flags.assume_not_in_use = 1; + for (i = 0; i < alloc_count; i++) { + TRACE_DEBUG2(1, 1, "host allocation: %d %x", + i, result->allocation_info[i].allocation); + destroy_buffer->allocations[i] = + result->allocation_info[i].allocation; + } + + if (args->flags.create_resource) { + TRACE_DEBUG(1, "created resource: %x", result->resource); + ret = dxg_copy_to_user(&input_args->resource, &result->resource, + sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + } + + alloc_private_data = (uint8_t *) result + + sizeof(struct dxgkvmb_command_createallocation_return) + + sizeof(struct dxgkvmb_command_allocinfo_return) * (alloc_count - 1); + + for (i = 0; i < alloc_count; i++) { + struct dxgkvmb_command_allocinfo_return *host_alloc; + struct d3dddi_allocationinfo2 *user_alloc; + + host_alloc = &result->allocation_info[i]; + user_alloc = &alloc_info[i]; + if (alloc_info->sysmem) { + ret = create_existing_sysmem(device, host_alloc, + dxgalloc[i], + args->flags.read_only != 0, + alloc_info->sysmem); + if (ret) + goto cleanup; + } + dxgalloc[i]->num_pages = + host_alloc->allocation_size >> PAGE_SHIFT; + dxgalloc[i]->cached = host_alloc->allocation_flags.cached; + if (host_alloc->priv_drv_data_size) { + ret = dxg_copy_to_user(user_alloc->priv_drv_data, + alloc_private_data, + host_alloc->priv_drv_data_size); + if (ret) + goto cleanup; + alloc_private_data += host_alloc->priv_drv_data_size; + } + ret = dxg_copy_to_user(&args->allocation_info[i].allocation, + &host_alloc->allocation, + sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + } + + ret = process_allocation_handles(process, device, args, result, + dxgalloc, resource); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&input_args->global_share, &args->global_share, + sizeof(d3dkmt_handle)); + +cleanup: + + if (ret) { + /* Free local handles before freeing the handles in the host */ + dxgdevice_acquire_alloc_list_lock(device); + if (dxgalloc) + for (i = 0; i < alloc_count; i++) + if (dxgalloc[i]) + dxgallocation_free_handle(dxgalloc[i]); + if (resource && args->flags.create_resource) + dxgresource_free_handle(resource); + dxgdevice_release_alloc_list_lock(device); + + /* Destroy allocations in the host to unmap gpadls */ + status = dxgvmb_send_sync_msg_ntstatus(channel, destroy_buffer, + destroy_buffer_size); + if (!NT_SUCCESS(status)) + pr_err("failed to destroy allocations: %x", status); + + dxgdevice_acquire_alloc_list_lock(device); + if (dxgalloc) { + for (i = 0; i < alloc_count; i++) { + if (dxgalloc[i]) { + dxgalloc[i]->alloc_handle = 0; + dxgallocation_destroy(dxgalloc[i]); + dxgalloc[i] = NULL; + } + } + } + if (resource && args->flags.create_resource) { + /* + * Prevent the resource memory from freeing. + * It will be freed in the top level function. + */ + dxgresource_acquire_reference(resource); + dxgresource_destroy(resource); + } + dxgdevice_release_alloc_list_lock(device); + } + + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_create_allocation(struct dxgprocess *process, + struct dxgdevice *device, + struct d3dkmt_createallocation *args, + struct d3dkmt_createallocation *__user + input_args, struct dxgresource *resource, + struct dxgallocation **dxgalloc, + struct d3dddi_allocationinfo2 *alloc_info, + struct d3dkmt_createstandardallocation + *standard_alloc) +{ + struct dxgkvmb_command_createallocation *command = NULL; + struct dxgkvmb_command_destroyallocation *destroy_buffer = NULL; + struct dxgkvmb_command_createallocation_return *result = NULL; + int ret; + int i; + uint result_size = 0; + uint cmd_size = 0; + uint destroy_buffer_size = 0; + uint priv_drv_data_size; + struct dxgvmbuschannel *channel = &device->adapter->channel; + + if (args->private_runtime_data_size >= DXG_MAX_VM_BUS_PACKET_SIZE || + args->priv_drv_data_size >= DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_BUFFER_OVERFLOW; + goto cleanup; + } + + /* + * Preallocate the buffer, which will be used for destruction in case + * of a failure + */ + destroy_buffer_size = sizeof(struct dxgkvmb_command_destroyallocation) + + args->alloc_count * sizeof(d3dkmt_handle); + destroy_buffer = dxgmem_alloc(process, DXGMEM_TMP, destroy_buffer_size); + if (destroy_buffer == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + /* Compute the total private driver size */ + + priv_drv_data_size = 0; + + for (i = 0; i < args->alloc_count; i++) { + if (alloc_info[i].priv_drv_data_size >= + DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_BUFFER_OVERFLOW; + goto cleanup; + } else { + priv_drv_data_size += alloc_info[i].priv_drv_data_size; + } + if (priv_drv_data_size >= DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_BUFFER_OVERFLOW; + goto cleanup; + } + } + + /* + * Private driver data for the result includes only per allocation + * private data + */ + result_size = sizeof(struct dxgkvmb_command_createallocation_return) + + (args->alloc_count - 1) * + sizeof(struct dxgkvmb_command_allocinfo_return) + + priv_drv_data_size; + result = dxgmem_alloc(process, DXGMEM_VMBUS, result_size); + if (result == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + /* Private drv data for the command includes the global private data */ + priv_drv_data_size += args->priv_drv_data_size; + + cmd_size = sizeof(struct dxgkvmb_command_createallocation) + + args->alloc_count * + sizeof(struct dxgkvmb_command_createallocation_allocinfo) + + args->private_runtime_data_size + priv_drv_data_size; + if (cmd_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_BUFFER_OVERFLOW; + goto cleanup; + } + + TRACE_DEBUG(1, "command size, driver_data_size %d %d %ld %ld", + cmd_size, priv_drv_data_size, + sizeof(struct dxgkvmb_command_createallocation), + sizeof(struct dxgkvmb_command_createallocation_allocinfo)); + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_CREATEALLOCATION, + process->host_handle); + command->device = args->device; + command->flags = args->flags; + command->resource = args->resource; + command->private_runtime_resource_handle = + args->private_runtime_resource_handle; + command->alloc_count = args->alloc_count; + command->private_runtime_data_size = args->private_runtime_data_size; + command->priv_drv_data_size = args->priv_drv_data_size; + if (args->flags.standard_allocation) { + /* + * Flags.ExistingSysMem cannot be set from user mode, so it + * needs to be set it here. + */ + command->flags.existing_sysmem = 1; + } + + ret = copy_private_data(args, command, alloc_info, standard_alloc); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + result, result_size); + if (ret) { + pr_err("send_create_allocation failed %x", ret); + goto cleanup; + } + + ret = create_local_allocations(process, device, args, input_args, + alloc_info, result, resource, dxgalloc, + destroy_buffer, destroy_buffer_size); + if (ret) + goto cleanup; + +cleanup: + + if (destroy_buffer) + dxgmem_free(process, DXGMEM_TMP, destroy_buffer); + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + if (result) + dxgmem_free(process, DXGMEM_VMBUS, result); + + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_destroy_allocation(struct dxgprocess *process, + struct dxgdevice *device, + struct dxgvmbuschannel *channel, + struct d3dkmt_destroyallocation2 *args, + d3dkmt_handle *alloc_handles) +{ + struct dxgkvmb_command_destroyallocation *destroy_buffer = NULL; + uint destroy_buffer_size = 0; + int ret = 0; + int allocations_size = args->alloc_count * sizeof(d3dkmt_handle); + + destroy_buffer_size = sizeof(struct dxgkvmb_command_destroyallocation) + + allocations_size; + destroy_buffer = dxgmem_alloc(process, DXGMEM_TMP, destroy_buffer_size); + if (destroy_buffer == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&destroy_buffer->hdr, + DXGK_VMBCOMMAND_DESTROYALLOCATION, + process->host_handle); + destroy_buffer->device = args->device; + destroy_buffer->resource = args->resource; + destroy_buffer->alloc_count = args->alloc_count; + destroy_buffer->flags = args->flags; + if (allocations_size) + memcpy(destroy_buffer->allocations, alloc_handles, + allocations_size); + + ret = dxgvmb_send_sync_msg_ntstatus(channel, destroy_buffer, + destroy_buffer_size); + +cleanup: + + if (destroy_buffer) + dxgmem_free(process, DXGMEM_TMP, destroy_buffer); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_make_resident(struct dxgprocess *process, + struct dxgdevice *device, + struct dxgvmbuschannel *channel, + struct d3dddi_makeresident *args) +{ + int ret = 0; + uint cmd_size; + struct dxgkvmb_command_makeresident_return result = { }; + struct dxgkvmb_command_makeresident *command = NULL; + + cmd_size = (args->alloc_count - 1) * sizeof(d3dkmt_handle) + + sizeof(struct dxgkvmb_command_makeresident); + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(command->allocations, args->allocation_list, + args->alloc_count * sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_MAKERESIDENT, + process->host_handle); + command->alloc_count = args->alloc_count; + command->paging_queue = args->paging_queue; + if (device) + command->device = device->handle; + command->flags = args->flags; + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + &result, sizeof(result)); + if (ret) { + pr_err("send_make_resident failed %x", ret); + goto cleanup; + } + + args->paging_fence_value = result.paging_fence_value; + args->num_bytes_to_trim = result.num_bytes_to_trim; + ret = result.status; + +cleanup: + + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_evict(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_evict *args) +{ + int ret = 0; + uint cmd_size; + struct dxgkvmb_command_evict_return result = { }; + struct dxgkvmb_command_evict *command = NULL; + + cmd_size = (args->alloc_count - 1) * sizeof(d3dkmt_handle) + + sizeof(struct dxgkvmb_command_evict); + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(command->allocations, args->allocations, + args->alloc_count * sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_EVICT, process->host_handle); + command->alloc_count = args->alloc_count; + command->device = args->device; + command->flags = args->flags; + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + &result, sizeof(result)); + if (ret) { + pr_err("send_evict failed %x", ret); + goto cleanup; + } + args->num_bytes_to_trim = result.num_bytes_to_trim; + +cleanup: + + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_submit_command(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_submitcommand *args) +{ + int ret = 0; + uint cmd_size; + struct dxgkvmb_command_submitcommand *command = NULL; + uint hbufsize = args->num_history_buffers * sizeof(d3dkmt_handle); + + cmd_size = sizeof(struct dxgkvmb_command_submitcommand) + + hbufsize + args->priv_drv_data_size; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(&command[1], args->history_buffer_array, + hbufsize); + if (ret) + goto cleanup; + ret = dxg_copy_from_user((uint8_t *) &command[1] + hbufsize, + args->priv_drv_data, args->priv_drv_data_size); + if (ret) + goto cleanup; + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_SUBMITCOMMAND, + process->host_handle); + command->args = *args; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_map_gpu_va(struct dxgprocess *process, + d3dkmt_handle device, + struct dxgvmbuschannel *channel, + struct d3dddi_mapgpuvirtualaddress *args) +{ + struct dxgkvmb_command_mapgpuvirtualaddress command; + struct dxgkvmb_command_mapgpuvirtualaddress_return result; + int ret = 0; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_MAPGPUVIRTUALADDRESS, + process->host_handle); + command.args = *args; + command.device = device; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), &result, + sizeof(result)); + if (ret) { + pr_err("%s failed %x", __func__, ret); + goto cleanup; + } + args->virtual_address = result.virtual_address; + args->paging_fence_value = result.paging_fence_value; + ret = result.status; + +cleanup: + + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_reserve_gpu_va(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dddi_reservegpuvirtualaddress *args) +{ + struct dxgkvmb_command_reservegpuvirtualaddress command; + struct dxgkvmb_command_reservegpuvirtualaddress_return result; + int ret = 0; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_RESERVEGPUVIRTUALADDRESS, + process->host_handle); + command.args = *args; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), &result, + sizeof(result)); + args->virtual_address = result.virtual_address; + + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_free_gpu_va(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_freegpuvirtualaddress *args) +{ + struct dxgkvmb_command_freegpuvirtualaddress command; + ntstatus status; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_FREEGPUVIRTUALADDRESS, + process->host_handle); + command.args = *args; + + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_update_gpu_va(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_updategpuvirtualaddress *args) +{ + struct dxgkvmb_command_updategpuvirtualaddress *command = NULL; + uint cmd_size; + uint op_size; + int ret = 0; + + if (args->num_operations == 0 || + (DXG_MAX_VM_BUS_PACKET_SIZE / + sizeof(struct d3dddi_updategpuvirtualaddress_operation)) < + args->num_operations) { + ret = STATUS_INVALID_PARAMETER; + pr_err("Invalid number of operations: %d", + args->num_operations); + goto cleanup; + } + + op_size = args->num_operations * + sizeof(struct d3dddi_updategpuvirtualaddress_operation); + cmd_size = sizeof(struct dxgkvmb_command_updategpuvirtualaddress) + + op_size - sizeof(args->operations[0]); + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + pr_err("Failed to allocate command"); + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_UPDATEGPUVIRTUALADDRESS, + process->host_handle); + command->fence_value = args->fence_value; + command->device = args->device; + command->context = args->context; + command->fence_object = args->fence_object; + command->num_operations = args->num_operations; + command->flags = args->flags.value; + ret = dxg_copy_from_user(command->operations, args->operations, + op_size); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +static void set_result(struct d3dkmt_createsynchronizationobject2 *args, + d3dgpu_virtual_address fence_gpu_va, uint8_t *va) +{ + args->info.periodic_monitored_fence.fence_gpu_virtual_address = + fence_gpu_va; + args->info.periodic_monitored_fence.fence_cpu_virtual_address = va; +} + +int dxgvmb_send_create_sync_object(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_createsynchronizationobject2 + *args, struct dxgsyncobject *syncobj) +{ + struct dxgkvmb_command_createsyncobject_return result = { }; + struct dxgkvmb_command_createsyncobject command = { }; + int ret = 0; + uint8_t *va = 0; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_CREATESYNCOBJECT, + process->host_handle); + command.args = *args; + command.client_hint = 1; /* CLIENTHINT_UMD */ + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), &result, + sizeof(result)); + if (ret) { + pr_err("%s failed %d", __func__, ret); + goto cleanup; + } + args->sync_object = result.sync_object; + if (syncobj->shared) { + if (result.global_sync_object == 0) { + pr_err("shared handle is 0"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + args->info.shared_handle = result.global_sync_object; + } + + if (syncobj->monitored_fence) { + va = dxg_map_iospace(result.fence_storage_address, PAGE_SIZE, + PROT_READ | PROT_WRITE, true); + if (va == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + if (args->info.type == D3DDDI_MONITORED_FENCE) { + args->info.monitored_fence.fence_gpu_virtual_address = + result.fence_gpu_va; + args->info.monitored_fence.fence_cpu_virtual_address = + va; + { + unsigned long value; + + TRACE_DEBUG(1, "fence cpu address: %p", va); + ret = dxg_copy_from_user(&value, va, + sizeof(uint64_t)); + if (ret) + pr_err("failed to read fence"); + else + TRACE_DEBUG(1, "fence value: %lx", + value); + } + } else { + set_result(args, result.fence_gpu_va, va); + } + syncobj->mapped_address = va; + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_destroy_sync_object(struct dxgprocess *process, + d3dkmt_handle sync_object) +{ + struct dxgkvmb_command_destroysyncobject command = { }; + ntstatus status; + + status = dxgglobal_acquire_channel_lock(); + if (status) + goto cleanup; + + command_vm_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_DESTROYSYNCOBJECT, + process->host_handle); + command.sync_object = sync_object; + + status = dxgvmb_send_sync_msg_ntstatus(dxgglobal_get_dxgvmbuschannel(), + &command, sizeof(command)); + + dxgglobal_release_channel_lock(); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_signal_sync_object(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dddicb_signalflags flags, + uint64_t legacy_fence_value, + d3dkmt_handle context, + uint object_count, + d3dkmt_handle __user *objects, + uint context_count, + d3dkmt_handle __user *contexts, + uint fence_count, + uint64_t __user *fences, + struct eventfd_ctx *cpu_event_handle, + d3dkmt_handle device) +{ + int ret = 0; + struct dxgkvmb_command_signalsyncobject *command = NULL; + uint object_size = object_count * sizeof(d3dkmt_handle); + uint context_size = context_count * sizeof(d3dkmt_handle); + uint fence_size = fences ? fence_count * sizeof(uint64_t) : 0; + uint8_t *current_pos; + uint cmd_size = sizeof(struct dxgkvmb_command_signalsyncobject) + + object_size + context_size + fence_size; + + if (context) + cmd_size += sizeof(d3dkmt_handle); + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_SIGNALSYNCOBJECT, + process->host_handle); + + if (flags.enqueue_cpu_event) + command->cpu_event_handle = (winhandle) cpu_event_handle; + else + command->device = device; + command->flags = flags; + command->fence_value = legacy_fence_value; + command->object_count = object_count; + command->context_count = context_count; + current_pos = (uint8_t *) &command[1]; + ret = dxg_copy_from_user(current_pos, objects, object_size); + if (ret) { + pr_err("Failed to read objects %p %d", + objects, object_size); + goto cleanup; + } + current_pos += object_size; + if (context) { + command->context_count++; + *(d3dkmt_handle *) current_pos = context; + current_pos += sizeof(d3dkmt_handle); + } + if (context_size) { + ret = dxg_copy_from_user(current_pos, contexts, context_size); + if (ret) { + pr_err("Failed to read contexts %p %d", + contexts, context_size); + goto cleanup; + } + current_pos += context_size; + } + if (fence_size) { + ret = dxg_copy_from_user(current_pos, fences, fence_size); + if (ret) { + pr_err("Failed to read fences %p %d", + fences, fence_size); + goto cleanup; + } + } + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_wait_sync_object_cpu(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct + d3dkmt_waitforsynchronizationobjectfromcpu + *args, u64 cpu_event) +{ + int ret = 0; + struct dxgkvmb_command_waitforsyncobjectfromcpu *command = NULL; + uint object_size = args->object_count * sizeof(d3dkmt_handle); + uint fence_size = args->object_count * sizeof(uint64_t); + uint8_t *current_pos; + uint cmd_size = sizeof(*command) + object_size + fence_size; + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMCPU, + process->host_handle); + command->device = args->device; + command->flags = args->flags; + command->object_count = args->object_count; + command->guest_event_pointer = (uint64_t) cpu_event; + current_pos = (uint8_t *) &command[1]; + ret = dxg_copy_from_user(current_pos, args->objects, object_size); + if (ret) + goto cleanup; + current_pos += object_size; + ret = dxg_copy_from_user(current_pos, args->fence_values, fence_size); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_wait_sync_object_gpu(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, uint object_count, + d3dkmt_handle *objects, uint64_t *fences, + bool legacy_fence) +{ + ntstatus status; + struct dxgkvmb_command_waitforsyncobjectfromgpu *command = NULL; + uint fence_size = object_count * sizeof(uint64_t); + uint object_size = object_count * sizeof(d3dkmt_handle); + uint8_t *current_pos; + uint cmd_size = object_size + fence_size - sizeof(uint64_t) + + sizeof(struct dxgkvmb_command_waitforsyncobjectfromgpu); + + if (object_count == 0 || object_count > D3DDDI_MAX_OBJECT_WAITED_ON) { + status = STATUS_INVALID_PARAMETER; + goto cleanup; + } + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + status = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU, + process->host_handle); + command->context = context; + command->object_count = object_count; + command->legacy_fence_object = legacy_fence; + current_pos = (uint8_t *) command->fence_values; + memcpy(current_pos, fences, fence_size); + current_pos += fence_size; + memcpy(current_pos, objects, object_size); + + status = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_lock2(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_lock2 *args, + struct d3dkmt_lock2 *__user outargs) +{ + int ret = 0; + struct dxgkvmb_command_lock2 command = { }; + struct dxgkvmb_command_lock2_return result = { }; + struct dxgallocation *alloc = NULL; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_LOCK2, process->host_handle); + command.args = *args; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (ret) + goto cleanup; + if (!NT_SUCCESS(result.status)) { + ret = result.status; + goto cleanup; + } + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + alloc = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGALLOCATION, + args->allocation); + if (alloc == NULL || alloc->cpu_address || alloc->cpu_address_refcount + || alloc->cpu_address_mapped) { + pr_err("%s invalid alloc", __func__); + ret = STATUS_INVALID_PARAMETER; + } else { + args->data = dxg_map_iospace((uint64_t) result. + cpu_visible_buffer_offset, + alloc->num_pages << PAGE_SHIFT, + PROT_READ | PROT_WRITE, + alloc->cached); + if (args->data == NULL) { + ret = STATUS_NO_MEMORY; + } else { + ret = dxg_copy_to_user(&outargs->data, &args->data, + sizeof(args->data)); + if (!ret) { + alloc->cpu_address = args->data; + alloc->cpu_address_mapped = true; + alloc->cpu_address_refcount = 1; + } else { + dxg_unmap_iospace(alloc->cpu_address, + alloc-> + num_pages << PAGE_SHIFT); + } + } + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_unlock2(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_unlock2 *args) +{ + ntstatus status; + struct dxgkvmb_command_unlock2 command = { }; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_UNLOCK2, + process->host_handle); + command.args = *args; + + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_update_alloc_property(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dddi_updateallocproperty *args, + struct d3dddi_updateallocproperty *__user + inargs) +{ + int ret; + ntstatus status; + struct dxgkvmb_command_updateallocationproperty command = { }; + struct dxgkvmb_command_updateallocationproperty_return result = { }; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_UPDATEALLOCATIONPROPERTY, + process->host_handle); + command.args = *args; + + status = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (status == STATUS_PENDING) { + ret = dxg_copy_to_user(&inargs->paging_fence_value, + &result.paging_fence_value, + sizeof(uint64_t)); + if (ret) + status = ret; + } + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_mark_device_as_error(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_markdeviceaserror *args) +{ + struct dxgkvmb_command_markdeviceaserror command = { }; + ntstatus status; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_MARKDEVICEASERROR, + process->host_handle); + command.args = *args; + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_set_allocation_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_setallocationpriority + *args) +{ + uint cmd_size = sizeof(struct dxgkvmb_command_setallocationpriority); + uint alloc_size = 0; + uint priority_size = 0; + struct dxgkvmb_command_setallocationpriority *command = NULL; + int ret = 0; + d3dkmt_handle *allocations; + + if (args->allocation_count > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (args->resource) { + priority_size = sizeof(uint); + if (args->allocation_count != 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } else { + if (args->allocation_count == 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + alloc_size = args->allocation_count * sizeof(d3dkmt_handle); + cmd_size += alloc_size; + priority_size = sizeof(uint) * args->allocation_count; + } + cmd_size += priority_size; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_SETALLOCATIONPRIORITY, + process->host_handle); + command->device = args->device; + command->allocation_count = args->allocation_count; + command->resource = args->resource; + allocations = (d3dkmt_handle *) &command[1]; + ret = dxg_copy_from_user(allocations, args->allocation_list, + alloc_size); + if (ret) + goto cleanup; + ret = dxg_copy_from_user((uint8_t *) allocations + alloc_size, + args->priorities, priority_size); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_get_allocation_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_getallocationpriority + *args) +{ + uint cmd_size = sizeof(struct dxgkvmb_command_getallocationpriority); + uint result_size; + uint alloc_size = 0; + uint priority_size = 0; + struct dxgkvmb_command_getallocationpriority *command = NULL; + struct dxgkvmb_command_getallocationpriority_return *result; + int ret = 0; + d3dkmt_handle *allocations; + + if (args->allocation_count > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (args->resource) { + priority_size = sizeof(uint); + if (args->allocation_count != 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } else { + if (args->allocation_count == 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + alloc_size = args->allocation_count * sizeof(d3dkmt_handle); + cmd_size += alloc_size; + priority_size = sizeof(uint) * args->allocation_count; + } + result_size = + sizeof(struct dxgkvmb_command_getallocationpriority_return) + + priority_size; + cmd_size += result_size; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_GETALLOCATIONPRIORITY, + process->host_handle); + command->device = args->device; + command->allocation_count = args->allocation_count; + command->resource = args->resource; + allocations = (d3dkmt_handle *) &command[1]; + ret = dxg_copy_from_user(allocations, args->allocation_list, + alloc_size); + if (ret) + goto cleanup; + + result = (void *)((uint8_t *) &command[1] + alloc_size); + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + result, result_size); + if (ret) + goto cleanup; + if (!NT_SUCCESS(result->status)) { + ret = result->status; + goto cleanup; + } + + ret = dxg_copy_to_user(args->priorities, (uint8_t *) result + + sizeof(struct + dxgkvmb_command_getallocationpriority_return), + priority_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_set_context_scheduling_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, + int priority, bool in_process) +{ + struct dxgkvmb_command_setcontextschedulingpriority2 command = { }; + ntstatus status; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_SETCONTEXTSCHEDULINGPRIORITY, + process->host_handle); + command.context = context; + command.priority = priority; + command.in_process = in_process; + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_get_context_scheduling_priority(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle context, + int *priority, bool in_process) +{ + struct dxgkvmb_command_getcontextschedulingpriority command = { }; + struct dxgkvmb_command_getcontextschedulingpriority_return result = { }; + int ret; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_GETCONTEXTSCHEDULINGPRIORITY, + process->host_handle); + command.context = context; + command.in_process = in_process; + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (!ret) { + ret = result.status; + *priority = result.priority; + } + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_offer_allocations(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_offerallocations *args) +{ + struct dxgkvmb_command_offerallocations *command; + int ret = 0; + uint alloc_size = sizeof(d3dkmt_handle) * args->allocation_count; + uint cmd_size = sizeof(struct dxgkvmb_command_offerallocations) + + alloc_size - sizeof(d3dkmt_handle); + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_OFFERALLOCATIONS, + process->host_handle); + command->flags = args->flags; + command->priority = args->priority; + command->device = args->device; + command->allocation_count = args->allocation_count; + if (args->resources) { + command->resources = true; + ret = dxg_copy_from_user(command->allocations, args->resources, + alloc_size); + } else { + ret = dxg_copy_from_user(command->allocations, + args->allocations, alloc_size); + } + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_reclaim_allocations(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle device, + struct d3dkmt_reclaimallocations2 *args, + uint64_t * __user paging_fence_value) +{ + struct dxgkvmb_command_reclaimallocations *command = NULL; + struct dxgkvmb_command_reclaimallocations_return *result = NULL; + int ret = 0; + uint alloc_size = sizeof(d3dkmt_handle) * args->allocation_count; + uint cmd_size = sizeof(struct dxgkvmb_command_reclaimallocations) + + alloc_size - sizeof(d3dkmt_handle); + uint result_size = sizeof(*result); + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_RECLAIMALLOCATIONS, + process->host_handle); + command->device = device; + command->paging_queue = args->paging_queue; + command->allocation_count = args->allocation_count; + command->write_results = args->results != NULL; + if (args->resources) { + command->resources = true; + ret = dxg_copy_from_user(command->allocations, args->resources, + alloc_size); + } else { + ret = dxg_copy_from_user(command->allocations, + args->allocations, alloc_size); + } + if (ret) + goto cleanup; + + if (command->write_results) + result_size += (args->allocation_count - 1) * + sizeof(enum d3dddi_reclaim_result); + result = dxgmem_alloc(process, DXGMEM_VMBUS, result_size); + if (result == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + result, result_size); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(paging_fence_value, + &result->paging_fence_value, sizeof(uint64_t)); + if (ret) + goto cleanup; + + ret = result->status; + if (NT_SUCCESS(result->status) && args->results) + ret = dxg_copy_to_user(args->results, result->discarded, + sizeof(result->discarded[0]) * + args->allocation_count); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + if (result) + dxgmem_free(process, DXGMEM_VMBUS, result); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_change_vidmem_reservation(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle other_process, + struct + d3dkmt_changevideomemoryreservation + *args) +{ + struct dxgkvmb_command_changevideomemoryreservation command = { }; + ntstatus status; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_CHANGEVIDEOMEMORYRESERVATION, + process->host_handle); + command.args = *args; + command.args.process = other_process; + + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_create_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_createhwqueue *args, + struct d3dkmt_createhwqueue *__user inargs, + struct dxghwqueue *hwqueue) +{ + struct dxgkvmb_command_createhwqueue command_on_stack = { }; + struct dxgkvmb_command_createhwqueue *command = &command_on_stack; + uint cmd_size = sizeof(struct dxgkvmb_command_createhwqueue); + int ret = 0; + bool command_allocated = false; + + if (args->priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("invalid private driver data size"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args->priv_drv_data_size) + cmd_size += args->priv_drv_data_size - 1; + + /* Input command is returned back as output */ + cmd_size = DXGK_DECL_VMBUS_ALIGN_FOR_OUTPUT(cmd_size); + + if (args->priv_drv_data_size) { + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + pr_err("failed to allocate memory"); + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_allocated = true; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_CREATEHWQUEUE, + process->host_handle); + command->context = args->context; + command->flags = args->flags; + command->priv_drv_data_size = args->priv_drv_data_size; + if (args->priv_drv_data_size) { + ret = dxg_copy_from_user(command->priv_drv_data, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + } + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + command, cmd_size); + if (ret) + goto cleanup; + + if (!NT_SUCCESS(command->status)) { + ret = command->status; + goto cleanup; + } + + ret = hmgrtable_assign_handle_safe(&process->handle_table, hwqueue, + HMGRENTRY_TYPE_DXGHWQUEUE, + command->hwqueue); + if (ret) + goto cleanup; + + hwqueue->handle = command->hwqueue; + + hwqueue->progress_fence_mapped_address = + dxg_map_iospace((unsigned long)command-> + hwqueue_progress_fence_cpuva, PAGE_SIZE, + PROT_READ | PROT_WRITE, true); + if (hwqueue->progress_fence_mapped_address == NULL) + goto cleanup; + + hwqueue->progress_fence_sync_object = command->hwqueue_progress_fence; + + ret = dxg_copy_to_user(&inargs->queue, &command->hwqueue, + sizeof(d3dkmt_handle)); + ret |= dxg_copy_to_user(&inargs->queue_progress_fence, + &command->hwqueue_progress_fence, + sizeof(d3dkmt_handle)); + ret |= + dxg_copy_to_user(&inargs->queue_progress_fence_cpu_va, + &hwqueue->progress_fence_mapped_address, + sizeof(inargs->queue_progress_fence_cpu_va)); + ret |= + dxg_copy_to_user(&inargs->queue_progress_fence_gpu_va, + &command->hwqueue_progress_fence_gpuva, + sizeof(uint64_t)); + if (args->priv_drv_data_size) + ret |= dxg_copy_to_user(args->priv_drv_data, + command->priv_drv_data, + args->priv_drv_data_size); + +cleanup: + if (ret) { + pr_err("%s failed %x", __func__, ret); + if (hwqueue->handle) { + hmgrtable_free_handle_safe(&process->handle_table, + HMGRENTRY_TYPE_DXGHWQUEUE, + hwqueue->handle); + hwqueue->handle = 0; + } + if (command->hwqueue) + dxgvmb_send_destroy_hwqueue(process, channel, + command->hwqueue); + } + if (command_allocated) + dxgmem_free(process, DXGMEM_VMBUS, command); + return ret; +} + +int dxgvmb_send_destroy_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle handle) +{ + ntstatus status; + struct dxgkvmb_command_destroyhwqueue command = { }; + + command_vgpu_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_DESTROYHWQUEUE, + process->host_handle); + command.hwqueue = handle; + + status = dxgvmb_send_sync_msg_ntstatus(channel, &command, + sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_query_adapter_info(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryadapterinfo *args) +{ + struct dxgkvmb_command_queryadapterinfo *command = NULL; + uint cmd_size; + int ret = 0; + + cmd_size = sizeof(*command) + args->private_data_size - 1; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxg_copy_from_user(command->private_data, + args->private_data, args->private_data_size); + if (ret) + goto cleanup; + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_QUERYADAPTERINFO, + process->host_handle); + command->private_data_size = args->private_data_size; + command->query_type = args->type; + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + command->private_data, + command->private_data_size); + if (ret) + goto cleanup; + switch (args->type) { + case KMTQAITYPE_ADAPTERTYPE: + case KMTQAITYPE_ADAPTERTYPE_RENDER: + { + struct d3dkmt_adaptertype *adapter_type = + (void *)command->private_data; + adapter_type->paravirtualized = 1; + adapter_type->display_supported = 0; + adapter_type->post_device = 0; + adapter_type->indirect_display_device = 0; + adapter_type->acg_supported = 0; + adapter_type->support_set_timings_from_vidpn = 0; + break; + } + default: + break; + } + ret = dxg_copy_to_user(args->private_data, command->private_data, + args->private_data_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_submit_command_to_hwqueue(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_submitcommandtohwqueue + *args) +{ + int ret = 0; + uint cmd_size; + struct dxgkvmb_command_submitcommandtohwqueue *command = NULL; + uint primaries_size = args->num_primaries * sizeof(d3dkmt_handle); + + cmd_size = sizeof(*command) + args->priv_drv_data_size + primaries_size; + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + if (primaries_size) { + ret = dxg_copy_from_user(&command[1], args->written_primaries, + primaries_size); + if (ret) + goto cleanup; + } + if (args->priv_drv_data_size) { + ret = dxg_copy_from_user((char *)&command[1] + primaries_size, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_SUBMITCOMMANDTOHWQUEUE, + process->host_handle); + command->args = *args; + + ret = dxgvmb_send_sync_msg_ntstatus(channel, command, cmd_size); + +cleanup: + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_query_clock_calibration(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryclockcalibration + *args, + struct d3dkmt_queryclockcalibration + *__user inargs) +{ + struct dxgkvmb_command_queryclockcalibration command; + struct dxgkvmb_command_queryclockcalibration_return result; + int ret; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_QUERYCLOCKCALIBRATION, + process->host_handle); + command.args = *args; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(&inargs->clock_data, &result.clock_data, + sizeof(result.clock_data)); + if (ret) + goto cleanup; + ret = result.status; + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_flush_heap_transitions(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_flushheaptransitions *args) +{ + struct dxgkvmb_command_flushheaptransitions command; + ntstatus status; + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_FLUSHHEAPTRANSITIONS, + process->host_handle); + status = + dxgvmb_send_sync_msg_ntstatus(channel, &command, sizeof(command)); + TRACE_FUNC_EXIT_ERR(__func__, status); + return status; +} + +int dxgvmb_send_query_alloc_residency(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryallocationresidency + *args) +{ + int ret = 0; + struct dxgkvmb_command_queryallocationresidency *command = NULL; + uint cmd_size = sizeof(*command); + uint alloc_size = 0; + uint result_allocation_size = 0; + struct dxgkvmb_command_queryallocationresidency_return *result = NULL; + uint result_size = sizeof(*result); + + if (args->allocation_count > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args->allocation_count) { + alloc_size = args->allocation_count * sizeof(d3dkmt_handle); + cmd_size += alloc_size; + result_allocation_size = args->allocation_count * + sizeof(args->residency_status[0]); + } else { + result_allocation_size = sizeof(args->residency_status[0]); + } + result_size += result_allocation_size; + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_QUERYALLOCATIONRESIDENCY, + process->host_handle); + command->args = *args; + if (alloc_size) { + ret = dxg_copy_from_user(&command[1], args->allocations, + alloc_size); + if (ret) + goto cleanup; + } + + result = dxgmem_alloc(process, DXGMEM_VMBUS, result_size); + if (result == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + result, result_size); + if (ret) + goto cleanup; + if (!NT_SUCCESS(result->status)) { + ret = result->status; + goto cleanup; + } + ret = dxg_copy_to_user(args->residency_status, &result[1], + result_allocation_size); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + if (result) + dxgmem_free(process, DXGMEM_VMBUS, result); + return ret; +} + +int dxgvmb_send_escape(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_escape *args) +{ + int ret = 0; + struct dxgkvmb_command_escape *command = NULL; + uint cmd_size = sizeof(*command); + + if (args->priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + cmd_size = cmd_size - sizeof(args->priv_drv_data[0]) + + args->priv_drv_data_size; + + command = dxgmem_alloc(process, DXGMEM_VMBUS, cmd_size); + if (command == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_ESCAPE, + process->host_handle); + command->adapter = args->adapter; + command->device = args->device; + command->type = args->type; + command->flags = args->flags; + command->priv_drv_data_size = args->priv_drv_data_size; + command->context = args->context; + if (args->priv_drv_data_size) + ret = dxg_copy_from_user(command->priv_drv_data, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg(channel, command, cmd_size, + command->priv_drv_data, + args->priv_drv_data_size); + if (ret) + goto cleanup; + + if (args->priv_drv_data_size) + ret = dxg_copy_to_user(args->priv_drv_data, + command->priv_drv_data, + args->priv_drv_data_size); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + if (command) + dxgmem_free(process, DXGMEM_VMBUS, command); + return ret; +} + +int dxgvmb_send_query_vidmem_info(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_queryvideomemoryinfo *args, + struct d3dkmt_queryvideomemoryinfo *__user + output) +{ + int ret = 0; + struct dxgkvmb_command_queryvideomemoryinfo command = { }; + struct dxgkvmb_command_queryvideomemoryinfo_return result = { }; + + command_vgpu_to_host_init2(&command.hdr, + dxgk_vmbcommand_queryvideomemoryinfo, + process->host_handle); + command.adapter = args->adapter; + command.memory_segment_group = args->memory_segment_group; + command.physical_adapter_index = args->physical_adapter_index; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&output->budget, &result.budget, + sizeof(output->budget)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(&output->current_usage, &result.current_usage, + sizeof(output->current_usage)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(&output->current_reservation, + &result.current_reservation, + sizeof(output->current_reservation)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(&output->available_for_reservation, + &result.available_for_reservation, + sizeof(output->available_for_reservation)); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_get_device_state(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_getdevicestate *args, + struct d3dkmt_getdevicestate *__user output) +{ + int ret; + struct dxgkvmb_command_getdevicestate command = { }; + struct dxgkvmb_command_getdevicestate_return result = { }; + + command_vgpu_to_host_init2(&command.hdr, + dxgk_vmbcommand_getdevicestate, + process->host_handle); + command.args = *args; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + if (ret) + goto cleanup; + + if (!NT_SUCCESS(result.status)) { + ret = result.status; + goto cleanup; + } + ret = dxg_copy_to_user(output, &result.args, sizeof(result.args)); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_open_sync_object(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle shared_handle, + d3dkmt_handle *host_handle) +{ + struct dxgkvmb_command_opensyncobject command = { }; + struct dxgkvmb_command_opensyncobject_return result = { }; + int ret = 0; + + command_vm_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_OPENSYNCOBJECT, + process->host_handle); + command.global_sync_object = shared_handle; + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + + dxgglobal_release_channel_lock(); + + if (ret) + goto cleanup; + + if (!NT_SUCCESS(result.status)) { + ret = result.status; + goto cleanup; + } + + *host_handle = result.sync_object; + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_open_sync_object_nt(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + struct d3dkmt_opensyncobjectfromnthandle2 + *args, struct dxgsyncobject *syncobj) +{ + struct dxgkvmb_command_opensyncobject command = { }; + struct dxgkvmb_command_opensyncobject_return result = { }; + int ret = 0; + + command_vm_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_OPENSYNCOBJECT, + process->host_handle); + command.device = args->device; + command.global_sync_object = syncobj->shared_owner->host_shared_handle; + command.flags = args->flags; + if (syncobj->monitored_fence) + command.engine_affinity = args->monitored_fence.engine_affinity; + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + &result, sizeof(result)); + + dxgglobal_release_channel_lock(); + + if (ret) + goto cleanup; + + if (!NT_SUCCESS(result.status)) { + ret = result.status; + goto cleanup; + } + + args->sync_object = result.sync_object; + if (syncobj->monitored_fence) { + void *va = dxg_map_iospace(result.guest_cpu_physical_address, + PAGE_SIZE, PROT_READ | PROT_WRITE, + true); + if (va == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + args->monitored_fence.fence_value_cpu_va = va; + args->monitored_fence.fence_value_gpu_va = + result.gpu_virtual_address; + syncobj->mapped_address = va; + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_create_nt_shared_object(struct dxgprocess *process, + d3dkmt_handle object, + d3dkmt_handle *shared_handle) +{ + struct dxgkvmb_command_createntsharedobject command = { }; + int ret; + + command_vm_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_CREATENTSHAREDOBJECT, + process->host_handle); + command.object = object; + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg(dxgglobal_get_dxgvmbuschannel(), + &command, sizeof(command), shared_handle, + sizeof(*shared_handle)); + + dxgglobal_release_channel_lock(); + + if (ret) + goto cleanup; + if (*shared_handle == 0) { + pr_err("failed to create NT shared object"); + ret = STATUS_INTERNAL_ERROR; + } + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_destroy_nt_shared_object(d3dkmt_handle shared_handle) +{ + struct dxgkvmb_command_destroyntsharedobject command = { }; + int ret; + + command_vm_to_host_init1(&command.hdr, + DXGK_VMBCOMMAND_DESTROYNTSHAREDOBJECT); + command.shared_handle = shared_handle; + + ret = dxgglobal_acquire_channel_lock(); + if (ret) + goto cleanup; + + ret = dxgvmb_send_sync_msg_ntstatus(dxgglobal_get_dxgvmbuschannel(), + &command, sizeof(command)); + + dxgglobal_release_channel_lock(); + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +int dxgvmb_send_open_resource(struct dxgprocess *process, + struct dxgvmbuschannel *channel, + d3dkmt_handle device, + bool nt_security_sharing, + d3dkmt_handle global_share, + uint allocation_count, + uint total_priv_drv_data_size, + d3dkmt_handle *resource_handle, + d3dkmt_handle *alloc_handles) +{ + struct dxgkvmb_command_openresource command = { }; + struct dxgkvmb_command_openresource_return *result = NULL; + d3dkmt_handle *handles; + int ret = 0; + int i; + uint result_size = allocation_count * sizeof(d3dkmt_handle) + + sizeof(*result); + + result = dxgmem_alloc(process, DXGMEM_VMBUS, result_size); + if (result == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command.hdr, DXGK_VMBCOMMAND_OPENRESOURCE, + process->host_handle); + command.device = device; + command.nt_security_sharing = nt_security_sharing; + command.global_share = global_share; + command.allocation_count = allocation_count; + command.total_priv_drv_data_size = total_priv_drv_data_size; + + ret = dxgvmb_send_sync_msg(channel, &command, sizeof(command), + result, result_size); + if (ret) + goto cleanup; + if (!NT_SUCCESS(result->status)) { + ret = result->status; + goto cleanup; + } + + *resource_handle = result->resource; + handles = (d3dkmt_handle *) &result[1]; + for (i = 0; i < allocation_count; i++) + alloc_handles[i] = handles[i]; + +cleanup: + TRACE_FUNC_EXIT_ERR(__func__, ret); + if (result) + dxgmem_free(process, DXGMEM_VMBUS, result); + return ret; +} + +int dxgvmb_send_get_standard_alloc_priv_data(struct dxgdevice *device, + enum d3dkmdt_standardallocationtype + alloc_type, + struct d3dkmdt_gdisurfacedata + *alloc_data, + uint physical_adapter_index, + uint *alloc_priv_driver_size, + void *priv_alloc_data) +{ + struct dxgkvmb_command_getstandardallocprivdata command = { }; + struct dxgkvmb_command_getstandardallocprivdata_return *result = NULL; + uint result_size = sizeof(*result); + int ret = 0; + + TRACE_DEBUG(1, "%s", __func__); + + if (priv_alloc_data) { + result_size = *alloc_priv_driver_size; + } + result = dxgmem_alloc(device->process, DXGMEM_VMBUS, result_size); + if (result == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + command_vgpu_to_host_init2(&command.hdr, + DXGK_VMBCOMMAND_DDIGETSTANDARDALLOCATIONDRIVERDATA, + device->process->host_handle); + + command.alloc_type = alloc_type; + command.priv_driver_data_size = *alloc_priv_driver_size; + command.physical_adapter_index = physical_adapter_index; + switch (alloc_type) { + case D3DKMDT_STANDARDALLOCATION_GDISURFACE: + command.gdi_surface = *alloc_data; + break; + case D3DKMDT_STANDARDALLOCATION_SHAREDPRIMARYSURFACE: + case D3DKMDT_STANDARDALLOCATION_SHADOWSURFACE: + case D3DKMDT_STANDARDALLOCATION_STAGINGSURFACE: + default: + pr_err("Invalid standard alloc type"); + goto cleanup; + } + + ret = dxgvmb_send_sync_msg(&device->adapter->channel, + &command, sizeof(command), result, + result_size); + if (ret) + goto cleanup; + if (!NT_SUCCESS(result->status)) { + ret = result->status; + goto cleanup; + } + if (*alloc_priv_driver_size && + result->priv_driver_data_size != *alloc_priv_driver_size) { + pr_err("Priv data size mismatch"); + goto cleanup; + } + *alloc_priv_driver_size = result->priv_driver_data_size; + if (priv_alloc_data) { + memcpy(priv_alloc_data, &result[1], + result->priv_driver_data_size); + } + +cleanup: + + if (result) + dxgmem_free(device->process, DXGMEM_VMBUS, result); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} diff --git a/drivers/gpu/dxgkrnl/dxgvmbus.h b/drivers/gpu/dxgkrnl/dxgvmbus.h new file mode 100644 index 000000000000..70f7b36d3245 --- /dev/null +++ b/drivers/gpu/dxgkrnl/dxgvmbus.h @@ -0,0 +1,859 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver + * VM bus interface with the host definitions + * + */ + +#ifndef _DXGVMBUS_H +#define _DXGVMBUS_H + +#include "d3dkmthk.h" + +struct dxgprocess; +struct dxgadapter; + +#define DXG_MAX_VM_BUS_PACKET_SIZE (1024 * 128) + +#define DXGK_DECL_VMBUS_OUTPUTSIZE(Type)\ + ((sizeof(##Type) + 0x7) & ~(uint)0x7) +#define DXGK_DECL_VMBUS_ALIGN_FOR_OUTPUT(Size) (((Size) + 0x7) & ~(uint)0x7) +/* + * Defines a structure, which has the size, multiple of 8 bytes. + */ +#define DXGK_DECL_ALIGNED8_STRUCT(Type, Name, OutputSize) \ + const uint _Size = DXGK_DECL_VMBUS_OUTPUTSIZE(Type); \ + uint8_t _AlignedStruct[_Size]; \ + ##Type & Name = (##Type &)_AlignedStruct; \ + uint OutputSize = _Size + +#define DXGK_BUFFER_VMBUS_ALIGNED(Buffer) (((Buffer) & 7) == 0) + +enum dxgkvmb_commandchanneltype { + DXGKVMB_VGPU_TO_HOST, + DXGKVMB_VM_TO_HOST, + DXGKVMB_HOST_TO_VM +}; + +/* + * + * Commands, sent to the host via the guest global VM bus channel + * DXG_GUEST_GLOBAL_VMBUS + * + */ + +#define DXG_VM_PROCESS_NAME_LENGTH 260 + +enum dxgkvmb_commandtype_global { + DXGK_VMBCOMMAND_VM_TO_HOST_FIRST = 1000, + DXGK_VMBCOMMAND_CREATEPROCESS = DXGK_VMBCOMMAND_VM_TO_HOST_FIRST, + DXGK_VMBCOMMAND_DESTROYPROCESS = 1001, + DXGK_VMBCOMMAND_OPENSYNCOBJECT = 1002, + DXGK_VMBCOMMAND_DESTROYSYNCOBJECT = 1003, + DXGK_VMBCOMMAND_CREATENTSHAREDOBJECT = 1004, + DXGK_VMBCOMMAND_DESTROYNTSHAREDOBJECT = 1005, + DXGK_VMBCOMMAND_SIGNALFENCE = 1006, + DXGK_VMBCOMMAND_NOTIFYPROCESSFREEZE = 1007, + DXGK_VMBCOMMAND_NOTIFYPROCESSTHAW = 1008, + DXGK_VMBCOMMAND_QUERYETWSESSION = 1009, + DXGK_VMBCOMMAND_SETIOSPACEREGION = 1010, + DXGK_VMBCOMMAND_COMPLETETRANSACTION = 1011, + DXGK_VMBCOMMAND_INVALID_VM_TO_HOST +}; + +/* + * + * Commands, sent to the host via the per adapter VM bus channel + * DXG_GUEST_VGPU_VMBUS + * + */ + +enum dxgkvmb_commandtype { + DXGK_VMBCOMMAND_CREATEDEVICE = 0, + DXGK_VMBCOMMAND_DESTROYDEVICE = 1, + DXGK_VMBCOMMAND_QUERYADAPTERINFO = 2, + DXGK_VMBCOMMAND_DDIQUERYADAPTERINFO = 3, + DXGK_VMBCOMMAND_CREATEALLOCATION = 4, + DXGK_VMBCOMMAND_DESTROYALLOCATION = 5, + DXGK_VMBCOMMAND_CREATECONTEXTVIRTUAL = 6, + DXGK_VMBCOMMAND_DESTROYCONTEXT = 7, + DXGK_VMBCOMMAND_CREATESYNCOBJECT = 8, + DXGK_VMBCOMMAND_CREATEPAGINGQUEUE = 9, + DXGK_VMBCOMMAND_DESTROYPAGINGQUEUE = 10, + DXGK_VMBCOMMAND_MAKERESIDENT = 11, + DXGK_VMBCOMMAND_EVICT = 12, + DXGK_VMBCOMMAND_ESCAPE = 13, + DXGK_VMBCOMMAND_OPENADAPTER = 14, + DXGK_VMBCOMMAND_CLOSEADAPTER = 15, + DXGK_VMBCOMMAND_FREEGPUVIRTUALADDRESS = 16, + DXGK_VMBCOMMAND_MAPGPUVIRTUALADDRESS = 17, + DXGK_VMBCOMMAND_RESERVEGPUVIRTUALADDRESS = 18, + DXGK_VMBCOMMAND_UPDATEGPUVIRTUALADDRESS = 19, + DXGK_VMBCOMMAND_SUBMITCOMMAND = 20, + dxgk_vmbcommand_queryvideomemoryinfo = 21, + DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMCPU = 22, + DXGK_VMBCOMMAND_LOCK2 = 23, + DXGK_VMBCOMMAND_UNLOCK2 = 24, + DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU = 25, + DXGK_VMBCOMMAND_SIGNALSYNCOBJECT = 26, + DXGK_VMBCOMMAND_SIGNALFENCENTSHAREDBYREF = 27, + dxgk_vmbcommand_getdevicestate = 28, + DXGK_VMBCOMMAND_MARKDEVICEASERROR = 29, + DXGK_VMBCOMMAND_ADAPTERSTOP = 30, + DXGK_VMBCOMMAND_SETQUEUEDLIMIT = 31, + DXGK_VMBCOMMAND_OPENRESOURCE = 32, + DXGK_VMBCOMMAND_SETCONTEXTSCHEDULINGPRIORITY = 33, + DXGK_VMBCOMMAND_PRESENTHISTORYTOKEN = 34, + DXGK_VMBCOMMAND_SETREDIRECTEDFLIPFENCEVALUE = 35, + DXGK_VMBCOMMAND_GETINTERNALADAPTERINFO = 36, + DXGK_VMBCOMMAND_FLUSHHEAPTRANSITIONS = 37, + DXGK_VMBCOMMAND_BLT = 38, + DXGK_VMBCOMMAND_DDIGETSTANDARDALLOCATIONDRIVERDATA = 39, + DXGK_VMBCOMMAND_CDDGDICOMMAND = 40, + DXGK_VMBCOMMAND_QUERYALLOCATIONRESIDENCY = 41, + DXGK_VMBCOMMAND_FLUSHDEVICE = 42, + DXGK_VMBCOMMAND_FLUSHADAPTER = 43, + DXGK_VMBCOMMAND_DDIGETNODEMETADATA = 44, + DXGK_VMBCOMMAND_SETEXISTINGSYSMEMSTORE = 45, + DXGK_VMBCOMMAND_ISSYNCOBJECTSIGNALED = 46, + DXGK_VMBCOMMAND_CDDSYNCGPUACCESS = 47, + DXGK_VMBCOMMAND_QUERYSTATISTICS = 48, + DXGK_VMBCOMMAND_CHANGEVIDEOMEMORYRESERVATION = 49, + DXGK_VMBCOMMAND_CREATEHWQUEUE = 50, + DXGK_VMBCOMMAND_DESTROYHWQUEUE = 51, + DXGK_VMBCOMMAND_SUBMITCOMMANDTOHWQUEUE = 52, + DXGK_VMBCOMMAND_GETDRIVERSTOREFILE = 53, + DXGK_VMBCOMMAND_READDRIVERSTOREFILE = 54, + DXGK_VMBCOMMAND_GETNEXTHARDLINK = 55, + DXGK_VMBCOMMAND_UPDATEALLOCATIONPROPERTY = 56, + DXGK_VMBCOMMAND_OFFERALLOCATIONS = 57, + DXGK_VMBCOMMAND_RECLAIMALLOCATIONS = 58, + DXGK_VMBCOMMAND_SETALLOCATIONPRIORITY = 59, + DXGK_VMBCOMMAND_GETALLOCATIONPRIORITY = 60, + DXGK_VMBCOMMAND_GETCONTEXTSCHEDULINGPRIORITY = 61, + DXGK_VMBCOMMAND_QUERYCLOCKCALIBRATION = 62, + DXGK_VMBCOMMAND_QUERYRESOURCEINFO = 64, + DXGK_VMBCOMMAND_INVALID +}; + +enum dxgkvmb_commandtype_host_to_vm { + DXGK_VMBCOMMAND_SIGNALGUESTEVENT, + DXGK_VMBCOMMAND_PROPAGATEPRESENTHISTORYTOKEN, + DXGK_VMBCOMMAND_SETGUESTDATA, + DXGK_VMBCOMMAND_SIGNALGUESTEVENTPASSIVE, + DXGK_VMBCOMMAND_SENDWNFNOTIFICATION, + DXGK_VMBCOMMAND_INVALID_HOST_TO_VM +}; + +struct dxgkvmb_command_vm_to_host { + uint64_t command_id; + d3dkmt_handle process; + enum dxgkvmb_commandchanneltype channel_type; + enum dxgkvmb_commandtype_global command_type; +}; + +struct dxgkvmb_command_vgpu_to_host { + uint64_t command_id; + d3dkmt_handle process; + u32 channel_type; + enum dxgkvmb_commandtype command_type; +}; + +struct dxgkvmb_command_host_to_vm { + uint64_t command_id; + d3dkmt_handle process; + enum dxgkvmb_commandchanneltype channel_type; + enum dxgkvmb_commandtype_host_to_vm command_type; +}; + +struct dxgkvmb_command_signalguestevent { + struct dxgkvmb_command_host_to_vm hdr; + uint64_t event; + winhandle process_id; + bool dereference_event; +}; + +struct dxgkvmb_command_opensyncobject { + struct dxgkvmb_command_vm_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle global_sync_object; + uint engine_affinity; + struct d3dddi_synchronizationobject_flags flags; +}; + +struct dxgkvmb_command_opensyncobject_return { + d3dkmt_handle sync_object; + ntstatus status; + d3dgpu_virtual_address gpu_virtual_address; + uint64_t guest_cpu_physical_address; +}; + +/* + * The command returns d3dkmt_handle of a shared object for the + * given pre-process object + */ +struct dxgkvmb_command_createntsharedobject { + struct dxgkvmb_command_vm_to_host hdr; + d3dkmt_handle object; +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_destroyntsharedobject { + struct dxgkvmb_command_vm_to_host hdr; + d3dkmt_handle shared_handle; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_setiospaceregion { + struct dxgkvmb_command_vm_to_host hdr; + uint64_t start; + uint64_t length; + uint shared_page_gpadl; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_setexistingsysmemstore { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle allocation; + uint gpadl; +}; + +struct dxgkvmb_command_createprocess { + struct dxgkvmb_command_vm_to_host hdr; + void *process; + winhandle process_id; + winwchar process_name[DXG_VM_PROCESS_NAME_LENGTH + 1]; + bool csrss_process:1; + bool dwm_process:1; + bool wow64_process:1; + bool linux_process:1; +}; + +struct dxgkvmb_command_createprocess_return { + d3dkmt_handle hprocess; +}; + +// The command returns ntstatus +struct dxgkvmb_command_destroyprocess { + struct dxgkvmb_command_vm_to_host hdr; +}; + +struct dxgkvmb_command_openadapter { + struct dxgkvmb_command_vgpu_to_host hdr; + uint vmbus_interface_version; + uint vmbus_last_compatible_interface_version; + struct winluid guest_adapter_luid; +}; + +struct dxgkvmb_command_openadapter_return { + d3dkmt_handle host_adapter_handle; + ntstatus status; + uint vmbus_interface_version; + uint vmbus_last_compatible_interface_version; +}; + +struct dxgkvmb_command_closeadapter { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle host_handle; +}; + +struct dxgkvmb_command_getinternaladapterinfo { + struct dxgkvmb_command_vgpu_to_host hdr; +}; + +struct dxgkvmb_command_getinternaladapterinfo_return { + struct dxgk_device_types device_types; + uint driver_store_copy_mode; + uint driver_ddi_version; + uint secure_virtual_machine:1; + uint virtual_machine_reset:1; + uint is_vail_supported:1; + struct winluid host_adapter_luid; + winwchar device_description[80]; + winwchar device_instance_id[W_MAX_PATH]; +}; + +struct dxgkvmb_command_queryadapterinfo { + struct dxgkvmb_command_vgpu_to_host hdr; + enum kmtqueryadapterinfotype query_type; + uint private_data_size; + uint8_t private_data[1]; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_setallocationpriority { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle resource; + uint allocation_count; + /* d3dkmt_handle allocations[allocation_count or 0]; */ + /* uint priorities[allocation_count or 1]; */ +}; + +struct dxgkvmb_command_getallocationpriority { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle resource; + uint allocation_count; + /* d3dkmt_handle allocations[allocation_count or 0]; */ +}; + +struct dxgkvmb_command_getallocationpriority_return { + ntstatus status; + /* uint priorities[allocation_count or 1]; */ +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_setcontextschedulingpriority { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; + int priority; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_setcontextschedulingpriority2 { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; + int priority; + bool in_process; +}; + +struct dxgkvmb_command_getcontextschedulingpriority { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; + bool in_process; +}; + +struct dxgkvmb_command_getcontextschedulingpriority_return { + ntstatus status; + int priority; +}; + +struct dxgkvmb_command_createdevice { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_createdeviceflags flags; + bool cdd_device; + void *error_code; +}; + +struct dxgkvmb_command_createdevice_return { + d3dkmt_handle device; +}; + +struct dxgkvmb_command_destroydevice { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; +}; + +struct dxgkvmb_command_makeresident { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle paging_queue; + struct d3dddi_makeresident_flags flags; + uint alloc_count; + d3dkmt_handle allocations[1]; +}; + +struct dxgkvmb_command_makeresident_return { + uint64_t paging_fence_value; + uint64_t num_bytes_to_trim; + ntstatus status; +}; + +struct dxgkvmb_command_evict { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + struct d3dddi_evict_flags flags; + uint alloc_count; + d3dkmt_handle allocations[1]; +}; + +struct dxgkvmb_command_evict_return { + uint64_t num_bytes_to_trim; +}; + +struct dxgkvmb_command_submitcommand { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_submitcommand args; + /* HistoryBufferHandles */ + /* PrivateDriverData */ +}; + +struct dxgkvmb_command_submitcommandtohwqueue { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_submitcommandtohwqueue args; + /* Written primaries */ + /* PrivateDriverData */ +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_flushheaptransitions { + struct dxgkvmb_command_vgpu_to_host hdr; +}; + +struct dxgkvmb_command_freegpuvirtualaddress { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_freegpuvirtualaddress args; +}; + +struct dxgkvmb_command_mapgpuvirtualaddress { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dddi_mapgpuvirtualaddress args; + d3dkmt_handle device; +}; + +struct dxgkvmb_command_mapgpuvirtualaddress_return { + d3dgpu_virtual_address virtual_address; + uint64_t paging_fence_value; + ntstatus status; +}; + +struct dxgkvmb_command_reservegpuvirtualaddress { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dddi_reservegpuvirtualaddress args; +}; + +struct dxgkvmb_command_reservegpuvirtualaddress_return { + d3dgpu_virtual_address virtual_address; + uint64_t paging_fence_value; +}; + +struct dxgkvmb_command_updategpuvirtualaddress { + struct dxgkvmb_command_vgpu_to_host hdr; + uint64_t fence_value; + d3dkmt_handle device; + d3dkmt_handle context; + d3dkmt_handle fence_object; + uint num_operations; + uint flags; + struct d3dddi_updategpuvirtualaddress_operation operations[1]; +}; + +struct dxgkvmb_command_queryclockcalibration { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_queryclockcalibration args; +}; + +struct dxgkvmb_command_queryclockcalibration_return { + ntstatus status; + struct dxgk_gpuclockdata clock_data; +}; + +struct dxgkvmb_command_createallocation_allocinfo { + uint flags; + uint priv_drv_data_size; + uint vidpn_source_id; +}; + +struct dxgkvmb_command_createallocation { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle resource; + uint private_runtime_data_size; + uint priv_drv_data_size; + uint alloc_count; + struct d3dkmt_createallocationflags flags; + winhandle private_runtime_resource_handle; + bool make_resident; +/* dxgkvmb_command_createallocation_allocinfo alloc_info[alloc_count]; */ +/* uint8_t private_rutime_data[private_runtime_data_size] */ +/* uint8_t priv_drv_data[] for each alloc_info */ +}; + +struct dxgkvmb_command_openresource { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + bool nt_security_sharing; + d3dkmt_handle global_share; + uint allocation_count; + uint total_priv_drv_data_size; +}; + +struct dxgkvmb_command_openresource_return { + d3dkmt_handle resource; + ntstatus status; +/* d3dkmt_handle allocation[allocation_count]; */ +}; + +struct dxgkvmb_command_getstandardallocprivdata { + struct dxgkvmb_command_vgpu_to_host hdr; + enum d3dkmdt_standardallocationtype alloc_type; + uint priv_driver_data_size; + uint priv_driver_resource_size; + uint physical_adapter_index; + union { + struct d3dkmdt_sharedprimarysurfacedata primary; + struct d3dkmdt_shadowsurfacedata shadow; + struct d3dkmtd_stagingsurfacedata staging; + struct d3dkmdt_gdisurfacedata gdi_surface; + }; +}; + +struct dxgkvmb_command_getstandardallocprivdata_return { + ntstatus status; + uint priv_driver_data_size; + uint priv_driver_resource_size; + union { + struct d3dkmdt_sharedprimarysurfacedata primary; + struct d3dkmdt_shadowsurfacedata shadow; + struct d3dkmtd_stagingsurfacedata staging; + struct d3dkmdt_gdisurfacedata gdi_surface; + }; +/* char alloc_priv_data[priv_driver_data_size]; */ +/* char resource_priv_data[priv_driver_resource_size]; */ +}; + +struct dxgkarg_describeallocation { + winhandle allocation; + uint width; + uint height; + uint format; + uint multisample_method; + struct d3dddi_rational refresh_rate; + uint private_driver_attribute; + uint flags; + uint rotation; +}; + +struct dxgkvmb_allocflags { + union { + uint flags; + struct { + uint primary:1; + uint cdd_primary:1; + uint dod_primary:1; + uint overlay:1; + uint reserved6:1; + uint capture:1; + uint reserved0:4; + uint reserved1:1; + uint existing_sysmem:1; + uint stereo:1; + uint direct_flip:1; + uint hardware_protected:1; + uint reserved2:1; + uint reserved3:1; + uint reserved4:1; + uint protected:1; + uint cached:1; + uint independent_primary:1; + uint reserved:11; + }; + }; +}; + +struct dxgkvmb_command_allocinfo_return { + d3dkmt_handle allocation; + uint priv_drv_data_size; + struct dxgkvmb_allocflags allocation_flags; + uint64_t allocation_size; + struct dxgkarg_describeallocation driver_info; +}; + +struct dxgkvmb_command_createallocation_return { + struct d3dkmt_createallocationflags flags; + d3dkmt_handle resource; + d3dkmt_handle global_share; + uint vgpu_flags; + struct dxgkvmb_command_allocinfo_return allocation_info[1]; + /* Private driver data for allocations */ +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_destroyallocation { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle resource; + uint alloc_count; + struct d3dddicb_destroyallocation2flags flags; + d3dkmt_handle allocations[1]; +}; + +struct dxgkvmb_command_createcontextvirtual { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; + d3dkmt_handle device; + uint node_ordinal; + uint engine_affinity; + struct d3dddi_createcontextflags flags; + enum d3dkmt_clienthint client_hint; + uint priv_drv_data_size; + uint8_t priv_drv_data[1]; +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_destroycontext { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; +}; + +struct dxgkvmb_command_createpagingqueue { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_createpagingqueue args; +}; + +struct dxgkvmb_command_createpagingqueue_return { + d3dkmt_handle paging_queue; + d3dkmt_handle sync_object; + uint64_t fence_storage_physical_address; + uint64_t fence_storage_offset; +}; + +struct dxgkvmb_command_destroypagingqueue { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle paging_queue; +}; + +struct dxgkvmb_command_createsyncobject { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_createsynchronizationobject2 args; + uint client_hint; +}; + +struct dxgkvmb_command_createsyncobject_return { + d3dkmt_handle sync_object; + d3dkmt_handle global_sync_object; + d3dgpu_virtual_address fence_gpu_va; + uint64_t fence_storage_address; + uint fence_storage_offset; +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_destroysyncobject { + struct dxgkvmb_command_vm_to_host hdr; + d3dkmt_handle sync_object; +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_signalsyncobject { + struct dxgkvmb_command_vgpu_to_host hdr; + uint object_count; + struct d3dddicb_signalflags flags; + uint context_count; + uint64_t fence_value; + union { + /* Pointer to the guest event object */ + winhandle cpu_event_handle; + /* Non zero when signal from CPU is done */ + d3dkmt_handle device; + }; + /* d3dkmt_handle ObjectHandleArray[object_count] */ + /* d3dkmt_handle ContextArray[context_count] */ + /* uint64_t MonitoredFenceValueArray[object_count] */ +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_waitforsyncobjectfromcpu { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + uint object_count; + struct d3dddi_waitforsynchronizationobjectfromcpu_flags flags; + uint64_t guest_event_pointer; + bool dereference_event; + /* d3dkmt_handle ObjectHandleArray[object_count] */ + /* uint64_t FenceValueArray [object_count] */ +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_waitforsyncobjectfromgpu { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle context; + /* Must be 1 when bLegacyFenceObject is TRUE */ + uint object_count; + bool legacy_fence_object; + uint64_t fence_values[1]; + /* d3dkmt_handle ObjectHandles[object_count] */ +}; + +struct dxgkvmb_command_lock2 { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_lock2 args; + bool use_legacy_lock; + uint flags; + uint priv_drv_data; +}; + +struct dxgkvmb_command_lock2_return { + ntstatus status; + void *cpu_visible_buffer_offset; +}; + +struct dxgkvmb_command_unlock2 { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_unlock2 args; + bool use_legacy_unlock; +}; + +struct dxgkvmb_command_updateallocationproperty { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dddi_updateallocproperty args; +}; + +struct dxgkvmb_command_updateallocationproperty_return { + uint64_t paging_fence_value; + ntstatus status; +}; + +struct dxgkvmb_command_markdeviceaserror { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_markdeviceaserror args; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_offerallocations { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + uint allocation_count; + enum d3dkmt_offer_priority priority; + struct d3dkmt_offer_flags flags; + bool resources; + d3dkmt_handle allocations[1]; +}; + +struct dxgkvmb_command_reclaimallocations { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle device; + d3dkmt_handle paging_queue; + uint allocation_count; + bool resources; + bool write_results; + d3dkmt_handle allocations[1]; +}; + +struct dxgkvmb_command_reclaimallocations_return { + uint64_t paging_fence_value; + ntstatus status; + enum d3dddi_reclaim_result discarded[1]; +}; + +/* Returns ntstatus */ +struct dxgkvmb_command_changevideomemoryreservation { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_changevideomemoryreservation args; +}; + +/* Returns the same structure */ +struct dxgkvmb_command_createhwqueue { + struct dxgkvmb_command_vgpu_to_host hdr; + ntstatus status; + d3dkmt_handle hwqueue; + d3dkmt_handle hwqueue_progress_fence; + void *hwqueue_progress_fence_cpuva; + d3dgpu_virtual_address hwqueue_progress_fence_gpuva; + d3dkmt_handle context; + struct d3dddi_createhwqueueflags flags; + uint priv_drv_data_size; + uint8_t priv_drv_data[1]; +}; + +/* The command returns ntstatus */ +struct dxgkvmb_command_destroyhwqueue { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle hwqueue; +}; + +struct dxgkvmb_command_queryallocationresidency { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_queryallocationresidency args; + /* d3dkmt_handle allocations[0 or number of allocations] */ +}; + +struct dxgkvmb_command_queryallocationresidency_return { + ntstatus status; + /* d3dkmt_allocationresidencystatus[NumAllocations] */ +}; + +/* Returns only private data */ +struct dxgkvmb_command_escape { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle adapter; + d3dkmt_handle device; + enum d3dkmt_escapetype type; + struct d3dddi_escapeflags flags; + uint priv_drv_data_size; + d3dkmt_handle context; + uint8_t priv_drv_data[1]; +}; + +struct dxgkvmb_command_queryvideomemoryinfo { + struct dxgkvmb_command_vgpu_to_host hdr; + d3dkmt_handle adapter; + enum d3dkmt_memory_segment_group memory_segment_group; + uint physical_adapter_index; +}; + +struct dxgkvmb_command_queryvideomemoryinfo_return { + uint64_t budget; + uint64_t current_usage; + uint64_t current_reservation; + uint64_t available_for_reservation; +}; + +struct dxgkvmb_command_getdevicestate { + struct dxgkvmb_command_vgpu_to_host hdr; + struct d3dkmt_getdevicestate args; +}; + +struct dxgkvmb_command_getdevicestate_return { + struct d3dkmt_getdevicestate args; + ntstatus status; +}; + +/* + * Helper functions + */ +static inline void command_vm_to_host_init2(struct dxgkvmb_command_vm_to_host + *command, + enum dxgkvmb_commandtype_global + type, d3dkmt_handle process) +{ + command->command_type = type; + command->process = process; + command->command_id = 0; + command->channel_type = DXGKVMB_VM_TO_HOST; +} + +static inline void command_vgpu_to_host_init0(struct dxgkvmb_command_vm_to_host + *command) +{ + command->command_type = DXGK_VMBCOMMAND_INVALID; + command->process = 0; + command->command_id = 0; + command->channel_type = DXGKVMB_VGPU_TO_HOST; +} + +static inline void command_vgpu_to_host_init1(struct + dxgkvmb_command_vgpu_to_host + *command, + enum dxgkvmb_commandtype type) +{ + command->command_type = type; + command->process = 0; + command->command_id = 0; + command->channel_type = DXGKVMB_VGPU_TO_HOST; +} + +static inline void command_vgpu_to_host_init2(struct + dxgkvmb_command_vgpu_to_host + *command, + enum dxgkvmb_commandtype type, + d3dkmt_handle process_handle) +{ + command->command_type = type; + command->process = process_handle; + command->command_id = 0; + command->channel_type = DXGKVMB_VGPU_TO_HOST; +} + +int dxgvmb_send_sync_msg(struct dxgvmbuschannel *channel, + void *command, + u32 command_size, void *result, u32 result_size); + +#endif /* _DXGVMBUS_H */ diff --git a/drivers/gpu/dxgkrnl/hmgr.c b/drivers/gpu/dxgkrnl/hmgr.c new file mode 100644 index 000000000000..450fb67e703f --- /dev/null +++ b/drivers/gpu/dxgkrnl/hmgr.c @@ -0,0 +1,593 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * Dxgkrnl Graphics Port Driver handle manager + * + */ + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/rwsem.h> + +#include "misc.h" +#include "dxgkrnl.h" +#include "hmgr.h" + +/* + * Handle parameters + */ +#define HMGRHANDLE_INSTANCE_BITS 6 +#define HMGRHANDLE_INDEX_BITS 24 +#define HMGRHANDLE_UNIQUE_BITS 2 + +#define HMGRHANDLE_INSTANCE_SHIFT 0 +#define HMGRHANDLE_INDEX_SHIFT \ + (HMGRHANDLE_INSTANCE_BITS + HMGRHANDLE_INSTANCE_SHIFT) +#define HMGRHANDLE_UNIQUE_SHIFT \ + (HMGRHANDLE_INDEX_BITS + HMGRHANDLE_INDEX_SHIFT) + +#define HMGRHANDLE_INSTANCE_MASK \ + (((1 << HMGRHANDLE_INSTANCE_BITS) - 1) << HMGRHANDLE_INSTANCE_SHIFT) +#define HMGRHANDLE_INDEX_MASK \ + (((1 << HMGRHANDLE_INDEX_BITS) - 1) << HMGRHANDLE_INDEX_SHIFT) +#define HMGRHANDLE_UNIQUE_MASK \ + (((1 << HMGRHANDLE_UNIQUE_BITS) - 1) << HMGRHANDLE_UNIQUE_SHIFT) + +#define HMGRHANDLE_INSTANCE_MAX ((1 << HMGRHANDLE_INSTANCE_BITS) - 1) +#define HMGRHANDLE_INDEX_MAX ((1 << HMGRHANDLE_INDEX_BITS) - 1) +#define HMGRHANDLE_UNIQUE_MAX ((1 << HMGRHANDLE_UNIQUE_BITS) - 1) + +/* + * Handle entry + */ +struct hmgrentry { + union { + void *object; + struct { + uint prev_free_index; + uint next_free_index; + }; + }; + uint type:HMGRENTRY_TYPE_BITS + 1; + uint unique:HMGRHANDLE_UNIQUE_BITS; + uint instance:HMGRHANDLE_INSTANCE_BITS; + uint destroyed:1; +}; + +#define HMGRTABLE_SIZE_INCREMENT 1024 +#define HMGRTABLE_MIN_FREE_ENTRIES 128 +#define HMGRTABLE_INVALID_INDEX (~((1 << HMGRHANDLE_INDEX_BITS) - 1)) +#define HMGRTABLE_SIZE_MAX 0xFFFFFFF + +static uint table_size_increment = HMGRTABLE_SIZE_INCREMENT; + +static inline uint get_unique(d3dkmt_handle h) +{ + return (h & HMGRHANDLE_UNIQUE_MASK) >> HMGRHANDLE_UNIQUE_SHIFT; +} + +static uint get_index(d3dkmt_handle h) +{ + return (h & HMGRHANDLE_INDEX_MASK) >> HMGRHANDLE_INDEX_SHIFT; +} + +uint get_instance(d3dkmt_handle h) +{ + return (h & HMGRHANDLE_INSTANCE_MASK) >> HMGRHANDLE_INSTANCE_SHIFT; +} + +static bool is_handle_valid(struct hmgrtable *table, d3dkmt_handle h, + bool ignore_destroyed, enum hmgrentry_type t) +{ + uint index = get_index(h); + uint unique = get_unique(h); + struct hmgrentry *entry; + + if (index >= table->table_size) { + pr_err("%s Invalid index %x %d\n", __func__, h, index); + return false; + } + + entry = &table->entry_table[index]; + if (unique != entry->unique) { + pr_err("%s Invalid unique %x %d %d %d %p", + __func__, h, unique, entry->unique, + index, entry->object); + return false; + } + + if (entry->destroyed && !ignore_destroyed) { + pr_err("%s Invalid destroyed", __func__); + return false; + } + + if (entry->type == HMGRENTRY_TYPE_FREE) { + pr_err("%s Entry is freed %x %d", __func__, h, index); + return false; + } + + if (t != HMGRENTRY_TYPE_FREE && t != entry->type) { + pr_err("%s type mismatch %x %d %d", __func__, h, + t, entry->type); + return false; + } + + return true; +} + +static d3dkmt_handle build_handle(uint index, uint unique, uint instance) +{ + uint handle_bits; + + handle_bits = (index << HMGRHANDLE_INDEX_SHIFT) & HMGRHANDLE_INDEX_MASK; + handle_bits |= (unique << HMGRHANDLE_UNIQUE_SHIFT) & + HMGRHANDLE_UNIQUE_MASK; + handle_bits |= (instance << HMGRHANDLE_INSTANCE_SHIFT) & + HMGRHANDLE_INSTANCE_MASK; + + return (d3dkmt_handle) handle_bits; +} + +inline uint hmgrtable_get_used_entry_count(struct hmgrtable *table) +{ + DXGKRNL_ASSERT(table->table_size >= table->free_count); + return (table->table_size - table->free_count); +} + +bool hmgrtable_mark_destroyed(struct hmgrtable *table, d3dkmt_handle h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return false; + + table->entry_table[get_index(h)].destroyed = true; + return true; +} + +bool hmgrtable_unmark_destroyed(struct hmgrtable *table, d3dkmt_handle h) +{ + if (!is_handle_valid(table, h, true, HMGRENTRY_TYPE_FREE)) + return true; + + DXGKRNL_ASSERT(table->entry_table[get_index(h)].destroyed); + table->entry_table[get_index(h)].destroyed = 0; + return true; +} + +static inline bool is_empty(struct hmgrtable *table) +{ + return (table->free_count == table->table_size); +} + +void print_status(struct hmgrtable *table) +{ + int i; + + TRACE_DEBUG(1, "hmgrtable head, tail %p %d %d\n", + table, table->free_handle_list_head, + table->free_handle_list_tail); + if (table->entry_table == NULL) + return; + for (i = 0; i < 3; i++) { + if (table->entry_table[i].type != HMGRENTRY_TYPE_FREE) + TRACE_DEBUG(1, "hmgrtable entry %p %d %p\n", + table, i, table->entry_table[i].object); + else + TRACE_DEBUG(1, "hmgrtable entry %p %d %d %d\n", + table, i, + table->entry_table[i].next_free_index, + table->entry_table[i].prev_free_index); + } +} + +static bool expand_table(struct hmgrtable *table, uint NumEntries) +{ + uint new_table_size; + struct hmgrentry *new_entry; + uint table_index; + uint new_free_count; + uint prev_free_index; + uint tail_index = table->free_handle_list_tail; + + TRACE_DEBUG(1, "%s\n", __func__); + + /* The tail should point to the last free element in the list */ + if (!(table->free_count == 0 || + table->entry_table[tail_index].next_free_index == + HMGRTABLE_INVALID_INDEX)) { + pr_err("%s:corruption\n", __func__); + return false; + } + + new_table_size = table->table_size + table_size_increment; + if (new_table_size < NumEntries) + new_table_size = NumEntries; + + if (new_table_size > HMGRHANDLE_INDEX_MAX) { + pr_err("%s:corruption\n", __func__); + return false; + } + + new_entry = (struct hmgrentry *) + dxgmem_alloc(table->process, DXGMEM_HANDLE_TABLE, + new_table_size * sizeof(struct hmgrentry)); + if (new_entry == NULL) { + pr_err("%s:allocation failed\n", __func__); + return false; + } + + if (table->entry_table) { + memcpy(new_entry, table->entry_table, + table->table_size * sizeof(struct hmgrentry)); + dxgmem_free(table->process, DXGMEM_HANDLE_TABLE, + table->entry_table); + } else { + table->free_handle_list_head = 0; + } + + table->entry_table = new_entry; + + /* Initialize new table entries and add to the free list */ + table_index = table->table_size; + new_free_count = table->free_count + table_size_increment; + + prev_free_index = table->free_handle_list_tail; + + while (table_index < new_table_size) { + struct hmgrentry *entry = &table->entry_table[table_index]; + + entry->prev_free_index = prev_free_index; + entry->next_free_index = table_index + 1; + entry->type = HMGRENTRY_TYPE_FREE; + entry->unique = 1; + entry->instance = 0; + prev_free_index = table_index; + + table_index++; + } + + table->entry_table[table_index - 1].next_free_index = + (uint) HMGRTABLE_INVALID_INDEX; + + if (table->free_count != 0) { + /* Link the current free list with the new entries */ + struct hmgrentry *entry; + + entry = &table->entry_table[table->free_handle_list_tail]; + entry->next_free_index = table->table_size; + } + table->free_handle_list_tail = new_table_size - 1; + if (table->free_handle_list_head == HMGRTABLE_INVALID_INDEX) + table->free_handle_list_head = table->table_size; + + table->table_size = new_table_size; + table->free_count = new_free_count; + + TRACE_DEBUG(1, "%s end\n", __func__); + return true; +} + +void hmgrtable_init(struct hmgrtable *table, struct dxgprocess *process) +{ + table->process = process; + table->entry_table = NULL; + table->table_size = 0; + table->free_handle_list_head = HMGRTABLE_INVALID_INDEX; + table->free_handle_list_tail = HMGRTABLE_INVALID_INDEX; + table->free_count = 0; + init_rwsem(&table->table_lock); +} + +void hmgrtable_destroy(struct hmgrtable *table) +{ + if (table->entry_table) { + dxgmem_free(table->process, DXGMEM_HANDLE_TABLE, + table->entry_table); + table->entry_table = NULL; + } +} + +void hmgrtable_lock(struct hmgrtable *table, enum dxglockstate state) +{ + dxglockorder_acquire(DXGLOCK_HANDLETABLE); + if (state == DXGLOCK_EXCL) + down_write(&table->table_lock); + else + down_read(&table->table_lock); +} + +void hmgrtable_unlock(struct hmgrtable *table, enum dxglockstate state) +{ + if (state == DXGLOCK_EXCL) + up_write(&table->table_lock); + else + up_read(&table->table_lock); + dxglockorder_release(DXGLOCK_HANDLETABLE); +} + +d3dkmt_handle hmgrtable_alloc_handle(struct hmgrtable *table, void *object, + enum hmgrentry_type type, bool make_valid) +{ + uint index; + struct hmgrentry *entry; + uint unique; + + DXGKRNL_ASSERT(type <= HMGRENTRY_TYPE_LIMIT); + DXGKRNL_ASSERT(type > HMGRENTRY_TYPE_FREE); + + if (table->free_count <= HMGRTABLE_MIN_FREE_ENTRIES) { + if (!expand_table(table, 0)) { + pr_err("hmgrtable expand_table failed\n"); + return 0; + } + } + + if (table->free_handle_list_head >= table->table_size) { + pr_err("hmgrtable corrupted handle table head\n"); + return 0; + } + + index = table->free_handle_list_head; + entry = &table->entry_table[index]; + + if (entry->type != HMGRENTRY_TYPE_FREE) { + pr_err("hmgrtable expected free handle\n"); + return 0; + } + + table->free_handle_list_head = entry->next_free_index; + + if (entry->next_free_index != table->free_handle_list_tail) { + if (entry->next_free_index >= table->table_size) { + pr_err("hmgrtable invalid next free index\n"); + return 0; + } + table->entry_table[entry->next_free_index].prev_free_index = + HMGRTABLE_INVALID_INDEX; + } + + unique = table->entry_table[index].unique; + + table->entry_table[index].object = object; + table->entry_table[index].type = type; + table->entry_table[index].instance = 0; + table->entry_table[index].destroyed = !make_valid; + table->free_count--; + + return build_handle(index, unique, table->entry_table[index].instance); +} + +int hmgrtable_assign_handle_safe(struct hmgrtable *table, void *object, + enum hmgrentry_type type, d3dkmt_handle h) +{ + int ret; + + hmgrtable_lock(table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(table, object, type, h); + hmgrtable_unlock(table, DXGLOCK_EXCL); + return ret; +} + +int hmgrtable_assign_handle(struct hmgrtable *table, void *object, + enum hmgrentry_type type, d3dkmt_handle h) +{ + uint index = get_index(h); + uint unique = get_unique(h); + struct hmgrentry *entry = NULL; + + TRACE_DEBUG(1, "%s %x, %d %p, %p\n", + __func__, h, index, object, table); + + if (index >= HMGRHANDLE_INDEX_MAX) { + pr_err("handle index is too big: %x %d", h, index); + return STATUS_INVALID_PARAMETER; + } + + if (index >= table->table_size) { + uint new_size = index + HMGRTABLE_SIZE_INCREMENT; + + if (new_size > HMGRHANDLE_INDEX_MAX) + new_size = HMGRHANDLE_INDEX_MAX; + if (!expand_table(table, new_size)) { + pr_err("failed to expand table\n"); + return STATUS_NO_MEMORY; + } + } + + entry = &table->entry_table[index]; + + if (entry->type != HMGRENTRY_TYPE_FREE) { + pr_err("the entry is already busy: %d %x", + entry->type, + hmgrtable_build_entry_handle(table, index)); + return STATUS_INVALID_PARAMETER; + } + + if (index != table->free_handle_list_tail) { + if (entry->next_free_index >= table->table_size) { + pr_err("hmgr: invalid next free index %d\n", + entry->next_free_index); + return STATUS_INVALID_PARAMETER; + } + table->entry_table[entry->next_free_index].prev_free_index = + entry->prev_free_index; + } else { + table->free_handle_list_tail = entry->prev_free_index; + } + + if (index != table->free_handle_list_head) { + if (entry->prev_free_index >= table->table_size) { + pr_err("hmgr: invalid next prev index %d\n", + entry->prev_free_index); + return STATUS_INVALID_PARAMETER; + } + table->entry_table[entry->prev_free_index].next_free_index = + entry->next_free_index; + } else { + table->free_handle_list_head = entry->next_free_index; + } + + entry->prev_free_index = HMGRTABLE_INVALID_INDEX; + entry->next_free_index = HMGRTABLE_INVALID_INDEX; + entry->object = object; + entry->type = type; + entry->instance = 0; + entry->unique = unique; + entry->destroyed = false; + + table->free_count--; + return 0; +} + +d3dkmt_handle hmgrtable_alloc_handle_safe(struct hmgrtable *table, void *obj, + enum hmgrentry_type type, + bool make_valid) +{ + d3dkmt_handle h; + + hmgrtable_lock(table, DXGLOCK_EXCL); + h = hmgrtable_alloc_handle(table, obj, type, make_valid); + hmgrtable_unlock(table, DXGLOCK_EXCL); + return h; +} + +void hmgrtable_free_handle(struct hmgrtable *table, enum hmgrentry_type t, + d3dkmt_handle h) +{ + struct hmgrentry *entry; + uint i = get_index(h); + + DXGKRNL_ASSERT(table->free_count < table->table_size); + DXGKRNL_ASSERT(table->free_count >= HMGRTABLE_MIN_FREE_ENTRIES); + + TRACE_DEBUG(1, "%s: %p %x\n", __func__, table, h); + + /* Ignore the destroyed flag when checking the handle */ + if (is_handle_valid(table, h, true, t)) { + entry = &table->entry_table[i]; + entry->unique = 1; + entry->type = HMGRENTRY_TYPE_FREE; + entry->destroyed = 0; + if (entry->unique != HMGRHANDLE_UNIQUE_MAX) + entry->unique += 1; + else + entry->unique = 1; + + table->free_count++; + + /* + * Insert the index to the free list at the tail. + */ + entry->next_free_index = HMGRTABLE_INVALID_INDEX; + entry->prev_free_index = table->free_handle_list_tail; + entry = &table->entry_table[table->free_handle_list_tail]; + entry->next_free_index = i; + table->free_handle_list_tail = i; + } else { + pr_err("%s:error: %d %x\n", __func__, i, h); + } +} + +void hmgrtable_free_handle_safe(struct hmgrtable *table, enum hmgrentry_type t, + d3dkmt_handle h) +{ + hmgrtable_lock(table, DXGLOCK_EXCL); + hmgrtable_free_handle(table, t, h); + hmgrtable_unlock(table, DXGLOCK_EXCL); +} + +d3dkmt_handle hmgrtable_build_entry_handle(struct hmgrtable *table, uint index) +{ + DXGKRNL_ASSERT(index < table->table_size); + + return build_handle(index, table->entry_table[index].unique, + table->entry_table[index].instance); +} + +void *hmgrtable_get_object(struct hmgrtable *table, d3dkmt_handle h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return NULL; + + return table->entry_table[get_index(h)].object; +} + +void *hmgrtable_get_object_by_type(struct hmgrtable *table, + enum hmgrentry_type type, d3dkmt_handle h) +{ + if (!is_handle_valid(table, h, false, type)) { + pr_err("%s invalid handle %x\n", __func__, h); + return NULL; + } + return table->entry_table[get_index(h)].object; +} + +void *hmgrtable_get_entry_object(struct hmgrtable *table, uint index) +{ + DXGKRNL_ASSERT(index < table->table_size); + DXGKRNL_ASSERT(table->entry_table[index].type != HMGRENTRY_TYPE_FREE); + + return table->entry_table[index].object; +} + +enum hmgrentry_type hmgrtable_get_entry_type(struct hmgrtable *table, + uint index) +{ + DXGKRNL_ASSERT(index < table->table_size); + return (enum hmgrentry_type)table->entry_table[index].type; +} + +enum hmgrentry_type hmgrtable_get_object_type(struct hmgrtable *table, + d3dkmt_handle h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return HMGRENTRY_TYPE_FREE; + + return hmgrtable_get_entry_type(table, get_index(h)); +} + +void *hmgrtable_get_object_ignore_destroyed(struct hmgrtable *table, + d3dkmt_handle h, + enum hmgrentry_type type) +{ + if (!is_handle_valid(table, h, true, type)) + return NULL; + return table->entry_table[get_index(h)].object; +} + +bool hmgrtable_next_entry(struct hmgrtable *tbl, uint *index, + enum hmgrentry_type *type, d3dkmt_handle *handle, + void **object) +{ + uint i; + struct hmgrentry *entry; + + for (i = *index; i < tbl->table_size; i++) { + entry = &tbl->entry_table[i]; + if (entry->type != HMGRENTRY_TYPE_FREE) { + *index = i + 1; + *object = entry->object; + *handle = build_handle(i, entry->unique, + entry->instance); + *type = entry->type; + return true; + } + } + return false; +} diff --git a/drivers/gpu/dxgkrnl/hmgr.h b/drivers/gpu/dxgkrnl/hmgr.h new file mode 100644 index 000000000000..2b508c2f7fcd --- /dev/null +++ b/drivers/gpu/dxgkrnl/hmgr.h @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Port Driver + * Handle manager definitions + * + */ + +#ifndef _HMGR_H_ +#define _HMGR_H_ + +#include "misc.h" + +struct hmgrentry; + +/* + * Handle manager table. + * + * Implementation notes: + * A list of free handles is built on top of the array of table entries. + * free_handle_list_head is the index of the first entry in the list. + * m_FreeHandleListTail is the index of an entry in the list, which is + * HMGRTABLE_MIN_FREE_ENTRIES from the head. It means that when a handle is + * freed, the next time the handle can be re-used is after allocating + * HMGRTABLE_MIN_FREE_ENTRIES number of handles. + * Handles are allocated from the start of the list and free handles are + * inserted after the tail of the list. + * + */ +struct hmgrtable { + struct dxgprocess *process; + struct hmgrentry *entry_table; + uint free_handle_list_head; + uint free_handle_list_tail; + uint table_size; + uint free_count; + struct rw_semaphore table_lock; +}; + +/* + * Handle entry data types. + */ +#define HMGRENTRY_TYPE_BITS 5 + +enum hmgrentry_type { + HMGRENTRY_TYPE_FREE = 0, + HMGRENTRY_TYPE_DXGADAPTER = 1, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE = 2, + HMGRENTRY_TYPE_DXGDEVICE = 3, + HMGRENTRY_TYPE_DXGRESOURCE = 4, + HMGRENTRY_TYPE_DXGALLOCATION = 5, + HMGRENTRY_TYPE_DXGOVERLAY = 6, + HMGRENTRY_TYPE_DXGCONTEXT = 7, + HMGRENTRY_TYPE_DXGSYNCOBJECT = 8, + HMGRENTRY_TYPE_DXGKEYEDMUTEX = 9, + HMGRENTRY_TYPE_DXGPAGINGQUEUE = 10, + HMGRENTRY_TYPE_DXGDEVICESYNCOBJECT = 11, + HMGRENTRY_TYPE_DXGPROCESS = 12, + HMGRENTRY_TYPE_DXGSHAREDVMOBJECT = 13, + HMGRENTRY_TYPE_DXGPROTECTEDSESSION = 14, + HMGRENTRY_TYPE_DXGHWQUEUE = 15, + HMGRENTRY_TYPE_DXGREMOTEBUNDLEOBJECT = 16, + HMGRENTRY_TYPE_DXGCOMPOSITIONSURFACEOBJECT = 17, + HMGRENTRY_TYPE_DXGCOMPOSITIONSURFACEPROXY = 18, + HMGRENTRY_TYPE_DXGTRACKEDWORKLOAD = 19, + HMGRENTRY_TYPE_LIMIT = ((1 << HMGRENTRY_TYPE_BITS) - 1), + HMGRENTRY_TYPE_MONITOREDFENCE = HMGRENTRY_TYPE_LIMIT + 1, +}; + +void hmgrtable_init(struct hmgrtable *tbl, struct dxgprocess *process); +void hmgrtable_destroy(struct hmgrtable *tbl); +void hmgrtable_lock(struct hmgrtable *tbl, enum dxglockstate state); +void hmgrtable_unlock(struct hmgrtable *tbl, enum dxglockstate state); +d3dkmt_handle hmgrtable_alloc_handle(struct hmgrtable *tbl, void *object, + enum hmgrentry_type t, bool make_valid); +d3dkmt_handle hmgrtable_alloc_handle_safe(struct hmgrtable *tbl, void *obj, + enum hmgrentry_type t, bool reserve); +int hmgrtable_assign_handle(struct hmgrtable *tbl, void *obj, + enum hmgrentry_type, d3dkmt_handle h); +int hmgrtable_assign_handle_safe(struct hmgrtable *tbl, void *obj, + enum hmgrentry_type t, d3dkmt_handle h); +void hmgrtable_free_handle(struct hmgrtable *tbl, enum hmgrentry_type t, + d3dkmt_handle h); +void hmgrtable_free_handle_safe(struct hmgrtable *tbl, enum hmgrentry_type t, + d3dkmt_handle h); +d3dkmt_handle hmgrtable_build_entry_handle(struct hmgrtable *tbl, uint index); +enum hmgrentry_type hmgrtable_get_object_type(struct hmgrtable *tbl, + d3dkmt_handle h); +void *hmgrtable_get_object(struct hmgrtable *tbl, d3dkmt_handle h); +void *hmgrtable_get_object_by_type(struct hmgrtable *tbl, enum hmgrentry_type t, + d3dkmt_handle h); +void *hmgrtable_get_object_ignore_destroyed(struct hmgrtable *tbl, + d3dkmt_handle h, + enum hmgrentry_type t); +bool hmgrtable_mark_destroyed(struct hmgrtable *tbl, d3dkmt_handle h); +bool hmgrtable_unmark_destroyed(struct hmgrtable *tbl, d3dkmt_handle h); +void *hmgrtable_get_entry_object(struct hmgrtable *tbl, uint index); +bool hmgrtable_next_entry(struct hmgrtable *tbl, uint *start_index, + enum hmgrentry_type *type, d3dkmt_handle *handle, + void **object); + +#endif diff --git a/drivers/gpu/dxgkrnl/ioctl.c b/drivers/gpu/dxgkrnl/ioctl.c new file mode 100644 index 000000000000..7d585b2cece8 --- /dev/null +++ b/drivers/gpu/dxgkrnl/ioctl.c @@ -0,0 +1,5269 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Driver + * Ioctl implementation + * + */ + +#include <linux/eventfd.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/anon_inodes.h> +#include <linux/mman.h> + +#include "dxgkrnl.h" +#include "dxgvmbus.h" + +struct ioctl_desc { + ntstatus(*ioctl_callback) (struct dxgprocess *p, void *__user arg); + u32 ioctl; + u32 arg_size; +}; +static struct ioctl_desc ioctls[LX_IO_MAX + 1]; + +static int dxgsyncobj_release(struct inode *inode, struct file *file) +{ + struct dxgsharedsyncobject *syncobj = file->private_data; + struct dxgthreadinfo *thread = dxglockorder_get_thread(); + + TRACE_DEBUG(1, "%s: %p", __func__, syncobj); + dxgmutex_lock(&syncobj->fd_mutex); + dxgsharedsyncobj_acquire_reference(syncobj); + syncobj->host_shared_handle_nt_reference--; + if (syncobj->host_shared_handle_nt_reference == 0) { + if (syncobj->host_shared_handle_nt) { + dxgvmb_send_destroy_nt_shared_object( + syncobj->host_shared_handle_nt); + TRACE_DEBUG(1, "Syncobj host_handle_nt destroyed: %x", + syncobj->host_shared_handle_nt); + syncobj->host_shared_handle_nt = 0; + } + dxgsharedsyncobj_release_reference(syncobj); + } + dxgmutex_unlock(&syncobj->fd_mutex); + dxgsharedsyncobj_release_reference(syncobj); + dxglockorder_put_thread(thread); + return 0; +} + +static const struct file_operations dxg_syncobj_fops = { + .release = dxgsyncobj_release, +}; + +static int dxgsharedresource_release(struct inode *inode, struct file *file) +{ + struct dxgsharedresource *resource = file->private_data; + struct dxgthreadinfo *thread = dxglockorder_get_thread(); + + TRACE_DEBUG(1, "%s: %p", __func__, resource); + dxgmutex_lock(&resource->fd_mutex); + dxgsharedresource_acquire_reference(resource); + resource->host_shared_handle_nt_reference--; + if (resource->host_shared_handle_nt_reference == 0) { + if (resource->host_shared_handle_nt) { + dxgvmb_send_destroy_nt_shared_object( + resource->host_shared_handle_nt); + TRACE_DEBUG(1, "Resource host_handle_nt destroyed: %x", + resource->host_shared_handle_nt); + resource->host_shared_handle_nt = 0; + } + dxgsharedresource_release_reference(resource); + } + dxgmutex_unlock(&resource->fd_mutex); + dxgsharedresource_release_reference(resource); + dxglockorder_put_thread(thread); + return 0; +} + +static const struct file_operations dxg_resource_fops = { + .release = dxgsharedresource_release, +}; + +static int dxgk_open_adapter_from_luid(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_openadapterfromluid args; + int ret = 0; + struct dxgadapter *entry; + struct dxgadapter *adapter = NULL; + struct d3dkmt_openadapterfromluid *__user result = inargs; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + dxgglobal_acquire_adapter_list_lock(DXGLOCK_SHARED); + dxgglobal_acquire_process_adapter_lock(); + + list_for_each_entry(entry, &dxgglobal->adapter_list_head, + adapter_list_entry) { + if (dxgadapter_acquire_lock_shared(entry) == 0) { + TRACE_DEBUG(1, "Compare luids: %d:%d %d:%d", + entry->luid.b, entry->luid.a, + args.adapter_luid.b, args.adapter_luid.a); + if (*(u64 *) &entry->luid == + *(u64 *) &args.adapter_luid) { + ret = + dxgprocess_open_adapter(process, entry, + &args.adapter_handle); + + if (NT_SUCCESS(ret)) { + ret = dxg_copy_to_user( + &result->adapter_handle, + &args.adapter_handle, + sizeof(d3dkmt_handle)); + } + adapter = entry; + } + dxgadapter_release_lock_shared(entry); + if (adapter) + break; + } + } + + dxgglobal_release_process_adapter_lock(); + dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); + + if (args.adapter_handle == 0) + ret = STATUS_INVALID_PARAMETER; + +cleanup: + + if (ret) + dxgprocess_close_adapter(process, args.adapter_handle); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgkp_enum_adapters(struct dxgprocess *process, + union d3dkmt_enumadapters_filter filter, + uint adapter_count_max, + struct d3dkmt_adapterinfo *__user info_out, + uint * __user adapter_count_out) +{ + int ret = 0; + struct dxgadapter *entry; + struct d3dkmt_adapterinfo *info = NULL; + struct dxgadapter **adapters = NULL; + int adapter_count = 0; + int i; + + TRACE_FUNC_ENTER(__func__); + if (info_out == NULL || adapter_count_max == 0) { + ret = 0; + TRACE_DEBUG(1, "buffer is NULL"); + ret = dxg_copy_to_user(adapter_count_out, + &dxgglobal->num_adapters, sizeof(uint)); + goto cleanup; + } + + if (adapter_count_max > 0xFFFF) { + pr_err("too many adapters"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + info = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct d3dkmt_adapterinfo) * + adapter_count_max); + if (info == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + adapters = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct dxgadapter *) * + adapter_count_max); + if (adapters == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + dxgglobal_acquire_adapter_list_lock(DXGLOCK_SHARED); + dxgglobal_acquire_process_adapter_lock(); + + list_for_each_entry(entry, &dxgglobal->adapter_list_head, + adapter_list_entry) { + if (dxgadapter_acquire_lock_shared(entry) == 0) { + struct d3dkmt_adapterinfo *inf = &info[adapter_count]; + + ret = dxgprocess_open_adapter(process, entry, + &inf->adapter_handle); + if (NT_SUCCESS(ret)) { + inf->adapter_luid = entry->luid; + adapters[adapter_count] = entry; + TRACE_DEBUG(1, "adapter: %x %x:%x", + inf->adapter_handle, + inf->adapter_luid.b, + inf->adapter_luid.a); + adapter_count++; + } + dxgadapter_release_lock_shared(entry); + } + if (ret) + break; + } + + dxgglobal_release_process_adapter_lock(); + dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); + + if (adapter_count > adapter_count_max) { + ret = STATUS_BUFFER_TOO_SMALL; + TRACE_DEBUG(1, "Too many adapters"); + ret = dxg_copy_to_user(adapter_count_out, + &dxgglobal->num_adapters, sizeof(uint)); + goto cleanup; + } + + ret = dxg_copy_to_user(adapter_count_out, &adapter_count, + sizeof(adapter_count)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(info_out, info, sizeof(info[0]) * adapter_count); + +cleanup: + + if (NT_SUCCESS(ret)) { + TRACE_DEBUG(1, "found %d adapters", adapter_count); + goto success; + } + if (info) { + for (i = 0; i < adapter_count; i++) + dxgprocess_close_adapter(process, + info[i].adapter_handle); + } +success: + if (info) + dxgmem_free(process, DXGMEM_TMP, info); + if (adapters) + dxgmem_free(process, DXGMEM_TMP, adapters); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgsharedresource_seal(struct dxgsharedresource *shared_resource) +{ + int ret = 0; + int i = 0; + uint8_t *private_data; + uint data_size; + struct dxgresource *resource; + struct dxgallocation *alloc; + + TRACE_DEBUG(1, "Sealing resource: %p", shared_resource); + + down_write(&shared_resource->adapter->shared_resource_list_lock); + if (shared_resource->sealed) { + TRACE_DEBUG(1, "Resource already sealed"); + goto cleanup; + } + shared_resource->sealed = 1; + if (!list_empty(&shared_resource->resource_list_head)) { + resource = + list_first_entry(&shared_resource->resource_list_head, + struct dxgresource, + shared_resource_list_entry); + TRACE_DEBUG(1, "First resource: %p", resource); + dxgmutex_lock(&resource->resource_mutex); + list_for_each_entry(alloc, &resource->alloc_list_head, + alloc_list_entry) { + TRACE_DEBUG(1, "Resource alloc: %p %d", alloc, + alloc->priv_drv_data->data_size); + shared_resource->allocation_count++; + shared_resource->alloc_private_data_size += + alloc->priv_drv_data->data_size; + if (shared_resource->alloc_private_data_size < + alloc->priv_drv_data->data_size) { + pr_err("alloc private data overflow"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup1; + } + } + if (shared_resource->alloc_private_data_size == 0) + goto cleanup1; + shared_resource->alloc_private_data = + dxgmem_alloc(NULL, DXGMEM_ALLOCPRIVATE, + shared_resource->alloc_private_data_size); + if (shared_resource->alloc_private_data == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup1; + } + shared_resource->alloc_private_data_sizes = + dxgmem_alloc(NULL, DXGMEM_ALLOCPRIVATE, + sizeof(uint)*shared_resource->allocation_count); + if (shared_resource->alloc_private_data_sizes == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup1; + } + private_data = shared_resource->alloc_private_data; + data_size = shared_resource->alloc_private_data_size; + i = 0; + list_for_each_entry(alloc, &resource->alloc_list_head, + alloc_list_entry) { + uint alloc_data_size = alloc->priv_drv_data->data_size; + + if (alloc_data_size) { + if (data_size < alloc_data_size) { + pr_err("Invalid private data size"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup1; + } + shared_resource->alloc_private_data_sizes[i] = + alloc_data_size; + memcpy(private_data, + alloc->priv_drv_data->data, + alloc_data_size); + dxgmem_free(alloc->process, DXGMEM_ALLOCPRIVATE, + alloc->priv_drv_data); + alloc->priv_drv_data = NULL; + private_data += alloc_data_size; + data_size -= alloc_data_size; + } + i++; + } + if (data_size != 0) { + pr_err("Data size mismatch"); + ret = STATUS_INVALID_PARAMETER; + } +cleanup1: + dxgmutex_unlock(&resource->resource_mutex); + } +cleanup: + up_write(&shared_resource->adapter->shared_resource_list_lock); + return ret; +} + +static int dxgk_enum_adapters(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_enumadapters2 args; + int ret = 0; + struct dxgadapter *entry; + struct d3dkmt_adapterinfo *info = NULL; + struct dxgadapter **adapters = NULL; + int adapter_count = 0; + int i; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.adapters == NULL) { + ret = 0; + TRACE_DEBUG(1, "buffer is NULL"); + args.num_adapters = dxgglobal->num_adapters; + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + goto cleanup; + } + if (args.num_adapters < dxgglobal->num_adapters) { + args.num_adapters = dxgglobal->num_adapters; + TRACE_DEBUG(1, "buffer is too small"); + ret = STATUS_BUFFER_TOO_SMALL; + goto cleanup; + } + + if (args.num_adapters > D3DKMT_ADAPTERS_MAX) { + TRACE_DEBUG(1, "too many adapters"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + info = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct d3dkmt_adapterinfo) * + args.num_adapters); + if (info == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + adapters = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct dxgadapter *) * + args.num_adapters); + if (adapters == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + dxgglobal_acquire_adapter_list_lock(DXGLOCK_SHARED); + dxgglobal_acquire_process_adapter_lock(); + + list_for_each_entry(entry, &dxgglobal->adapter_list_head, + adapter_list_entry) { + if (dxgadapter_acquire_lock_shared(entry) == 0) { + struct d3dkmt_adapterinfo *inf = &info[adapter_count]; + + ret = dxgprocess_open_adapter(process, entry, + &inf->adapter_handle); + if (NT_SUCCESS(ret)) { + inf->adapter_luid = entry->luid; + adapters[adapter_count] = entry; + TRACE_DEBUG(1, "adapter: %x %llx", + inf->adapter_handle, + *(u64 *) &inf->adapter_luid); + adapter_count++; + } + dxgadapter_release_lock_shared(entry); + } + if (ret) + break; + } + + dxgglobal_release_process_adapter_lock(); + dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); + + args.num_adapters = adapter_count; + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(args.adapters, info, + sizeof(info[0]) * args.num_adapters); + if (ret) + goto cleanup; + +cleanup: + + if (ret) { + if (info) { + for (i = 0; i < args.num_adapters; i++) { + dxgprocess_close_adapter(process, + info[i]. + adapter_handle); + } + } + } else { + TRACE_DEBUG(1, "found %d adapters", args.num_adapters); + } + + if (info) + dxgmem_free(process, DXGMEM_TMP, info); + if (adapters) + dxgmem_free(process, DXGMEM_TMP, adapters); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_enum_adapters3(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_enumadapters3 args; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = dxgkp_enum_adapters(process, args.filter, + args.adapter_count, + args.adapters, + &((struct d3dkmt_enumadapters3 *)inargs)-> + adapter_count); + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_close_adapter(struct dxgprocess *process, void *__user inargs) +{ + d3dkmt_handle args; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = dxgprocess_close_adapter(process, args); + if (ret) + pr_err("%s failed", __func__); + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_query_adapter_info(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryadapterinfo args; + int ret = 0; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.private_data_size > DXG_MAX_VM_BUS_PACKET_SIZE || + args.private_data_size == 0) { + pr_err("invalid private data size"); + goto cleanup; + } + + TRACE_DEBUG(1, "Type: %d Size: %x", args.type, args.private_data_size); + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) + goto cleanup; + + ret = dxgvmb_send_query_adapter_info(process, &adapter->channel, &args); + + dxgadapter_release_lock_shared(adapter); + +cleanup: + + if (adapter) + dxgadapter_release_reference(adapter); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_create_device(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_createdevice args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + d3dkmt_handle host_device_handle = 0; + bool adapter_locked = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + /* The call acquires reference on the adapter */ + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgdevice_create(adapter, process); + if (device == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) + goto cleanup; + + adapter_locked = true; + + host_device_handle = dxgvmb_send_create_device(adapter, process, &args); + if (host_device_handle) { + ret = + dxg_copy_to_user(&((struct d3dkmt_createdevice *)inargs)-> + device, &host_device_handle, + sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, device, + HMGRENTRY_TYPE_DXGDEVICE, + host_device_handle); + if (!ret) { + device->handle = host_device_handle; + device->handle_valid = 1; + device->object_state = DXGOBJECTSTATE_ACTIVE; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + } + +cleanup: + + if (ret) { + if (host_device_handle) + dxgvmb_send_destroy_device(adapter, process, + host_device_handle); + if (device) + dxgdevice_destroy(device); + } + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + + if (adapter) + dxgadapter_release_reference(adapter); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_destroy_device(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_destroydevice args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + device = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGDEVICE, + args.device); + if (device) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGDEVICE, args.device); + device->handle_valid = 0; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (device == NULL) { + pr_err("invalid device handle: %x", args.device); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + + dxgdevice_destroy(device); + + if (dxgadapter_acquire_lock_shared(adapter) == 0) { + dxgvmb_send_destroy_device(adapter, process, args.device); + dxgadapter_release_lock_shared(adapter); + } + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_create_context_virtual(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_createcontextvirtual args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + struct dxgcontext *context = NULL; + d3dkmt_handle host_context_handle = 0; + bool device_lock_acquired = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) + goto cleanup; + + device_lock_acquired = true; + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + context = dxgcontext_create(device); + if (context == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + host_context_handle = dxgvmb_send_create_context(adapter, + process, &args); + if (host_context_handle) { + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, context, + HMGRENTRY_TYPE_DXGCONTEXT, + host_context_handle); + if (!ret) + context->handle = host_context_handle; + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + if (ret) + goto cleanup; + ret = + dxg_copy_to_user(& + ((struct d3dkmt_createcontextvirtual *) + inargs)->context, &host_context_handle, + sizeof(d3dkmt_handle)); + } else { + pr_err("invalid host handle"); + ret = STATUS_INVALID_PARAMETER; + } + +cleanup: + + if (ret) { + if (host_context_handle) { + dxgvmb_send_destroy_context(adapter, process, + host_context_handle); + } + if (context) + dxgcontext_destroy_safe(process, context); + } + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) { + if (device_lock_acquired) + dxgdevice_release_lock_shared(device); + dxgdevice_release_reference(device); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_destroy_context(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_destroycontext args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgcontext *context = NULL; + struct dxgdevice *device = NULL; + d3dkmt_handle device_handle = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + context = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (context) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGCONTEXT, args.context); + context->handle = 0; + device_handle = context->device_handle; + context->object_state = DXGOBJECTSTATE_DESTROYED; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (context == NULL) { + pr_err("invalid context handle: %x", args.context); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, device_handle); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_destroy_context(adapter, process, args.context); + + dxgcontext_destroy_safe(process, context); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_create_hwcontext(struct dxgprocess *process, + void *__user inargs) +{ + /* This is obsolete entry point */ + return STATUS_NOT_SUPPORTED; +} + +static int dxgk_destroy_hwcontext(struct dxgprocess *process, + void *__user inargs) +{ + /* This is obsolete entry point */ + return STATUS_NOT_SUPPORTED; +} + +static int dxgk_create_hwqueue(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_createhwqueue args; + struct dxgdevice *device = NULL; + struct dxgcontext *context = NULL; + struct dxgadapter *adapter = NULL; + struct dxghwqueue *hwqueue = NULL; + int ret = 0; + bool device_lock_acquired = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) + goto cleanup; + + device_lock_acquired = true; + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + context = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + + if (context == NULL) { + pr_err("Invalid context handle %x", args.context); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + hwqueue = dxghwqueue_create(context); + if (hwqueue == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_create_hwqueue(process, &adapter->channel, &args, + inargs, hwqueue); + +cleanup: + + if (ret && hwqueue) + dxghwqueue_destroy(process, hwqueue); + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device_lock_acquired) + dxgdevice_release_lock_shared(device); + + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_destroy_hwqueue(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_destroyhwqueue args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + struct dxghwqueue *hwqueue = NULL; + d3dkmt_handle device_handle = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + hwqueue = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGHWQUEUE, + args.queue); + if (hwqueue) { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGHWQUEUE, args.queue); + hwqueue->handle = 0; + device_handle = hwqueue->device_handle; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (hwqueue == NULL) { + pr_err("invalid hwqueue handle: %x", args.queue); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, device_handle); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_destroy_hwqueue(process, &adapter->channel, + args.queue); + + dxghwqueue_destroy(process, hwqueue); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_create_paging_queue(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_createpagingqueue args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct dxgpagingqueue *pqueue = NULL; + int ret = 0; + d3dkmt_handle host_handle = 0; + bool device_lock_acquired = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) + goto cleanup; + + device_lock_acquired = true; + adapter = device->adapter; + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + pqueue = dxgpagingqueue_create(device); + if (pqueue == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxgvmb_send_create_paging_queue(process, &adapter->channel, + device, &args, pqueue); + if (NT_SUCCESS(ret)) { + host_handle = args.paging_queue; + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, pqueue, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + host_handle); + if (NT_SUCCESS(ret)) { + pqueue->handle = host_handle; + ret = hmgrtable_assign_handle(&process->handle_table, + NULL, + HMGRENTRY_TYPE_MONITOREDFENCE, + args.sync_object); + if (NT_SUCCESS(ret)) + pqueue->syncobj_handle = args.sync_object; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + /* should not fail after this */ + } + +cleanup: + + if (ret) { + if (pqueue) + dxgpagingqueue_destroy(pqueue); + if (host_handle) + dxgvmb_send_destroy_paging_queue(process, + &adapter->channel, + host_handle); + } + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) { + if (device_lock_acquired) + dxgdevice_release_lock_shared(device); + dxgdevice_release_reference(device); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_destroy_paging_queue(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dddi_destroypagingqueue args; + struct dxgpagingqueue *paging_queue = NULL; + int ret = 0; + d3dkmt_handle device_handle = 0; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + paging_queue = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + if (paging_queue) { + device_handle = paging_queue->device_handle; + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_MONITOREDFENCE, + paging_queue->syncobj_handle); + paging_queue->syncobj_handle = 0; + paging_queue->handle = 0; + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + if (device_handle) + device = dxgprocess_device_by_handle(process, device_handle); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_destroy_paging_queue(process, &adapter->channel, + args.paging_queue); + + dxgpagingqueue_destroy(paging_queue); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) { + dxgdevice_release_lock_shared(device); + dxgdevice_release_reference(device); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int get_standard_alloc_priv_data(struct dxgdevice *device, + struct d3dkmt_createstandardallocation + *alloc_info, + uint *standard_alloc_priv_data_size, + void **standard_alloc_priv_data) +{ + int ret = 0; + struct d3dkmdt_gdisurfacedata gdi_data = { }; + uint priv_data_size = 0; + void *priv_data = NULL; + + TRACE_DEBUG(1, "%s", __func__); + + gdi_data.type = D3DKMDT_GDISURFACE_TEXTURE_CROSSADAPTER; + gdi_data.width = alloc_info->existing_heap_data.size; + gdi_data.height = 1; + gdi_data.format = D3DDDIFMT_UNKNOWN; + + *standard_alloc_priv_data_size = 0; + ret = dxgvmb_send_get_standard_alloc_priv_data(device, + D3DKMDT_STANDARDALLOCATION_GDISURFACE, + &gdi_data, 0, + &priv_data_size, NULL); + if (ret) + goto cleanup; + TRACE_DEBUG(1, "Priv data size: %d", priv_data_size); + if (priv_data_size == 0) + goto cleanup; + priv_data = dxgmem_alloc(device->process, DXGMEM_TMP, priv_data_size); + if (priv_data == NULL) { + ret = STATUS_NO_MEMORY; + pr_err("failed to allocate memory for priv data: %d", + priv_data_size); + goto cleanup; + } + ret = dxgvmb_send_get_standard_alloc_priv_data(device, + D3DKMDT_STANDARDALLOCATION_GDISURFACE, + &gdi_data, 0, + &priv_data_size, + priv_data); + if (ret) + goto cleanup; + *standard_alloc_priv_data_size = priv_data_size; + *standard_alloc_priv_data = priv_data; + priv_data = NULL; + +cleanup: + if (priv_data) + dxgmem_free(device->process, DXGMEM_TMP, priv_data); + TRACE_FUNC_EXIT_ERR(__func__, ret); + return ret; +} + +static int dxgk_create_allocation(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_createallocation args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + struct d3dddi_allocationinfo2 *alloc_info = NULL; + struct d3dkmt_createstandardallocation standard_alloc; + uint alloc_info_size = 0; + struct dxgresource *resource = NULL; + struct dxgallocation **dxgalloc = NULL; + struct dxgsharedresource *shared_resource = NULL; + bool resource_mutex_acquired = false; + uint standard_alloc_priv_data_size = 0; + void *standard_alloc_priv_data = NULL; + int i; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || + args.alloc_count == 0) { + pr_err("invalid number of allocations to create"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + alloc_info_size = sizeof(struct d3dddi_allocationinfo2) * + args.alloc_count; + alloc_info = dxgmem_alloc(process, DXGMEM_TMP, alloc_info_size); + if (alloc_info == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(alloc_info, args.allocation_info, + alloc_info_size); + if (ret) + goto cleanup; + + for (i = 0; i < args.alloc_count; i++) { + if (args.flags.standard_allocation) { + if (alloc_info[i].priv_drv_data_size != 0) { + pr_err("private data size is not zero"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } + if (alloc_info[i].priv_drv_data_size >= + DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("private data size is too big: %d %d %ld", + i, alloc_info[i].priv_drv_data_size, + sizeof(alloc_info[0])); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } + + if (args.flags.existing_section || args.flags.create_protected) { + pr_err("invalid allocation flags"); + goto cleanup; + } + + if (args.flags.standard_allocation) { + if (args.standard_allocation == NULL) { + pr_err("invalid standard allocation"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + ret = dxg_copy_from_user(&standard_alloc, + args.standard_allocation, + sizeof(standard_alloc)); + if (ret) + goto cleanup; + if (alloc_info[0].sysmem == NULL || + args.priv_drv_data_size != 0 || + args.alloc_count != 1 || + standard_alloc.type != + D3DKMT_STANDARDALLOCATIONTYPE_EXISTINGHEAP || + standard_alloc.existing_heap_data.size == 0 || + standard_alloc.existing_heap_data.size & (PAGE_SIZE - 1) || + (unsigned long)alloc_info[0].sysmem & (PAGE_SIZE - 1)) { + pr_err("invalid standard allocation"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + args.priv_drv_data_size = + sizeof(struct d3dkmt_createstandardallocation); + } + + if (args.flags.create_shared && !args.flags.create_resource) { + pr_err("create_resource must be set for create_shared"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + if (args.flags.standard_allocation) { + ret = get_standard_alloc_priv_data(device, + &standard_alloc, + &standard_alloc_priv_data_size, + &standard_alloc_priv_data); + if (ret) + goto cleanup; + TRACE_DEBUG(1, "Alloc private data: %d", + standard_alloc_priv_data_size); + } + + if (args.flags.create_resource) { + resource = dxgresource_create(device); + if (resource == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + resource->private_runtime_handle = + args.private_runtime_resource_handle; + if (args.flags.create_shared) { + shared_resource = dxgsharedresource_create(adapter); + if (shared_resource == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + shared_resource->runtime_private_data_size = + args.priv_drv_data_size; + shared_resource->resource_private_data_size = + args.priv_drv_data_size; + if (args.flags.nt_security_sharing) + shared_resource->nt_security = 1; + + shared_resource->runtime_private_data_size = + args.private_runtime_data_size; + shared_resource->resource_private_data_size = + args.priv_drv_data_size; + dxgsharedresource_add_resource(shared_resource, + resource); + if (args.private_runtime_data_size) { + shared_resource->runtime_private_data = + dxgmem_alloc(NULL, + DXGMEM_RUNTIMEPRIVATE, + args. + private_runtime_data_size); + if (shared_resource->runtime_private_data == + NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = + dxg_copy_from_user(shared_resource-> + runtime_private_data, + args. + private_runtime_data, + args. + private_runtime_data_size); + if (ret) + goto cleanup; + } + if (args.priv_drv_data_size) { + shared_resource->resource_private_data = + dxgmem_alloc(NULL, + DXGMEM_RESOURCEPRIVATE, + args.priv_drv_data_size); + if (shared_resource->resource_private_data == + NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = + dxg_copy_from_user(shared_resource-> + resource_private_data, + args.priv_drv_data, + args.priv_drv_data_size); + if (ret) + goto cleanup; + } + } + } else { + if (args.resource) { + /* Adding new allocations to the given resource */ + + dxgprocess_ht_lock_shared_down(process); + resource = + hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGRESOURCE, + args.resource); + dxgresource_acquire_reference(resource); + dxgprocess_ht_lock_shared_up(process); + + if (resource == NULL || resource->device != device) { + pr_err("invalid resource handle %x", + args.resource); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (resource->shared_owner && + resource->shared_owner->sealed) { + pr_err("Resource is sealed"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + /* Synchronize with resource destruction */ + dxgmutex_lock(&resource->resource_mutex); + if (!dxgresource_is_active(resource)) { + dxgmutex_unlock(&resource->resource_mutex); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + resource_mutex_acquired = true; + } + } + + dxgalloc = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct dxgallocation *) * + args.alloc_count); + if (dxgalloc == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + for (i = 0; i < args.alloc_count; i++) { + struct dxgallocation *alloc; + uint priv_data_size = alloc_info[i].priv_drv_data_size; + + if (alloc_info[i].sysmem && !args.flags.standard_allocation) { + if ((unsigned long) + alloc_info[i].sysmem & (PAGE_SIZE - 1)) { + pr_err("invalid sysmem alloc %d, %p", + i, alloc_info[i].sysmem); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } + if ((alloc_info[0].sysmem == NULL) != + (alloc_info[i].sysmem == NULL)) { + pr_err("All allocations must have sysmem pointer"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + dxgalloc[i] = dxgallocation_create(process); + if (dxgalloc[i] == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + alloc = dxgalloc[i]; + + if (resource) + dxgresource_add_alloc(resource, alloc); + else + dxgdevice_add_alloc(device, alloc); + if (args.flags.create_shared) { + /* Remember alloc private data to use it during open */ + alloc->priv_drv_data = dxgmem_alloc(process, + DXGMEM_ALLOCPRIVATE, + priv_data_size + + offsetof(struct + privdata, + data) - 1); + if (alloc->priv_drv_data == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + if (args.flags.standard_allocation) { + memcpy(alloc->priv_drv_data->data, + standard_alloc_priv_data, + standard_alloc_priv_data_size); + alloc->priv_drv_data->data_size = + standard_alloc_priv_data_size; + } else { + ret = + dxg_copy_from_user(alloc->priv_drv_data-> + data, + alloc_info[i]. + priv_drv_data, + priv_data_size); + if (ret) + goto cleanup; + alloc->priv_drv_data->data_size = + priv_data_size; + } + } + } + + ret = dxgvmb_send_create_allocation(process, device, &args, inargs, + resource, dxgalloc, alloc_info, + &standard_alloc); + if (ret) + goto cleanup; + +cleanup: + + if (resource_mutex_acquired) { + dxgmutex_unlock(&resource->resource_mutex); + dxgresource_release_reference(resource); + } + if (ret) { + if (dxgalloc) { + for (i = 0; i < args.alloc_count; i++) { + if (dxgalloc[i]) + dxgallocation_destroy(dxgalloc[i]); + } + } + if (resource && args.flags.create_resource) { + if (shared_resource) { + dxgsharedresource_remove_resource + (shared_resource, resource); + } + dxgresource_destroy(resource); + } + } + if (shared_resource) + dxgsharedresource_release_reference(shared_resource); + if (dxgalloc) + dxgmem_free(process, DXGMEM_TMP, dxgalloc); + if (standard_alloc_priv_data) + dxgmem_free(process, DXGMEM_TMP, standard_alloc_priv_data); + if (alloc_info) + dxgmem_free(process, DXGMEM_TMP, alloc_info); + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) { + dxgdevice_release_lock_shared(device); + dxgdevice_release_reference(device); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +int validate_alloc(struct dxgallocation *alloc0, + struct dxgallocation *alloc, + struct dxgdevice *device, d3dkmt_handle alloc_handle) +{ + uint fail_reason; + + if (alloc == NULL) { + fail_reason = 1; + goto cleanup; + } + if (alloc->resource_owner != alloc0->resource_owner) { + fail_reason = 2; + goto cleanup; + } + if (alloc->resource_owner) { + if (alloc->owner.resource != alloc0->owner.resource) { + fail_reason = 3; + goto cleanup; + } + if (alloc->owner.resource->device != device) { + fail_reason = 4; + goto cleanup; + } + if (alloc->owner.resource->shared_owner) { + fail_reason = 5; + goto cleanup; + } + } else { + if (alloc->owner.device != device) { + fail_reason = 6; + goto cleanup; + } + } + return 0; +cleanup: + pr_err("Alloc validation failed: reason: %d %x", + fail_reason, alloc_handle); + return STATUS_INVALID_PARAMETER; +} + +static int dxgk_destroy_allocation(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_destroyallocation2 args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int ret = 0; + d3dkmt_handle *alloc_handles = NULL; + struct dxgallocation **allocs = NULL; + struct dxgresource *resource = NULL; + int i; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || + ((args.alloc_count == 0) == (args.resource == 0))) { + pr_err("invalid number of allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.alloc_count) { + uint handle_size = sizeof(d3dkmt_handle) * args.alloc_count; + + alloc_handles = dxgmem_alloc(process, DXGMEM_TMP, handle_size); + if (alloc_handles == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + allocs = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(struct dxgallocation *) * + args.alloc_count); + if (allocs == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(alloc_handles, args.allocations, + handle_size); + if (ret) + goto cleanup; + } + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + /* Acquire the device lock to synchronize with the device destriction */ + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + /* + * Destroy the local allocation handles first. If the host handle + * is destroyed first, another object could be assigned to the process + * table at he same place as the allocation handle and it will fail. + */ + if (args.alloc_count) { + dxgprocess_ht_lock_exclusive_down(process); + for (i = 0; i < args.alloc_count; i++) { + allocs[i] = + hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGALLOCATION, + alloc_handles[i]); + ret = + validate_alloc(allocs[0], allocs[i], device, + alloc_handles[i]); + if (ret) { + dxgprocess_ht_lock_exclusive_up(process); + goto cleanup; + } + } + dxgprocess_ht_lock_exclusive_up(process); + for (i = 0; i < args.alloc_count; i++) + dxgallocation_free_handle(allocs[i]); + } else { + struct dxgallocation *alloc; + + dxgprocess_ht_lock_exclusive_down(process); + resource = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGRESOURCE, + args.resource); + if (resource == NULL) { + pr_err("Invalid resource handle: %x", + args.resource); + ret = STATUS_INVALID_PARAMETER; + } else if (resource->device != device) { + pr_err("Resource belongs to wrong device: %x", + args.resource); + ret = STATUS_INVALID_PARAMETER; + } else { + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGRESOURCE, + args.resource); + resource->object_state = DXGOBJECTSTATE_DESTROYED; + resource->handle = 0; + resource->handle_valid = 0; + } + dxgprocess_ht_lock_exclusive_up(process); + + if (ret) + goto cleanup; + + dxgdevice_acquire_alloc_list_lock_shared(device); + list_for_each_entry(alloc, &resource->alloc_list_head, + alloc_list_entry) { + dxgallocation_free_handle(alloc); + } + dxgdevice_release_alloc_list_lock_shared(device); + } + + if (args.alloc_count && allocs[0]->resource_owner) + resource = allocs[0]->owner.resource; + + if (resource) { + dxgresource_acquire_reference(resource); + dxgmutex_lock(&resource->resource_mutex); + } + + ret = dxgvmb_send_destroy_allocation(process, device, &adapter->channel, + &args, alloc_handles); + + /* + * Destroy the allocations after the host destroyed it. + * The allocation gpadl teardown will wait until the host unmaps its + * gpadl. + */ + dxgdevice_acquire_alloc_list_lock(device); + if (args.alloc_count) { + for (i = 0; i < args.alloc_count; i++) { + if (allocs[i]) { + allocs[i]->alloc_handle = 0; + dxgallocation_destroy(allocs[i]); + } + } + } else { + dxgresource_destroy(resource); + } + dxgdevice_release_alloc_list_lock(device); + + if (resource) { + dxgmutex_unlock(&resource->resource_mutex); + dxgresource_release_reference(resource); + } + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) { + dxgdevice_release_lock_shared(device); + dxgdevice_release_reference(device); + } + + if (alloc_handles) + dxgmem_free(process, DXGMEM_TMP, alloc_handles); + + if (allocs) + dxgmem_free(process, DXGMEM_TMP, allocs); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_make_resident(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0, ret2; + struct d3dddi_makeresident args; + struct d3dddi_makeresident *input = inargs; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || + args.alloc_count == 0) { + pr_err("invalid number of allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (args.paging_queue == 0) { + pr_err("paging queue is missing"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_make_resident(process, NULL, &adapter->channel, + &args); + if (ret && ret != STATUS_PENDING) + goto cleanup; + + ret2 = dxg_copy_to_user(&input->paging_fence_value, + &args.paging_fence_value, sizeof(uint64_t)); + if (ret2) { + ret = ret2; + goto cleanup; + } + + ret2 = dxg_copy_to_user(&input->num_bytes_to_trim, + &args.num_bytes_to_trim, sizeof(uint64_t)); + if (ret2) { + ret = ret2; + goto cleanup; + } + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + + return ret; +} + +static int dxgk_evict(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0; + struct d3dkmt_evict args; + struct d3dkmt_evict *input = inargs; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || + args.alloc_count == 0) { + pr_err("invalid number of allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_evict(process, &adapter->channel, &args); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&input->num_bytes_to_trim, + &args.num_bytes_to_trim, sizeof(uint64_t)); + if (ret) + goto cleanup; + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_offer_allocations(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_offerallocations args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.allocation_count > D3DKMT_CREATEALLOCATION_MAX || + args.allocation_count == 0) { + pr_err("invalid number of allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if ((args.resources == NULL) == (args.allocations == NULL)) { + pr_err("invalid pointer to resources/allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_offer_allocations(process, &adapter->channel, &args); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_reclaim_allocations(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_reclaimallocations2 args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.allocation_count > D3DKMT_CREATEALLOCATION_MAX || + args.allocation_count == 0) { + pr_err("invalid number of allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if ((args.resources == NULL) == (args.allocations == NULL)) { + pr_err("invalid pointer to resources/allocations"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_reclaim_allocations(process, &adapter->channel, + device->handle, &args, + &((struct + d3dkmt_reclaimallocations2 *) + inargs)->paging_fence_value); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_submit_command(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0; + struct d3dkmt_submitcommand args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.broadcast_context_count > D3DDDI_MAX_BROADCAST_CONTEXT || + args.broadcast_context_count == 0) { + pr_err("invalid number of contexts"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("invalid private data size"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.num_history_buffers > 1024) { + pr_err("invalid number of history buffers"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.num_primaries > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("invalid number of primaries"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + args.broadcast_context[0]); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_submit_command(process, &adapter->channel, &args); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_submit_command_to_hwqueue(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_submitcommandtohwqueue args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("invalid private data size"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.num_primaries > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("invalid number of primaries"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGHWQUEUE, + args.hwqueue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_submit_command_to_hwqueue(process, &adapter->channel, + &args); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_submit_signal_to_hwqueue(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_submitsignalsyncobjectstohwqueue args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + d3dkmt_handle hwqueue; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.hwqueue_count > D3DDDI_MAX_BROADCAST_CONTEXT || + args.hwqueue_count == 0) { + pr_err("invalid hwqueue count"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.object_count > D3DDDI_MAX_OBJECT_SIGNALED || + args.object_count == 0) { + pr_err("invalid number of syn cobject"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxg_copy_from_user(&hwqueue, args.hwqueues, + sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGHWQUEUE, + hwqueue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_signal_sync_object(process, &adapter->channel, + args.flags, 0, 0, + args.object_count, args.objects, + args.hwqueue_count, args.hwqueues, + args.object_count, + args.fence_values, NULL, 0); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_submit_wait_to_hwqueue(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_submitwaitforsyncobjectstohwqueue args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int ret = 0; + d3dkmt_handle *objects = NULL; + uint object_size; + uint64_t *fences = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count > D3DDDI_MAX_OBJECT_WAITED_ON || + args.object_count == 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + object_size = sizeof(d3dkmt_handle) * args.object_count; + objects = dxgmem_alloc(process, DXGMEM_TMP, object_size); + if (objects == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(objects, args.objects, object_size); + if (ret) + goto cleanup; + + object_size = sizeof(uint64_t) * args.object_count; + fences = dxgmem_alloc(process, DXGMEM_TMP, object_size); + if (fences == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(fences, args.fence_values, object_size); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGHWQUEUE, + args.hwqueue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_wait_sync_object_gpu(process, &adapter->channel, + args.hwqueue, args.object_count, + objects, fences, false); + +cleanup: + + if (objects) + dxgmem_free(process, DXGMEM_TMP, objects); + if (fences) + dxgmem_free(process, DXGMEM_TMP, fences); + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_map_gpu_va(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0, ret2; + struct d3dddi_mapgpuvirtualaddress args; + struct d3dddi_mapgpuvirtualaddress *input = inargs; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_map_gpu_va(process, 0, &adapter->channel, &args); + if (ret && ret != (int)STATUS_PENDING) + goto cleanup; + + ret2 = dxg_copy_to_user(&input->paging_fence_value, + &args.paging_fence_value, sizeof(uint64_t)); + if (ret2) { + ret = ret2; + goto cleanup; + } + + ret2 = dxg_copy_to_user(&input->virtual_address, &args.virtual_address, + sizeof(args.virtual_address)); + if (ret2) { + ret = ret2; + goto cleanup; + } + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_reserve_gpu_va(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0; + struct d3dddi_reservegpuvirtualaddress args; + struct d3dddi_reservegpuvirtualaddress *input = inargs; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.adapter); + if (device == NULL) { + pr_err("invalid adapter or paging queue: 0x%x", + args.adapter); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + dxgadapter_acquire_reference(adapter); + dxgdevice_release_reference(device); + } else { + args.adapter = adapter->host_handle; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + dxgadapter_release_reference(adapter); + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_reserve_gpu_va(process, &adapter->channel, &args); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&input->virtual_address, &args.virtual_address, + sizeof(args.virtual_address)); + if (ret) + goto cleanup; + +cleanup: + + if (adapter) { + dxgadapter_release_lock_shared(adapter); + dxgadapter_release_reference(adapter); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_free_gpu_va(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0; + struct d3dkmt_freegpuvirtualaddress args; + struct dxgadapter *adapter = NULL; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + dxgadapter_release_reference(adapter); + adapter = NULL; + goto cleanup; + } + + args.adapter = adapter->host_handle; + ret = dxgvmb_send_free_gpu_va(process, &adapter->channel, &args); + +cleanup: + + if (adapter) { + dxgadapter_release_lock_shared(adapter); + dxgadapter_release_reference(adapter); + } + + return ret; +} + +static int dxgk_update_gpu_va(struct dxgprocess *process, void *__user inargs) +{ + int ret = 0; + struct d3dkmt_updategpuvirtualaddress args; + struct d3dkmt_updategpuvirtualaddress *input = inargs; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_update_gpu_va(process, &adapter->channel, &args); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&input->fence_value, &args.fence_value, + sizeof(args.fence_value)); + if (ret) + goto cleanup; + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + return ret; +} + +static int dxgk_create_sync_object(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_createsynchronizationobject2 args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct eventfd_ctx *event = NULL; + struct dxgsyncobject *syncobj = NULL; + bool host_event_added = false; + bool device_lock_acquired = false; + struct dxgsharedsyncobject *syncobjgbl = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) + goto cleanup; + + device_lock_acquired = true; + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + syncobj = dxgsyncobject_create(process, device, adapter, args.info.type, + args.info.flags); + if (syncobj == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.info.flags.shared && syncobj->monitored_fence && + !args.info.flags.nt_security_sharing) { + pr_err("monitored fence requires nt_security_sharing"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.info.type == D3DDDI_CPU_NOTIFICATION) { + event = eventfd_ctx_fdget((int) + args.info.cpu_notification.event); + if (IS_ERR(event)) { + pr_err("failed to reference the event"); + event = NULL; + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + syncobj->host_event->event_id = dxgglobal_new_host_event_id(); + syncobj->host_event->cpu_event = event; + syncobj->host_event->remove_from_list = false; + syncobj->host_event->destroy_after_signal = false; + dxgglobal_add_host_event(syncobj->host_event); + host_event_added = true; + args.info.cpu_notification.event = + syncobj->host_event->event_id; + TRACE_DEBUG(1, "creating CPU notification event: %lld", + args.info.cpu_notification.event); + } + + ret = dxgvmb_send_create_sync_object(process, &adapter->channel, &args, + syncobj); + if (ret) + goto cleanup; + + if (args.info.flags.shared) { + if (args.info.shared_handle == 0) { + pr_err("shared handle should not be 0"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + syncobjgbl = dxgsharedsyncobj_create(device->adapter, syncobj); + if (syncobjgbl == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + dxgsharedsyncobj_add_syncobj(syncobjgbl, syncobj); + + syncobjgbl->host_shared_handle = args.info.shared_handle; + if (!args.info.flags.nt_security_sharing) { + hmgrtable_lock(&dxgglobal->handle_table, DXGLOCK_EXCL); + syncobjgbl->global_shared_handle = + hmgrtable_alloc_handle(&dxgglobal->handle_table, + syncobjgbl, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + true); + if (syncobjgbl->global_shared_handle) { + args.info.shared_handle = + syncobjgbl->global_shared_handle; + } else { + ret = STATUS_NO_MEMORY; + } + hmgrtable_unlock(&dxgglobal->handle_table, + DXGLOCK_EXCL); + if (ret) + goto cleanup; + } + } + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, syncobj, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + args.sync_object); + if (!ret) + syncobj->handle = args.sync_object; + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + +cleanup: + + if (ret) { + if (syncobj) { + dxgsyncobject_destroy(process, syncobj); + if (args.sync_object) + dxgvmb_send_destroy_sync_object(process, + args. + sync_object); + event = NULL; + } + if (event) + eventfd_ctx_put(event); + } + if (syncobjgbl) + dxgsharedsyncobj_release_reference(syncobjgbl); + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device_lock_acquired) + dxgdevice_release_lock_shared(device); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_destroy_sync_object(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_destroysynchronizationobject args; + struct dxgsyncobject *syncobj = NULL; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + TRACE_DEBUG(1, "handle 0x%x", args.sync_object); + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + syncobj = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + args.sync_object); + if (syncobj) { + TRACE_DEBUG(1, "syncobj 0x%p", syncobj); + syncobj->handle = 0; + hmgrtable_free_handle(&process->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + args.sync_object); + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (syncobj == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + dxgsyncobject_destroy(process, syncobj); + + ret = dxgvmb_send_destroy_sync_object(process, args.sync_object); + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_open_sync_object_nt(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_opensyncobjectfromnthandle2 args; + struct dxgsyncobject *syncobj = NULL; + struct dxgsharedsyncobject *syncobj_fd = NULL; + struct file *file = NULL; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct d3dddi_synchronizationobject_flags flags = { }; + int ret; + bool device_lock_acquired = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + args.sync_object = 0; + + if (args.device) { + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) + goto cleanup; + } else { + pr_err("device handle is missing"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) + goto cleanup; + + device_lock_acquired = true; + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + file = fget(args.nt_handle); + if (!file) { + pr_err("failed to get file from handle: %llx", + args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (file->f_op != &dxg_syncobj_fops) { + pr_err("invalid fd: %llx", args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + syncobj_fd = file->private_data; + if (syncobj_fd == NULL) { + pr_err("invalid private data: %llx", args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + flags.shared = 1; + flags.nt_security_sharing = 1; + syncobj = dxgsyncobject_create(process, device, adapter, + syncobj_fd->type, flags); + if (syncobj == NULL) { + pr_err("failed to create sync object"); + goto cleanup; + } + + dxgsharedsyncobj_add_syncobj(syncobj_fd, syncobj); + + ret = dxgvmb_send_open_sync_object_nt(process, &dxgglobal->channel, + &args, syncobj); + if (ret) { + pr_err("failed to open sync object on host: %x", + syncobj_fd->host_shared_handle); + goto cleanup; + } + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, syncobj, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + args.sync_object); + if (!ret) { + syncobj->handle = args.sync_object; + dxgsyncobject_acquire_reference(syncobj); + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + if (!ret) + goto success; + +cleanup: + + if (syncobj) { + dxgsyncobject_destroy(process, syncobj); + syncobj = NULL; + } + + if (args.sync_object) + dxgvmb_send_destroy_sync_object(process, args.sync_object); + +success: + + if (file) + fput(file); + if (syncobj) + dxgsyncobject_release_reference(syncobj); + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device_lock_acquired) + dxgdevice_release_lock_shared(device); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_open_sync_object(struct dxgprocess *process, + void *__user inargs) +{ + d3dkmt_handle shared_handle = 0; + d3dkmt_handle new_handle = 0; + struct d3dkmt_opensynchronizationobject *__user inp = inargs; + struct dxgsyncobject *syncobj = NULL; + struct dxgsharedsyncobject *syncobjgbl = NULL; + struct d3dddi_synchronizationobject_flags flags = { }; + int ret; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&shared_handle, &inp->shared_handle, + sizeof(shared_handle)); + if (ret) + goto cleanup; + + hmgrtable_lock(&dxgglobal->handle_table, DXGLOCK_SHARED); + syncobjgbl = hmgrtable_get_object_by_type(&dxgglobal->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + shared_handle); + if (syncobjgbl) + dxgsharedsyncobj_acquire_reference(syncobjgbl); + hmgrtable_unlock(&dxgglobal->handle_table, DXGLOCK_SHARED); + + if (syncobjgbl == NULL) { + pr_err("invalid sync object shared handle: %x", + shared_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (syncobjgbl->monitored_fence) { + pr_err("Open monitored fence using global handle"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + flags.shared = 1; + syncobj = dxgsyncobject_create(process, NULL, syncobjgbl->adapter, + syncobjgbl->type, flags); + if (syncobj == NULL) { + pr_err("failed to create sync object"); + goto cleanup; + } + + dxgsharedsyncobj_add_syncobj(syncobjgbl, syncobj); + + ret = dxgvmb_send_open_sync_object(process, &dxgglobal->channel, + syncobjgbl->host_shared_handle, + &new_handle); + if (ret) { + pr_err("failed to open sync object on host: %x", + syncobjgbl->host_shared_handle); + goto cleanup; + } + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, syncobj, + HMGRENTRY_TYPE_DXGSYNCOBJECT, new_handle); + if (!ret) { + syncobj->handle = new_handle; + dxgsyncobject_acquire_reference(syncobj); + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(&inp->sync_object, &new_handle, + sizeof(new_handle)); + if (!ret) + goto success; + +cleanup: + + if (syncobj) { + dxgsyncobject_destroy(process, syncobj); + syncobj = NULL; + } + + if (new_handle) + dxgvmb_send_destroy_sync_object(process, new_handle); + +success: + + if (syncobj) + dxgsyncobject_release_reference(syncobj); + if (syncobjgbl) + dxgsharedsyncobj_release_reference(syncobjgbl); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_signal_sync_object(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_signalsynchronizationobject2 args; + struct d3dkmt_signalsynchronizationobject2 *__user in_args = inargs; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int ret = 0; + uint fence_count = 1; + struct eventfd_ctx *event = NULL; + struct dxghostevent *host_event = NULL; + bool host_event_added = false; + u64 host_event_id = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.context_count >= D3DDDI_MAX_BROADCAST_CONTEXT || + args.object_count > D3DDDI_MAX_OBJECT_SIGNALED) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.flags.enqueue_cpu_event) { + host_event = dxgmem_alloc(process, DXGMEM_EVENT, + sizeof(*host_event)); + if (host_event == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + host_event->process = process; + event = eventfd_ctx_fdget((int)args.cpu_event_handle); + if (IS_ERR(event)) { + pr_err("failed to reference the event"); + event = NULL; + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + fence_count = 0; + host_event->cpu_event = event; + host_event_id = dxgglobal_new_host_event_id(); + host_event->event_id = host_event_id; + host_event->remove_from_list = true; + host_event->destroy_after_signal = true; + dxgglobal_add_host_event(host_event); + host_event_added = true; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_signal_sync_object(process, &adapter->channel, + args.flags, args.fence.fence_value, + args.context, args.object_count, + in_args->object_array, + args.context_count, + in_args->contexts, fence_count, + NULL, (void *)host_event_id, 0); + + /* + * When the send operation succeeds, the host event will be destroyed + * after signal from the host + */ + +cleanup: + + if (ret) { + if (host_event_added) { + /* The event might be signaled and destroyed by host */ + host_event = dxgglobal_get_host_event(host_event_id); + if (host_event) { + eventfd_ctx_put(event); + event = NULL; + dxgmem_free(process, DXGMEM_EVENT, host_event); + host_event = NULL; + } + } + if (event) + eventfd_ctx_put(event); + if (host_event) + dxgmem_free(process, DXGMEM_EVENT, host_event); + } + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_signal_sync_object_cpu(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_signalsynchronizationobjectfromcpu args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_signal_sync_object(process, &adapter->channel, + args.flags, 0, 0, + args.object_count, args.objects, 0, + NULL, args.object_count, + args.fence_values, NULL, + args.device); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_signal_sync_object_gpu(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_signalsynchronizationobjectfromgpu args; + struct d3dkmt_signalsynchronizationobjectfromgpu *__user user_args = + inargs; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct d3dddicb_signalflags flags = { }; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count == 0 || + args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_signal_sync_object(process, &adapter->channel, + flags, 0, 0, args.object_count, + args.objects, 1, + &user_args->context, + args.object_count, + args.monitored_fence_values, NULL, + 0); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_signal_sync_object_gpu2(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_signalsynchronizationobjectfromgpu2 args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + d3dkmt_handle context_handle; + struct eventfd_ctx *event = NULL; + uint64_t *fences = NULL; + uint fence_count = 0; + int ret = 0; + struct dxghostevent *host_event = NULL; + bool host_event_added = false; + u64 host_event_id = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.flags.enqueue_cpu_event) { + if (args.object_count != 0 || args.cpu_event_handle == 0) { + pr_err("Bad input for EnqueueCpuEvent: %d %lld", + args.object_count, args.cpu_event_handle); + + } + } else if (args.object_count == 0 || + args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE || + args.context_count == 0 || + args.context_count > DXG_MAX_VM_BUS_PACKET_SIZE) { + pr_err("Invalid input: %d %d", + args.object_count, args.context_count); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxg_copy_from_user(&context_handle, args.contexts, + sizeof(d3dkmt_handle)); + if (ret) + goto cleanup; + + if (args.flags.enqueue_cpu_event) { + host_event = dxgmem_alloc(process, DXGMEM_EVENT, + sizeof(*host_event)); + if (host_event == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + host_event->process = process; + event = eventfd_ctx_fdget((int)args.cpu_event_handle); + if (IS_ERR(event)) { + pr_err("failed to reference the event"); + event = NULL; + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + fence_count = 0; + host_event->cpu_event = event; + host_event_id = dxgglobal_new_host_event_id(); + host_event->event_id = host_event_id; + host_event->remove_from_list = true; + host_event->destroy_after_signal = true; + dxgglobal_add_host_event(host_event); + host_event_added = true; + } else { + fences = args.monitored_fence_values; + fence_count = args.object_count; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + context_handle); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_signal_sync_object(process, &adapter->channel, + args.flags, 0, 0, + args.object_count, args.objects, + args.context_count, args.contexts, + fence_count, fences, + (void *)host_event_id, 0); + +cleanup: + + if (ret) { + if (host_event_added) { + /* The event might be signaled and destroyed by host */ + host_event = dxgglobal_get_host_event(host_event_id); + if (host_event) { + eventfd_ctx_put(event); + event = NULL; + dxgmem_free(process, DXGMEM_EVENT, host_event); + host_event = NULL; + } + } + if (event) + eventfd_ctx_put(event); + if (host_event) + dxgmem_free(process, DXGMEM_EVENT, host_event); + } + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_wait_sync_object(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_waitforsynchronizationobject2 args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int ret = 0; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count > D3DDDI_MAX_OBJECT_WAITED_ON || + args.object_count == 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + TRACE_DEBUG(1, "Fence value: %lld", args.fence.fence_value); + ret = dxgvmb_send_wait_sync_object_gpu(process, &adapter->channel, + args.context, args.object_count, + args.object_array, + &args.fence.fence_value, true); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_wait_sync_object_cpu(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_waitforsynchronizationobjectfromcpu args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct eventfd_ctx *event = NULL; + struct dxghostevent host_event = { }; + struct dxghostevent *async_host_event = NULL; + struct completion local_event = { }; + u64 event_id = 0; + int ret = 0; + unsigned long t; + bool host_event_added = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE || + args.object_count == 0) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (args.async_event) { + async_host_event = dxgmem_alloc(process, DXGMEM_EVENT, + sizeof(*async_host_event)); + if (async_host_event == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + async_host_event->process = process; + event = eventfd_ctx_fdget((int)args.async_event); + if (IS_ERR(event)) { + pr_err("failed to reference the event"); + event = NULL; + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + async_host_event->cpu_event = event; + async_host_event->event_id = dxgglobal_new_host_event_id(); + async_host_event->destroy_after_signal = true; + dxgglobal_add_host_event(async_host_event); + event_id = async_host_event->event_id; + host_event_added = true; + } else { + init_completion(&local_event); + host_event.completion_event = &local_event; + host_event.event_id = dxgglobal_new_host_event_id(); + dxgglobal_add_host_event(&host_event); + event_id = host_event.event_id; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_wait_sync_object_cpu(process, &adapter->channel, + &args, event_id); + if (ret) + goto cleanup; + + if (args.async_event == 0) { + t = wait_for_completion_timeout(&local_event, (10 * HZ)); + if (!t) { + TRACE_DEBUG(1, "timeout waiting for completion"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + } + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + if (host_event.event_id) + dxgglobal_remove_host_event(&host_event); + if (ret) { + if (host_event_added) { + async_host_event = dxgglobal_get_host_event(event_id); + if (async_host_event) { + eventfd_ctx_put(event); + event = NULL; + dxgmem_free(process, DXGMEM_EVENT, + async_host_event); + async_host_event = NULL; + } + } + if (event) + eventfd_ctx_put(event); + if (async_host_event) + dxgmem_free(process, DXGMEM_EVENT, async_host_event); + } + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_wait_sync_object_gpu(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_waitforsynchronizationobjectfromgpu args; + struct dxgcontext *context = NULL; + d3dkmt_handle device_handle = 0; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + struct dxgsyncobject *syncobj = NULL; + d3dkmt_handle *objects = NULL; + uint object_size; + uint64_t *fences = NULL; + int ret = 0; + enum hmgrentry_type syncobj_type = HMGRENTRY_TYPE_FREE; + bool monitored_fence = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE || + args.object_count == 0) { + pr_err("Invalid object count: %d", args.object_count); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + object_size = sizeof(d3dkmt_handle) * args.object_count; + objects = dxgmem_alloc(process, DXGMEM_TMP, object_size); + if (objects == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(objects, args.objects, object_size); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + context = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGCONTEXT, + args.context); + if (context) { + device_handle = context->device_handle; + syncobj_type = + hmgrtable_get_object_type(&process->handle_table, + objects[0]); + } + if (device_handle == 0) { + pr_err("Invalid context handle: %x", args.context); + ret = STATUS_INVALID_PARAMETER; + } else { + if (syncobj_type == HMGRENTRY_TYPE_MONITOREDFENCE) { + monitored_fence = true; + } else if (syncobj_type == HMGRENTRY_TYPE_DXGSYNCOBJECT) { + syncobj = + hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGSYNCOBJECT, + objects[0]); + if (syncobj == NULL) { + pr_err("Invalid syncobj: %x", objects[0]); + ret = STATUS_INVALID_PARAMETER; + } else { + monitored_fence = syncobj->monitored_fence; + } + } else { + pr_err("Invalid syncobj type: %x", objects[0]); + ret = STATUS_INVALID_PARAMETER; + } + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + + if (ret) + goto cleanup; + + if (monitored_fence) { + object_size = sizeof(uint64_t) * args.object_count; + fences = dxgmem_alloc(process, DXGMEM_TMP, object_size); + if (fences == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + ret = dxg_copy_from_user(fences, args.monitored_fence_values, + object_size); + if (ret) + goto cleanup; + } else { + fences = &args.fence_value; + } + + device = dxgprocess_device_by_handle(process, device_handle); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_wait_sync_object_gpu(process, &adapter->channel, + args.context, args.object_count, + objects, fences, + !monitored_fence); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + if (objects) + dxgmem_free(process, DXGMEM_TMP, objects); + if (fences && fences != &args.fence_value) + dxgmem_free(process, DXGMEM_TMP, fences); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_lock2(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_lock2 args; + struct d3dkmt_lock2 *__user result = inargs; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + struct dxgallocation *alloc = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + args.data = NULL; + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + alloc = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGALLOCATION, + args.allocation); + if (alloc == NULL) { + ret = STATUS_INVALID_PARAMETER; + } else { + if (alloc->cpu_address) { + ret = dxg_copy_to_user(&result->data, + &alloc->cpu_address, + sizeof(args.data)); + if (NT_SUCCESS(ret)) { + args.data = alloc->cpu_address; + if (alloc->cpu_address_mapped) + alloc->cpu_address_refcount++; + } + } + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + if (ret) + goto cleanup; + if (args.data) + goto success; + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_lock2(process, &adapter->channel, &args, result); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) + dxgdevice_release_reference(device); + +success: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_unlock2(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_unlock2 args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + struct dxgallocation *alloc = NULL; + bool done = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + alloc = hmgrtable_get_object_by_type(&process->handle_table, + HMGRENTRY_TYPE_DXGALLOCATION, + args.allocation); + if (alloc == NULL) { + ret = STATUS_INVALID_PARAMETER; + } else { + if (alloc->cpu_address == NULL) { + pr_err("Allocation is not locked: %p", alloc); + ret = STATUS_INVALID_PARAMETER; + } else if (alloc->cpu_address_mapped) { + if (alloc->cpu_address_refcount > 0) { + alloc->cpu_address_refcount--; + if (alloc->cpu_address_refcount != 0) { + done = true; + } else { + dxg_unmap_iospace(alloc->cpu_address, + alloc-> + num_pages << + PAGE_SHIFT); + alloc->cpu_address_mapped = false; + alloc->cpu_address = NULL; + } + } else { + pr_err("Bad cpu access refcount"); + done = true; + } + } + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + if (done) + goto success; + if (ret) + goto cleanup; + + /* + * The call acquires reference on the device. It is safe to access the + * adapter, because the device holds reference on it. + */ + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_unlock2(process, &adapter->channel, &args); + if (ret) + goto cleanup; + +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) + dxgdevice_release_reference(device); + +success: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_update_alloc_property(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dddi_updateallocproperty args; + int ret = 0; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGPAGINGQUEUE, + args.paging_queue); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_update_alloc_property(process, &adapter->channel, + &args, inargs); + +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_mark_device_as_error(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_markdeviceaserror args; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_mark_device_as_error(process, &adapter->channel, + &args); +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_query_alloc_residency(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryallocationresidency args; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if ((args.allocation_count == 0) == (args.resource == 0)) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_query_alloc_residency(process, &adapter->channel, + &args); +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_set_allocation_priority(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_setallocationpriority args; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_set_allocation_priority(process, &adapter->channel, + &args); +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_get_allocation_priority(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_getallocationpriority args; + struct dxgadapter *adapter = NULL; + struct dxgdevice *device = NULL; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_get_allocation_priority(process, &adapter->channel, + &args); +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static long set_context_scheduling_priority(struct dxgprocess *process, + d3dkmt_handle hcontext, + int priority, bool in_process) +{ + int ret = 0; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + hcontext); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_set_context_scheduling_priority(process, + &adapter->channel, + hcontext, priority, + in_process); + if (ret) + pr_err("send_set_context_scheduling_priority failed"); +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + return ret; +} + +static int dxgk_set_context_scheduling_priority(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_setcontextschedulingpriority args; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = set_context_scheduling_priority(process, args.context, + args.priority, false); +cleanup: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static long get_context_scheduling_priority(struct dxgprocess *process, + d3dkmt_handle hcontext, + __user int *priority, + bool in_process) +{ + int ret = 0; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + int pri = 0; + + device = dxgprocess_device_by_object_handle(process, + HMGRENTRY_TYPE_DXGCONTEXT, + hcontext); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + ret = dxgvmb_send_get_context_scheduling_priority(process, + &adapter->channel, + hcontext, &pri, + in_process); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(priority, &pri, sizeof(pri)); + +cleanup: + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + + return ret; +} + +static int dxgk_get_context_scheduling_priority(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_getcontextschedulingpriority args; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = get_context_scheduling_priority(process, args.context, + &((struct + d3dkmt_getcontextschedulingpriority + *) + inargs)->priority, false); +cleanup: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_set_context_process_scheduling_priority(struct dxgprocess + *process, + void *__user inargs) +{ + struct d3dkmt_setcontextinprocessschedulingpriority args; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = set_context_scheduling_priority(process, args.context, + args.priority, true); +cleanup: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_get_context_process_scheduling_priority(struct dxgprocess + *process, + void *__user inargs) +{ + struct d3dkmt_getcontextinprocessschedulingpriority args; + int ret; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = get_context_scheduling_priority(process, args.context, + &((struct + d3dkmt_getcontextinprocessschedulingpriority + *) + inargs)->priority, true); +cleanup: + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_change_vidmem_reservation(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_changevideomemoryreservation args; + int ret; + struct dxgadapter *adapter = NULL; + bool adapter_locked = false; + + TRACE_FUNC_ENTER(__func__); + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.process != 0) { + pr_err("setting memory reservation for other process"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + adapter_locked = true; + args.adapter = 0; + ret = dxgvmb_send_change_vidmem_reservation(process, &adapter->channel, + 0, &args); + +cleanup: + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + if (adapter) + dxgadapter_release_reference(adapter); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_query_clock_calibration(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryclockcalibration args; + int ret; + struct dxgadapter *adapter = NULL; + bool adapter_locked = false; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + adapter_locked = true; + + args.adapter = adapter->host_handle; + ret = dxgvmb_send_query_clock_calibration(process, &adapter->channel, + &args, inargs); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + +cleanup: + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + if (adapter) + dxgadapter_release_reference(adapter); + return ret; +} + +static int dxgk_flush_heap_transitions(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_flushheaptransitions args; + int ret; + struct dxgadapter *adapter = NULL; + bool adapter_locked = false; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + adapter_locked = true; + + args.adapter = adapter->host_handle; + ret = dxgvmb_send_flush_heap_transitions(process, &adapter->channel, + &args); + if (ret) + goto cleanup; + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + +cleanup: + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + if (adapter) + dxgadapter_release_reference(adapter); + return ret; +} + +static int handle_table_escape(struct dxgprocess *process, + struct d3dkmt_escape *args, + struct d3dkmt_ht_desc *cmdin) +{ + int ret = 0; + struct d3dkmt_ht_desc cmd; + struct hmgrtable *table; + + dxgmutex_lock(&process->process_mutex); + cmd = *cmdin; + if (cmd.index >= 2) { + pr_err("invalid table index"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + table = process->test_handle_table[cmd.index]; + if (table == NULL) { + table = dxgmem_alloc(process, DXGMEM_HANDLE_TABLE, + sizeof(*table)); + if (table == NULL) { + pr_err("failed to allocate handle table"); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + hmgrtable_init(table, process); + process->test_handle_table[cmd.index] = table; + } + switch (cmd.command) { + case D3DKMT_HT_COMMAND_ALLOC: + cmd.handle = hmgrtable_alloc_handle_safe(table, cmd.object, + (enum hmgrentry_type) + cmd.object_type, true); + ret = dxg_copy_to_user(args->priv_drv_data, &cmd, sizeof(cmd)); + break; + case D3DKMT_HT_COMMAND_FREE: + hmgrtable_free_handle_safe(table, + (enum hmgrentry_type)cmd.object_type, + cmd.handle); + break; + break; + case D3DKMT_HT_COMMAND_ASSIGN: + ret = hmgrtable_assign_handle_safe(table, cmd.object, + (enum hmgrentry_type)cmd. + object_type, cmd.handle); + break; + case D3DKMT_HT_COMMAND_GET: + hmgrtable_lock(table, DXGLOCK_SHARED); + cmd.object = hmgrtable_get_object_by_type(table, + (enum hmgrentry_type) + cmd.object_type, + cmd.handle); + hmgrtable_unlock(table, DXGLOCK_SHARED); + ret = dxg_copy_to_user(args->priv_drv_data, &cmd, sizeof(cmd)); + break; + case D3DKMT_HT_COMMAND_DESTROY: + if (table) { + hmgrtable_destroy(table); + dxgmem_free(process, DXGMEM_HANDLE_TABLE, table); + } + process->test_handle_table[cmd.index] = NULL; + break; + default: + ret = STATUS_INVALID_PARAMETER; + pr_err("unknoen handle table command"); + break; + } + +cleanup: + dxgmutex_unlock(&process->process_mutex); + return ret; +} + +static int dxgk_escape(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_escape args; + int ret; + struct dxgadapter *adapter = NULL; + bool adapter_locked = false; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + adapter_locked = true; + + if (args.type == D3DKMT_ESCAPE_DRT_TEST) { + struct d3dkmt_ht_desc drtcmd; + + if (args.priv_drv_data_size >= sizeof(drtcmd)) { + ret = dxg_copy_from_user(&drtcmd, + args.priv_drv_data, + sizeof(drtcmd)); + if (ret) + goto cleanup; + if (drtcmd.head.command == + D3DKMT_DRT_TEST_COMMAND_HANDLETABLE) { + dxgadapter_release_lock_shared(adapter); + adapter_locked = false; + ret = handle_table_escape(process, &args, + &drtcmd); + goto cleanup; + } + } + } + + args.adapter = adapter->host_handle; + ret = dxgvmb_send_escape(process, &adapter->channel, &args); + if (ret) + goto cleanup; + +cleanup: + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + if (adapter) + dxgadapter_release_reference(adapter); + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_query_vidmem_info(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryvideomemoryinfo args; + int ret; + struct dxgadapter *adapter = NULL; + bool adapter_locked = false; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.process != 0) { + pr_err("query vidmem info from another process "); + goto cleanup; + } + + adapter = dxgprocess_adapter_by_handle(process, args.adapter); + if (adapter == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + adapter_locked = true; + + args.adapter = adapter->host_handle; + ret = dxgvmb_send_query_vidmem_info(process, &adapter->channel, + &args, inargs); + +cleanup: + + if (adapter_locked) + dxgadapter_release_lock_shared(adapter); + if (adapter) + dxgadapter_release_reference(adapter); + if (ret) + pr_err("%s failed: %x", __func__, ret); + return ret; +} + +static int dxgk_get_device_state(struct dxgprocess *process, + void *__user inargs) +{ + int ret = 0; + struct d3dkmt_getdevicestate args; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgvmb_send_get_device_state(process, &adapter->channel, + &args, inargs); + +cleanup: + + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_reference(device); + if (ret) + pr_err("%s failed %x", __func__, ret); + + return ret; +} + +static int dxgsharedsyncobj_get_host_nt_handle(struct dxgsharedsyncobject + *syncobj, + struct dxgprocess *process, + d3dkmt_handle object_handle) +{ + int ret = 0; + + dxgmutex_lock(&syncobj->fd_mutex); + if (syncobj->host_shared_handle_nt_reference == 0) { + ret = dxgvmb_send_create_nt_shared_object(process, + object_handle, + &syncobj-> + host_shared_handle_nt); + if (ret) + goto cleanup; + TRACE_DEBUG(1, "Host_shared_handle_ht: %x", + syncobj->host_shared_handle_nt); + dxgsharedsyncobj_acquire_reference(syncobj); + } + syncobj->host_shared_handle_nt_reference++; +cleanup: + dxgmutex_unlock(&syncobj->fd_mutex); + return ret; +} + +static int dxgsharedresource_get_host_nt_handle(struct dxgsharedresource + *resource, + struct dxgprocess *process, + d3dkmt_handle object_handle) +{ + int ret = 0; + + dxgmutex_lock(&resource->fd_mutex); + if (resource->host_shared_handle_nt_reference == 0) { + ret = dxgvmb_send_create_nt_shared_object(process, + object_handle, + &resource-> + host_shared_handle_nt); + if (ret) + goto cleanup; + TRACE_DEBUG(1, "Resource host_shared_handle_ht: %x", + resource->host_shared_handle_nt); + dxgsharedresource_acquire_reference(resource); + } + resource->host_shared_handle_nt_reference++; +cleanup: + dxgmutex_unlock(&resource->fd_mutex); + return ret; +} + +enum dxg_sharedobject_type { + DXG_SHARED_SYNCOBJECT, + DXG_SHARED_RESOURCE +}; + +static int get_object_fd(enum dxg_sharedobject_type type, + void *object, int *fdout) +{ + struct file *file; + int fd; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_err("get_unused_fd_flags failed: %x", fd); + return STATUS_INTERNAL_ERROR; + } + + switch (type) { + case DXG_SHARED_SYNCOBJECT: + file = anon_inode_getfile("dxgsyncobj", + &dxg_syncobj_fops, object, 0); + break; + case DXG_SHARED_RESOURCE: + file = anon_inode_getfile("dxgresource", + &dxg_resource_fops, object, 0); + break; + default: + return STATUS_INVALID_PARAMETER; + }; + if (IS_ERR(file)) { + pr_err("anon_inode_getfile failed: %x", fd); + put_unused_fd(fd); + return STATUS_INTERNAL_ERROR; + } + + fd_install(fd, file); + *fdout = fd; + return 0; +} + +static int dxgk_share_objects(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_shareobjects args; + enum hmgrentry_type object_type; + struct dxgsyncobject *syncobj = NULL; + struct dxgresource *resource = NULL; + struct dxgsharedsyncobject *shared_syncobj = NULL; + struct dxgsharedresource *shared_resource = NULL; + d3dkmt_handle *handles = NULL; + int object_fd = 0; + void *obj = NULL; + uint handle_size; + int ret; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + if (args.object_count == 0 || args.object_count > 1) { + pr_err("invalid object count %d", args.object_count); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + handle_size = args.object_count * sizeof(d3dkmt_handle); + + handles = dxgmem_alloc(process, DXGMEM_TMP, handle_size); + if (handles == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + ret = dxg_copy_from_user(handles, args.objects, handle_size); + if (ret) + goto cleanup; + + TRACE_DEBUG(1, "Sharing handle: %x", handles[0]); + + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); + object_type = hmgrtable_get_object_type(&process->handle_table, + handles[0]); + obj = hmgrtable_get_object(&process->handle_table, handles[0]); + if (obj == NULL) { + pr_err("invalid object handle %x", handles[0]); + ret = STATUS_INVALID_PARAMETER; + } else { + switch (object_type) { + case HMGRENTRY_TYPE_DXGSYNCOBJECT: + syncobj = obj; + if (syncobj->shared) { + dxgsyncobject_acquire_reference(syncobj); + shared_syncobj = syncobj->shared_owner; + } else { + pr_err("sync object is not shared"); + syncobj = NULL; + ret = STATUS_INVALID_PARAMETER; + } + break; + case HMGRENTRY_TYPE_DXGRESOURCE: + resource = obj; + if (resource->shared_owner) { + dxgresource_acquire_reference(resource); + shared_resource = resource->shared_owner; + } else { + resource = NULL; + pr_err("resource object is not shared"); + ret = STATUS_INVALID_PARAMETER; + } + break; + default: + pr_err("invalid object type %d", object_type); + ret = STATUS_INVALID_PARAMETER; + break; + } + } + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); + + if (ret) + goto cleanup; + + switch (object_type) { + case HMGRENTRY_TYPE_DXGSYNCOBJECT: + ret = get_object_fd(DXG_SHARED_SYNCOBJECT, shared_syncobj, + &object_fd); + if (!ret) + ret = + dxgsharedsyncobj_get_host_nt_handle(shared_syncobj, + process, + handles[0]); + break; + case HMGRENTRY_TYPE_DXGRESOURCE: + ret = get_object_fd(DXG_SHARED_RESOURCE, shared_resource, + &object_fd); + if (!ret) + ret = + dxgsharedresource_get_host_nt_handle + (shared_resource, process, handles[0]); + break; + default: + ret = STATUS_INVALID_PARAMETER; + break; + } + + if (ret) + goto cleanup; + + TRACE_DEBUG(1, "Object FD: %x", object_fd); + + { + winhandle tmp = (winhandle) object_fd; + + ret = dxg_copy_to_user(args.shared_handle, &tmp, + sizeof(winhandle)); + } + +cleanup: + if (ret) { + if (object_fd > 0) + put_unused_fd(object_fd); + } + + if (handles) + dxgmem_free(process, DXGMEM_TMP, handles); + + if (syncobj) + dxgsyncobject_release_reference(syncobj); + + if (resource) + dxgresource_release_reference(resource); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_invalidate_cache(struct dxgprocess *process, + void *__user inargs) +{ + pr_err("%s is not implemented", __func__); + return STATUS_NOT_IMPLEMENTED; +} + +static int dxgk_query_resource_info(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryresourceinfo args; + struct dxgdevice *device = NULL; + struct dxgsharedresource *shared_resource = NULL; + int ret; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + hmgrtable_lock(&dxgglobal->handle_table, DXGLOCK_SHARED); + shared_resource = hmgrtable_get_object_by_type(&dxgglobal->handle_table, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE, + args.global_share); + if (shared_resource) { + if (!dxgsharedresource_acquire_reference(shared_resource)) + shared_resource = NULL; + } + hmgrtable_unlock(&dxgglobal->handle_table, DXGLOCK_SHARED); + + if (shared_resource == NULL) { + pr_err("Invalid shared resource handle: %x", + args.global_share); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + ret = dxgsharedresource_seal(shared_resource); + if (ret) + goto cleanup; + + args.private_runtime_data_size = + shared_resource->runtime_private_data_size; + args.resource_priv_drv_data_size = + shared_resource->resource_private_data_size; + args.allocation_count = shared_resource->allocation_count; + args.total_priv_drv_data_size = + shared_resource->alloc_private_data_size; + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + +cleanup: + + if (shared_resource) + dxgsharedresource_release_reference(shared_resource); + if (device) + dxgdevice_release_lock_shared(device); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_query_resource_info_nt(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_queryresourceinfofromnthandle args; + int ret; + struct dxgdevice *device = NULL; + struct dxgsharedresource *shared_resource = NULL; + struct file *file = NULL; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + file = fget(args.nt_handle); + if (!file) { + pr_err("failed to get file from handle: %llx", + args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (file->f_op != &dxg_resource_fops) { + pr_err("invalid fd: %llx", args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + shared_resource = file->private_data; + if (shared_resource == NULL) { + pr_err("invalid private data: %llx", args.nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + device = dxgprocess_device_by_handle(process, args.device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + ret = dxgsharedresource_seal(shared_resource); + if (ret) + goto cleanup; + + args.private_runtime_data_size = + shared_resource->runtime_private_data_size; + args.resource_priv_drv_data_size = + shared_resource->resource_private_data_size; + args.allocation_count = shared_resource->allocation_count; + args.total_priv_drv_data_size = + shared_resource->alloc_private_data_size; + + ret = dxg_copy_to_user(inargs, &args, sizeof(args)); + +cleanup: + + if (file) + fput(file); + if (device) + dxgdevice_release_lock_shared(device); + if (device) + dxgdevice_release_reference(device); + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +int assign_resource_handles(struct dxgprocess *process, + struct dxgsharedresource *shared_resource, + struct d3dkmt_openresourcefromnthandle *args, + d3dkmt_handle resource_handle, + struct dxgresource *resource, + struct dxgallocation **allocs, + d3dkmt_handle *handles) +{ + int ret = 0; + int i; + uint8_t *cur_priv_data; + struct d3dddi_openallocationinfo2 open_alloc_info = { }; + + TRACE_DEBUG(1, "%s", __func__); + + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); + ret = hmgrtable_assign_handle(&process->handle_table, resource, + HMGRENTRY_TYPE_DXGRESOURCE, + resource_handle); + if (ret) + goto cleanup; + resource->handle = resource_handle; + resource->handle_valid = 1; + cur_priv_data = shared_resource->alloc_private_data; + for (i = 0; i < args->allocation_count; i++) { + ret = hmgrtable_assign_handle(&process->handle_table, allocs[i], + HMGRENTRY_TYPE_DXGALLOCATION, + handles[i]); + if (ret) + goto cleanup; + allocs[i]->alloc_handle = handles[i]; + allocs[i]->handle_valid = 1; + open_alloc_info.allocation = handles[i]; + if (shared_resource->alloc_private_data_sizes) + open_alloc_info.priv_drv_data_size = + shared_resource->alloc_private_data_sizes[i]; + else + open_alloc_info.priv_drv_data_size = 0; + + open_alloc_info.priv_drv_data = cur_priv_data; + cur_priv_data += open_alloc_info.priv_drv_data_size; + + ret = dxg_copy_to_user(&args->open_alloc_info[i], + &open_alloc_info, + sizeof(open_alloc_info)); + if (ret) + goto cleanup; + } +cleanup: + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); + if (ret) { + for (i = 0; i < args->allocation_count; i++) + dxgallocation_free_handle(allocs[i]); + dxgresource_free_handle(resource); + } + TRACE_DEBUG(1, "%s end %x", __func__, ret); + return ret; +} + +int open_resource(struct dxgprocess *process, + struct d3dkmt_openresourcefromnthandle *args, + bool nt_handle, __user d3dkmt_handle *res_out) +{ + int ret = 0; + int i; + d3dkmt_handle *alloc_handles = NULL; + int alloc_handles_size = sizeof(d3dkmt_handle) * args->allocation_count; + struct dxgsharedresource *shared_resource = NULL; + struct dxgresource *resource = NULL; + struct dxgallocation **allocs = NULL; + d3dkmt_handle global_share; + struct dxgdevice *device = NULL; + struct dxgadapter *adapter = NULL; + d3dkmt_handle resource_handle = 0; + struct file *file = NULL; + + TRACE_DEBUG(1, "Opening resource handle: %llx", args->nt_handle); + + if (nt_handle) { + file = fget(args->nt_handle); + if (!file) { + pr_err("failed to get file from handle: %llx", + args->nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (file->f_op != &dxg_resource_fops) { + pr_err("invalid fd type: %llx", args->nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + shared_resource = file->private_data; + if (shared_resource == NULL) { + pr_err("invalid private data: %llx", + args->nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + if (!dxgsharedresource_acquire_reference(shared_resource)) + shared_resource = NULL; + else + global_share = shared_resource->host_shared_handle_nt; + } else { + hmgrtable_lock(&dxgglobal->handle_table, DXGLOCK_SHARED); + shared_resource = + hmgrtable_get_object_by_type(&dxgglobal->handle_table, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE, + (d3dkmt_handle) args-> + nt_handle); + if (shared_resource) { + if (!dxgsharedresource_acquire_reference + (shared_resource)) + shared_resource = NULL; + else + global_share = + shared_resource->host_shared_handle; + } + hmgrtable_unlock(&dxgglobal->handle_table, DXGLOCK_SHARED); + } + + if (shared_resource == NULL) { + pr_err("Invalid shared resource handle: %x", + (d3dkmt_handle) args->nt_handle); + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + TRACE_DEBUG(1, "Shared resource: %p %x", shared_resource, + global_share); + + device = dxgprocess_device_by_handle(process, args->device); + if (device == NULL) { + ret = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + ret = dxgdevice_acquire_lock_shared(device); + if (ret) { + dxgdevice_release_reference(device); + device = NULL; + goto cleanup; + } + + adapter = device->adapter; + ret = dxgadapter_acquire_lock_shared(adapter); + if (ret) { + adapter = NULL; + goto cleanup; + } + + ret = dxgsharedresource_seal(shared_resource); + if (ret) + goto cleanup; + + if (args->allocation_count != shared_resource->allocation_count || + args->private_runtime_data_size < + shared_resource->runtime_private_data_size || + args->resource_priv_drv_data_size < + shared_resource->resource_private_data_size || + args->total_priv_drv_data_size < + shared_resource->alloc_private_data_size) { + ret = STATUS_INVALID_PARAMETER; + pr_err("Invalid data sizes"); + goto cleanup; + } + + alloc_handles = dxgmem_alloc(process, DXGMEM_TMP, alloc_handles_size); + if (alloc_handles == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + allocs = dxgmem_alloc(process, DXGMEM_TMP, + sizeof(void *) * args->allocation_count); + if (allocs == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + + resource = dxgresource_create(device); + if (resource == NULL) { + ret = STATUS_NO_MEMORY; + goto cleanup; + } + dxgsharedresource_add_resource(shared_resource, resource); + + for (i = 0; i < args->allocation_count; i++) { + allocs[i] = dxgallocation_create(process); + if (allocs[i] == NULL) + goto cleanup; + dxgresource_add_alloc(resource, allocs[i]); + } + + ret = dxgvmb_send_open_resource(process, &adapter->channel, + device->handle, nt_handle, global_share, + args->allocation_count, + args->total_priv_drv_data_size, + &resource_handle, alloc_handles); + if (ret) { + pr_err("dxgvmb_send_open_resource failed"); + goto cleanup; + } + + if (shared_resource->runtime_private_data_size) { + ret = dxg_copy_to_user(args->private_runtime_data, + shared_resource->runtime_private_data, + shared_resource-> + runtime_private_data_size); + if (ret) + goto cleanup; + } + + if (shared_resource->resource_private_data_size) { + ret = dxg_copy_to_user(args->resource_priv_drv_data, + shared_resource->resource_private_data, + shared_resource-> + resource_private_data_size); + if (ret) + goto cleanup; + } + + if (shared_resource->alloc_private_data_size) { + ret = dxg_copy_to_user(args->total_priv_drv_data, + shared_resource->alloc_private_data, + shared_resource-> + alloc_private_data_size); + if (ret) + goto cleanup; + } + + ret = assign_resource_handles(process, shared_resource, args, + resource_handle, resource, allocs, + alloc_handles); + if (ret) + goto cleanup; + + ret = dxg_copy_to_user(res_out, &resource_handle, + sizeof(d3dkmt_handle)); + +cleanup: + + if (ret) { + if (resource_handle) { + struct d3dkmt_destroyallocation2 tmp = { }; + + tmp.flags.assume_not_in_use = 1; + tmp.device = args->device; + tmp.resource = resource_handle; + ret = dxgvmb_send_destroy_allocation(process, device, + &adapter->channel, + &tmp, NULL); + } + if (resource) + dxgresource_destroy(resource); + } + + if (file) + fput(file); + if (allocs) + dxgmem_free(process, DXGMEM_TMP, allocs); + if (shared_resource) + dxgsharedresource_release_reference(shared_resource); + if (alloc_handles) + dxgmem_free(process, DXGMEM_TMP, alloc_handles); + if (adapter) + dxgadapter_release_lock_shared(adapter); + if (device) + dxgdevice_release_lock_shared(device); + if (device) + dxgdevice_release_reference(device); + + return ret; +} + +static int dxgk_open_resource(struct dxgprocess *process, void *__user inargs) +{ + struct d3dkmt_openresource args; + struct d3dkmt_openresourcefromnthandle args_nt = { }; + int ret; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + args_nt.device = args.device; + args_nt.nt_handle = (winhandle) args.global_share; + args_nt.allocation_count = args.allocation_count; + args_nt.open_alloc_info = args.open_alloc_info; + args_nt.private_runtime_data_size = args.private_runtime_data_size; + args_nt.private_runtime_data = args.private_runtime_data; + args_nt.resource_priv_drv_data_size = args.resource_priv_drv_data_size; + args_nt.resource_priv_drv_data = args.resource_priv_drv_data; + args_nt.total_priv_drv_data_size = args.total_priv_drv_data_size; + args_nt.total_priv_drv_data = args.total_priv_drv_data; + + ret = open_resource(process, &args_nt, false, + &((struct d3dkmt_openresource *)inargs)->resource); + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static ntstatus dxgk_open_resource_nt(struct dxgprocess *process, + void *__user inargs) +{ + struct d3dkmt_openresourcefromnthandle args; + int ret; + + TRACE_FUNC_ENTER(__func__); + + ret = dxg_copy_from_user(&args, inargs, sizeof(args)); + if (ret) + goto cleanup; + + ret = open_resource(process, &args, true, + &((struct d3dkmt_openresourcefromnthandle *) + inargs)->resource); + +cleanup: + + TRACE_FUNC_EXIT(__func__, ret); + return ret; +} + +static int dxgk_render(struct dxgprocess *process, void *__user inargs) +{ + pr_err("%s is not implemented", __func__); + return STATUS_NOT_IMPLEMENTED; +} + +static int dxgk_create_context(struct dxgprocess *process, void *__user inargs) +{ + pr_err("%s is not implemented", __func__); + return STATUS_NOT_IMPLEMENTED; +} + +static int dxgk_get_shared_resource_adapter_luid(struct dxgprocess *process, + void *__user inargs) +{ + pr_err("shared_resource_adapter_luid is not implemented"); + return STATUS_NOT_IMPLEMENTED; +} + +/* + * IOCTL processing + */ +static int dxgk_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + struct dxgthreadinfo *thread; + int code = _IOC_NR(p1); + ntstatus status; + struct dxgprocess *process; + + if (code < 1 || code > LX_IO_MAX) { + pr_err("bad ioctl %x %x %x %x", + code, _IOC_TYPE(p1), _IOC_SIZE(p1), _IOC_DIR(p1)); + return STATUS_INVALID_PARAMETER; + } + if (ioctls[code].ioctl_callback == NULL) { + pr_err("ioctl callback is NULL %x", code); + return STATUS_INTERNAL_ERROR; + } + if (ioctls[code].ioctl != p1) { + pr_err("ioctl mismatch. Code: %x User: %x Kernel: %x", + code, p1, ioctls[code].ioctl); + return STATUS_INTERNAL_ERROR; + } + process = (struct dxgprocess *)f->private_data; + if (process->tgid != current->tgid) { + pr_err("Call from a wrong process: %d %d", + process->tgid, current->tgid); + return STATUS_INVALID_PARAMETER; + } + thread = dxglockorder_get_thread(); + status = ioctls[code].ioctl_callback(process, (void *__user)p2); + dxglockorder_check_empty(thread); + dxglockorder_put_thread(thread); + return status; +} + +long dxgk_compat_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + TRACE_DEBUG(2, "compat ioctl %x", p1); + return dxgk_ioctl(f, p1, p2); +} + +long dxgk_unlocked_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + TRACE_DEBUG(2, "unlocked ioctl %x Code:%d", p1, _IOC_NR(p1)); + return dxgk_ioctl(f, p1, p2); +} + +#define SET_IOCTL(callback, v) \ + ioctls[_IOC_NR(v)].ioctl_callback = callback; \ + ioctls[_IOC_NR(v)].ioctl = v + +void ioctl_desc_init(void) +{ + memset(ioctls, 0, sizeof(ioctls)); + SET_IOCTL(/*0x1 */ dxgk_open_adapter_from_luid, + LX_DXOPENADAPTERFROMLUID); + SET_IOCTL(/*0x2 */ dxgk_create_device, + LX_DXCREATEDEVICE); + SET_IOCTL(/*0x3 */ dxgk_create_context, + LX_DXCREATECONTEXT); + SET_IOCTL(/*0x4 */ dxgk_create_context_virtual, + LX_DXCREATECONTEXTVIRTUAL); + SET_IOCTL(/*0x5 */ dxgk_destroy_context, + LX_DXDESTROYCONTEXT); + SET_IOCTL(/*0x6 */ dxgk_create_allocation, + LX_DXCREATEALLOCATION); + SET_IOCTL(/*0x7 */ dxgk_create_paging_queue, + LX_DXCREATEPAGINGQUEUE); + SET_IOCTL(/*0x8 */ dxgk_reserve_gpu_va, + LX_DXRESERVEGPUVIRTUALADDRESS); + SET_IOCTL(/*0x9 */ dxgk_query_adapter_info, + LX_DXQUERYADAPTERINFO); + SET_IOCTL(/*0xa */ dxgk_query_vidmem_info, + LX_DXQUERYVIDEOMEMORYINFO); + SET_IOCTL(/*0xb */ dxgk_make_resident, + LX_DXMAKERESIDENT); + SET_IOCTL(/*0xc */ dxgk_map_gpu_va, + LX_DXMAPGPUVIRTUALADDRESS); + SET_IOCTL(/*0xd */ dxgk_escape, + LX_DXESCAPE); + SET_IOCTL(/*0xe */ dxgk_get_device_state, + LX_DXGETDEVICESTATE); + SET_IOCTL(/*0xf */ dxgk_submit_command, + LX_DXSUBMITCOMMAND); + SET_IOCTL(/*0x10 */ dxgk_create_sync_object, + LX_DXCREATESYNCHRONIZATIONOBJECT); + SET_IOCTL(/*0x11 */ dxgk_signal_sync_object, + LX_DXSIGNALSYNCHRONIZATIONOBJECT); + SET_IOCTL(/*0x12 */ dxgk_wait_sync_object, + LX_DXWAITFORSYNCHRONIZATIONOBJECT); + SET_IOCTL(/*0x13 */ dxgk_destroy_allocation, + LX_DXDESTROYALLOCATION2); + SET_IOCTL(/*0x14 */ dxgk_enum_adapters, + LX_DXENUMADAPTERS2); + SET_IOCTL(/*0x15 */ dxgk_close_adapter, + LX_DXCLOSEADAPTER); + SET_IOCTL(/*0x16 */ dxgk_change_vidmem_reservation, + LX_DXCHANGEVIDEOMEMORYRESERVATION); + SET_IOCTL(/*0x17 */ dxgk_create_hwcontext, + LX_DXCREATEHWCONTEXT); + SET_IOCTL(/*0x18 */ dxgk_create_hwqueue, + LX_DXCREATEHWQUEUE); + SET_IOCTL(/*0x19 */ dxgk_destroy_device, + LX_DXDESTROYDEVICE); + SET_IOCTL(/*0x1a */ dxgk_destroy_hwcontext, + LX_DXDESTROYHWCONTEXT); + SET_IOCTL(/*0x1b */ dxgk_destroy_hwqueue, + LX_DXDESTROYHWQUEUE); + SET_IOCTL(/*0x1c */ dxgk_destroy_paging_queue, + LX_DXDESTROYPAGINGQUEUE); + SET_IOCTL(/*0x1d */ dxgk_destroy_sync_object, + LX_DXDESTROYSYNCHRONIZATIONOBJECT); + SET_IOCTL(/*0x1e */ dxgk_evict, + LX_DXEVICT); + SET_IOCTL(/*0x1f */ dxgk_flush_heap_transitions, + LX_DXFLUSHHEAPTRANSITIONS); + SET_IOCTL(/*0x20 */ dxgk_free_gpu_va, + LX_DXFREEGPUVIRTUALADDRESS); + SET_IOCTL(/*0x21 */ dxgk_get_context_process_scheduling_priority, + LX_DXGETCONTEXTINPROCESSSCHEDULINGPRIORITY); + SET_IOCTL(/*0x22 */ dxgk_get_context_scheduling_priority, + LX_DXGETCONTEXTSCHEDULINGPRIORITY); + SET_IOCTL(/*0x23 */ dxgk_get_shared_resource_adapter_luid, + LX_DXGETSHAREDRESOURCEADAPTERLUID); + SET_IOCTL(/*0x24 */ dxgk_invalidate_cache, + LX_DXINVALIDATECACHE); + SET_IOCTL(/*0x25 */ dxgk_lock2, + LX_DXLOCK2); + SET_IOCTL(/*0x26 */ dxgk_mark_device_as_error, + LX_DXMARKDEVICEASERROR); + SET_IOCTL(/*0x27 */ dxgk_offer_allocations, + LX_DXOFFERALLOCATIONS); + SET_IOCTL(/*0x28 */ dxgk_open_resource, + LX_DXOPENRESOURCE); + SET_IOCTL(/*0x29 */ dxgk_open_sync_object, + LX_DXOPENSYNCHRONIZATIONOBJECT); + SET_IOCTL(/*0x2a */ dxgk_query_alloc_residency, + LX_DXQUERYALLOCATIONRESIDENCY); + SET_IOCTL(/*0x2b */ dxgk_query_resource_info, + LX_DXQUERYRESOURCEINFO); + SET_IOCTL(/*0x2c */ dxgk_reclaim_allocations, + LX_DXRECLAIMALLOCATIONS2); + SET_IOCTL(/*0x2d */ dxgk_render, + LX_DXRENDER); + SET_IOCTL(/*0x2e */ dxgk_set_allocation_priority, + LX_DXSETALLOCATIONPRIORITY); + SET_IOCTL(/*0x2f */ dxgk_set_context_process_scheduling_priority, + LX_DXSETCONTEXTINPROCESSSCHEDULINGPRIORITY); + SET_IOCTL(/*0x30 */ dxgk_set_context_scheduling_priority, + LX_DXSETCONTEXTSCHEDULINGPRIORITY); + SET_IOCTL(/*0x31 */ dxgk_signal_sync_object_cpu, + LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU); + SET_IOCTL(/*0x32 */ dxgk_signal_sync_object_gpu, + LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU); + SET_IOCTL(/*0x33 */ dxgk_signal_sync_object_gpu2, + LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2); + SET_IOCTL(/*0x34 */ dxgk_submit_command_to_hwqueue, + LX_DXSUBMITCOMMANDTOHWQUEUE); + SET_IOCTL(/*0x35 */ dxgk_submit_wait_to_hwqueue, + LX_DXSUBMITWAITFORSYNCOBJECTSTOHWQUEUE); + SET_IOCTL(/*0x36 */ dxgk_submit_signal_to_hwqueue, + LX_DXSUBMITSIGNALSYNCOBJECTSTOHWQUEUE); + SET_IOCTL(/*0x37 */ dxgk_unlock2, + LX_DXUNLOCK2); + SET_IOCTL(/*0x38 */ dxgk_update_alloc_property, + LX_DXUPDATEALLOCPROPERTY); + SET_IOCTL(/*0x39 */ dxgk_update_gpu_va, + LX_DXUPDATEGPUVIRTUALADDRESS); + SET_IOCTL(/*0x3a */ dxgk_wait_sync_object_cpu, + LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMCPU); + SET_IOCTL(/*0x3b */ dxgk_wait_sync_object_gpu, + LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU); + SET_IOCTL(/*0x3c */ dxgk_get_allocation_priority, + LX_DXGETALLOCATIONPRIORITY); + SET_IOCTL(/*0x3d */ dxgk_query_clock_calibration, + LX_DXQUERYCLOCKCALIBRATION); + SET_IOCTL(/*0x3e */ dxgk_enum_adapters3, + LX_DXENUMADAPTERS3); + SET_IOCTL(/*0x3f */ dxgk_share_objects, + LX_DXSHAREOBJECTS); + SET_IOCTL(/*0x40 */ dxgk_open_sync_object_nt, + LX_DXOPENSYNCOBJECTFROMNTHANDLE2); + SET_IOCTL(/*0x41 */ dxgk_query_resource_info_nt, + LX_DXQUERYRESOURCEINFOFROMNTHANDLE); + SET_IOCTL(/*0x42 */ dxgk_open_resource_nt, + LX_DXOPENRESOURCEFROMNTHANDLE); +} diff --git a/drivers/gpu/dxgkrnl/misc.c b/drivers/gpu/dxgkrnl/misc.c new file mode 100644 index 000000000000..6f88e0108612 --- /dev/null +++ b/drivers/gpu/dxgkrnl/misc.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Driver + * Helper functions + * + */ + +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> + +#include "dxgkrnl.h" +#include "misc.h" + +static atomic_t dxg_memory[DXGMEM_LAST]; + +winwchar *wcsncpy(winwchar *dest, const winwchar *src, size_t n) +{ + int i; + + for (i = 0; i < n; i++) { + dest[i] = src[i]; + if (src[i] == 0) { + i++; + break; + } + } + dest[i - 1] = 0; + return dest; +} + +int dxg_copy_from_user(void *to, const void __user *from, unsigned long len) +{ + int ret = copy_from_user(to, from, len); + + if (ret) { + pr_err("copy_from_user failed: %p %p %lx\n", + to, from, len); + ret = STATUS_INVALID_PARAMETER; + } + return ret; +} + +int dxg_copy_to_user(void *to, const void __user *from, unsigned long len) +{ + int ret = copy_to_user(to, from, len); + + if (ret) { + pr_err("copy_to_user failed: %p %p %lx\n", to, from, len); + ret = STATUS_INVALID_PARAMETER; + } + return ret; +} + +void *dxgmem_alloc(struct dxgprocess *process, enum dxgk_memory_tag tag, + size_t size) +{ + void *address = vzalloc(size); + + if (address) { + if (process) + atomic_inc(&process->dxg_memory[tag]); + else + atomic_inc(&dxg_memory[tag]); + } + return address; +} + +void dxgmem_free(struct dxgprocess *process, enum dxgk_memory_tag tag, + void *address) +{ + vfree(address); + if (process) { + if (atomic_read(&process->dxg_memory[tag]) == 0) { + pr_err("%s process error: %d\n", + __func__, tag); + dump_stack(); + } else { + atomic_dec(&process->dxg_memory[tag]); + } + } else { + if (atomic_read(&dxg_memory[tag]) == 0) { + pr_err("%s error: %d\n", __func__, tag); + dump_stack(); + } else { + atomic_dec(&dxg_memory[tag]); + } + } +} + +void *dxgmem_kalloc(enum dxgk_memory_tag tag, size_t size, gfp_t flags) +{ + void *address = kzalloc(size, flags); + + if (address) + atomic_inc(&dxg_memory[tag]); + return address; +} + +void dxgmem_kfree(enum dxgk_memory_tag tag, void *address) +{ + kfree(address); + if (atomic_read(&dxg_memory[tag]) == 0) + pr_err("dxgmem_free error: %d\n", tag); + atomic_dec(&dxg_memory[tag]); +} + +void dxgmem_check(struct dxgprocess *process, enum dxgk_memory_tag ignore_tag) +{ + int i; + int v; + + for (i = 0; i < DXGMEM_LAST; i++) { + if (process) + v = atomic_read(&process->dxg_memory[i]); + else + v = atomic_read(&dxg_memory[i]); + if (i != ignore_tag && v != 0) + pr_err("memory leak: %2d %3d\n", i, v); + } +} + +void dxgmutex_init(struct dxgmutex *m, enum dxgk_lockorder order) +{ + mutex_init(&m->mutex); + m->lock_order = order; +} + +void dxgmutex_lock(struct dxgmutex *m) +{ + dxglockorder_acquire(m->lock_order); + mutex_lock(&m->mutex); +} + +void dxgmutex_unlock(struct dxgmutex *m) +{ + mutex_unlock(&m->mutex); + dxglockorder_release(m->lock_order); +} + +void dxglockorder_acquire(enum dxgk_lockorder order) +{ + struct dxgthreadinfo *info = dxglockorder_get_thread(); + int index = info->current_lock_index; + + TRACE_LOCK_ORDER(1, "%s %p %d %d", + __func__, info->thread, index, order); + if (index == DXGK_MAX_LOCK_DEPTH) { + pr_err("Lock depth exceeded"); + dxg_panic(); + } + if (index != 0) { + struct dxgk_lockinfo *lock_info = &info->lock_info[index - 1]; + + if (lock_info->lock_order <= order) { + pr_err("Invalid lock order: %d %d %d", index, + lock_info->lock_order, order); + dxg_panic(); + } + } + info->lock_info[index].lock_order = order; + info->current_lock_index++; + dxglockorder_put_thread(info); +} + +void dxglockorder_release(enum dxgk_lockorder order) +{ + struct dxgthreadinfo *info = dxglockorder_get_thread(); + int index; + int i; + + info->current_lock_index--; + index = info->current_lock_index; + TRACE_LOCK_ORDER(1, "dxglockorder release %p %d %d", + info->thread, index, order); + if (index < 0) { + pr_err("Lock depth underflow"); + dxg_panic(); + } + for (i = index; i >= 0; i--) { + if (info->lock_info[i].lock_order == order) { + memmove(&info->lock_info[i], &info->lock_info[index], + (index - i) * sizeof(info->lock_info[0])); + break; + } + } + if (i < 0) { + pr_err("Failed to find lock order to release"); + dxg_panic(); + } + memset(&info->lock_info[index], 0, sizeof(info->lock_info[0])); + dxglockorder_put_thread(info); +} + +struct dxgthreadinfo *dxglockorder_get_thread(void) +{ + struct dxgthreadinfo *info = NULL; + struct dxgthreadinfo *entry = NULL; + struct task_struct *thread = current; + + mutex_lock(&dxgglobal->thread_info_mutex); + list_for_each_entry(entry, &dxgglobal->thread_info_list_head, + thread_info_list_entry) { + if (entry->thread == thread) { + info = entry; + TRACE_LOCK_ORDER(1, "dxglockorder found thread %p %d", + thread, info->refcount + 1); + break; + } + } + if (info == NULL) { + info = dxgmem_kalloc(DXGMEM_THREADINFO, + sizeof(struct dxgthreadinfo), GFP_ATOMIC); + if (info) { + TRACE_LOCK_ORDER(1, "dxglockorder new thread %p", + thread); + info->thread = thread; + list_add(&info->thread_info_list_entry, + &dxgglobal->thread_info_list_head); + } + } + if (info) + info->refcount++; + mutex_unlock(&dxgglobal->thread_info_mutex); + return info; +} + +void dxglockorder_put_thread(struct dxgthreadinfo *info) +{ + if (info) { + TRACE_LOCK_ORDER(1, "dxglockorder remove thread %p %d", + info->thread, info->refcount); + if (info->refcount <= 0) { + pr_err("Invalid refcount for thread info: %p %d", + info, info->refcount); + dxg_panic(); + return; + } + info->refcount--; + if (info->refcount == 0) { + TRACE_LOCK_ORDER(1, "dxglockorder remove thread %p", + info->thread); + if (info->current_lock_index != 0) { + pr_err("A lock is not released: %d %d", + info->current_lock_index, + info->lock_info[ + info->current_lock_index].lock_order); + dxg_panic(); + } + + if (!info->lock_held) { + mutex_lock(&dxgglobal->thread_info_mutex); + list_del(&info->thread_info_list_entry); + mutex_unlock(&dxgglobal->thread_info_mutex); + } + + dxgmem_kfree(DXGMEM_THREADINFO, info); + } + } +} + +void dxglockorder_check_empty(struct dxgthreadinfo *info) +{ + if (info->refcount != 1) { + pr_err("A lock is not released"); + dxg_panic(); + } +} + +void dxg_panic(void) +{ + dump_stack(); + BUG_ON(true); +} diff --git a/drivers/gpu/dxgkrnl/misc.h b/drivers/gpu/dxgkrnl/misc.h new file mode 100644 index 000000000000..b5130cce767f --- /dev/null +++ b/drivers/gpu/dxgkrnl/misc.h @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2019, Microsoft Corporation. + * + * Author: + * Iouri Tarassov <iourit@xxxxxxxxxxxxx> + * + * Dxgkrnl Graphics Driver + * Misc definitions + * + */ + +#ifndef _MISC_H_ +#define _MISC_H_ + +struct dxgprocess; + +enum dxgk_memory_tag { + DXGMEM_GLOBAL = 0, + DXGMEM_ADAPTER = 1, + DXGMEM_THREADINFO = 2, + DXGMEM_PROCESS = 3, + DXGMEM_VMBUS = 4, + DXGMEM_DEVICE = 5, + DXGMEM_RESOURCE = 6, + DXGMEM_CONTEXT = 7, + DXGMEM_PQUEUE = 8, + DXGMEM_SYNCOBJ = 9, + DXGMEM_PROCESS_ADAPTER = 10, + DXGMEM_HWQUEUE = 11, + DXGMEM_HANDLE_TABLE = 12, + DXGMEM_TMP = 13, + DXGMEM_ALLOCATION = 14, + DXGMEM_EVENT = 15, + DXGMEM_HOSTEVENT = 16, + DXGMEM_SHAREDSYNCOBJ = 17, + DXGMEM_SHAREDRESOURCE = 18, + DXGMEM_ALLOCPRIVATE = 19, + DXGMEM_RUNTIMEPRIVATE = 20, + DXGMEM_RESOURCEPRIVATE = 21, + DXGMEM_LAST +}; + +void dxgmem_check(struct dxgprocess *process, enum dxgk_memory_tag ignore_tag); +void *dxgmem_alloc(struct dxgprocess *process, enum dxgk_memory_tag tag, + size_t size); +void dxgmem_free(struct dxgprocess *process, enum dxgk_memory_tag tag, + void *address); +void *dxgmem_kalloc(enum dxgk_memory_tag tag, size_t size, gfp_t flags); +void dxgmem_kfree(enum dxgk_memory_tag tag, void *address); + +#define DXGK_MAX_LOCK_DEPTH 64 +#define W_MAX_PATH 260 + +#define d3dkmt_handle u32 +#define d3dgpu_virtual_address u64 +#define winwchar u16 +#define winhandle u64 +#define ntstatus int +#define winbool u32 +#define d3dgpu_size_t u64 + +struct winluid { + uint a; + uint b; +}; + +/* + * Synchronization lock hierarchy. + * dxgadapter->adapter_process_list_lock + * + * The higher enum value, the higher is the lock order. + * When a lower lock ois held, the higher lock should not be acquired. + */ +enum dxgk_lockorder { + DXGLOCK_INVALID = 0, + DXGLOCK_PROCESSADAPTERDEVICELIST, /* device_list_mutex */ + DXGLOCK_GLOBAL_HOSTEVENTLIST, /* host_event_list_mutex */ + DXGLOCK_GLOBAL_CHANNEL, /* channel_lock */ + DXGLOCK_FDMUTEX, /* fd_mutex */ + DXGLOCK_PROCESSLIST, /* plistmutex */ + DXGLOCK_HANDLETABLE, /* table_lock */ + DXGLOCK_DEVICE_CONTEXTLIST, /* context_list_lock */ + DXGLOCK_DEVICE_ALLOCLIST, /* alloc_list_lock */ + DXGLOCK_RESOURCE, /* resource_mutex */ + DXGLOCK_SHAREDRESOURCELIST, /* shared_resource_list_lock */ + DXGLOCK_ADAPTER, /* core_lock */ + DXGLOCK_DEVICE, /* device_lock */ + DXGLOCK_PROCESSMUTEX, /* process->process_mutex */ + DXGLOCK_PROCESSADAPTER, /* process_adapter_mutex */ + DXGLOCK_GLOBAL_ADAPTERLIST, /* adapter_list_lock */ + DXGLOCK_GLOBAL_DEVICE, /* device_mutex */ +}; + +struct dxgk_lockinfo { + enum dxgk_lockorder lock_order; +}; + +struct dxgthreadinfo { + struct list_head thread_info_list_entry; + struct task_struct *thread; + int refcount; + int current_lock_index; + struct dxgk_lockinfo lock_info[DXGK_MAX_LOCK_DEPTH]; + bool lock_held; +}; + +void dxglockorder_acquire(enum dxgk_lockorder order); +void dxglockorder_release(enum dxgk_lockorder order); +void dxglockorder_check_empty(struct dxgthreadinfo *info); +struct dxgthreadinfo *dxglockorder_get_thread(void); +void dxglockorder_put_thread(struct dxgthreadinfo *info); +void dxg_panic(void); + +struct dxgmutex { + struct mutex mutex; + enum dxgk_lockorder lock_order; +}; +void dxgmutex_init(struct dxgmutex *m, enum dxgk_lockorder order); +void dxgmutex_lock(struct dxgmutex *m); +void dxgmutex_unlock(struct dxgmutex *m); + +winwchar *wcsncpy(winwchar *dest, const winwchar *src, size_t n); + +enum dxglockstate { + DXGLOCK_SHARED, + DXGLOCK_EXCL +}; + +#define STATUS_SUCCESS ((ntstatus)0) +#define STATUS_GRAPHICS_DRIVER_MISMATCH ((ntstatus)0xC01E0009L) +#define STATUS_OBJECT_NAME_INVALID ((ntstatus)0xC0000033L) +#define STATUS_OBJECT_PATH_INVALID ((ntstatus)0xC0000039L) +#define STATUS_DEVICE_REMOVED ((ntstatus)0xC00002B6L) +#define STATUS_DISK_FULL ((ntstatus)0xC000007FL) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE ((ntstatus)0xC01E0200L) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST ((ntstatus)0xC01E0116L) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED ((ntstatus)0xC01E0112L) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE ((ntstatus)0xC01E0113L) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE ((ntstatus)0xC01E0114L) +#define STATUS_ILLEGAL_CHARACTER ((ntstatus)0xC0000161L) +#define STATUS_INVALID_HANDLE ((ntstatus)0xC0000008L) +#define STATUS_ILLEGAL_INSTRUCTION ((ntstatus)0xC000001DL) +#define STATUS_INVALID_PARAMETER_1 ((ntstatus)0xC00000EFL) +#define STATUS_INVALID_PARAMETER_2 ((ntstatus)0xC00000F0L) +#define STATUS_INVALID_PARAMETER_3 ((ntstatus)0xC00000F1L) +#define STATUS_INVALID_PARAMETER_4 ((ntstatus)0xC00000F2L) +#define STATUS_INVALID_PARAMETER_5 ((ntstatus)0xC00000F3L) +#define STATUS_INVALID_PARAMETER_6 ((ntstatus)0xC00000F4L) +#define STATUS_INVALID_PARAMETER_7 ((ntstatus)0xC00000F5L) +#define STATUS_INVALID_PARAMETER_8 ((ntstatus)0xC00000F6L) +#define STATUS_INVALID_PARAMETER_9 ((ntstatus)0xC00000F7L) +#define STATUS_INVALID_PARAMETER_10 ((ntstatus)0xC00000F8L) +#define STATUS_INVALID_PARAMETER_11 ((ntstatus)0xC00000F9L) +#define STATUS_INVALID_PARAMETER_12 ((ntstatus)0xC00000FAL) +#define STATUS_IN_PAGE_ERROR ((ntstatus)0xC0000006L) +#define STATUS_NOT_IMPLEMENTED ((ntstatus)0xC0000002L) +#define STATUS_PENDING ((ntstatus)0x00000103L) +#define STATUS_ACCESS_DENIED ((ntstatus)0xC0000022L) +#define STATUS_BUFFER_TOO_SMALL ((ntstatus)0xC0000023L) +#define STATUS_OBJECT_PATH_SYNTAX_BAD ((ntstatus)0xC000003BL) +#define STATUS_OBJECT_TYPE_MISMATCH ((ntstatus)0xC0000024L) +#define STATUS_GRAPHICS_ALLOCATION_BUSY ((ntstatus)0xC01E0102L) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE ((ntstatus)0xC01E0115L) +#define STATUS_PRIVILEGED_INSTRUCTION ((ntstatus)0xC0000096L) +#define STATUS_SHARING_VIOLATION ((ntstatus)0xC0000043L) +#define STATUS_BUFFER_OVERFLOW ((ntstatus)0x80000005L) +#define STATUS_MEDIA_WRITE_PROTECTED ((ntstatus)0xC00000A2L) +#define STATUS_INTEGER_OVERFLOW ((ntstatus)0xC0000095L) +#define STATUS_PRIVILEGE_NOT_HELD ((ntstatus)0xC0000061L) +#define STATUS_NOT_SUPPORTED ((ntstatus)0xC00000BBL) +#define STATUS_HOST_UNREACHABLE ((ntstatus)0xC000023DL) +#define STATUS_NETWORK_UNREACHABLE ((ntstatus)0xC000023CL) +#define STATUS_CONNECTION_REFUSED ((ntstatus)0xC0000236L) +#define STATUS_CONNECTION_REFUSED ((ntstatus)0xC0000236L) +#define STATUS_TIMEOUT ((ntstatus)0x00000102L) +#define STATUS_WRONG_VOLUME ((ntstatus)0xC0000012L) +#define STATUS_IO_TIMEOUT ((ntstatus)0xC00000B5L) +#define STATUS_RETRY ((ntstatus)0xC000022DL) +#define STATUS_CANCELLED ((ntstatus)0xC0000120L) +#define STATUS_CONNECTION_DISCONNECTED ((ntstatus)0xC000020CL) +#define STATUS_CONNECTION_RESET ((ntstatus)0xC000020DL) +#define STATUS_CONNECTION_ABORTED ((ntstatus)0xC0000241L) +#define STATUS_INVALID_PARAMETER ((ntstatus)0xC000000DL) +#define STATUS_INVALID_DEVICE_REQUEST ((ntstatus)0xC0000010L) +#define STATUS_OBJECT_NAME_NOT_FOUND ((ntstatus)0xC0000034L) +#define STATUS_OBJECT_PATH_NOT_FOUND ((ntstatus)0xC000003AL) +#define STATUS_NOT_FOUND ((ntstatus)0xC0000225L) +#define STATUS_DELETE_PENDING ((ntstatus)0xC0000056L) +#define STATUS_BAD_NETWORK_NAME ((ntstatus)0xC00000CCL) +#define STATUS_CANNOT_DELETE ((ntstatus)0xC0000121L) +#define STATUS_INTERNAL_ERROR ((ntstatus)0xC00000E5L) +#define STATUS_OBJECTID_EXISTS ((ntstatus)0xC000022BL) +#define STATUS_DUPLICATE_OBJECTID ((ntstatus)0xC000022AL) +#define STATUS_ADDRESS_ALREADY_EXISTS ((ntstatus)0xC000020AL) +#define STATUS_ACCESS_VIOLATION ((ntstatus)0xC0000005L) +#define STATUS_INSUFFICIENT_RESOURCES ((ntstatus)0xC000009AL) +#define STATUS_NO_MEMORY ((ntstatus)0xC0000017L) +#define STATUS_COMMITMENT_LIMIT ((ntstatus)0xC000012DL) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY ((ntstatus)0xC01E0100L) +#define STATUS_OBJECTID_NOT_FOUND ((ntstatus)0xC00002F0L) +#define STATUS_DIRECTORY_NOT_EMPTY ((ntstatus)0xC0000101L) +#define STATUS_OBJECT_NAME_EXISTS ((ntstatus)0x40000000L) +#define STATUS_OBJECT_NAME_COLLISION ((ntstatus)0xC0000035L) +#define STATUS_UNSUCCESSFUL ((ntstatus)0xC0000001L) +#define STATUS_NOT_IMPLEMENTED ((ntstatus)0xC0000002L) +#define NT_SUCCESS(status) ((int)status >= 0) + +#define DXGKRNL_ASSERT(exp) +#define UNUSED(x) (void)(x) + +#undef pr_fmt +#define pr_fmt(fmt) "dxgk:err: " fmt +#define pr_fmt2(fmt) "dxgk:err: " fmt + +#define DXGKDEBUG 1 +/* #define USEPRINTK 1 */ + +#ifndef DXGKDEBUG +#define TRACE_DEBUG(...) +#define TRACE_DEFINE(...) +#define TRACE_FUNC_ENTER(...) +#define TRACE_FUNC_EXIT(...) +#else +#ifdef USEPRINTK +#define TRACE_DEBUG(level, fmt, ...)\ + printk(KERN_DEBUG pr_fmt2(fmt), ##__VA_ARGS__); + +#define TRACE_DEBUG2(level, offset, fmt, ...) \ +do { \ + if (offset == 0) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ + else \ + printk(KERN_DEBUG pr_fmt2(fmt), ##__VA_ARGS__); \ +} while (false) + +#define TRACE_FUNC_ENTER(msg) \ + printk(KERN_DEBUG "dxgk: %s", msg) +#define TRACE_FUNC_EXIT(msg, ret) \ +do { \ + if (!NT_SUCCESS(ret)) \ + dxg_pr_err("%s %x %d", msg, ret, ret); \ + else \ + printk(KERN_DEBUG "dxgk: %s end", msg); \ +} while (false) +#define TRACE_FUNC_EXIT_ERR(msg, ret) \ +do { \ + if (!NT_SUCCESS(ret)) \ + dxg_pr_err("%s %x", msg, ret); \ +} while (false) +#else +#define TRACE_DEBUG(level, fmt, ...)\ + dev_dbg(dxgglobaldev, pr_fmt2(fmt), ##__VA_ARGS__) + +#define TRACE_DEBUG2(level, offset, fmt, ...) \ +do { \ + if (offset == 0) \ + dev_dbg(dxgglobaldev, pr_fmt(fmt), ##__VA_ARGS__); \ + else \ + dev_dbg(dxgglobaldev, pr_fmt2(fmt), ##__VA_ARGS__);\ +} while (false) + +#define TRACE_FUNC_ENTER(msg) \ + dev_dbg(dxgglobaldev, "dxgk: %s", msg) +#define TRACE_FUNC_EXIT(msg, ret) \ +do { \ + if (!NT_SUCCESS(ret)) \ + dev_dbg(dxgglobaldev, "dxgk:err: %s %x", msg, ret); \ + else \ + dev_dbg(dxgglobaldev, "dxgk: %s end", msg); \ +} while (false) +#define TRACE_FUNC_EXIT_ERR(msg, ret) \ +do { \ + if (!NT_SUCCESS(ret)) \ + dev_dbg(dxgglobaldev, "dxgk:err: %s %x", msg, ret); \ +} while (false) +#endif /* USEPRINTK */ +#define TRACE_DEFINE(arg) arg +#endif + +#ifdef DXGKDEBUGLOCKORDER +#define TRACE_LOCK_ORDER(...) TRACE_DEBUG(...) +#else +#define TRACE_LOCK_ORDER(...) +#endif + +#endif -- 2.25.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel