// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2022, Microsoft Corporation. * * Author: * Iouri Tarassov * * Dxgkrnl Graphics Driver * Ioctl implementation * */ #include #include #include #include #include #include #include #include "dxgkrnl.h" #include "dxgvmbus.h" #include "dxgsyncfile.h" #undef dev_fmt #define dev_fmt(fmt) "dxgk: " fmt struct ioctl_desc { int (*ioctl_callback)(struct dxgprocess *p, void __user *arg); u32 ioctl; }; void dxgsharedsyncobj_put(struct dxgsharedsyncobject *syncobj) { DXG_TRACE("Release syncobj: %p", syncobj); mutex_lock(&syncobj->fd_mutex); kref_get(&syncobj->ssyncobj_kref); syncobj->host_shared_handle_nt_reference--; if (syncobj->host_shared_handle_nt_reference == 0) { if (syncobj->host_shared_handle_nt.v) { dxgvmb_send_destroy_nt_shared_object( syncobj->host_shared_handle_nt); DXG_TRACE("Syncobj host_handle_nt destroyed: %x", syncobj->host_shared_handle_nt.v); syncobj->host_shared_handle_nt.v = 0; } kref_put(&syncobj->ssyncobj_kref, dxgsharedsyncobj_release); } mutex_unlock(&syncobj->fd_mutex); kref_put(&syncobj->ssyncobj_kref, dxgsharedsyncobj_release); } static int dxgsyncobj_release(struct inode *inode, struct file *file) { struct dxgsharedsyncobject *syncobj = file->private_data; dxgsharedsyncobj_put(syncobj); 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; DXG_TRACE("Release resource: %p", resource); mutex_lock(&resource->fd_mutex); kref_get(&resource->sresource_kref); resource->host_shared_handle_nt_reference--; if (resource->host_shared_handle_nt_reference == 0) { if (resource->host_shared_handle_nt.v) { dxgvmb_send_destroy_nt_shared_object( resource->host_shared_handle_nt); DXG_TRACE("Resource host_handle_nt destroyed: %x", resource->host_shared_handle_nt.v); resource->host_shared_handle_nt.v = 0; } kref_put(&resource->sresource_kref, dxgsharedresource_destroy); } mutex_unlock(&resource->fd_mutex); kref_put(&resource->sresource_kref, dxgsharedresource_destroy); return 0; } static const struct file_operations dxg_resource_fops = { .release = dxgsharedresource_release, }; static int dxgkio_open_adapter_from_luid(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_openadapterfromluid args; int ret; struct dxgadapter *entry; struct dxgadapter *adapter = NULL; struct d3dkmt_openadapterfromluid *__user result = inargs; struct dxgglobal *dxgglobal = dxggbl(); ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("Faled to copy input args"); ret = -EFAULT; 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) { if (*(u64 *) &entry->luid == *(u64 *) &args.adapter_luid) { ret = dxgprocess_open_adapter(process, entry, &args.adapter_handle); if (ret >= 0) { ret = copy_to_user( &result->adapter_handle, &args.adapter_handle, sizeof(struct d3dkmthandle)); if (ret) ret = -EFAULT; } 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.v == 0) ret = -EINVAL; cleanup: if (ret < 0) dxgprocess_close_adapter(process, args.adapter_handle); DXG_TRACE_IOCTL_END(ret); return ret; } static struct d3dkmthandle find_dxgprocess_handle(u64 pid) { struct dxgglobal *dxgglobal = dxggbl(); struct dxgprocess *entry; struct d3dkmthandle host_handle = {}; mutex_lock(&dxgglobal->plistmutex); list_for_each_entry(entry, &dxgglobal->plisthead, plistentry) { if (entry->vpid == pid) { host_handle.v = entry->host_handle.v; break; } } mutex_unlock(&dxgglobal->plistmutex); return host_handle; } static int dxgkio_query_statistics(struct dxgprocess *process, void __user *inargs) { struct d3dkmt_querystatistics *args; int ret; struct dxgadapter *entry; struct dxgadapter *adapter = NULL; struct winluid tmp; struct dxgglobal *dxgglobal = dxggbl(); struct d3dkmthandle host_process_handle = process->host_handle; u64 pid; args = vzalloc(sizeof(struct d3dkmt_querystatistics)); if (args == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(args, inargs, sizeof(*args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } /* Find the host process handle when needed */ pid = args->process; if (pid) { host_process_handle = find_dxgprocess_handle(pid); if (host_process_handle.v == 0) { DXG_ERR("Invalid process ID is specified: %lld", pid); ret = -EINVAL; goto cleanup; } args->process = 0; } dxgglobal_acquire_adapter_list_lock(DXGLOCK_SHARED); list_for_each_entry(entry, &dxgglobal->adapter_list_head, adapter_list_entry) { if (dxgadapter_acquire_lock_shared(entry) == 0) { if (*(u64 *) &entry->luid == *(u64 *) &args->adapter_luid) { adapter = entry; break; } dxgadapter_release_lock_shared(entry); } } dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); if (adapter) { tmp = args->adapter_luid; args->adapter_luid = adapter->host_adapter_luid; ret = dxgvmb_send_query_statistics(host_process_handle, adapter, args); if (ret >= 0) { args->adapter_luid = tmp; ret = copy_to_user(inargs, args, sizeof(*args)); if (ret) { DXG_ERR("failed to copy args"); ret = -EFAULT; } } dxgadapter_release_lock_shared(adapter); } cleanup: if (args) vfree(args); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkp_enum_adapters(struct dxgprocess *process, union d3dkmt_enumadapters_filter filter, u32 adapter_count_max, struct d3dkmt_adapterinfo *__user info_out, u32 * __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; struct dxgglobal *dxgglobal = dxggbl(); if (info_out == NULL || adapter_count_max == 0) { ret = copy_to_user(adapter_count_out, &dxgglobal->num_adapters, sizeof(u32)); if (ret) { DXG_ERR("copy_to_user faled"); ret = -EFAULT; } goto cleanup; } if (adapter_count_max > 0xFFFF) { DXG_ERR("too many adapters"); ret = -EINVAL; goto cleanup; } info = vzalloc(sizeof(struct d3dkmt_adapterinfo) * adapter_count_max); if (info == NULL) { ret = -ENOMEM; goto cleanup; } adapters = vzalloc(sizeof(struct dxgadapter *) * adapter_count_max); if (adapters == NULL) { ret = -ENOMEM; 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 (entry->compute_only && !filter.include_compute_only) continue; if (dxgadapter_acquire_lock_shared(entry) == 0) { struct d3dkmt_adapterinfo *inf = &info[adapter_count]; ret = dxgprocess_open_adapter(process, entry, &inf->adapter_handle); if (ret >= 0) { inf->adapter_luid = entry->luid; adapters[adapter_count] = entry; DXG_TRACE("adapter: %x %x:%x", inf->adapter_handle.v, inf->adapter_luid.b, inf->adapter_luid.a); adapter_count++; } dxgadapter_release_lock_shared(entry); } if (ret < 0) break; } dxgglobal_release_process_adapter_lock(); dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); if (adapter_count > adapter_count_max) { struct ntstatus status; status.v = STATUS_BUFFER_TOO_SMALL; ret = ntstatus2int(status); DXG_TRACE("Too many adapters"); ret = copy_to_user(adapter_count_out, &dxgglobal->num_adapters, sizeof(u32)); if (ret) { DXG_ERR("copy_to_user failed"); ret = -EFAULT; } goto cleanup; } ret = copy_to_user(adapter_count_out, &adapter_count, sizeof(adapter_count)); if (ret) { DXG_ERR("failed to copy adapter_count"); ret = -EFAULT; goto cleanup; } ret = copy_to_user(info_out, info, sizeof(info[0]) * adapter_count); if (ret) { DXG_ERR("failed to copy adapter info"); ret = -EFAULT; } cleanup: if (ret >= 0) { DXG_TRACE("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) vfree(info); if (adapters) vfree(adapters); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgsharedresource_seal(struct dxgsharedresource *shared_resource) { int ret = 0; int i = 0; u8 *private_data; u32 data_size; struct dxgresource *resource; struct dxgallocation *alloc; struct dxgsharedallocdata *alloc_info; DXG_TRACE("Sealing resource: %p", shared_resource); down_write(&shared_resource->adapter->shared_resource_list_lock); if (shared_resource->sealed) { DXG_TRACE("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); DXG_TRACE("First resource: %p", resource); mutex_lock(&resource->resource_mutex); list_for_each_entry(alloc, &resource->alloc_list_head, alloc_list_entry) { DXG_TRACE("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) { DXG_ERR("alloc private data overflow"); ret = -EINVAL; goto cleanup1; } } if (shared_resource->alloc_private_data_size == 0) { ret = -EINVAL; goto cleanup1; } shared_resource->alloc_private_data = vzalloc(shared_resource->alloc_private_data_size); if (shared_resource->alloc_private_data == NULL) { ret = -EINVAL; goto cleanup1; } shared_resource->alloc_info = vzalloc(sizeof(struct dxgsharedallocdata) * shared_resource->allocation_count); if (shared_resource->alloc_info == NULL) { ret = -EINVAL; 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) { u32 alloc_data_size = alloc->priv_drv_data->data_size; if (alloc_data_size) { if (data_size < alloc_data_size) { DXG_ERR( "Invalid private data size"); ret = -EINVAL; goto cleanup1; } alloc_info = &shared_resource->alloc_info[i]; alloc_info->private_data_size = alloc_data_size; alloc_info->num_pages = alloc->num_pages; alloc_info->cached = alloc->cached; memcpy(private_data, alloc->priv_drv_data->data, alloc_data_size); vfree(alloc->priv_drv_data); alloc->priv_drv_data = NULL; private_data += alloc_data_size; data_size -= alloc_data_size; } i++; } if (data_size != 0) { DXG_ERR("Data size mismatch"); ret = -EINVAL; } cleanup1: mutex_unlock(&resource->resource_mutex); } cleanup: up_write(&shared_resource->adapter->shared_resource_list_lock); return ret; } static int dxgkio_enum_adapters(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_enumadapters2 args; int ret; struct dxgadapter *entry; struct d3dkmt_adapterinfo *info = NULL; struct dxgadapter **adapters = NULL; int adapter_count = 0; int i; struct dxgglobal *dxgglobal = dxggbl(); ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.adapters == NULL) { DXG_TRACE("buffer is NULL"); args.num_adapters = dxgglobal->num_adapters; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy args to user"); ret = -EFAULT; } goto cleanup; } if (args.num_adapters < dxgglobal->num_adapters) { args.num_adapters = dxgglobal->num_adapters; DXG_TRACE("buffer is too small"); ret = -EOVERFLOW; goto cleanup; } if (args.num_adapters > D3DKMT_ADAPTERS_MAX) { DXG_TRACE("too many adapters"); ret = -EINVAL; goto cleanup; } info = vzalloc(sizeof(struct d3dkmt_adapterinfo) * args.num_adapters); if (info == NULL) { ret = -ENOMEM; goto cleanup; } adapters = vzalloc(sizeof(struct dxgadapter *) * args.num_adapters); if (adapters == NULL) { ret = -ENOMEM; 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 (entry->compute_only) continue; if (dxgadapter_acquire_lock_shared(entry) == 0) { struct d3dkmt_adapterinfo *inf = &info[adapter_count]; ret = dxgprocess_open_adapter(process, entry, &inf->adapter_handle); if (ret >= 0) { inf->adapter_luid = entry->luid; adapters[adapter_count] = entry; DXG_TRACE("adapter: %x %llx", inf->adapter_handle.v, *(u64 *) &inf->adapter_luid); adapter_count++; } dxgadapter_release_lock_shared(entry); } if (ret < 0) break; } dxgglobal_release_process_adapter_lock(); dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); args.num_adapters = adapter_count; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy args to user"); ret = -EFAULT; goto cleanup; } ret = copy_to_user(args.adapters, info, sizeof(info[0]) * args.num_adapters); if (ret) { DXG_ERR("failed to copy adapter info to user"); ret = -EFAULT; } cleanup: if (ret < 0) { if (info) { for (i = 0; i < args.num_adapters; i++) { dxgprocess_close_adapter(process, info[i].adapter_handle); } } } else { DXG_TRACE("found %d adapters", args.num_adapters); } if (info) vfree(info); if (adapters) vfree(adapters); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_enum_adapters3(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_enumadapters3 args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = dxgkp_enum_adapters(process, args.filter, args.adapter_count, args.adapters, &((struct d3dkmt_enumadapters3 *)inargs)-> adapter_count); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_close_adapter(struct dxgprocess *process, void *__user inargs) { struct d3dkmthandle args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = dxgprocess_close_adapter(process, args); if (ret < 0) DXG_ERR("failed to close adapter: %d", ret); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_query_adapter_info(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_queryadapterinfo args; int ret; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.private_data_size > DXG_MAX_VM_BUS_PACKET_SIZE || args.private_data_size == 0) { DXG_ERR("invalid private data size"); ret = -EINVAL; goto cleanup; } DXG_TRACE("Type: %d Size: %x", args.type, args.private_data_size); adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; ret = dxgvmb_send_query_adapter_info(process, adapter, &args); dxgadapter_release_lock_shared(adapter); cleanup: if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_create_device(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_createdevice args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct d3dkmthandle host_device_handle = {}; bool adapter_locked = false; bool device_creation_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } /* The call acquires reference on the adapter */ adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } mutex_lock(&adapter->device_creation_lock); device_creation_locked = true; device = dxgdevice_create(adapter, process); if (device == NULL) { ret = -ENOMEM; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; host_device_handle = dxgvmb_send_create_device(adapter, process, &args); if (host_device_handle.v) { ret = copy_to_user(&((struct d3dkmt_createdevice *)inargs)-> device, &host_device_handle, sizeof(struct d3dkmthandle)); if (ret) { DXG_ERR("failed to copy device handle"); ret = -EFAULT; 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 >= 0) { device->handle = host_device_handle; device->handle_valid = 1; device->object_state = DXGOBJECTSTATE_ACTIVE; } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); } cleanup: if (ret < 0) { if (host_device_handle.v) dxgvmb_send_destroy_device(adapter, process, host_device_handle); if (device) dxgdevice_destroy(device); } if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (device_creation_locked) mutex_unlock(&adapter->device_creation_lock); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_destroy_device(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_destroydevice args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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) { DXG_ERR("invalid device handle: %x", args.device.v); ret = -EINVAL; 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: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_create_context_virtual(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_createcontextvirtual args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct dxgcontext *context = NULL; struct d3dkmthandle host_context_handle = {}; bool device_lock_acquired = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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 = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) goto cleanup; device_lock_acquired = true; adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } context = dxgcontext_create(device); if (context == NULL) { ret = -ENOMEM; goto cleanup; } host_context_handle = dxgvmb_send_create_context(adapter, process, &args); if (host_context_handle.v) { ret = copy_to_user(&((struct d3dkmt_createcontextvirtual *) inargs)->context, &host_context_handle, sizeof(struct d3dkmthandle)); if (ret) { DXG_ERR("failed to copy context handle"); ret = -EFAULT; goto cleanup; } hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); ret = hmgrtable_assign_handle(&process->handle_table, context, HMGRENTRY_TYPE_DXGCONTEXT, host_context_handle); if (ret >= 0) context->handle = host_context_handle; hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); } else { DXG_ERR("invalid host handle"); ret = -EINVAL; } cleanup: if (ret < 0) { if (host_context_handle.v) { 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); kref_put(&device->device_kref, dxgdevice_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_destroy_context(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_destroycontext args; int ret; struct dxgadapter *adapter = NULL; struct dxgcontext *context = NULL; struct dxgdevice *device = NULL; struct d3dkmthandle device_handle = {}; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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.v = 0; device_handle = context->device_handle; context->object_state = DXGOBJECTSTATE_DESTROYED; } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (context == NULL) { DXG_ERR("invalid context handle: %x", args.context.v); ret = -EINVAL; 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { 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) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; bool device_lock_acquired = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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 = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) 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) { DXG_ERR("Invalid context handle %x", args.context.v); ret = -EINVAL; goto cleanup; } hwqueue = dxghwqueue_create(context); if (hwqueue == NULL) { ret = -ENOMEM; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_create_hwqueue(process, adapter, &args, inargs, hwqueue); cleanup: if (ret < 0 && hwqueue) dxghwqueue_destroy(process, hwqueue); if (adapter) dxgadapter_release_lock_shared(adapter); if (device_lock_acquired) dxgdevice_release_lock_shared(device); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_destroy_hwqueue(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_destroyhwqueue args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct dxghwqueue *hwqueue = NULL; struct d3dkmthandle device_handle = {}; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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.v = 0; device_handle = hwqueue->device_handle; } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (hwqueue == NULL) { DXG_ERR("invalid hwqueue handle: %x", args.queue.v); ret = -EINVAL; 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_destroy_hwqueue(process, adapter, args.queue); dxghwqueue_destroy(process, hwqueue); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; struct d3dkmthandle host_handle = {}; bool device_lock_acquired = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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 = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) goto cleanup; device_lock_acquired = true; adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } pqueue = dxgpagingqueue_create(device); if (pqueue == NULL) { ret = -ENOMEM; goto cleanup; } ret = dxgvmb_send_create_paging_queue(process, device, &args, pqueue); if (ret >= 0) { host_handle = args.paging_queue; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); ret = hmgrtable_assign_handle(&process->handle_table, pqueue, HMGRENTRY_TYPE_DXGPAGINGQUEUE, host_handle); if (ret >= 0) { pqueue->handle = host_handle; ret = hmgrtable_assign_handle(&process->handle_table, NULL, HMGRENTRY_TYPE_MONITOREDFENCE, args.sync_object); if (ret >= 0) pqueue->syncobj_handle = args.sync_object; } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); /* should not fail after this */ } cleanup: if (ret < 0) { if (pqueue) dxgpagingqueue_destroy(pqueue); if (host_handle.v) dxgvmb_send_destroy_paging_queue(process, adapter, host_handle); } if (adapter) dxgadapter_release_lock_shared(adapter); if (device) { if (device_lock_acquired) dxgdevice_release_lock_shared(device); kref_put(&device->device_kref, dxgdevice_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_destroy_paging_queue(struct dxgprocess *process, void *__user inargs) { struct d3dddi_destroypagingqueue args; struct dxgpagingqueue *paging_queue = NULL; int ret; struct d3dkmthandle device_handle = {}; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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.v = 0; paging_queue->handle.v = 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.v) device = dxgprocess_device_by_handle(process, device_handle); if (device == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_destroy_paging_queue(process, adapter, args.paging_queue); dxgpagingqueue_destroy(paging_queue); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) { dxgdevice_release_lock_shared(device); kref_put(&device->device_kref, dxgdevice_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int get_standard_alloc_priv_data(struct dxgdevice *device, struct d3dkmt_createstandardallocation *alloc_info, u32 *standard_alloc_priv_data_size, void **standard_alloc_priv_data, u32 *standard_res_priv_data_size, void **standard_res_priv_data) { int ret; struct d3dkmdt_gdisurfacedata gdi_data = { }; u32 priv_data_size = 0; u32 res_priv_data_size = 0; void *priv_data = NULL; void *res_priv_data = NULL; 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_stdalloc_data(device, _D3DKMDT_STANDARDALLOCATION_GDISURFACE, &gdi_data, 0, &priv_data_size, NULL, &res_priv_data_size, NULL); if (ret < 0) goto cleanup; DXG_TRACE("Priv data size: %d", priv_data_size); if (priv_data_size == 0) { ret = -EINVAL; goto cleanup; } priv_data = vzalloc(priv_data_size); if (priv_data == NULL) { ret = -ENOMEM; DXG_ERR("failed to allocate memory for priv data: %d", priv_data_size); goto cleanup; } if (res_priv_data_size) { res_priv_data = vzalloc(res_priv_data_size); if (res_priv_data == NULL) { ret = -ENOMEM; DXG_ERR( "failed to alloc memory for res priv data: %d", res_priv_data_size); goto cleanup; } } ret = dxgvmb_send_get_stdalloc_data(device, _D3DKMDT_STANDARDALLOCATION_GDISURFACE, &gdi_data, 0, &priv_data_size, priv_data, &res_priv_data_size, res_priv_data); if (ret < 0) goto cleanup; *standard_alloc_priv_data_size = priv_data_size; *standard_alloc_priv_data = priv_data; *standard_res_priv_data_size = res_priv_data_size; *standard_res_priv_data = res_priv_data; priv_data = NULL; res_priv_data = NULL; cleanup: if (priv_data) vfree(priv_data); if (res_priv_data) vfree(res_priv_data); if (ret) DXG_TRACE("err: %d", ret); return ret; } static int dxgkio_create_allocation(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_createallocation args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct d3dddi_allocationinfo2 *alloc_info = NULL; struct d3dkmt_createstandardallocation standard_alloc; u32 alloc_info_size = 0; struct dxgresource *resource = NULL; struct dxgallocation **dxgalloc = NULL; struct dxgsharedresource *shared_resource = NULL; bool resource_mutex_acquired = false; u32 standard_alloc_priv_data_size = 0; void *standard_alloc_priv_data = NULL; u32 res_priv_data_size = 0; void *res_priv_data = NULL; int i; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || args.alloc_count == 0) { DXG_ERR("invalid number of allocations to create"); ret = -EINVAL; goto cleanup; } alloc_info_size = sizeof(struct d3dddi_allocationinfo2) * args.alloc_count; alloc_info = vzalloc(alloc_info_size); if (alloc_info == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(alloc_info, args.allocation_info, alloc_info_size); if (ret) { DXG_ERR("failed to copy alloc info"); ret = -EFAULT; goto cleanup; } for (i = 0; i < args.alloc_count; i++) { if (args.flags.standard_allocation) { if (alloc_info[i].priv_drv_data_size != 0) { DXG_ERR("private data size not zero"); ret = -EINVAL; goto cleanup; } } if (alloc_info[i].priv_drv_data_size >= DXG_MAX_VM_BUS_PACKET_SIZE) { DXG_ERR("private data size too big: %d %d %ld", i, alloc_info[i].priv_drv_data_size, sizeof(alloc_info[0])); ret = -EINVAL; goto cleanup; } } if (args.flags.existing_section || args.flags.create_protected) { DXG_ERR("invalid allocation flags"); ret = -EINVAL; goto cleanup; } if (args.flags.standard_allocation) { if (args.standard_allocation == NULL) { DXG_ERR("invalid standard allocation"); ret = -EINVAL; goto cleanup; } ret = copy_from_user(&standard_alloc, args.standard_allocation, sizeof(standard_alloc)); if (ret) { DXG_ERR("failed to copy std alloc data"); ret = -EFAULT; goto cleanup; } if (standard_alloc.type == _D3DKMT_STANDARDALLOCATIONTYPE_EXISTINGHEAP) { if (alloc_info[0].sysmem == NULL || (unsigned long)alloc_info[0].sysmem & (PAGE_SIZE - 1)) { DXG_ERR("invalid sysmem pointer"); ret = STATUS_INVALID_PARAMETER; goto cleanup; } if (!args.flags.existing_sysmem) { DXG_ERR("expect existing_sysmem flag"); ret = STATUS_INVALID_PARAMETER; goto cleanup; } } else if (standard_alloc.type == _D3DKMT_STANDARDALLOCATIONTYPE_CROSSADAPTER) { if (args.flags.existing_sysmem) { DXG_ERR("existing_sysmem flag invalid"); ret = STATUS_INVALID_PARAMETER; goto cleanup; } if (alloc_info[0].sysmem != NULL) { DXG_ERR("sysmem should be NULL"); ret = STATUS_INVALID_PARAMETER; goto cleanup; } } else { DXG_ERR("invalid standard allocation type"); ret = STATUS_INVALID_PARAMETER; goto cleanup; } if (args.priv_drv_data_size != 0 || args.alloc_count != 1 || standard_alloc.existing_heap_data.size == 0 || standard_alloc.existing_heap_data.size & (PAGE_SIZE - 1)) { DXG_ERR("invalid standard allocation"); ret = -EINVAL; goto cleanup; } args.priv_drv_data_size = sizeof(struct d3dkmt_createstandardallocation); } if (args.flags.create_shared && !args.flags.create_resource) { DXG_ERR("create_resource must be set for create_shared"); ret = -EINVAL; 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 = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { 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, &res_priv_data_size, &res_priv_data); if (ret < 0) goto cleanup; DXG_TRACE("Alloc private data: %d", standard_alloc_priv_data_size); } if (args.flags.create_resource) { resource = dxgresource_create(device); if (resource == NULL) { ret = -ENOMEM; goto cleanup; } resource->private_runtime_handle = args.private_runtime_resource_handle; if (args.flags.create_shared) { if (!args.flags.nt_security_sharing) { DXG_ERR( "nt_security_sharing must be set"); ret = -EINVAL; goto cleanup; } shared_resource = dxgsharedresource_create(adapter); if (shared_resource == NULL) { ret = -ENOMEM; goto cleanup; } shared_resource->runtime_private_data_size = args.priv_drv_data_size; shared_resource->resource_private_data_size = args.priv_drv_data_size; 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.flags.standard_allocation) { shared_resource->resource_private_data = res_priv_data; shared_resource->resource_private_data_size = res_priv_data_size; res_priv_data = NULL; } if (args.private_runtime_data_size) { shared_resource->runtime_private_data = vzalloc(args.private_runtime_data_size); if (shared_resource->runtime_private_data == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user( shared_resource->runtime_private_data, args.private_runtime_data, args.private_runtime_data_size); if (ret) { DXG_ERR( "failed to copy runtime data"); ret = -EFAULT; goto cleanup; } } if (args.priv_drv_data_size && !args.flags.standard_allocation) { shared_resource->resource_private_data = vzalloc(args.priv_drv_data_size); if (shared_resource->resource_private_data == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user( shared_resource->resource_private_data, args.priv_drv_data, args.priv_drv_data_size); if (ret) { DXG_ERR( "failed to copy res data"); ret = -EFAULT; goto cleanup; } } } } else { if (args.resource.v) { /* 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); if (resource != NULL) kref_get(&resource->resource_kref); dxgprocess_ht_lock_shared_up(process); if (resource == NULL || resource->device != device) { DXG_ERR("invalid resource handle %x", args.resource.v); ret = -EINVAL; goto cleanup; } if (resource->shared_owner && resource->shared_owner->sealed) { DXG_ERR("Resource is sealed"); ret = -EINVAL; goto cleanup; } /* Synchronize with resource destruction */ mutex_lock(&resource->resource_mutex); if (!dxgresource_is_active(resource)) { mutex_unlock(&resource->resource_mutex); ret = -EINVAL; goto cleanup; } resource_mutex_acquired = true; } } dxgalloc = vzalloc(sizeof(struct dxgallocation *) * args.alloc_count); if (dxgalloc == NULL) { ret = -ENOMEM; goto cleanup; } for (i = 0; i < args.alloc_count; i++) { struct dxgallocation *alloc; u32 priv_data_size; if (args.flags.standard_allocation) priv_data_size = standard_alloc_priv_data_size; else 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)) { DXG_ERR("invalid sysmem alloc %d, %p", i, alloc_info[i].sysmem); ret = -EINVAL; goto cleanup; } } if ((alloc_info[0].sysmem == NULL) != (alloc_info[i].sysmem == NULL)) { DXG_ERR("All allocs must have sysmem pointer"); ret = -EINVAL; goto cleanup; } dxgalloc[i] = dxgallocation_create(process); if (dxgalloc[i] == NULL) { ret = -ENOMEM; goto cleanup; } alloc = dxgalloc[i]; if (resource) { ret = dxgresource_add_alloc(resource, alloc); if (ret < 0) goto cleanup; } else { dxgdevice_add_alloc(device, alloc); } if (args.flags.create_shared) { /* Remember alloc private data to use it during open */ alloc->priv_drv_data = vzalloc(priv_data_size + offsetof(struct privdata, data)); if (alloc->priv_drv_data == NULL) { ret = -ENOMEM; goto cleanup; } if (args.flags.standard_allocation) { memcpy(alloc->priv_drv_data->data, standard_alloc_priv_data, priv_data_size); } else { ret = copy_from_user( alloc->priv_drv_data->data, alloc_info[i].priv_drv_data, priv_data_size); if (ret) { DXG_ERR( "failed to copy priv data"); ret = -EFAULT; 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); cleanup: if (resource_mutex_acquired) mutex_unlock(&resource->resource_mutex); if (ret < 0) { 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) kref_put(&shared_resource->sresource_kref, dxgsharedresource_destroy); if (dxgalloc) vfree(dxgalloc); if (standard_alloc_priv_data) vfree(standard_alloc_priv_data); if (res_priv_data) vfree(res_priv_data); if (alloc_info) vfree(alloc_info); if (adapter) dxgadapter_release_lock_shared(adapter); if (resource && !args.flags.create_resource) kref_put(&resource->resource_kref, dxgresource_release); if (device) { dxgdevice_release_lock_shared(device); kref_put(&device->device_kref, dxgdevice_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int validate_alloc(struct dxgallocation *alloc0, struct dxgallocation *alloc, struct dxgdevice *device, struct d3dkmthandle alloc_handle) { u32 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: DXG_ERR("Alloc validation failed: reason: %d %x", fail_reason, alloc_handle.v); return -EINVAL; } static int dxgkio_destroy_allocation(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_destroyallocation2 args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; int ret; struct d3dkmthandle *alloc_handles = NULL; struct dxgallocation **allocs = NULL; struct dxgresource *resource = NULL; int i; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.alloc_count > D3DKMT_CREATEALLOCATION_MAX || ((args.alloc_count == 0) == (args.resource.v == 0))) { DXG_ERR("invalid number of allocations"); ret = -EINVAL; goto cleanup; } if (args.alloc_count) { u32 handle_size = sizeof(struct d3dkmthandle) * args.alloc_count; alloc_handles = vzalloc(handle_size); if (alloc_handles == NULL) { ret = -ENOMEM; goto cleanup; } allocs = vzalloc(sizeof(struct dxgallocation *) * args.alloc_count); if (allocs == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(alloc_handles, args.allocations, handle_size); if (ret) { DXG_ERR("failed to copy alloc handles"); ret = -EFAULT; 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 = -EINVAL; goto cleanup; } /* Acquire the device lock to synchronize with the device destriction */ ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { 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 the 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 < 0) { 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) { DXG_ERR("Invalid resource handle: %x", args.resource.v); ret = -EINVAL; } else if (resource->device != device) { DXG_ERR("Resource belongs to wrong device: %x", args.resource.v); ret = -EINVAL; } else { hmgrtable_free_handle(&process->handle_table, HMGRENTRY_TYPE_DXGRESOURCE, args.resource); resource->object_state = DXGOBJECTSTATE_DESTROYED; resource->handle.v = 0; resource->handle_valid = 0; } dxgprocess_ht_lock_exclusive_up(process); if (ret < 0) 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) { kref_get(&resource->resource_kref); mutex_lock(&resource->resource_mutex); } ret = dxgvmb_send_destroy_allocation(process, device, &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.v = 0; dxgallocation_destroy(allocs[i]); } } } else { dxgresource_destroy(resource); } dxgdevice_release_alloc_list_lock(device); if (resource) { mutex_unlock(&resource->resource_mutex); kref_put(&resource->resource_kref, dxgresource_release); } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) { dxgdevice_release_lock_shared(device); kref_put(&device->device_kref, dxgdevice_release); } if (alloc_handles) vfree(alloc_handles); if (allocs) vfree(allocs); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_make_resident(struct dxgprocess *process, void *__user inargs) { int ret, ret2; struct d3dddi_makeresident args; struct d3dddi_makeresident *input = inargs; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.alloc_count > D3DKMT_MAKERESIDENT_ALLOC_MAX || args.alloc_count == 0) { DXG_ERR("invalid number of allocations"); ret = -EINVAL; goto cleanup; } if (args.paging_queue.v == 0) { DXG_ERR("paging queue is missing"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGPAGINGQUEUE, args.paging_queue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_make_resident(process, adapter, &args); if (ret < 0) goto cleanup; /* STATUS_PENING is a success code > 0. It is returned to user mode */ if (!(ret == STATUS_PENDING || ret == 0)) { DXG_ERR("Unexpected error %x", ret); goto cleanup; } ret2 = copy_to_user(&input->paging_fence_value, &args.paging_fence_value, sizeof(u64)); if (ret2) { DXG_ERR("failed to copy paging fence"); ret = -EFAULT; goto cleanup; } ret2 = copy_to_user(&input->num_bytes_to_trim, &args.num_bytes_to_trim, sizeof(u64)); if (ret2) { DXG_ERR("failed to copy bytes to trim"); ret = -EFAULT; goto cleanup; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_evict(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_evict args; struct d3dkmt_evict *input = inargs; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.alloc_count > D3DKMT_MAKERESIDENT_ALLOC_MAX || args.alloc_count == 0) { DXG_ERR("invalid number of allocations"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_evict(process, adapter, &args); if (ret < 0) goto cleanup; ret = copy_to_user(&input->num_bytes_to_trim, &args.num_bytes_to_trim, sizeof(u64)); if (ret) { DXG_ERR("failed to copy bytes to trim to user"); ret = -EFAULT; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_offer_allocations(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_offerallocations args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.allocation_count > D3DKMT_MAKERESIDENT_ALLOC_MAX || args.allocation_count == 0) { DXG_ERR("invalid number of allocations"); ret = -EINVAL; goto cleanup; } if ((args.resources == NULL) == (args.allocations == NULL)) { DXG_ERR("invalid pointer to resources/allocations"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_offer_allocations(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_reclaim_allocations(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_reclaimallocations2 args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct d3dkmt_reclaimallocations2 * __user in_args = inargs; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.allocation_count > D3DKMT_MAKERESIDENT_ALLOC_MAX || args.allocation_count == 0) { DXG_ERR("invalid number of allocations"); ret = -EINVAL; goto cleanup; } if ((args.resources == NULL) == (args.allocations == NULL)) { DXG_ERR("invalid pointer to resources/allocations"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGPAGINGQUEUE, args.paging_queue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_reclaim_allocations(process, adapter, device->handle, &args, &in_args->paging_fence_value); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_submit_command(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_submitcommand args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.broadcast_context_count > D3DDDI_MAX_BROADCAST_CONTEXT || args.broadcast_context_count == 0) { DXG_ERR("invalid number of contexts"); ret = -EINVAL; goto cleanup; } if (args.priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { DXG_ERR("invalid private data size"); ret = -EINVAL; goto cleanup; } if (args.num_history_buffers > 1024) { DXG_ERR("invalid number of history buffers"); ret = -EINVAL; goto cleanup; } if (args.num_primaries > DXG_MAX_VM_BUS_PACKET_SIZE) { DXG_ERR("invalid number of primaries"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGCONTEXT, args.broadcast_context[0]); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_submit_command(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_submit_command_to_hwqueue(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_submitcommandtohwqueue args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.priv_drv_data_size > DXG_MAX_VM_BUS_PACKET_SIZE) { DXG_ERR("invalid private data size"); ret = -EINVAL; goto cleanup; } if (args.num_primaries > DXG_MAX_VM_BUS_PACKET_SIZE) { DXG_ERR("invalid number of primaries"); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGHWQUEUE, args.hwqueue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_submit_command_hwqueue(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_submit_signal_to_hwqueue(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_submitsignalsyncobjectstohwqueue args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct d3dkmthandle hwqueue = {}; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.hwqueue_count > D3DDDI_MAX_BROADCAST_CONTEXT || args.hwqueue_count == 0) { DXG_ERR("invalid hwqueue count: %d", args.hwqueue_count); ret = -EINVAL; goto cleanup; } if (args.object_count > D3DDDI_MAX_OBJECT_SIGNALED || args.object_count == 0) { DXG_ERR("invalid number of syncobjects: %d", args.object_count); ret = -EINVAL; goto cleanup; } ret = copy_from_user(&hwqueue, args.hwqueues, sizeof(struct d3dkmthandle)); if (ret) { DXG_ERR("failed to copy hwqueue handle"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGHWQUEUE, hwqueue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_signal_sync_object(process, adapter, args.flags, 0, zerohandle, args.object_count, args.objects, args.hwqueue_count, args.hwqueues, args.object_count, args.fence_values, NULL, zerohandle); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_submit_wait_to_hwqueue(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_submitwaitforsyncobjectstohwqueue args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; int ret; struct d3dkmthandle *objects = NULL; u32 object_size; u64 *fences = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count > D3DDDI_MAX_OBJECT_WAITED_ON || args.object_count == 0) { ret = -EINVAL; goto cleanup; } object_size = sizeof(struct d3dkmthandle) * args.object_count; objects = vzalloc(object_size); if (objects == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(objects, args.objects, object_size); if (ret) { DXG_ERR("failed to copy objects"); ret = -EFAULT; goto cleanup; } object_size = sizeof(u64) * args.object_count; fences = vzalloc(object_size); if (fences == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(fences, args.fence_values, object_size); if (ret) { DXG_ERR("failed to copy fence values"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGHWQUEUE, args.hwqueue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_wait_sync_object_gpu(process, adapter, args.hwqueue, args.object_count, objects, fences, false); cleanup: if (objects) vfree(objects); if (fences) vfree(fences); if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_map_gpu_va(struct dxgprocess *process, void *__user inargs) { int ret, ret2; struct d3dddi_mapgpuvirtualaddress args; struct d3dddi_mapgpuvirtualaddress *input = inargs; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGPAGINGQUEUE, args.paging_queue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_map_gpu_va(process, zerohandle, adapter, &args); if (ret < 0) goto cleanup; /* STATUS_PENING is a success code > 0. It is returned to user mode */ if (!(ret == STATUS_PENDING || ret == 0)) { DXG_ERR("Unexpected error %x", ret); goto cleanup; } ret2 = copy_to_user(&input->paging_fence_value, &args.paging_fence_value, sizeof(u64)); if (ret2) { DXG_ERR("failed to copy paging fence to user"); ret = -EFAULT; goto cleanup; } ret2 = copy_to_user(&input->virtual_address, &args.virtual_address, sizeof(args.virtual_address)); if (ret2) { DXG_ERR("failed to copy va to user"); ret = -EFAULT; goto cleanup; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_reserve_gpu_va(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dddi_reservegpuvirtualaddress args; struct d3dddi_reservegpuvirtualaddress *input = inargs; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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) { DXG_ERR("invalid adapter or paging queue: 0x%x", args.adapter.v); ret = -EINVAL; goto cleanup; } adapter = device->adapter; kref_get(&adapter->adapter_kref); kref_put(&device->device_kref, dxgdevice_release); } else { args.adapter = adapter->host_handle; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { kref_put(&adapter->adapter_kref, dxgadapter_release); adapter = NULL; goto cleanup; } ret = dxgvmb_send_reserve_gpu_va(process, adapter, &args); if (ret < 0) goto cleanup; ret = copy_to_user(&input->virtual_address, &args.virtual_address, sizeof(args.virtual_address)); if (ret) { DXG_ERR("failed to copy VA to user"); ret = -EFAULT; } cleanup: if (adapter) { dxgadapter_release_lock_shared(adapter); kref_put(&adapter->adapter_kref, dxgadapter_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_free_gpu_va(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_freegpuvirtualaddress args; struct dxgadapter *adapter = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { kref_put(&adapter->adapter_kref, dxgadapter_release); adapter = NULL; goto cleanup; } args.adapter = adapter->host_handle; ret = dxgvmb_send_free_gpu_va(process, adapter, &args); cleanup: if (adapter) { dxgadapter_release_lock_shared(adapter); kref_put(&adapter->adapter_kref, dxgadapter_release); } return ret; } static int dxgkio_update_gpu_va(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_updategpuvirtualaddress args; struct d3dkmt_updategpuvirtualaddress *input = inargs; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_update_gpu_va(process, adapter, &args); if (ret < 0) goto cleanup; ret = copy_to_user(&input->fence_value, &args.fence_value, sizeof(args.fence_value)); if (ret) { DXG_ERR("failed to copy fence value to user"); ret = -EFAULT; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); return ret; } static int dxgkio_create_sync_object(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_createsynchronizationobject2 args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct eventfd_ctx *event = NULL; struct dxgsyncobject *syncobj = NULL; bool device_lock_acquired = false; struct dxgsharedsyncobject *syncobjgbl = NULL; struct dxghosteventcpu *host_event = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) goto cleanup; device_lock_acquired = true; adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } syncobj = dxgsyncobject_create(process, device, adapter, args.info.type, args.info.flags); if (syncobj == NULL) { ret = -EINVAL; goto cleanup; } if (args.info.type == _D3DDDI_CPU_NOTIFICATION) { event = eventfd_ctx_fdget((int) args.info.cpu_notification.event); if (IS_ERR(event)) { DXG_ERR("failed to reference the event"); event = NULL; ret = -EINVAL; goto cleanup; } host_event = syncobj->host_event; host_event->hdr.event_id = dxgglobal_new_host_event_id(); host_event->cpu_event = event; host_event->remove_from_list = false; host_event->destroy_after_signal = false; host_event->hdr.event_type = dxghostevent_cpu_event; dxgglobal_add_host_event(&host_event->hdr); args.info.cpu_notification.event = host_event->hdr.event_id; DXG_TRACE("creating CPU notification event: %lld", args.info.cpu_notification.event); } ret = dxgvmb_send_create_sync_object(process, adapter, &args, syncobj); if (ret < 0) goto cleanup; if (args.info.flags.shared) { if (args.info.shared_handle.v == 0) { DXG_ERR("shared handle should not be 0"); ret = -EINVAL; goto cleanup; } syncobjgbl = dxgsharedsyncobj_create(device->adapter, syncobj); if (syncobjgbl == NULL) { ret = -ENOMEM; goto cleanup; } dxgsharedsyncobj_add_syncobj(syncobjgbl, syncobj); syncobjgbl->host_shared_handle = args.info.shared_handle; } ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy output args"); ret = -EFAULT; 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 >= 0) syncobj->handle = args.sync_object; hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); cleanup: if (ret < 0) { if (syncobj) { dxgsyncobject_destroy(process, syncobj); if (args.sync_object.v) dxgvmb_send_destroy_sync_object(process, args.sync_object); event = NULL; } if (event) eventfd_ctx_put(event); } if (syncobjgbl) kref_put(&syncobjgbl->ssyncobj_kref, dxgsharedsyncobj_release); if (adapter) dxgadapter_release_lock_shared(adapter); if (device_lock_acquired) dxgdevice_release_lock_shared(device); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_destroy_sync_object(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_destroysynchronizationobject args; struct dxgsyncobject *syncobj = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } DXG_TRACE("handle 0x%x", args.sync_object.v); hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); syncobj = hmgrtable_get_object_by_type(&process->handle_table, HMGRENTRY_TYPE_DXGSYNCOBJECT, args.sync_object); if (syncobj) { DXG_TRACE("syncobj 0x%p", syncobj); syncobj->handle.v = 0; hmgrtable_free_handle(&process->handle_table, HMGRENTRY_TYPE_DXGSYNCOBJECT, args.sync_object); } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (syncobj == NULL) { ret = -EINVAL; goto cleanup; } dxgsyncobject_destroy(process, syncobj); ret = dxgvmb_send_destroy_sync_object(process, args.sync_object); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; struct dxgglobal *dxgglobal = dxggbl(); ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } args.sync_object.v = 0; if (args.device.v) { device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { return -EINVAL; goto cleanup; } } else { DXG_ERR("device handle is missing"); ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) goto cleanup; device_lock_acquired = true; adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } file = fget(args.nt_handle); if (!file) { DXG_ERR("failed to get file from handle: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } if (file->f_op != &dxg_syncobj_fops) { DXG_ERR("invalid fd: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } syncobj_fd = file->private_data; if (syncobj_fd == NULL) { DXG_ERR("invalid private data: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } flags.shared = 1; flags.nt_security_sharing = 1; syncobj = dxgsyncobject_create(process, device, adapter, syncobj_fd->type, flags); if (syncobj == NULL) { DXG_ERR("failed to create sync object"); ret = -ENOMEM; goto cleanup; } dxgsharedsyncobj_add_syncobj(syncobj_fd, syncobj); ret = dxgvmb_send_open_sync_object_nt(process, &dxgglobal->channel, &args, syncobj); if (ret < 0) { DXG_ERR("failed to open sync object on host: %x", syncobj_fd->host_shared_handle.v); 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 >= 0) { syncobj->handle = args.sync_object; kref_get(&syncobj->syncobj_kref); } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (ret < 0) goto cleanup; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret == 0) goto success; DXG_ERR("failed to copy output args"); ret = -EFAULT; cleanup: if (syncobj) { dxgsyncobject_destroy(process, syncobj); syncobj = NULL; } if (args.sync_object.v) dxgvmb_send_destroy_sync_object(process, args.sync_object); success: if (file) fput(file); if (syncobj) kref_put(&syncobj->syncobj_kref, dxgsyncobject_release); if (adapter) dxgadapter_release_lock_shared(adapter); if (device_lock_acquired) dxgdevice_release_lock_shared(device); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; u32 fence_count = 1; struct eventfd_ctx *event = NULL; struct dxghosteventcpu *host_event = NULL; bool host_event_added = false; u64 host_event_id = 0; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.context_count >= D3DDDI_MAX_BROADCAST_CONTEXT || args.object_count > D3DDDI_MAX_OBJECT_SIGNALED) { ret = -EINVAL; goto cleanup; } if (args.flags.enqueue_cpu_event) { host_event = kzalloc(sizeof(*host_event), GFP_KERNEL); if (host_event == NULL) { ret = -ENOMEM; goto cleanup; } host_event->process = process; event = eventfd_ctx_fdget((int)args.cpu_event_handle); if (IS_ERR(event)) { DXG_ERR("failed to reference the event"); event = NULL; ret = -EINVAL; goto cleanup; } fence_count = 0; host_event->cpu_event = event; host_event_id = dxgglobal_new_host_event_id(); host_event->hdr.event_type = dxghostevent_cpu_event; host_event->hdr.event_id = host_event_id; host_event->remove_from_list = true; host_event->destroy_after_signal = true; dxgglobal_add_host_event(&host_event->hdr); host_event_added = true; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGCONTEXT, args.context); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_signal_sync_object(process, adapter, 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, zerohandle); /* * When the send operation succeeds, the host event will be destroyed * after signal from the host */ cleanup: if (ret < 0) { if (host_event_added) { /* The event might be signaled and destroyed by host */ host_event = (struct dxghosteventcpu *) dxgglobal_get_host_event(host_event_id); if (host_event) { eventfd_ctx_put(event); event = NULL; kfree(host_event); host_event = NULL; } } if (event) eventfd_ctx_put(event); kfree(host_event); } if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_signal_sync_object_cpu(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_signalsynchronizationobjectfromcpu args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count == 0 || args.object_count > D3DDDI_MAX_OBJECT_SIGNALED) { DXG_TRACE("Too many syncobjects : %d", args.object_count); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_signal_sync_object(process, adapter, args.flags, 0, zerohandle, 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) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count == 0 || args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE) { ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGCONTEXT, args.context); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_signal_sync_object(process, adapter, flags, 0, zerohandle, args.object_count, args.objects, 1, &user_args->context, args.object_count, args.monitored_fence_values, NULL, zerohandle); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_signal_sync_object_gpu2(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_signalsynchronizationobjectfromgpu2 args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct d3dkmthandle context_handle; struct eventfd_ctx *event = NULL; u64 *fences = NULL; u32 fence_count = 0; int ret; struct dxghosteventcpu *host_event = NULL; bool host_event_added = false; u64 host_event_id = 0; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.flags.enqueue_cpu_event) { if (args.object_count != 0 || args.cpu_event_handle == 0) { DXG_ERR("Bad input in EnqueueCpuEvent: %d %lld", args.object_count, args.cpu_event_handle); ret = -EINVAL; goto cleanup; } } 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) { DXG_ERR("Invalid input: %d %d", args.object_count, args.context_count); ret = -EINVAL; goto cleanup; } ret = copy_from_user(&context_handle, args.contexts, sizeof(struct d3dkmthandle)); if (ret) { DXG_ERR("failed to copy context handle"); ret = -EFAULT; goto cleanup; } if (args.flags.enqueue_cpu_event) { host_event = kzalloc(sizeof(*host_event), GFP_KERNEL); if (host_event == NULL) { ret = -ENOMEM; goto cleanup; } host_event->process = process; event = eventfd_ctx_fdget((int)args.cpu_event_handle); if (IS_ERR(event)) { DXG_ERR("failed to reference the event"); event = NULL; ret = -EINVAL; goto cleanup; } fence_count = 0; host_event->cpu_event = event; host_event_id = dxgglobal_new_host_event_id(); host_event->hdr.event_id = host_event_id; host_event->hdr.event_type = dxghostevent_cpu_event; host_event->remove_from_list = true; host_event->destroy_after_signal = true; dxgglobal_add_host_event(&host_event->hdr); 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_signal_sync_object(process, adapter, args.flags, 0, zerohandle, args.object_count, args.objects, args.context_count, args.contexts, fence_count, fences, (void *)host_event_id, zerohandle); cleanup: if (ret < 0) { if (host_event_added) { /* The event might be signaled and destroyed by host */ host_event = (struct dxghosteventcpu *) dxgglobal_get_host_event(host_event_id); if (host_event) { eventfd_ctx_put(event); event = NULL; kfree(host_event); host_event = NULL; } } if (event) eventfd_ctx_put(event); kfree(host_event); } if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_wait_sync_object(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_waitforsynchronizationobject2 args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count > D3DDDI_MAX_OBJECT_WAITED_ON || args.object_count == 0) { ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGCONTEXT, args.context); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } DXG_TRACE("Fence value: %lld", args.fence.fence_value); ret = dxgvmb_send_wait_sync_object_gpu(process, adapter, args.context, args.object_count, args.object_array, &args.fence.fence_value, true); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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 dxghosteventcpu host_event = { }; struct dxghosteventcpu *async_host_event = NULL; struct completion local_event = { }; u64 event_id = 0; int ret; bool host_event_added = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE || args.object_count == 0) { ret = -EINVAL; goto cleanup; } if (args.async_event) { async_host_event = kzalloc(sizeof(*async_host_event), GFP_KERNEL); if (async_host_event == NULL) { ret = -EINVAL; goto cleanup; } async_host_event->process = process; event = eventfd_ctx_fdget((int)args.async_event); if (IS_ERR(event)) { DXG_ERR("failed to reference the event"); event = NULL; ret = -EINVAL; goto cleanup; } async_host_event->cpu_event = event; async_host_event->hdr.event_id = dxgglobal_new_host_event_id(); async_host_event->destroy_after_signal = true; async_host_event->hdr.event_type = dxghostevent_cpu_event; dxgglobal_add_host_event(&async_host_event->hdr); event_id = async_host_event->hdr.event_id; host_event_added = true; } else { init_completion(&local_event); host_event.completion_event = &local_event; host_event.hdr.event_id = dxgglobal_new_host_event_id(); host_event.hdr.event_type = dxghostevent_cpu_event; dxgglobal_add_host_event(&host_event.hdr); event_id = host_event.hdr.event_id; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_wait_sync_object_cpu(process, adapter, &args, true, event_id); if (ret < 0) goto cleanup; if (args.async_event == 0) { dxgadapter_release_lock_shared(adapter); adapter = NULL; ret = wait_for_completion_interruptible(&local_event); if (ret) { DXG_ERR("wait_completion_interruptible: %d", ret); ret = -ERESTARTSYS; } } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); if (host_event.hdr.event_id) dxgglobal_remove_host_event(&host_event.hdr); if (ret < 0) { if (host_event_added) { async_host_event = (struct dxghosteventcpu *) dxgglobal_get_host_event(event_id); if (async_host_event) { if (async_host_event->hdr.event_type == dxghostevent_cpu_event) { eventfd_ctx_put(event); event = NULL; kfree(async_host_event); async_host_event = NULL; } else { DXG_ERR("Invalid event type"); DXGKRNL_ASSERT(0); } } } if (event) eventfd_ctx_put(event); kfree(async_host_event); } DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_wait_sync_object_gpu(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_waitforsynchronizationobjectfromgpu args; struct dxgcontext *context = NULL; struct d3dkmthandle device_handle = {}; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct dxgsyncobject *syncobj = NULL; struct d3dkmthandle *objects = NULL; u32 object_size; u64 *fences = NULL; int ret; enum hmgrentry_type syncobj_type = HMGRENTRY_TYPE_FREE; bool monitored_fence = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE || args.object_count == 0) { DXG_ERR("Invalid object count: %d", args.object_count); ret = -EINVAL; goto cleanup; } object_size = sizeof(struct d3dkmthandle) * args.object_count; objects = vzalloc(object_size); if (objects == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(objects, args.objects, object_size); if (ret) { DXG_ERR("failed to copy objects"); ret = -EFAULT; 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.v == 0) { DXG_ERR("Invalid context handle: %x", args.context.v); ret = -EINVAL; } 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) { DXG_ERR("Invalid syncobj: %x", objects[0].v); ret = -EINVAL; } else { monitored_fence = syncobj->monitored_fence; } } else { DXG_ERR("Invalid syncobj type: %x", objects[0].v); ret = -EINVAL; } } hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); if (ret < 0) goto cleanup; if (monitored_fence) { object_size = sizeof(u64) * args.object_count; fences = vzalloc(object_size); if (fences == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(fences, args.monitored_fence_values, object_size); if (ret) { DXG_ERR("failed to copy fences"); ret = -EFAULT; goto cleanup; } } else { fences = &args.fence_value; } device = dxgprocess_device_by_handle(process, device_handle); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_wait_sync_object_gpu(process, adapter, args.context, args.object_count, objects, fences, !monitored_fence); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); if (objects) vfree(objects); if (fences && fences != &args.fence_value) vfree(fences); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_lock2(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_lock2 args; struct d3dkmt_lock2 *__user result = inargs; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct dxgallocation *alloc = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } args.data = NULL; hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); alloc = hmgrtable_get_object_by_type(&process->handle_table, HMGRENTRY_TYPE_DXGALLOCATION, args.allocation); if (alloc == NULL) { ret = -EINVAL; } else { if (alloc->cpu_address) { ret = copy_to_user(&result->data, &alloc->cpu_address, sizeof(args.data)); if (ret == 0) { args.data = alloc->cpu_address; if (alloc->cpu_address_mapped) alloc->cpu_address_refcount++; } else { DXG_ERR("Failed to copy cpu address"); ret = -EFAULT; } } } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (ret < 0) 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_lock2(process, adapter, &args, result); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); success: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_unlock2(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_unlock2 args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; struct dxgallocation *alloc = NULL; bool done = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); alloc = hmgrtable_get_object_by_type(&process->handle_table, HMGRENTRY_TYPE_DXGALLOCATION, args.allocation); if (alloc == NULL) { ret = -EINVAL; } else { if (alloc->cpu_address == NULL) { DXG_ERR("Allocation is not locked: %p", alloc); ret = -EINVAL; } 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 { DXG_ERR("Invalid cpu access refcount"); done = true; } } } hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (done) goto success; if (ret < 0) 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_unlock2(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); success: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_update_alloc_property(struct dxgprocess *process, void *__user inargs) { struct d3dddi_updateallocproperty args; int ret; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_object_handle(process, HMGRENTRY_TYPE_DXGPAGINGQUEUE, args.paging_queue); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_update_alloc_property(process, adapter, &args, inargs); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_mark_device_as_error(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_markdeviceaserror args; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } device->execution_state = _D3DKMT_DEVICEEXECUTION_RESET; ret = dxgvmb_send_mark_device_as_error(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_query_alloc_residency(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_queryallocationresidency args; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if ((args.allocation_count == 0) == (args.resource.v == 0)) { ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_query_alloc_residency(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_set_allocation_priority(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_setallocationpriority args; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_set_allocation_priority(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_get_allocation_priority(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_getallocationpriority args; struct dxgadapter *adapter = NULL; struct dxgdevice *device = NULL; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_get_allocation_priority(process, adapter, &args); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int set_context_scheduling_priority(struct dxgprocess *process, struct d3dkmthandle 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_set_context_sch_priority(process, adapter, hcontext, priority, in_process); if (ret < 0) DXG_ERR("send_set_context_scheduling_priority failed"); cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); return ret; } static int dxgkio_set_context_scheduling_priority(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_setcontextschedulingpriority args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = set_context_scheduling_priority(process, args.context, args.priority, false); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int get_context_scheduling_priority(struct dxgprocess *process, struct d3dkmthandle hcontext, int __user *priority, bool in_process) { int ret; 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 = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgvmb_send_get_context_sch_priority(process, adapter, hcontext, &pri, in_process); if (ret < 0) goto cleanup; ret = copy_to_user(priority, &pri, sizeof(pri)); if (ret) { DXG_ERR("failed to copy priority to user"); ret = -EFAULT; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); return ret; } static int dxgkio_get_context_scheduling_priority(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_getcontextschedulingpriority args; struct d3dkmt_getcontextschedulingpriority __user *input = inargs; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = get_context_scheduling_priority(process, args.context, &input->priority, false); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_set_context_process_scheduling_priority(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_setcontextinprocessschedulingpriority args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = set_context_scheduling_priority(process, args.context, args.priority, true); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_get_context_process_scheduling_priority(struct dxgprocess *process, void __user *inargs) { struct d3dkmt_getcontextinprocessschedulingpriority args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = get_context_scheduling_priority(process, args.context, &((struct d3dkmt_getcontextinprocessschedulingpriority *) inargs)->priority, true); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_change_vidmem_reservation(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_changevideomemoryreservation args; int ret; struct dxgadapter *adapter = NULL; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.process != 0) { DXG_ERR("setting memory reservation for other process"); ret = -EINVAL; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; args.adapter.v = 0; ret = dxgvmb_send_change_vidmem_reservation(process, adapter, zerohandle, &args); cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_query_clock_calibration(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_queryclockcalibration args; int ret; struct dxgadapter *adapter = NULL; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; args.adapter = adapter->host_handle; ret = dxgvmb_send_query_clock_calibration(process, adapter, &args, inargs); if (ret < 0) goto cleanup; cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_flush_heap_transitions(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_flushheaptransitions args; int ret; struct dxgadapter *adapter = NULL; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; args.adapter = adapter->host_handle; ret = dxgvmb_send_flush_heap_transitions(process, adapter, &args); if (ret < 0) goto cleanup; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy output args"); ret = -EFAULT; } cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_invalidate_cache(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_invalidatecache args; int ret; struct dxgdevice *device = NULL; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } ret = dxgvmb_send_invalidate_cache(process, device->adapter, &args); cleanup: if (device) { dxgdevice_release_lock_shared(device); kref_put(&device->device_kref, dxgdevice_release); } DXG_TRACE_IOCTL_END(ret); return ret; } static int build_test_command_buffer(struct dxgprocess *process, struct dxgadapter *adapter, struct d3dkmt_escape *args) { int ret; struct d3dddi_buildtestcommandbuffer cmd; struct d3dkmt_escape newargs = *args; u32 buf_size; struct d3dddi_buildtestcommandbuffer *buf = NULL; struct d3dddi_buildtestcommandbuffer *__user ucmd; ucmd = args->priv_drv_data; if (args->priv_drv_data_size < sizeof(struct d3dddi_buildtestcommandbuffer)) { DXG_ERR("Invalid private data size"); return -EINVAL; } ret = copy_from_user(&cmd, ucmd, sizeof(cmd)); if (ret) { DXG_ERR("Failed to copy private data"); return -EFAULT; } if (cmd.dma_buffer_size < sizeof(u32) || cmd.dma_buffer_size > D3DDDI_MAXTESTBUFFERSIZE || cmd.dma_buffer_priv_data_size > D3DDDI_MAXTESTBUFFERPRIVATEDRIVERDATASIZE) { DXG_ERR("Invalid DMA buffer or private data size"); return -EINVAL; } /* Allocate a new buffer for the escape call */ buf_size = sizeof(struct d3dddi_buildtestcommandbuffer) + cmd.dma_buffer_size + cmd.dma_buffer_priv_data_size; buf = vzalloc(buf_size); if (buf == NULL) { ret = -ENOMEM; goto cleanup; } *buf = cmd; buf->dma_buffer = NULL; buf->dma_buffer_priv_data = NULL; /* Replace private data in the escape arguments and call the host */ newargs.priv_drv_data = buf; newargs.priv_drv_data_size = buf_size; ret = dxgvmb_send_escape(process, adapter, &newargs, false); if (ret) { DXG_ERR("Host failed escape"); goto cleanup; } ret = copy_to_user(&ucmd->dma_buffer_size, &buf->dma_buffer_size, sizeof(u32)); if (ret) { DXG_ERR("Failed to dma size to user"); ret = -EFAULT; goto cleanup; } ret = copy_to_user(&ucmd->dma_buffer_priv_data_size, &buf->dma_buffer_priv_data_size, sizeof(u32)); if (ret) { DXG_ERR("Failed to dma private data size to user"); ret = -EFAULT; goto cleanup; } ret = copy_to_user(cmd.dma_buffer, (char *)buf + sizeof(*buf), buf->dma_buffer_size); if (ret) { DXG_ERR("Failed to copy dma buffer to user"); ret = -EFAULT; goto cleanup; } if (buf->dma_buffer_priv_data_size) { ret = copy_to_user(cmd.dma_buffer_priv_data, (char *)buf + sizeof(*buf) + cmd.dma_buffer_size, buf->dma_buffer_priv_data_size); if (ret) { DXG_ERR("Failed to copy private data to user"); ret = -EFAULT; goto cleanup; } } cleanup: if (buf) vfree(buf); return ret; } static int driver_known_escape(struct dxgprocess *process, struct dxgadapter *adapter, struct d3dkmt_escape *args) { enum d3dkmt_escapetype escape_type; int ret = 0; if (args->priv_drv_data_size < sizeof(enum d3dddi_knownescapetype)) { DXG_ERR("Invalid private data size"); return -EINVAL; } ret = copy_from_user(&escape_type, args->priv_drv_data, sizeof(escape_type)); if (ret) { DXG_ERR("Failed to read escape type"); return -EFAULT; } switch (escape_type) { case _D3DDDI_DRIVERESCAPETYPE_TRANSLATEALLOCATIONHANDLE: case _D3DDDI_DRIVERESCAPETYPE_TRANSLATERESOURCEHANDLE: /* * The host and VM handles are the same */ break; case _D3DDDI_DRIVERESCAPETYPE_BUILDTESTCOMMANDBUFFER: ret = build_test_command_buffer(process, adapter, args); break; default: ret = dxgvmb_send_escape(process, adapter, args, true); break; } return ret; } static int dxgkio_escape(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_escape args; int ret; struct dxgadapter *adapter = NULL; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { ret = -EFAULT; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; args.adapter = adapter->host_handle; if (args.type == _D3DKMT_ESCAPE_DRIVERPRIVATE && args.flags.driver_known_escape) ret = driver_known_escape(process, adapter, &args); else ret = dxgvmb_send_escape(process, adapter, &args, true); cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_query_vidmem_info(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_queryvideomemoryinfo args; int ret; struct dxgadapter *adapter = NULL; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.process != 0) { DXG_ERR("query vidmem info from another process"); ret = -EINVAL; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; args.adapter = adapter->host_handle; ret = dxgvmb_send_query_vidmem_info(process, adapter, &args, inargs); cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); if (ret < 0) DXG_ERR("failed: %x", ret); return ret; } static int dxgkio_get_device_state(struct dxgprocess *process, void *__user inargs) { int ret; struct d3dkmt_getdevicestate args; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; int global_device_state_counter = 0; struct dxgglobal *dxgglobal = dxggbl(); ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } if (args.state_type == _D3DKMT_DEVICESTATE_EXECUTION) { global_device_state_counter = atomic_read(&dxgglobal->device_state_counter); if (device->execution_state_counter == global_device_state_counter) { args.execution_state = device->execution_state; ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy args to user"); ret = -EFAULT; } goto cleanup; } } ret = dxgvmb_send_get_device_state(process, adapter, &args, inargs); if (ret == 0 && args.state_type == _D3DKMT_DEVICESTATE_EXECUTION) { device->execution_state = args.execution_state; device->execution_state_counter = global_device_state_counter; } cleanup: if (adapter) dxgadapter_release_lock_shared(adapter); if (device) kref_put(&device->device_kref, dxgdevice_release); if (ret < 0) DXG_ERR("Failed to get device state %x", ret); return ret; } int dxgsharedsyncobj_get_host_nt_handle(struct dxgsharedsyncobject *syncobj, struct dxgprocess *process, struct d3dkmthandle objecthandle) { int ret = 0; mutex_lock(&syncobj->fd_mutex); if (syncobj->host_shared_handle_nt_reference == 0) { ret = dxgvmb_send_create_nt_shared_object(process, objecthandle, &syncobj->host_shared_handle_nt); if (ret < 0) goto cleanup; DXG_TRACE("Host_shared_handle_ht: %x", syncobj->host_shared_handle_nt.v); kref_get(&syncobj->ssyncobj_kref); } syncobj->host_shared_handle_nt_reference++; cleanup: mutex_unlock(&syncobj->fd_mutex); return ret; } static int dxgsharedresource_get_host_nt_handle(struct dxgsharedresource *resource, struct dxgprocess *process, struct d3dkmthandle objecthandle) { int ret = 0; mutex_lock(&resource->fd_mutex); if (resource->host_shared_handle_nt_reference == 0) { ret = dxgvmb_send_create_nt_shared_object(process, objecthandle, &resource->host_shared_handle_nt); if (ret < 0) goto cleanup; DXG_TRACE("Resource host_shared_handle_ht: %x", resource->host_shared_handle_nt.v); kref_get(&resource->sresource_kref); } resource->host_shared_handle_nt_reference++; cleanup: mutex_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 **filp) { struct file *file; int fd; fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) { DXG_ERR("get_unused_fd_flags failed: %x", fd); return -ENOTRECOVERABLE; } 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 -EINVAL; }; if (IS_ERR(file)) { DXG_ERR("anon_inode_getfile failed: %x", fd); put_unused_fd(fd); return -ENOTRECOVERABLE; } *fdout = fd; *filp = file; return 0; } static int dxgkio_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; struct d3dkmthandle *handles = NULL; int object_fd = -1; struct file *filp = NULL; void *obj = NULL; u32 handle_size; int ret; u64 tmp = 0; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } if (args.object_count == 0 || args.object_count > 1) { DXG_ERR("invalid object count %d", args.object_count); ret = -EINVAL; goto cleanup; } handle_size = args.object_count * sizeof(struct d3dkmthandle); handles = vzalloc(handle_size); if (handles == NULL) { ret = -ENOMEM; goto cleanup; } ret = copy_from_user(handles, args.objects, handle_size); if (ret) { DXG_ERR("failed to copy object handles"); ret = -EFAULT; goto cleanup; } DXG_TRACE("Sharing handle: %x", handles[0].v); 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) { DXG_ERR("invalid object handle %x", handles[0].v); ret = -EINVAL; } else { switch (object_type) { case HMGRENTRY_TYPE_DXGSYNCOBJECT: syncobj = obj; if (syncobj->shared) { kref_get(&syncobj->syncobj_kref); shared_syncobj = syncobj->shared_owner; } else { DXG_ERR("sync object is not shared"); syncobj = NULL; ret = -EINVAL; } break; case HMGRENTRY_TYPE_DXGRESOURCE: resource = obj; if (resource->shared_owner) { kref_get(&resource->resource_kref); shared_resource = resource->shared_owner; } else { resource = NULL; DXG_ERR("resource object shared"); ret = -EINVAL; } break; default: DXG_ERR("invalid object type %d", object_type); ret = -EINVAL; break; } } hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); if (ret < 0) goto cleanup; switch (object_type) { case HMGRENTRY_TYPE_DXGSYNCOBJECT: ret = get_object_fd(DXG_SHARED_SYNCOBJECT, shared_syncobj, &object_fd, &filp); if (ret < 0) { DXG_ERR("get_object_fd failed for sync object"); goto cleanup; } ret = dxgsharedsyncobj_get_host_nt_handle(shared_syncobj, process, handles[0]); if (ret < 0) { DXG_ERR("get_host_nt_handle failed"); goto cleanup; } break; case HMGRENTRY_TYPE_DXGRESOURCE: ret = get_object_fd(DXG_SHARED_RESOURCE, shared_resource, &object_fd, &filp); if (ret < 0) { DXG_ERR("get_object_fd failed for resource"); goto cleanup; } ret = dxgsharedresource_get_host_nt_handle(shared_resource, process, handles[0]); if (ret < 0) { DXG_ERR("get_host_res_nt_handle failed"); goto cleanup; } ret = dxgsharedresource_seal(shared_resource); if (ret < 0) { DXG_ERR("dxgsharedresource_seal failed"); goto cleanup; } break; default: ret = -EINVAL; break; } if (ret < 0) goto cleanup; DXG_TRACE("Object FD: %x", object_fd); tmp = (u64) object_fd; ret = copy_to_user(args.shared_handle, &tmp, sizeof(u64)); if (ret) { DXG_ERR("failed to copy shared handle"); ret = -EFAULT; goto cleanup; } fd_install(object_fd, filp); cleanup: if (ret < 0) { if (filp) fput(filp); if (object_fd >= 0) put_unused_fd(object_fd); } if (handles) vfree(handles); if (syncobj) kref_put(&syncobj->syncobj_kref, dxgsyncobject_release); if (resource) kref_put(&resource->resource_kref, dxgresource_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_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; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } file = fget(args.nt_handle); if (!file) { DXG_ERR("failed to get file from handle: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } if (file->f_op != &dxg_resource_fops) { DXG_ERR("invalid fd: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } shared_resource = file->private_data; if (shared_resource == NULL) { DXG_ERR("invalid private data: %llx", args.nt_handle); ret = -EINVAL; goto cleanup; } device = dxgprocess_device_by_handle(process, args.device); if (device == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } ret = dxgsharedresource_seal(shared_resource); if (ret < 0) 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 = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy output args"); ret = -EFAULT; } cleanup: if (file) fput(file); if (device) dxgdevice_release_lock_shared(device); if (device) kref_put(&device->device_kref, dxgdevice_release); DXG_TRACE_IOCTL_END(ret); return ret; } static int assign_resource_handles(struct dxgprocess *process, struct dxgsharedresource *shared_resource, struct d3dkmt_openresourcefromnthandle *args, struct d3dkmthandle resource_handle, struct dxgresource *resource, struct dxgallocation **allocs, struct d3dkmthandle *handles) { int ret; int i; u8 *cur_priv_data; u32 total_priv_data_size = 0; struct d3dddi_openallocationinfo2 open_alloc_info = { }; struct dxgsharedallocdata *alloc_info; hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); ret = hmgrtable_assign_handle(&process->handle_table, resource, HMGRENTRY_TYPE_DXGRESOURCE, resource_handle); if (ret < 0) goto cleanup; resource->handle = resource_handle; resource->handle_valid = 1; cur_priv_data = args->total_priv_drv_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 < 0) goto cleanup; allocs[i]->alloc_handle = handles[i]; allocs[i]->handle_valid = 1; open_alloc_info.allocation = handles[i]; if (shared_resource->alloc_info) { alloc_info = &shared_resource->alloc_info[i]; open_alloc_info.priv_drv_data_size = alloc_info->private_data_size; allocs[i]->num_pages = alloc_info->num_pages; allocs[i]->cached = alloc_info->cached; } else { open_alloc_info.priv_drv_data_size = 0; } total_priv_data_size += open_alloc_info.priv_drv_data_size; open_alloc_info.priv_drv_data = cur_priv_data; cur_priv_data += open_alloc_info.priv_drv_data_size; ret = copy_to_user(&args->open_alloc_info[i], &open_alloc_info, sizeof(open_alloc_info)); if (ret) { DXG_ERR("failed to copy alloc info"); ret = -EFAULT; goto cleanup; } } args->total_priv_drv_data_size = total_priv_data_size; cleanup: hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); if (ret < 0) { for (i = 0; i < args->allocation_count; i++) dxgallocation_free_handle(allocs[i]); dxgresource_free_handle(resource); } return ret; } static int open_resource(struct dxgprocess *process, struct d3dkmt_openresourcefromnthandle *args, __user struct d3dkmthandle *res_out, __user u32 *total_driver_data_size_out) { int ret = 0; int i; struct d3dkmthandle *alloc_handles = NULL; int alloc_handles_size = sizeof(struct d3dkmthandle) * args->allocation_count; struct dxgsharedresource *shared_resource = NULL; struct dxgresource *resource = NULL; struct dxgallocation **allocs = NULL; struct d3dkmthandle global_share = {}; struct dxgdevice *device = NULL; struct dxgadapter *adapter = NULL; struct d3dkmthandle resource_handle = {}; struct file *file = NULL; DXG_TRACE("Opening resource handle: %llx", args->nt_handle); file = fget(args->nt_handle); if (!file) { DXG_ERR("failed to get file from handle: %llx", args->nt_handle); ret = -EINVAL; goto cleanup; } if (file->f_op != &dxg_resource_fops) { DXG_ERR("invalid fd type: %llx", args->nt_handle); ret = -EINVAL; goto cleanup; } shared_resource = file->private_data; if (shared_resource == NULL) { DXG_ERR("invalid private data: %llx", args->nt_handle); ret = -EINVAL; goto cleanup; } if (kref_get_unless_zero(&shared_resource->sresource_kref) == 0) shared_resource = NULL; else global_share = shared_resource->host_shared_handle_nt; if (shared_resource == NULL) { DXG_ERR("Invalid shared resource handle: %x", (u32)args->nt_handle); ret = -EINVAL; goto cleanup; } DXG_TRACE("Shared resource: %p %x", shared_resource, global_share.v); device = dxgprocess_device_by_handle(process, args->device); if (device == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgdevice_acquire_lock_shared(device); if (ret < 0) { kref_put(&device->device_kref, dxgdevice_release); device = NULL; goto cleanup; } adapter = device->adapter; ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) { adapter = NULL; goto cleanup; } ret = dxgsharedresource_seal(shared_resource); if (ret < 0) 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 = -EINVAL; DXG_ERR("Invalid data sizes"); goto cleanup; } alloc_handles = vzalloc(alloc_handles_size); if (alloc_handles == NULL) { ret = -ENOMEM; goto cleanup; } allocs = vzalloc(sizeof(void *) * args->allocation_count); if (allocs == NULL) { ret = -ENOMEM; goto cleanup; } resource = dxgresource_create(device); if (resource == NULL) { ret = -ENOMEM; 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; ret = dxgresource_add_alloc(resource, allocs[i]); if (ret < 0) goto cleanup; } ret = dxgvmb_send_open_resource(process, adapter, device->handle, global_share, args->allocation_count, args->total_priv_drv_data_size, &resource_handle, alloc_handles); if (ret < 0) { DXG_ERR("dxgvmb_send_open_resource failed"); goto cleanup; } if (shared_resource->runtime_private_data_size) { ret = copy_to_user(args->private_runtime_data, shared_resource->runtime_private_data, shared_resource->runtime_private_data_size); if (ret) { DXG_ERR("failed to copy runtime data"); ret = -EFAULT; goto cleanup; } } if (shared_resource->resource_private_data_size) { ret = copy_to_user(args->resource_priv_drv_data, shared_resource->resource_private_data, shared_resource->resource_private_data_size); if (ret) { DXG_ERR("failed to copy resource data"); ret = -EFAULT; goto cleanup; } } if (shared_resource->alloc_private_data_size) { ret = copy_to_user(args->total_priv_drv_data, shared_resource->alloc_private_data, shared_resource->alloc_private_data_size); if (ret) { DXG_ERR("failed to copy alloc data"); ret = -EFAULT; goto cleanup; } } ret = assign_resource_handles(process, shared_resource, args, resource_handle, resource, allocs, alloc_handles); if (ret < 0) goto cleanup; ret = copy_to_user(res_out, &resource_handle, sizeof(struct d3dkmthandle)); if (ret) { DXG_ERR("failed to copy resource handle to user"); ret = -EFAULT; goto cleanup; } ret = copy_to_user(total_driver_data_size_out, &args->total_priv_drv_data_size, sizeof(u32)); if (ret) { DXG_ERR("failed to copy total driver data size"); ret = -EFAULT; } cleanup: if (ret < 0) { if (resource_handle.v) { 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, &tmp, NULL); } if (resource) dxgresource_destroy(resource); } if (file) fput(file); if (allocs) vfree(allocs); if (shared_resource) kref_put(&shared_resource->sresource_kref, dxgsharedresource_destroy); if (alloc_handles) vfree(alloc_handles); if (adapter) dxgadapter_release_lock_shared(adapter); if (device) dxgdevice_release_lock_shared(device); if (device) kref_put(&device->device_kref, dxgdevice_release); return ret; } static int dxgkio_open_resource_nt(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_openresourcefromnthandle args; struct d3dkmt_openresourcefromnthandle *__user args_user = inargs; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = open_resource(process, &args, &args_user->resource, &args_user->total_priv_drv_data_size); cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_share_object_with_host(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_shareobjectwithhost args; int ret; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } ret = dxgvmb_send_share_object_with_host(process, &args); if (ret) { DXG_ERR("dxgvmb_send_share_object_with_host dailed"); goto cleanup; } ret = copy_to_user(inargs, &args, sizeof(args)); if (ret) { DXG_ERR("failed to copy data to user"); ret = -EFAULT; } cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_enum_processes(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_enumprocesses args; struct d3dkmt_enumprocesses *__user input = inargs; struct dxgadapter *adapter = NULL; struct dxgadapter *entry; struct dxgglobal *dxgglobal = dxggbl(); struct dxgprocess_adapter *pentry; int nump = 0; /* Current number of processes*/ struct ntstatus status; int ret, ret1; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; 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 (*(u64 *) &entry->luid == *(u64 *) &args.adapter_luid) { adapter = entry; break; } } if (adapter == NULL) { DXG_ERR("Failed to find dxgadapter"); ret = -EINVAL; goto cleanup_locks; } list_for_each_entry(pentry, &adapter->adapter_process_list_head, adapter_process_list_entry) { if (pentry->process->nspid == task_active_pid_ns(current)) nump++; } if (nump > args.buffer_count || args.buffer == NULL) { status.v = STATUS_BUFFER_TOO_SMALL; ret = ntstatus2int(status); goto cleanup_locks; } nump = 0; list_for_each_entry(pentry, &adapter->adapter_process_list_head, adapter_process_list_entry) { if (pentry->process->nspid != task_active_pid_ns(current)) continue; if (nump == args.buffer_count) { status.v = STATUS_BUFFER_TOO_SMALL; ret = ntstatus2int(status); goto cleanup_locks; } ret = copy_to_user(&args.buffer[nump], &pentry->process->vpid, sizeof(u32)); if (ret) { DXG_ERR("failed to copy data to user"); ret = -EFAULT; goto cleanup_locks; } nump++; } cleanup_locks: dxgglobal_release_process_adapter_lock(); dxgglobal_release_adapter_list_lock(DXGLOCK_SHARED); ret1 = copy_to_user(&input->buffer_count, &nump, sizeof(u32)); if (ret1) { DXG_ERR("failed to copy buffer count to user"); ret = -EFAULT; } cleanup: DXG_TRACE_IOCTL_END(ret); return ret; } static int dxgkio_is_feature_enabled(struct dxgprocess *process, void *__user inargs) { struct d3dkmt_isfeatureenabled args; struct dxgadapter *adapter = NULL; struct d3dkmt_isfeatureenabled *__user uargs = inargs; int ret; bool adapter_locked = false; ret = copy_from_user(&args, inargs, sizeof(args)); if (ret) { DXG_ERR("failed to copy input args"); ret = -EFAULT; goto cleanup; } adapter = dxgprocess_adapter_by_handle(process, args.adapter); if (adapter == NULL) { ret = -EINVAL; goto cleanup; } ret = dxgadapter_acquire_lock_shared(adapter); if (ret < 0) goto cleanup; adapter_locked = true; ret = dxgvmb_send_is_feature_enabled(adapter, &args); if (ret) goto cleanup; ret = copy_to_user(&uargs->result, &args.result, sizeof(args.result)); cleanup: if (adapter_locked) dxgadapter_release_lock_shared(adapter); if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); DXG_TRACE_IOCTL_END(ret); return ret; } static struct ioctl_desc ioctls[] = { /* 0x00 */ {}, /* 0x01 */ {dxgkio_open_adapter_from_luid, LX_DXOPENADAPTERFROMLUID}, /* 0x02 */ {dxgkio_create_device, LX_DXCREATEDEVICE}, /* 0x03 */ {}, /* 0x04 */ {dxgkio_create_context_virtual, LX_DXCREATECONTEXTVIRTUAL}, /* 0x05 */ {dxgkio_destroy_context, LX_DXDESTROYCONTEXT}, /* 0x06 */ {dxgkio_create_allocation, LX_DXCREATEALLOCATION}, /* 0x07 */ {dxgkio_create_paging_queue, LX_DXCREATEPAGINGQUEUE}, /* 0x08 */ {dxgkio_reserve_gpu_va, LX_DXRESERVEGPUVIRTUALADDRESS}, /* 0x09 */ {dxgkio_query_adapter_info, LX_DXQUERYADAPTERINFO}, /* 0x0a */ {dxgkio_query_vidmem_info, LX_DXQUERYVIDEOMEMORYINFO}, /* 0x0b */ {dxgkio_make_resident, LX_DXMAKERESIDENT}, /* 0x0c */ {dxgkio_map_gpu_va, LX_DXMAPGPUVIRTUALADDRESS}, /* 0x0d */ {dxgkio_escape, LX_DXESCAPE}, /* 0x0e */ {dxgkio_get_device_state, LX_DXGETDEVICESTATE}, /* 0x0f */ {dxgkio_submit_command, LX_DXSUBMITCOMMAND}, /* 0x10 */ {dxgkio_create_sync_object, LX_DXCREATESYNCHRONIZATIONOBJECT}, /* 0x11 */ {dxgkio_signal_sync_object, LX_DXSIGNALSYNCHRONIZATIONOBJECT}, /* 0x12 */ {dxgkio_wait_sync_object, LX_DXWAITFORSYNCHRONIZATIONOBJECT}, /* 0x13 */ {dxgkio_destroy_allocation, LX_DXDESTROYALLOCATION2}, /* 0x14 */ {dxgkio_enum_adapters, LX_DXENUMADAPTERS2}, /* 0x15 */ {dxgkio_close_adapter, LX_DXCLOSEADAPTER}, /* 0x16 */ {dxgkio_change_vidmem_reservation, LX_DXCHANGEVIDEOMEMORYRESERVATION}, /* 0x17 */ {}, /* 0x18 */ {dxgkio_create_hwqueue, LX_DXCREATEHWQUEUE}, /* 0x19 */ {dxgkio_destroy_device, LX_DXDESTROYDEVICE}, /* 0x1a */ {}, /* 0x1b */ {dxgkio_destroy_hwqueue, LX_DXDESTROYHWQUEUE}, /* 0x1c */ {dxgkio_destroy_paging_queue, LX_DXDESTROYPAGINGQUEUE}, /* 0x1d */ {dxgkio_destroy_sync_object, LX_DXDESTROYSYNCHRONIZATIONOBJECT}, /* 0x1e */ {dxgkio_evict, LX_DXEVICT}, /* 0x1f */ {dxgkio_flush_heap_transitions, LX_DXFLUSHHEAPTRANSITIONS}, /* 0x20 */ {dxgkio_free_gpu_va, LX_DXFREEGPUVIRTUALADDRESS}, /* 0x21 */ {dxgkio_get_context_process_scheduling_priority, LX_DXGETCONTEXTINPROCESSSCHEDULINGPRIORITY}, /* 0x22 */ {dxgkio_get_context_scheduling_priority, LX_DXGETCONTEXTSCHEDULINGPRIORITY}, /* 0x23 */ {}, /* 0x24 */ {dxgkio_invalidate_cache, LX_DXINVALIDATECACHE}, /* 0x25 */ {dxgkio_lock2, LX_DXLOCK2}, /* 0x26 */ {dxgkio_mark_device_as_error, LX_DXMARKDEVICEASERROR}, /* 0x27 */ {dxgkio_offer_allocations, LX_DXOFFERALLOCATIONS}, /* 0x28 */ {}, /* 0x29 */ {}, /* 0x2a */ {dxgkio_query_alloc_residency, LX_DXQUERYALLOCATIONRESIDENCY}, /* 0x2b */ {}, /* 0x2c */ {dxgkio_reclaim_allocations, LX_DXRECLAIMALLOCATIONS2}, /* 0x2d */ {}, /* 0x2e */ {dxgkio_set_allocation_priority, LX_DXSETALLOCATIONPRIORITY}, /* 0x2f */ {dxgkio_set_context_process_scheduling_priority, LX_DXSETCONTEXTINPROCESSSCHEDULINGPRIORITY}, /* 0x30 */ {dxgkio_set_context_scheduling_priority, LX_DXSETCONTEXTSCHEDULINGPRIORITY}, /* 0x31 */ {dxgkio_signal_sync_object_cpu, LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU}, /* 0x32 */ {dxgkio_signal_sync_object_gpu, LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU}, /* 0x33 */ {dxgkio_signal_sync_object_gpu2, LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2}, /* 0x34 */ {dxgkio_submit_command_to_hwqueue, LX_DXSUBMITCOMMANDTOHWQUEUE}, /* 0x35 */ {dxgkio_submit_signal_to_hwqueue, LX_DXSUBMITSIGNALSYNCOBJECTSTOHWQUEUE}, /* 0x36 */ {dxgkio_submit_wait_to_hwqueue, LX_DXSUBMITWAITFORSYNCOBJECTSTOHWQUEUE}, /* 0x37 */ {dxgkio_unlock2, LX_DXUNLOCK2}, /* 0x38 */ {dxgkio_update_alloc_property, LX_DXUPDATEALLOCPROPERTY}, /* 0x39 */ {dxgkio_update_gpu_va, LX_DXUPDATEGPUVIRTUALADDRESS}, /* 0x3a */ {dxgkio_wait_sync_object_cpu, LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMCPU}, /* 0x3b */ {dxgkio_wait_sync_object_gpu, LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU}, /* 0x3c */ {dxgkio_get_allocation_priority, LX_DXGETALLOCATIONPRIORITY}, /* 0x3d */ {dxgkio_query_clock_calibration, LX_DXQUERYCLOCKCALIBRATION}, /* 0x3e */ {dxgkio_enum_adapters3, LX_DXENUMADAPTERS3}, /* 0x3f */ {dxgkio_share_objects, LX_DXSHAREOBJECTS}, /* 0x40 */ {dxgkio_open_sync_object_nt, LX_DXOPENSYNCOBJECTFROMNTHANDLE2}, /* 0x41 */ {dxgkio_query_resource_info_nt, LX_DXQUERYRESOURCEINFOFROMNTHANDLE}, /* 0x42 */ {dxgkio_open_resource_nt, LX_DXOPENRESOURCEFROMNTHANDLE}, /* 0x43 */ {dxgkio_query_statistics, LX_DXQUERYSTATISTICS}, /* 0x44 */ {dxgkio_share_object_with_host, LX_DXSHAREOBJECTWITHHOST}, /* 0x45 */ {dxgkio_create_sync_file, LX_DXCREATESYNCFILE}, /* 0x46 */ {dxgkio_wait_sync_file, LX_DXWAITSYNCFILE}, /* 0x47 */ {dxgkio_open_syncobj_from_syncfile, LX_DXOPENSYNCOBJECTFROMSYNCFILE}, /* 0x48 */ {dxgkio_enum_processes, LX_DXENUMPROCESSES}, /* 0x49 */ {dxgkio_is_feature_enabled, LX_ISFEATUREENABLED}, }; /* * IOCTL processing * The driver IOCTLs return * - 0 in case of success * - positive values, which are Windows NTSTATUS (for example, STATUS_PENDING). * Positive values are success codes. * - Linux negative error codes */ static int dxgk_ioctl(struct file *f, unsigned int p1, unsigned long p2) { int code = _IOC_NR(p1); int status; struct dxgprocess *process; if (code < 1 || code >= ARRAY_SIZE(ioctls)) { DXG_ERR("bad ioctl %x %x %x %x", code, _IOC_TYPE(p1), _IOC_SIZE(p1), _IOC_DIR(p1)); return -ENOTTY; } if (ioctls[code].ioctl_callback == NULL) { DXG_ERR("ioctl callback is NULL %x", code); return -ENOTTY; } if (ioctls[code].ioctl != p1) { DXG_ERR("ioctl mismatch. Code: %x User: %x Kernel: %x", code, p1, ioctls[code].ioctl); return -ENOTTY; } process = (struct dxgprocess *)f->private_data; if (process->tgid != current->tgid) { DXG_ERR("Call from a wrong process: %d %d", process->tgid, current->tgid); return -ENOTTY; } status = ioctls[code].ioctl_callback(process, (void *__user)p2); return status; } long dxgk_compat_ioctl(struct file *f, unsigned int p1, unsigned long p2) { DXG_TRACE("compat ioctl %x", p1); return dxgk_ioctl(f, p1, p2); } long dxgk_unlocked_ioctl(struct file *f, unsigned int p1, unsigned long p2) { DXG_TRACE("unlocked ioctl %x Code:%d", p1, _IOC_NR(p1)); return dxgk_ioctl(f, p1, p2); } #ifdef DEBUG void dxgk_validate_ioctls(void) { int i; for (i = 0; i < ARRAY_SIZE(ioctls); i++) { if (ioctls[i].ioctl && _IOC_NR(ioctls[i].ioctl) != i) { DXG_ERR("Invalid ioctl"); DXGKRNL_ASSERT(0); } } } #endif