mirror of
https://github.com/armbian/build.git
synced 2025-09-17 19:51:31 +02:00
> tl,dr: add 4 small-ish UEFI-like kernels, with Microsoft patches & fixes, for use with Microsoft WSL2 on x86/arm64 and 6.1.y/6.6.y - the boards are UEFI derivatives, using a common `microsoft` vendor include to modify - `KERNELPATCHDIR`/`LINUXFAMILY` (for now, we don't want those patches in regular UEFI builds / .debs) - disable `EXTRAWIFI` (kernel is for a VM, will never have wifi so doesn't need any drivers) - `LINUXCONFIG`, so we can use Microsoft's own monolithic kernel, required for WSL2 (their initrd is a mistery) - really, what we're mostly interested right now are the kernels (in the future we might have an "Armbian" WSL2 app in the Microsoft Store) - `current` `6.1.y`: - rebased from https://github.com/microsoft/WSL2-Linux-Kernel/tree/linux-msft-wsl-6.1.y onto real 6.1.y - using Microsoft's `.config` exactly (monolithic, there are no `=m`'s) - `edge` `6.6.y`: - also from https://github.com/microsoft/WSL2-Linux-Kernel/tree/linux-msft-wsl-6.1.y but rebased onto 6.6.y - using updated Microsoft's `.config` (monolithic, there are no `=m`'s) - dropped 2 of 6.1.y's patches that were actually upstreamed in the meantime: - `mm-page_reporting-Add-checks-for-page_reporting_order-param` - mainlined in https://lore.kernel.org/all/1664517699-1085-2-git-send-email-shradhagupta@linux.microsoft.com/ - `hv_balloon-Add-support-for-configurable-order-free-page-reporting` - mainlined in https://lore.kernel.org/all/1664517699-1085-3-git-send-email-shradhagupta@linux.microsoft.com/ - drop the `arm64: hyperv: Enable Hyper-V synthetic clocks/timers` patch, since it causes asm breakage on 6.6.y - a shame, but I tried and can't fix it myself - @kelleymh ? - add my own patch to fix: - `1709-drivers-hv-dxgkrnl-restore-uuid_le_cmp-removed-from-upstream-in-f5b3c341a.patch` due to https://lore.kernel.org/all/20230202145412.87569-1-andriy.shevchenko@linux.intel.com/ landing in 6.6 - `1710-drivers-hv-dxgkrnl-adapt-dxg_remove_vmbus-to-96ec29396-s-reality-void-return.patch` to adapt to https://lore.kernel.org/all/TYCP286MB2323A93C55526E4DF239D3ACCAFA9@TYCP286MB2323.JPNP286.PROD.OUTLOOK.COM/
967 lines
24 KiB
Diff
967 lines
24 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Iouri Tarassov <iourit@linux.microsoft.com>
|
|
Date: Wed, 24 Mar 2021 11:10:28 -0700
|
|
Subject: drivers: hv: dxgkrnl: Driver initialization and loading
|
|
|
|
- Create skeleton and add basic functionality for the Hyper-V
|
|
compute device driver (dxgkrnl).
|
|
|
|
- Register for PCI and VMBus driver notifications and handle
|
|
initialization of VMBus channels.
|
|
|
|
- Connect the dxgkrnl module to the drivers/hv/ Makefile and Kconfig
|
|
|
|
- Create a MAINTAINERS entry
|
|
|
|
A VMBus channel is a communication interface between the Hyper-V guest
|
|
and the host. The are two type of VMBus channels, used in the driver:
|
|
- the global channel
|
|
- per virtual compute device channel
|
|
|
|
A PCI device is created for each virtual compute device, projected
|
|
by the host. The device vendor is PCI_VENDOR_ID_MICROSOFT and device
|
|
id is PCI_DEVICE_ID_VIRTUAL_RENDER. dxg_pci_probe_device handles
|
|
arrival of such devices. The PCI config space of the virtual compute
|
|
device has luid of the corresponding virtual compute device VM
|
|
bus channel. This is how the compute device adapter objects are
|
|
linked to VMBus channels.
|
|
|
|
VMBus interface version is exchanged by reading/writing the PCI config
|
|
space of the virtual compute device.
|
|
|
|
The IO space is used to handle CPU accessible compute device
|
|
allocations. Hyper-V allocates IO space for the global VMBus channel.
|
|
|
|
Signed-off-by: Iouri Tarassov <iourit@linux.microsoft.com>
|
|
[kms: Forward port to v6.1]
|
|
Signed-off-by: Kelsey Steele <kelseysteele@microsoft.com>
|
|
---
|
|
MAINTAINERS | 7 +
|
|
drivers/hv/Kconfig | 2 +
|
|
drivers/hv/Makefile | 1 +
|
|
drivers/hv/dxgkrnl/Kconfig | 26 +
|
|
drivers/hv/dxgkrnl/Makefile | 5 +
|
|
drivers/hv/dxgkrnl/dxgkrnl.h | 155 +++
|
|
drivers/hv/dxgkrnl/dxgmodule.c | 506 ++++++++++
|
|
drivers/hv/dxgkrnl/dxgvmbus.c | 92 ++
|
|
drivers/hv/dxgkrnl/dxgvmbus.h | 19 +
|
|
include/uapi/misc/d3dkmthk.h | 27 +
|
|
10 files changed, 840 insertions(+)
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index 07a9c274c0e2..e79dae6368a1 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -9551,6 +9551,13 @@ F: Documentation/devicetree/bindings/mtd/ti,am654-hbmc.yaml
|
|
F: drivers/mtd/hyperbus/
|
|
F: include/linux/mtd/hyperbus.h
|
|
|
|
+Hyper-V vGPU DRIVER
|
|
+M: Iouri Tarassov <iourit@microsoft.com>
|
|
+L: linux-hyperv@vger.kernel.org
|
|
+S: Supported
|
|
+F: drivers/hv/dxgkrnl/
|
|
+F: include/uapi/misc/d3dkmthk.h
|
|
+
|
|
HYPERVISOR VIRTUAL CONSOLE DRIVER
|
|
L: linuxppc-dev@lists.ozlabs.org
|
|
S: Odd Fixes
|
|
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
|
|
index 6802f981ba8c..5a7aa3e567ab 100644
|
|
--- a/drivers/hv/Kconfig
|
|
+++ b/drivers/hv/Kconfig
|
|
@@ -30,4 +30,6 @@ config HYPERV_BALLOON
|
|
help
|
|
Select this option to enable Hyper-V Balloon driver.
|
|
|
|
+source "drivers/hv/dxgkrnl/Kconfig"
|
|
+
|
|
endmenu
|
|
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
|
|
index d76df5c8c2a9..aa1cbdb5d0d2 100644
|
|
--- a/drivers/hv/Makefile
|
|
+++ b/drivers/hv/Makefile
|
|
@@ -2,6 +2,7 @@
|
|
obj-$(CONFIG_HYPERV) += hv_vmbus.o
|
|
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
|
|
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
|
|
+obj-$(CONFIG_DXGKRNL) += dxgkrnl/
|
|
|
|
CFLAGS_hv_trace.o = -I$(src)
|
|
CFLAGS_hv_balloon.o = -I$(src)
|
|
diff --git a/drivers/hv/dxgkrnl/Kconfig b/drivers/hv/dxgkrnl/Kconfig
|
|
new file mode 100644
|
|
index 000000000000..bcd92bbff939
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/Kconfig
|
|
@@ -0,0 +1,26 @@
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
+# Configuration for the hyper-v virtual compute driver (dxgkrnl)
|
|
+#
|
|
+
|
|
+config DXGKRNL
|
|
+ tristate "Microsoft Paravirtualized GPU support"
|
|
+ depends on HYPERV
|
|
+ depends on 64BIT || COMPILE_TEST
|
|
+ help
|
|
+ This driver supports paravirtualized virtual compute devices, exposed
|
|
+ by Microsoft Hyper-V when Linux is running inside of a virtual machine
|
|
+ hosted by Windows. The virtual machines needs to be configured to use
|
|
+ host compute adapters. The driver name is dxgkrnl.
|
|
+
|
|
+ An example of such virtual machine is a Windows Subsystem for
|
|
+ Linux container. When such container is instantiated, the Windows host
|
|
+ assigns compatible host GPU adapters to the container. The corresponding
|
|
+ virtual GPU devices appear on the PCI bus in the container. These
|
|
+ devices are enumerated and accessed by this driver.
|
|
+
|
|
+ Communications with the driver are done by using the Microsoft libdxcore
|
|
+ library, which translates the D3DKMT interface
|
|
+ <https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/>
|
|
+ to the driver IOCTLs. The virtual GPU devices are paravirtualized,
|
|
+ which means that access to the hardware is done in the host. The driver
|
|
+ communicates with the host using Hyper-V VM bus communication channels.
|
|
diff --git a/drivers/hv/dxgkrnl/Makefile b/drivers/hv/dxgkrnl/Makefile
|
|
new file mode 100644
|
|
index 000000000000..76349064b60a
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/Makefile
|
|
@@ -0,0 +1,5 @@
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
+# Makefile for the hyper-v compute device driver (dxgkrnl).
|
|
+
|
|
+obj-$(CONFIG_DXGKRNL) += dxgkrnl.o
|
|
+dxgkrnl-y := dxgmodule.o dxgvmbus.o
|
|
diff --git a/drivers/hv/dxgkrnl/dxgkrnl.h b/drivers/hv/dxgkrnl/dxgkrnl.h
|
|
new file mode 100644
|
|
index 000000000000..f7900840d1ed
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/dxgkrnl.h
|
|
@@ -0,0 +1,155 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2022, Microsoft Corporation.
|
|
+ *
|
|
+ * Author:
|
|
+ * Iouri Tarassov <iourit@linux.microsoft.com>
|
|
+ *
|
|
+ * Dxgkrnl Graphics Driver
|
|
+ * Headers for internal objects
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _DXGKRNL_H
|
|
+#define _DXGKRNL_H
|
|
+
|
|
+#include <linux/uuid.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/semaphore.h>
|
|
+#include <linux/refcount.h>
|
|
+#include <linux/rwsem.h>
|
|
+#include <linux/atomic.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/gfp.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/hyperv.h>
|
|
+#include <uapi/misc/d3dkmthk.h>
|
|
+#include <linux/version.h>
|
|
+
|
|
+struct dxgadapter;
|
|
+
|
|
+/*
|
|
+ * Driver private data.
|
|
+ * A single /dev/dxg device is created per virtual machine.
|
|
+ */
|
|
+struct dxgdriver{
|
|
+ struct dxgglobal *dxgglobal;
|
|
+ struct device *dxgdev;
|
|
+ struct pci_driver pci_drv;
|
|
+ struct hv_driver vmbus_drv;
|
|
+};
|
|
+extern struct dxgdriver dxgdrv;
|
|
+
|
|
+#define DXGDEV dxgdrv.dxgdev
|
|
+
|
|
+struct dxgvmbuschannel {
|
|
+ struct vmbus_channel *channel;
|
|
+ struct hv_device *hdev;
|
|
+ spinlock_t packet_list_mutex;
|
|
+ struct list_head packet_list_head;
|
|
+ struct kmem_cache *packet_cache;
|
|
+ atomic64_t packet_request_id;
|
|
+};
|
|
+
|
|
+int dxgvmbuschannel_init(struct dxgvmbuschannel *ch, struct hv_device *hdev);
|
|
+void dxgvmbuschannel_destroy(struct dxgvmbuschannel *ch);
|
|
+void dxgvmbuschannel_receive(void *ctx);
|
|
+
|
|
+/*
|
|
+ * The structure defines an offered vGPU vm bus channel.
|
|
+ */
|
|
+struct dxgvgpuchannel {
|
|
+ struct list_head vgpu_ch_list_entry;
|
|
+ struct winluid adapter_luid;
|
|
+ struct hv_device *hdev;
|
|
+};
|
|
+
|
|
+struct dxgglobal {
|
|
+ struct dxgdriver *drvdata;
|
|
+ struct dxgvmbuschannel channel;
|
|
+ struct hv_device *hdev;
|
|
+ u32 num_adapters;
|
|
+ u32 vmbus_ver; /* Interface version */
|
|
+ struct resource *mem;
|
|
+ u64 mmiospace_base;
|
|
+ u64 mmiospace_size;
|
|
+ struct miscdevice dxgdevice;
|
|
+ struct mutex device_mutex;
|
|
+
|
|
+ /*
|
|
+ * List of the vGPU VM bus channels (dxgvgpuchannel)
|
|
+ * Protected by device_mutex
|
|
+ */
|
|
+ struct list_head vgpu_ch_list_head;
|
|
+
|
|
+ /* protects acces to the global VM bus channel */
|
|
+ struct rw_semaphore channel_lock;
|
|
+
|
|
+ bool global_channel_initialized;
|
|
+ bool async_msg_enabled;
|
|
+ bool misc_registered;
|
|
+ bool pci_registered;
|
|
+ bool vmbus_registered;
|
|
+};
|
|
+
|
|
+static inline struct dxgglobal *dxggbl(void)
|
|
+{
|
|
+ return dxgdrv.dxgglobal;
|
|
+}
|
|
+
|
|
+struct dxgprocess {
|
|
+ /* Placeholder */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * The convention is that VNBus instance id is a GUID, but the host sets
|
|
+ * the lower part of the value to the host adapter LUID. The function
|
|
+ * provides the necessary conversion.
|
|
+ */
|
|
+static inline void guid_to_luid(guid_t *guid, struct winluid *luid)
|
|
+{
|
|
+ *luid = *(struct winluid *)&guid->b[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * VM bus interface
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * The interface version is used to ensure that the host and the guest use the
|
|
+ * same VM bus protocol. It needs to be incremented every time the VM bus
|
|
+ * interface changes. DXGK_VMBUS_LAST_COMPATIBLE_INTERFACE_VERSION is
|
|
+ * incremented each time the earlier versions of the interface are no longer
|
|
+ * compatible with the current version.
|
|
+ */
|
|
+#define DXGK_VMBUS_INTERFACE_VERSION_OLD 27
|
|
+#define DXGK_VMBUS_INTERFACE_VERSION 40
|
|
+#define DXGK_VMBUS_LAST_COMPATIBLE_INTERFACE_VERSION 16
|
|
+
|
|
+#ifdef DEBUG
|
|
+
|
|
+void dxgk_validate_ioctls(void);
|
|
+
|
|
+#define DXG_TRACE(fmt, ...) do { \
|
|
+ trace_printk(dev_fmt(fmt) "\n", ##__VA_ARGS__); \
|
|
+} while (0)
|
|
+
|
|
+#define DXG_ERR(fmt, ...) do { \
|
|
+ dev_err(DXGDEV, fmt, ##__VA_ARGS__); \
|
|
+ trace_printk("*** dxgkerror *** " dev_fmt(fmt) "\n", ##__VA_ARGS__); \
|
|
+} while (0)
|
|
+
|
|
+#else
|
|
+
|
|
+#define DXG_TRACE(...)
|
|
+#define DXG_ERR(fmt, ...) do { \
|
|
+ dev_err(DXGDEV, fmt, ##__VA_ARGS__); \
|
|
+} while (0)
|
|
+
|
|
+#endif /* DEBUG */
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/hv/dxgkrnl/dxgmodule.c b/drivers/hv/dxgkrnl/dxgmodule.c
|
|
new file mode 100644
|
|
index 000000000000..de02edc4d023
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/dxgmodule.c
|
|
@@ -0,0 +1,506 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2022, Microsoft Corporation.
|
|
+ *
|
|
+ * Author:
|
|
+ * Iouri Tarassov <iourit@linux.microsoft.com>
|
|
+ *
|
|
+ * Dxgkrnl Graphics Driver
|
|
+ * Interface with Linux kernel, PCI driver and the VM bus driver
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/eventfd.h>
|
|
+#include <linux/hyperv.h>
|
|
+#include <linux/pci.h>
|
|
+#include "dxgkrnl.h"
|
|
+
|
|
+#define PCI_VENDOR_ID_MICROSOFT 0x1414
|
|
+#define PCI_DEVICE_ID_VIRTUAL_RENDER 0x008E
|
|
+
|
|
+#undef pr_fmt
|
|
+#define pr_fmt(fmt) "dxgk: " fmt
|
|
+
|
|
+/*
|
|
+ * Interface from dxgglobal
|
|
+ */
|
|
+
|
|
+struct vmbus_channel *dxgglobal_get_vmbus(void)
|
|
+{
|
|
+ return dxggbl()->channel.channel;
|
|
+}
|
|
+
|
|
+struct dxgvmbuschannel *dxgglobal_get_dxgvmbuschannel(void)
|
|
+{
|
|
+ return &dxggbl()->channel;
|
|
+}
|
|
+
|
|
+int dxgglobal_acquire_channel_lock(void)
|
|
+{
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ down_read(&dxgglobal->channel_lock);
|
|
+ if (dxgglobal->channel.channel == NULL) {
|
|
+ DXG_ERR("Failed to acquire global channel lock");
|
|
+ return -ENODEV;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+void dxgglobal_release_channel_lock(void)
|
|
+{
|
|
+ up_read(&dxggbl()->channel_lock);
|
|
+}
|
|
+
|
|
+const struct file_operations dxgk_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Interface with the PCI driver
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Part of the PCI config space of the compute device is used for
|
|
+ * configuration data. Reading/writing of the PCI config space is forwarded
|
|
+ * to the host.
|
|
+ *
|
|
+ * Below are offsets in the PCI config spaces for various configuration values.
|
|
+ */
|
|
+
|
|
+/* Compute device VM bus channel instance ID */
|
|
+#define DXGK_VMBUS_CHANNEL_ID_OFFSET 192
|
|
+
|
|
+/* DXGK_VMBUS_INTERFACE_VERSION (u32) */
|
|
+#define DXGK_VMBUS_VERSION_OFFSET (DXGK_VMBUS_CHANNEL_ID_OFFSET + \
|
|
+ sizeof(guid_t))
|
|
+
|
|
+/* Luid of the virtual GPU on the host (struct winluid) */
|
|
+#define DXGK_VMBUS_VGPU_LUID_OFFSET (DXGK_VMBUS_VERSION_OFFSET + \
|
|
+ sizeof(u32))
|
|
+
|
|
+/* The guest writes its capabilities to this address */
|
|
+#define DXGK_VMBUS_GUESTCAPS_OFFSET (DXGK_VMBUS_VERSION_OFFSET + \
|
|
+ sizeof(u32))
|
|
+
|
|
+/* Capabilities of the guest driver, reported to the host */
|
|
+struct dxgk_vmbus_guestcaps {
|
|
+ union {
|
|
+ struct {
|
|
+ u32 wsl2 : 1;
|
|
+ u32 reserved : 31;
|
|
+ };
|
|
+ u32 guest_caps;
|
|
+ };
|
|
+};
|
|
+
|
|
+/*
|
|
+ * A helper function to read PCI config space.
|
|
+ */
|
|
+static int dxg_pci_read_dwords(struct pci_dev *dev, int offset, int size,
|
|
+ void *val)
|
|
+{
|
|
+ int off = offset;
|
|
+ int ret;
|
|
+ int i;
|
|
+
|
|
+ /* Make sure the offset and size are 32 bit aligned */
|
|
+ if (offset & 3 || size & 3)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < size / sizeof(int); i++) {
|
|
+ ret = pci_read_config_dword(dev, off, &((int *)val)[i]);
|
|
+ if (ret) {
|
|
+ DXG_ERR("Failed to read PCI config: %d", off);
|
|
+ return ret;
|
|
+ }
|
|
+ off += sizeof(int);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dxg_pci_probe_device(struct pci_dev *dev,
|
|
+ const struct pci_device_id *id)
|
|
+{
|
|
+ int ret;
|
|
+ guid_t guid;
|
|
+ u32 vmbus_interface_ver = DXGK_VMBUS_INTERFACE_VERSION;
|
|
+ struct winluid vgpu_luid = {};
|
|
+ struct dxgk_vmbus_guestcaps guest_caps = {.wsl2 = 1};
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ mutex_lock(&dxgglobal->device_mutex);
|
|
+
|
|
+ if (dxgglobal->vmbus_ver == 0) {
|
|
+ /* Report capabilities to the host */
|
|
+
|
|
+ ret = pci_write_config_dword(dev, DXGK_VMBUS_GUESTCAPS_OFFSET,
|
|
+ guest_caps.guest_caps);
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+
|
|
+ /* Negotiate the VM bus version */
|
|
+
|
|
+ ret = pci_read_config_dword(dev, DXGK_VMBUS_VERSION_OFFSET,
|
|
+ &vmbus_interface_ver);
|
|
+ if (ret == 0 && vmbus_interface_ver != 0)
|
|
+ dxgglobal->vmbus_ver = vmbus_interface_ver;
|
|
+ else
|
|
+ dxgglobal->vmbus_ver = DXGK_VMBUS_INTERFACE_VERSION_OLD;
|
|
+
|
|
+ if (dxgglobal->vmbus_ver < DXGK_VMBUS_INTERFACE_VERSION)
|
|
+ goto read_channel_id;
|
|
+
|
|
+ ret = pci_write_config_dword(dev, DXGK_VMBUS_VERSION_OFFSET,
|
|
+ DXGK_VMBUS_INTERFACE_VERSION);
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+
|
|
+ if (dxgglobal->vmbus_ver > DXGK_VMBUS_INTERFACE_VERSION)
|
|
+ dxgglobal->vmbus_ver = DXGK_VMBUS_INTERFACE_VERSION;
|
|
+ }
|
|
+
|
|
+read_channel_id:
|
|
+
|
|
+ /* Get the VM bus channel ID for the virtual GPU */
|
|
+ ret = dxg_pci_read_dwords(dev, DXGK_VMBUS_CHANNEL_ID_OFFSET,
|
|
+ sizeof(guid), (int *)&guid);
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+
|
|
+ if (dxgglobal->vmbus_ver >= DXGK_VMBUS_INTERFACE_VERSION) {
|
|
+ ret = dxg_pci_read_dwords(dev, DXGK_VMBUS_VGPU_LUID_OFFSET,
|
|
+ sizeof(vgpu_luid), &vgpu_luid);
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ DXG_TRACE("Adapter channel: %pUb", &guid);
|
|
+ DXG_TRACE("Vmbus interface version: %d", dxgglobal->vmbus_ver);
|
|
+ DXG_TRACE("Host luid: %x-%x", vgpu_luid.b, vgpu_luid.a);
|
|
+
|
|
+cleanup:
|
|
+
|
|
+ mutex_unlock(&dxgglobal->device_mutex);
|
|
+
|
|
+ if (ret)
|
|
+ DXG_TRACE("err: %d", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void dxg_pci_remove_device(struct pci_dev *dev)
|
|
+{
|
|
+ /* Placeholder */
|
|
+}
|
|
+
|
|
+static struct pci_device_id dxg_pci_id_table[] = {
|
|
+ {
|
|
+ .vendor = PCI_VENDOR_ID_MICROSOFT,
|
|
+ .device = PCI_DEVICE_ID_VIRTUAL_RENDER,
|
|
+ .subvendor = PCI_ANY_ID,
|
|
+ .subdevice = PCI_ANY_ID
|
|
+ },
|
|
+ { 0 }
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Interface with the VM bus driver
|
|
+ */
|
|
+
|
|
+static int dxgglobal_getiospace(struct dxgglobal *dxgglobal)
|
|
+{
|
|
+ /* Get mmio space for the global channel */
|
|
+ struct hv_device *hdev = dxgglobal->hdev;
|
|
+ struct vmbus_channel *channel = hdev->channel;
|
|
+ resource_size_t pot_start = 0;
|
|
+ resource_size_t pot_end = -1;
|
|
+ int ret;
|
|
+
|
|
+ dxgglobal->mmiospace_size = channel->offermsg.offer.mmio_megabytes;
|
|
+ if (dxgglobal->mmiospace_size == 0) {
|
|
+ DXG_TRACE("Zero mmio space is offered");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dxgglobal->mmiospace_size <<= 20;
|
|
+ DXG_TRACE("mmio offered: %llx", dxgglobal->mmiospace_size);
|
|
+
|
|
+ ret = vmbus_allocate_mmio(&dxgglobal->mem, hdev, pot_start, pot_end,
|
|
+ dxgglobal->mmiospace_size, 0x10000, false);
|
|
+ if (ret) {
|
|
+ DXG_ERR("Unable to allocate mmio memory: %d", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ dxgglobal->mmiospace_size = dxgglobal->mem->end -
|
|
+ dxgglobal->mem->start + 1;
|
|
+ dxgglobal->mmiospace_base = dxgglobal->mem->start;
|
|
+ DXG_TRACE("mmio allocated %llx %llx %llx %llx",
|
|
+ dxgglobal->mmiospace_base, dxgglobal->mmiospace_size,
|
|
+ dxgglobal->mem->start, dxgglobal->mem->end);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int dxgglobal_init_global_channel(void)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ ret = dxgvmbuschannel_init(&dxgglobal->channel, dxgglobal->hdev);
|
|
+ if (ret) {
|
|
+ DXG_ERR("dxgvmbuschannel_init failed: %d", ret);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ ret = dxgglobal_getiospace(dxgglobal);
|
|
+ if (ret) {
|
|
+ DXG_ERR("getiospace failed: %d", ret);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ hv_set_drvdata(dxgglobal->hdev, dxgglobal);
|
|
+
|
|
+error:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void dxgglobal_destroy_global_channel(void)
|
|
+{
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ down_write(&dxgglobal->channel_lock);
|
|
+
|
|
+ dxgglobal->global_channel_initialized = false;
|
|
+
|
|
+ if (dxgglobal->mem) {
|
|
+ vmbus_free_mmio(dxgglobal->mmiospace_base,
|
|
+ dxgglobal->mmiospace_size);
|
|
+ dxgglobal->mem = NULL;
|
|
+ }
|
|
+
|
|
+ dxgvmbuschannel_destroy(&dxgglobal->channel);
|
|
+
|
|
+ if (dxgglobal->hdev) {
|
|
+ hv_set_drvdata(dxgglobal->hdev, NULL);
|
|
+ dxgglobal->hdev = NULL;
|
|
+ }
|
|
+
|
|
+ up_write(&dxgglobal->channel_lock);
|
|
+}
|
|
+
|
|
+static const struct hv_vmbus_device_id dxg_vmbus_id_table[] = {
|
|
+ /* Per GPU Device GUID */
|
|
+ { HV_GPUP_DXGK_VGPU_GUID },
|
|
+ /* Global Dxgkgnl channel for the virtual machine */
|
|
+ { HV_GPUP_DXGK_GLOBAL_GUID },
|
|
+ { }
|
|
+};
|
|
+
|
|
+static int dxg_probe_vmbus(struct hv_device *hdev,
|
|
+ const struct hv_vmbus_device_id *dev_id)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct winluid luid;
|
|
+ struct dxgvgpuchannel *vgpuch;
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ mutex_lock(&dxgglobal->device_mutex);
|
|
+
|
|
+ if (uuid_le_cmp(hdev->dev_type, dxg_vmbus_id_table[0].guid) == 0) {
|
|
+ /* This is a new virtual GPU channel */
|
|
+ guid_to_luid(&hdev->channel->offermsg.offer.if_instance, &luid);
|
|
+ DXG_TRACE("vGPU channel: %pUb",
|
|
+ &hdev->channel->offermsg.offer.if_instance);
|
|
+ vgpuch = kzalloc(sizeof(struct dxgvgpuchannel), GFP_KERNEL);
|
|
+ if (vgpuch == NULL) {
|
|
+ ret = -ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+ vgpuch->adapter_luid = luid;
|
|
+ vgpuch->hdev = hdev;
|
|
+ list_add_tail(&vgpuch->vgpu_ch_list_entry,
|
|
+ &dxgglobal->vgpu_ch_list_head);
|
|
+ } else if (uuid_le_cmp(hdev->dev_type,
|
|
+ dxg_vmbus_id_table[1].guid) == 0) {
|
|
+ /* This is the global Dxgkgnl channel */
|
|
+ DXG_TRACE("Global channel: %pUb",
|
|
+ &hdev->channel->offermsg.offer.if_instance);
|
|
+ if (dxgglobal->hdev) {
|
|
+ /* This device should appear only once */
|
|
+ DXG_ERR("global channel already exists");
|
|
+ ret = -EBADE;
|
|
+ goto error;
|
|
+ }
|
|
+ dxgglobal->hdev = hdev;
|
|
+ } else {
|
|
+ /* Unknown device type */
|
|
+ DXG_ERR("Unknown VM bus device type");
|
|
+ ret = -ENODEV;
|
|
+ }
|
|
+
|
|
+error:
|
|
+
|
|
+ mutex_unlock(&dxgglobal->device_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dxg_remove_vmbus(struct hv_device *hdev)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct dxgvgpuchannel *vgpu_channel;
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
+
|
|
+ mutex_lock(&dxgglobal->device_mutex);
|
|
+
|
|
+ if (uuid_le_cmp(hdev->dev_type, dxg_vmbus_id_table[0].guid) == 0) {
|
|
+ DXG_TRACE("Remove virtual GPU channel");
|
|
+ list_for_each_entry(vgpu_channel,
|
|
+ &dxgglobal->vgpu_ch_list_head,
|
|
+ vgpu_ch_list_entry) {
|
|
+ if (vgpu_channel->hdev == hdev) {
|
|
+ list_del(&vgpu_channel->vgpu_ch_list_entry);
|
|
+ kfree(vgpu_channel);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else if (uuid_le_cmp(hdev->dev_type,
|
|
+ dxg_vmbus_id_table[1].guid) == 0) {
|
|
+ DXG_TRACE("Remove global channel device");
|
|
+ dxgglobal_destroy_global_channel();
|
|
+ } else {
|
|
+ /* Unknown device type */
|
|
+ DXG_ERR("Unknown device type");
|
|
+ ret = -ENODEV;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dxgglobal->device_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+MODULE_DEVICE_TABLE(vmbus, dxg_vmbus_id_table);
|
|
+MODULE_DEVICE_TABLE(pci, dxg_pci_id_table);
|
|
+
|
|
+/*
|
|
+ * Global driver data
|
|
+ */
|
|
+
|
|
+struct dxgdriver dxgdrv = {
|
|
+ .vmbus_drv.name = KBUILD_MODNAME,
|
|
+ .vmbus_drv.id_table = dxg_vmbus_id_table,
|
|
+ .vmbus_drv.probe = dxg_probe_vmbus,
|
|
+ .vmbus_drv.remove = dxg_remove_vmbus,
|
|
+ .vmbus_drv.driver = {
|
|
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
|
+ },
|
|
+ .pci_drv.name = KBUILD_MODNAME,
|
|
+ .pci_drv.id_table = dxg_pci_id_table,
|
|
+ .pci_drv.probe = dxg_pci_probe_device,
|
|
+ .pci_drv.remove = dxg_pci_remove_device
|
|
+};
|
|
+
|
|
+static struct dxgglobal *dxgglobal_create(void)
|
|
+{
|
|
+ struct dxgglobal *dxgglobal;
|
|
+
|
|
+ dxgglobal = kzalloc(sizeof(struct dxgglobal), GFP_KERNEL);
|
|
+ if (!dxgglobal)
|
|
+ return NULL;
|
|
+
|
|
+ mutex_init(&dxgglobal->device_mutex);
|
|
+
|
|
+ INIT_LIST_HEAD(&dxgglobal->vgpu_ch_list_head);
|
|
+
|
|
+ init_rwsem(&dxgglobal->channel_lock);
|
|
+
|
|
+ return dxgglobal;
|
|
+}
|
|
+
|
|
+static void dxgglobal_destroy(struct dxgglobal *dxgglobal)
|
|
+{
|
|
+ if (dxgglobal) {
|
|
+ mutex_lock(&dxgglobal->device_mutex);
|
|
+ dxgglobal_destroy_global_channel();
|
|
+ mutex_unlock(&dxgglobal->device_mutex);
|
|
+
|
|
+ if (dxgglobal->vmbus_registered)
|
|
+ vmbus_driver_unregister(&dxgdrv.vmbus_drv);
|
|
+
|
|
+ dxgglobal_destroy_global_channel();
|
|
+
|
|
+ if (dxgglobal->pci_registered)
|
|
+ pci_unregister_driver(&dxgdrv.pci_drv);
|
|
+
|
|
+ if (dxgglobal->misc_registered)
|
|
+ misc_deregister(&dxgglobal->dxgdevice);
|
|
+
|
|
+ dxgglobal->drvdata->dxgdev = NULL;
|
|
+
|
|
+ kfree(dxgglobal);
|
|
+ dxgglobal = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int __init dxg_drv_init(void)
|
|
+{
|
|
+ int ret;
|
|
+ struct dxgglobal *dxgglobal = NULL;
|
|
+
|
|
+ dxgglobal = dxgglobal_create();
|
|
+ if (dxgglobal == NULL) {
|
|
+ pr_err("dxgglobal_init failed");
|
|
+ ret = -ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+ dxgglobal->drvdata = &dxgdrv;
|
|
+
|
|
+ dxgglobal->dxgdevice.minor = MISC_DYNAMIC_MINOR;
|
|
+ dxgglobal->dxgdevice.name = "dxg";
|
|
+ dxgglobal->dxgdevice.fops = &dxgk_fops;
|
|
+ dxgglobal->dxgdevice.mode = 0666;
|
|
+ ret = misc_register(&dxgglobal->dxgdevice);
|
|
+ if (ret) {
|
|
+ pr_err("misc_register failed: %d", ret);
|
|
+ goto error;
|
|
+ }
|
|
+ dxgglobal->misc_registered = true;
|
|
+ dxgdrv.dxgdev = dxgglobal->dxgdevice.this_device;
|
|
+ dxgdrv.dxgglobal = dxgglobal;
|
|
+
|
|
+ ret = vmbus_driver_register(&dxgdrv.vmbus_drv);
|
|
+ if (ret) {
|
|
+ DXG_ERR("vmbus_driver_register failed: %d", ret);
|
|
+ goto error;
|
|
+ }
|
|
+ dxgglobal->vmbus_registered = true;
|
|
+
|
|
+ ret = pci_register_driver(&dxgdrv.pci_drv);
|
|
+ if (ret) {
|
|
+ DXG_ERR("pci_driver_register failed: %d", ret);
|
|
+ goto error;
|
|
+ }
|
|
+ dxgglobal->pci_registered = true;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ /* This function does the cleanup */
|
|
+ dxgglobal_destroy(dxgglobal);
|
|
+ dxgdrv.dxgglobal = NULL;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit dxg_drv_exit(void)
|
|
+{
|
|
+ dxgglobal_destroy(dxgdrv.dxgglobal);
|
|
+}
|
|
+
|
|
+module_init(dxg_drv_init);
|
|
+module_exit(dxg_drv_exit);
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_DESCRIPTION("Microsoft Dxgkrnl virtual compute device Driver");
|
|
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.c b/drivers/hv/dxgkrnl/dxgvmbus.c
|
|
new file mode 100644
|
|
index 000000000000..deb880e34377
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/dxgvmbus.c
|
|
@@ -0,0 +1,92 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2022, Microsoft Corporation.
|
|
+ *
|
|
+ * Author:
|
|
+ * Iouri Tarassov <iourit@linux.microsoft.com>
|
|
+ *
|
|
+ * Dxgkrnl Graphics Driver
|
|
+ * VM bus interface implementation
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/completion.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/eventfd.h>
|
|
+#include <linux/hyperv.h>
|
|
+#include <linux/mman.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/pagemap.h>
|
|
+#include "dxgkrnl.h"
|
|
+#include "dxgvmbus.h"
|
|
+
|
|
+#undef pr_fmt
|
|
+#define pr_fmt(fmt) "dxgk: " fmt
|
|
+
|
|
+#define RING_BUFSIZE (256 * 1024)
|
|
+
|
|
+/*
|
|
+ * The structure is used to track VM bus packets, waiting for completion.
|
|
+ */
|
|
+struct dxgvmbuspacket {
|
|
+ struct list_head packet_list_entry;
|
|
+ u64 request_id;
|
|
+ struct completion wait;
|
|
+ void *buffer;
|
|
+ u32 buffer_length;
|
|
+ int status;
|
|
+ bool completed;
|
|
+};
|
|
+
|
|
+int dxgvmbuschannel_init(struct dxgvmbuschannel *ch, struct hv_device *hdev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ch->hdev = hdev;
|
|
+ spin_lock_init(&ch->packet_list_mutex);
|
|
+ INIT_LIST_HEAD(&ch->packet_list_head);
|
|
+ atomic64_set(&ch->packet_request_id, 0);
|
|
+
|
|
+ ch->packet_cache = kmem_cache_create("DXGK packet cache",
|
|
+ sizeof(struct dxgvmbuspacket), 0,
|
|
+ 0, NULL);
|
|
+ if (ch->packet_cache == NULL) {
|
|
+ DXG_ERR("packet_cache alloc failed");
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0)
|
|
+ hdev->channel->max_pkt_size = DXG_MAX_VM_BUS_PACKET_SIZE;
|
|
+#endif
|
|
+ ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE,
|
|
+ NULL, 0, dxgvmbuschannel_receive, ch);
|
|
+ if (ret) {
|
|
+ DXG_ERR("vmbus_open failed: %d", ret);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ ch->channel = hdev->channel;
|
|
+
|
|
+cleanup:
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void dxgvmbuschannel_destroy(struct dxgvmbuschannel *ch)
|
|
+{
|
|
+ kmem_cache_destroy(ch->packet_cache);
|
|
+ ch->packet_cache = NULL;
|
|
+
|
|
+ if (ch->channel) {
|
|
+ vmbus_close(ch->channel);
|
|
+ ch->channel = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Receive callback for messages from the host */
|
|
+void dxgvmbuschannel_receive(void *ctx)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.h b/drivers/hv/dxgkrnl/dxgvmbus.h
|
|
new file mode 100644
|
|
index 000000000000..6cdca5e03d1f
|
|
--- /dev/null
|
|
+++ b/drivers/hv/dxgkrnl/dxgvmbus.h
|
|
@@ -0,0 +1,19 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2022, Microsoft Corporation.
|
|
+ *
|
|
+ * Author:
|
|
+ * Iouri Tarassov <iourit@linux.microsoft.com>
|
|
+ *
|
|
+ * Dxgkrnl Graphics Driver
|
|
+ * VM bus interface with the host definitions
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _DXGVMBUS_H
|
|
+#define _DXGVMBUS_H
|
|
+
|
|
+#define DXG_MAX_VM_BUS_PACKET_SIZE (1024 * 128)
|
|
+
|
|
+#endif /* _DXGVMBUS_H */
|
|
diff --git a/include/uapi/misc/d3dkmthk.h b/include/uapi/misc/d3dkmthk.h
|
|
new file mode 100644
|
|
index 000000000000..5d973604400c
|
|
--- /dev/null
|
|
+++ b/include/uapi/misc/d3dkmthk.h
|
|
@@ -0,0 +1,27 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2019, Microsoft Corporation.
|
|
+ *
|
|
+ * Author:
|
|
+ * Iouri Tarassov <iourit@linux.microsoft.com>
|
|
+ *
|
|
+ * Dxgkrnl Graphics Driver
|
|
+ * User mode WDDM interface definitions
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _D3DKMTHK_H
|
|
+#define _D3DKMTHK_H
|
|
+
|
|
+/*
|
|
+ * Matches the Windows LUID definition.
|
|
+ * LUID is a locally unique identifier (similar to GUID, but not global),
|
|
+ * which is guaranteed to be unique intil the computer is rebooted.
|
|
+ */
|
|
+struct winluid {
|
|
+ __u32 a;
|
|
+ __u32 b;
|
|
+};
|
|
+
|
|
+#endif /* _D3DKMTHK_H */
|
|
--
|
|
Armbian
|
|
|