From nobody Mon Apr 6 10:42:02 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D99B63FFAA8 for ; Thu, 19 Mar 2026 20:26:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773951968; cv=none; b=fvyqCP+7MRfzAfS3i06dnKat1mRFK6rLE15+6a9JEB85g59gsdAYJufSTkRebcvD1oq00mamGybpaHOFHvzzVz0AyHE2chsgxY49OfVxGxJPQQVrx4KpdlzpIPuC2h5cgqp9ih+JjjpX2vOiHEdLQDaWn+yWV8GqXINohd3DHUI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773951968; c=relaxed/simple; bh=VR58u3V3i58J8N6ZNFVtkIjqDKPXetLd0h+s5znAmcE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c0qLxgZleyPGenchVqY1NUjE8B6lNRaByiFGS0YLleXx2gHBCVSniV11FWt7QSHqiLLr772/RCLIGf6rkoNUyUtaVFdHeTKDcOZa0r8mA8XctDbbjolySQVVSKYIrE6YNdW9VJi3lh5vfR926HXm+zxOUkHNgQIEU910JBhkQ78= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=V2fJl+nH; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="V2fJl+nH" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-48540d21f7dso14993505e9.0 for ; Thu, 19 Mar 2026 13:26:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773951963; x=1774556763; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DDHDicT0bUGDGWHfwGKo943zbmk1k4eN+/Sr+skSI6Y=; b=V2fJl+nHWAeNcjSxnuPqlE3ublca6IzhVBTM7dMRwfv7/BlY2AloK/bgJZe4xl3Zag lPYe8Xit+jgAZtbTeOa+RXph1/JCmtBambJtzIRHnpukdTe7+ADZX5dainwMy2+qHN/5 Itr2IvWLxV5vAWdWMhBsUlSed7jlZisSvewG3tPIkfJu8+Gxuwchg/jsNHC0MicmVXRv QyD4DTmJmyisJWeKDFj80HwtGNWI103kkBgkuJyw98RMuUOwTsj1TwAkJhTZJWgtrxFR xIIjGCte1kefmwrAJu5BU1yBCUJxfUBij9U0pZItRbCRUOhzXdvR81zz+PRCJkA/I0/Q tpIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773951963; x=1774556763; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=DDHDicT0bUGDGWHfwGKo943zbmk1k4eN+/Sr+skSI6Y=; b=dEP0SrQLCGavX22f1E1nNsctsurGqyXsejkOvZCKAj/gLvzq/EfW0PCNtmlAG8hxRt E0nPkErzPEvcZMEIduuIfEmCiJJ/Jxpq0PU1CidTjE8ix2/ZntKLmOfNPNUfMsta/Bff oBVefVQCjvul9APVqaUi+IjfegWS4eYeWRWqzeOUEX/RYIjtz/MQs0TFzZ5CiAkNKCMd bdV6eY4tT/SuZLz8W8n5oKsHaJQFARQETdrfwG6o9Sf2aenNKmuQqYM/hKTW55jBorgN IBc3JuCvO4TllbH6SPKL7TszmWO+FtQhQ62sl6AQw22NGTk+xIxAW0lVG40dVmKE8qgA aCiQ== X-Gm-Message-State: AOJu0YyaL/gUXU4vt+WIOasfIdJfzwrsBq3tF8rOoZUTt1MveRZ7BBcg FrhnVwtXMalAgF+XZ5JL31EIiJ90YXA7lxRNbfI3XGVo47KR7GtoJkjJ X-Gm-Gg: ATEYQzyIBxEu1/2a7W+mH/BGKfKI7EB7EueKZOY6V9qCkhLGKMLxrdN//2sVoAYtjg4 Mp3GHBUIu3AzDyh0iDHstCHh4933lodPugg58Knqw/UvhNqSilgVyK4phPNV+oJ15/ZmJsJdL8O mbJBEegI7tJRwgMT2clFMMaX0uPbHpb5ahaxXMDsXyi3BaasVjl4oGXuboHah+IsD/49rU5FQnG Wy9tXk3wAaqctwFpvaKN1rmFgirWIkfV6w+u/0G632o+U5sj+TdWxtPpkNG2Gi+yBYxd9blEQHz /1zEQe3m8G4gcnK1AIXopiTF6AMzDYAUeiI1sKF48Iuxh3X3YEoFBlX+qhv5ji5+jM93Lx1BRO8 rGxJ7iILAqZPQXtkwvjTqfXjb8vwOcIAyMlhER9NgvfGXH0H/JqC3STD6Pue0gzga8z1FFj/eXU 5PP1YNrqElA3ufItu5MwTMX5XhmeJv7aKbIQK37xJmUxJBCW3W X-Received: by 2002:a05:600c:34d2:b0:485:1878:7b8c with SMTP id 5b1f17b1804b1-486ff029163mr6284935e9.18.1773951962928; Thu, 19 Mar 2026 13:26:02 -0700 (PDT) Received: from LQ5W56KC4T ([2001:8a0:672f:7800:e0e1:55cd:f0b:b1e5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43b644ae16fsm1347544f8f.8.2026.03.19.13.26.02 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 19 Mar 2026 13:26:02 -0700 (PDT) From: Eric Curtin X-Google-Original-From: Eric Curtin To: linux-hyperv@vger.kernel.org Cc: linux-kernel@vger.kernel.org, iourit@linux.microsoft.com, wei.liu@kernel.org, decui@microsoft.com, haiyangz@microsoft.com Subject: [PATCH 44/55] drivers: hv: dxgkrnl: Implement known escapes Date: Thu, 19 Mar 2026 20:24:58 +0000 Message-ID: <20260319202509.63802-45-eric.curtin@docker.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260319202509.63802-1-eric.curtin@docker.com> References: <20260319202509.63802-1-eric.curtin@docker.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Iouri Tarassov Implement an escape to build test command buffer. Implement other known escapes. Signed-off-by: Iouri Tarassov [kms: forward port to 6.6 from 6.1. No code changes made.] Signed-off-by: Kelsey Steele --- drivers/hv/dxgkrnl/dxgkrnl.h | 3 +- drivers/hv/dxgkrnl/dxgvmbus.c | 40 +++++--- drivers/hv/dxgkrnl/ioctl.c | 170 +++++++++++++++++++++++++++++----- include/uapi/misc/d3dkmthk.h | 31 +++++++ 4 files changed, 205 insertions(+), 39 deletions(-) diff --git a/drivers/hv/dxgkrnl/dxgkrnl.h b/drivers/hv/dxgkrnl/dxgkrnl.h index ebf81cffa289..9599ec8e0f1d 100644 --- a/drivers/hv/dxgkrnl/dxgkrnl.h +++ b/drivers/hv/dxgkrnl/dxgkrnl.h @@ -953,7 +953,8 @@ int dxgvmb_send_query_alloc_residency(struct dxgprocess= *process, *args); int dxgvmb_send_escape(struct dxgprocess *process, struct dxgadapter *adapter, - struct d3dkmt_escape *args); + struct d3dkmt_escape *args, + bool user_mode); int dxgvmb_send_query_vidmem_info(struct dxgprocess *process, struct dxgadapter *adapter, struct d3dkmt_queryvideomemoryinfo *args, diff --git a/drivers/hv/dxgkrnl/dxgvmbus.c b/drivers/hv/dxgkrnl/dxgvmbus.c index 2436e1a7bc73..de28c6162a70 100644 --- a/drivers/hv/dxgkrnl/dxgvmbus.c +++ b/drivers/hv/dxgkrnl/dxgvmbus.c @@ -2174,7 +2174,8 @@ int dxgvmb_send_query_alloc_residency(struct dxgproce= ss *process, =20 int dxgvmb_send_escape(struct dxgprocess *process, struct dxgadapter *adapter, - struct d3dkmt_escape *args) + struct d3dkmt_escape *args, + bool user_mode) { int ret; struct dxgkvmb_command_escape *command =3D NULL; @@ -2203,13 +2204,18 @@ int dxgvmb_send_escape(struct dxgprocess *process, command->priv_drv_data_size =3D args->priv_drv_data_size; command->context =3D args->context; if (args->priv_drv_data_size) { - ret =3D copy_from_user(command->priv_drv_data, - args->priv_drv_data, - args->priv_drv_data_size); - if (ret) { - DXG_ERR("failed to copy priv data"); - ret =3D -EFAULT; - goto cleanup; + if (user_mode) { + ret =3D copy_from_user(command->priv_drv_data, + args->priv_drv_data, + args->priv_drv_data_size); + if (ret) { + DXG_ERR("failed to copy priv data"); + ret =3D -EFAULT; + goto cleanup; + } + } else { + memcpy(command->priv_drv_data, args->priv_drv_data, + args->priv_drv_data_size); } } =20 @@ -2220,12 +2226,18 @@ int dxgvmb_send_escape(struct dxgprocess *process, goto cleanup; =20 if (args->priv_drv_data_size) { - ret =3D copy_to_user(args->priv_drv_data, - command->priv_drv_data, - args->priv_drv_data_size); - if (ret) { - DXG_ERR("failed to copy priv data"); - ret =3D -EINVAL; + if (user_mode) { + ret =3D copy_to_user(args->priv_drv_data, + command->priv_drv_data, + args->priv_drv_data_size); + if (ret) { + DXG_ERR("failed to copy priv data"); + ret =3D -EINVAL; + } + } else { + memcpy(args->priv_drv_data, + command->priv_drv_data, + args->priv_drv_data_size); } } =20 diff --git a/drivers/hv/dxgkrnl/ioctl.c b/drivers/hv/dxgkrnl/ioctl.c index 5ff4b27af19d..f8ca79d098f3 100644 --- a/drivers/hv/dxgkrnl/ioctl.c +++ b/drivers/hv/dxgkrnl/ioctl.c @@ -4257,10 +4257,8 @@ dxgkio_change_vidmem_reservation(struct dxgprocess *= process, void *__user inargs } =20 ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) { - adapter =3D NULL; + if (ret < 0) goto cleanup; - } adapter_locked =3D true; args.adapter.v =3D 0; ret =3D dxgvmb_send_change_vidmem_reservation(process, adapter, @@ -4299,10 +4297,8 @@ dxgkio_query_clock_calibration(struct dxgprocess *pr= ocess, void *__user inargs) } =20 ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) { - adapter =3D NULL; + if (ret < 0) goto cleanup; - } adapter_locked =3D true; =20 args.adapter =3D adapter->host_handle; @@ -4349,10 +4345,8 @@ dxgkio_flush_heap_transitions(struct dxgprocess *pro= cess, void *__user inargs) } =20 ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) { - adapter =3D NULL; + if (ret < 0) goto cleanup; - } adapter_locked =3D true; =20 args.adapter =3D adapter->host_handle; @@ -4417,6 +4411,134 @@ dxgkio_invalidate_cache(struct dxgprocess *process,= void *__user inargs) return ret; } =20 +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 =3D *args; + u32 buf_size; + struct d3dddi_buildtestcommandbuffer *buf =3D NULL; + struct d3dddi_buildtestcommandbuffer *__user ucmd; + + ucmd =3D args->priv_drv_data; + if (args->priv_drv_data_size < + sizeof(struct d3dddi_buildtestcommandbuffer)) { + DXG_ERR("Invalid private data size"); + return -EINVAL; + } + ret =3D 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 =3D sizeof(struct d3dddi_buildtestcommandbuffer) + + cmd.dma_buffer_size + + cmd.dma_buffer_priv_data_size; + buf =3D vzalloc(buf_size); + if (buf =3D=3D NULL) { + ret =3D -ENOMEM; + goto cleanup; + } + *buf =3D cmd; + buf->dma_buffer =3D NULL; + buf->dma_buffer_priv_data =3D NULL; + + /* Replace private data in the escape arguments and call the host */ + newargs.priv_drv_data =3D buf; + newargs.priv_drv_data_size =3D buf_size; + ret =3D dxgvmb_send_escape(process, adapter, &newargs, false); + if (ret) { + DXG_ERR("Host failed escape"); + goto cleanup; + } + + ret =3D copy_to_user(&ucmd->dma_buffer_size, &buf->dma_buffer_size, + sizeof(u32)); + if (ret) { + DXG_ERR("Failed to dma size to user"); + ret =3D -EFAULT; + goto cleanup; + } + ret =3D 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 =3D -EFAULT; + goto cleanup; + } + ret =3D 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 =3D -EFAULT; + goto cleanup; + } + if (buf->dma_buffer_priv_data_size) { + ret =3D 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 =3D -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 =3D 0; + + if (args->priv_drv_data_size < sizeof(enum d3dddi_knownescapetype)) + { + DXG_ERR("Invalid private data size"); + return -EINVAL; + } + ret =3D 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 =3D build_test_command_buffer(process, adapter, args); + break; + default: + ret =3D dxgvmb_send_escape(process, adapter, args, true); + break; + } + return ret; +} + static int dxgkio_escape(struct dxgprocess *process, void *__user inargs) { @@ -4438,14 +4560,17 @@ dxgkio_escape(struct dxgprocess *process, void *__u= ser inargs) } =20 ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) { - adapter =3D NULL; + if (ret < 0) goto cleanup; - } adapter_locked =3D true; =20 args.adapter =3D adapter->host_handle; - ret =3D dxgvmb_send_escape(process, adapter, &args); + + if (args.type =3D=3D _D3DKMT_ESCAPE_DRIVERPRIVATE && + args.flags.driver_known_escape) + ret =3D driver_known_escape(process, adapter, &args); + else + ret =3D dxgvmb_send_escape(process, adapter, &args, true); =20 cleanup: =20 @@ -4485,10 +4610,8 @@ dxgkio_query_vidmem_info(struct dxgprocess *process,= void *__user inargs) } =20 ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) { - adapter =3D NULL; + if (ret < 0) goto cleanup; - } adapter_locked =3D true; =20 args.adapter =3D adapter->host_handle; @@ -5323,9 +5446,9 @@ dxgkio_is_feature_enabled(struct dxgprocess *process,= void *__user inargs) { struct d3dkmt_isfeatureenabled args; struct dxgadapter *adapter =3D NULL; - struct dxgglobal *dxgglobal =3D dxggbl(); struct d3dkmt_isfeatureenabled *__user uargs =3D inargs; int ret; + bool adapter_locked =3D false; =20 ret =3D copy_from_user(&args, inargs, sizeof(args)); if (ret) { @@ -5340,11 +5463,10 @@ dxgkio_is_feature_enabled(struct dxgprocess *proces= s, void *__user inargs) goto cleanup; } =20 - if (adapter) { - ret =3D dxgadapter_acquire_lock_shared(adapter); - if (ret < 0) - goto cleanup; - } + ret =3D dxgadapter_acquire_lock_shared(adapter); + if (ret < 0) + goto cleanup; + adapter_locked =3D true; =20 ret =3D dxgvmb_send_is_feature_enabled(adapter, &args); if (ret) @@ -5354,10 +5476,10 @@ dxgkio_is_feature_enabled(struct dxgprocess *proces= s, void *__user inargs) =20 cleanup: =20 - if (adapter) { + if (adapter_locked) dxgadapter_release_lock_shared(adapter); + if (adapter) kref_put(&adapter->adapter_kref, dxgadapter_release); - } =20 DXG_TRACE_IOCTL_END(ret); return ret; diff --git a/include/uapi/misc/d3dkmthk.h b/include/uapi/misc/d3dkmthk.h index 5b345ddaf66e..db40e8ff40b0 100644 --- a/include/uapi/misc/d3dkmthk.h +++ b/include/uapi/misc/d3dkmthk.h @@ -237,6 +237,37 @@ struct d3dddi_destroypagingqueue { struct d3dkmthandle paging_queue; }; =20 +enum d3dddi_knownescapetype { + _D3DDDI_DRIVERESCAPETYPE_TRANSLATEALLOCATIONHANDLE =3D 0, + _D3DDDI_DRIVERESCAPETYPE_TRANSLATERESOURCEHANDLE =3D 1, + _D3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE =3D 2, + _D3DDDI_DRIVERESCAPETYPE_BUILDTESTCOMMANDBUFFER =3D 3, +}; + +struct d3dddi_translate_allocation_handle { + enum d3dddi_knownescapetype escape_type; + struct d3dkmthandle allocation; +}; + +struct d3dddi_testcommand { + char buffer[72]; +}; + +#define D3DDDI_MAXTESTBUFFERSIZE 4096 +#define D3DDDI_MAXTESTBUFFERPRIVATEDRIVERDATASIZE 1024 + +struct d3dddi_buildtestcommandbuffer { + enum d3dddi_knownescapetype escape_type; + struct d3dkmthandle device; + struct d3dkmthandle context; + __u32 flags; + struct d3dddi_testcommand command; + void *dma_buffer; + void *dma_buffer_priv_data; + __u32 dma_buffer_size; + __u32 dma_buffer_priv_data_size; +}; + enum d3dkmt_escapetype { _D3DKMT_ESCAPE_DRIVERPRIVATE =3D 0, _D3DKMT_ESCAPE_VIDMM =3D 1,