From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 88A2123393D; Sat, 18 Jul 2026 02:20:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341207; cv=none; b=bWcsHtnRexhyKLIGK8UTpHtCXZs8CCM6/F9SocRtZx0gRyLvFkXFzB4z1e1s/sw4ybyRWXRWrCeEQo6OZMAulUjY/mUJoXBJr1vxB3u4fAu86QmZqzNInL0toSk1j4iygBRUpqzg5nd2edS6y3LfoMcroLUDsUz5DdhC4G4CRHY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341207; c=relaxed/simple; bh=dUz9oqU1zePUkAzCMNHuHlSbV3vJtCGb7qfW6mqwJak=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cqFy1yB6rXwrIbi9gmDb7fTIvL9abkg5x2SMw0NN5sQdeMd7adoKl7IP+ifB3leNFNYfoGcBv6ZzovsSYuL97FsXRI/+DfHeYhtkMUa8nk91uFZ+i2SARlWcWiXwc8mWb/HPItzcxmoO3RptHnUMiZT3kvGEHjPYwO91XhNv0AM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=RgO9RJCh; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="RgO9RJCh" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id B867220B716A; Fri, 17 Jul 2026 19:19:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com B867220B716A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341195; bh=mGUiASiW4xdHOq2Ai9YveAIT8+APQ1HC+eGS4P57hRU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RgO9RJChJILt0EElDJjvEVZVAA2rZnQkTTAZLK174Sh+POrBWOSr5VFlboTSFE+L3 tE6uoeVE1Um2fRLGz9hDk03o7/uO+CtXc+epfD48Bk7KHOuQG2Q9h3JzlWGDX2oU8U QyuFMoaJZoyxbENgYTY2tnXxDG9c3dejIQG2ukUE= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 1/9] mshv: Provide a way to get partition ID if running in a VMM process Date: Fri, 17 Jul 2026 19:19:41 -0700 Message-ID: <20260718021949.926306-2-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Many PCI passthru related hypercalls require partition ID of the target guest. Guests are actually managed by MSHV driver and the partition ID is only maintained there. Add a field in the partition struct in MSHV driver to save the tgid of the VMM process creating the partition, and add a function there to retrieve partition ID if the current process is a VMM process. Signed-off-by: Mukesh R Reviewed-by: Anirudh Rayabharam (Microsoft) --- drivers/hv/mshv_root.h | 1 + drivers/hv/mshv_root_main.c | 22 ++++++++++++++++++++++ include/asm-generic/mshyperv.h | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index 1f086dcb7aa1..a85c24dcc701 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -138,6 +138,7 @@ struct mshv_partition { =20 struct mshv_girq_routing_table __rcu *pt_girq_tbl; u64 isolation_type; + pid_t pt_vmm_tgid; bool import_completed; bool pt_initialized; #if IS_ENABLED(CONFIG_DEBUG_FS) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index bd1359eb58dd..02c107458be9 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1908,6 +1908,27 @@ mshv_partition_release(struct inode *inode, struct f= ile *filp) return 0; } =20 +/* Given a process tgid, return partition id if it is a VMM process */ +u64 mshv_current_partid(void) +{ + struct mshv_partition *pt; + int i; + u64 ret_ptid =3D HV_PARTITION_ID_INVALID; + + rcu_read_lock(); + + hash_for_each_rcu(mshv_root.pt_htable, i, pt, pt_hnode) { + if (pt->pt_vmm_tgid =3D=3D current->tgid) { + ret_ptid =3D pt->pt_id; + break; + } + } + + rcu_read_unlock(); + return ret_ptid; +} +EXPORT_SYMBOL_GPL(mshv_current_partid); + static int add_partition(struct mshv_partition *partition) { @@ -2073,6 +2094,7 @@ mshv_ioctl_create_partition(void __user *user_arg, st= ruct device *module_dev) goto cleanup_irq_srcu; =20 partition->pt_id =3D pt_id; + partition->pt_vmm_tgid =3D current->tgid; =20 ret =3D add_partition(partition); if (ret) diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index bf601d67cecb..e8cbc4e3f7ad 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -350,6 +350,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u3= 2 acpi_id); int hv_call_notify_all_processors_started(void); bool hv_lp_exists(u32 lp_index); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); +u64 mshv_current_partid(void); =20 #else /* CONFIG_MSHV_ROOT */ static inline bool hv_root_partition(void) { return false; } @@ -380,6 +381,10 @@ static inline int hv_call_create_vp(int node, u64 part= ition_id, u32 vp_index, u3 { return -EOPNOTSUPP; } +static inline u64 mshv_current_partid(void) +{ + return HV_PARTITION_ID_INVALID; +} #endif /* CONFIG_MSHV_ROOT */ =20 static inline int hv_deposit_memory(u64 partition_id, u64 status) --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 38C3726738B; Sat, 18 Jul 2026 02:20:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341209; cv=none; b=hRLfaAYaIxGT8AokA3QKY8QNqlKnbLyRbwx5ZlUQQ/P3o84uMds4Ynrlq0L7GlfaoWWgq/dQJpo/suKGppjdgz+3fjD4PEUSSHYcXIZU3cGxxN6slgWxXz+2C/3g7DtGqxCFtGzvb8WhuTWMT/NTVzaeCCNub6jle/Vvcw4XOFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341209; c=relaxed/simple; bh=oC/nH3zlnc2lJLesQHkusb5kQ9EczOdzvgZC/7Ouoic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KURnBnnh4KqAmgPZUzBUMvmIPrAv0DShHN4TijRwX/Id4aYh7S2wB95lVyGvfgzXnSdC08BoNJAamNLJFlf0465Bw5/NomNkmw3t0y9o0keSVK/NihNYDuVnpxo8c/7dVR+HtqQw8YBXpva1qnkeCmw43XuxFqhAHdWi0gzI38w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=CDQ3oyKM; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="CDQ3oyKM" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 68B5F20B716B; Fri, 17 Jul 2026 19:19:56 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 68B5F20B716B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341197; bh=50+2NaZG8RZczIOZNFqtKzTKNUyFb8ZRF1YOIrM0NEU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CDQ3oyKM04I6dJdXfVuQ6KBLl44l/td5QiV2YWw0ICOI3iO5fqLpo9sJyCrWAGAHw jHc2OPr6EgBHGioumOgZ8PNFBynJzxnxQJQN+IZHX4IG7zxiXZyOtBh+qamygkpjmC WxADt2PTlHisqU+C3Qks2rrb08nKTfqV+MqgynkI= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 2/9] mshv: Add declarations and definitions for VFIO-MSHV bridge device Date: Fri, 17 Jul 2026 19:19:42 -0700 Message-ID: <20260718021949.926306-3-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Add data structs needed by the subsequent patch that introduces a new module to implement VFIO-MSHV pseudo device. Signed-off-by: Mukesh R Reviewed-by: Souradeep Chakrabarti --- drivers/hv/mshv_root.h | 19 +++++++++++++++++++ include/uapi/linux/mshv.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index a85c24dcc701..b9880d0bdc4d 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -227,6 +227,25 @@ struct port_table_info { }; }; =20 +struct mshv_device { + const struct mshv_device_ops *device_ops; + struct mshv_partition *device_pt; + void *device_private; + struct hlist_node device_ptnode; +}; + +struct mshv_device_ops { + const char *device_name; + long (*device_create)(struct mshv_device *dev); + void (*device_release)(struct mshv_device *dev); + long (*device_set_attr)(struct mshv_device *dev, + struct mshv_device_attr *attr); + long (*device_has_attr)(struct mshv_device *dev, + struct mshv_device_attr *attr); +}; + +extern struct mshv_device_ops mshv_vfio_device_ops; + int mshv_update_routing_table(struct mshv_partition *partition, const struct mshv_user_irq_entry *entries, unsigned int numents); diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index 32ff92b6342b..be6fe3ee8707 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -404,4 +404,34 @@ struct mshv_sint_mask { /* hv_hvcall device */ #define MSHV_HVCALL_SETUP _IOW(MSHV_IOCTL, 0x1E, struct mshv_vtl_hv= call_setup) #define MSHV_HVCALL _IOWR(MSHV_IOCTL, 0x1F, struct mshv_vtl_h= vcall) + +/* Device passhthru */ +#define MSHV_CREATE_DEVICE_TEST 1 + +enum { + MSHV_DEV_TYPE_VFIO, + MSHV_DEV_TYPE_MAX, +}; + +struct mshv_create_device { + __u32 type; /* in: MSHV_DEV_TYPE_xxx */ + __u32 fd; /* out: device handle */ + __u32 flags; /* in: MSHV_CREATE_DEVICE_xxx */ +}; + +#define MSHV_DEV_VFIO_FILE 1 +#define MSHV_DEV_VFIO_FILE_ADD 1 +#define MSHV_DEV_VFIO_FILE_DEL 2 + +struct mshv_device_attr { + __u32 flags; /* no flags currently defined */ + __u32 group; /* device-defined */ + __u64 attr; /* group-defined */ + __u64 addr; /* userspace address of attr data */ +}; + +/* Device fds created with MSHV_CREATE_DEVICE */ +#define MSHV_SET_DEVICE_ATTR _IOW(MSHV_IOCTL, 0x00, struct mshv_device_att= r) +#define MSHV_HAS_DEVICE_ATTR _IOW(MSHV_IOCTL, 0x01, struct mshv_device_att= r) + #endif --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B450425B0B0; Sat, 18 Jul 2026 02:20:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341212; cv=none; b=Qvyepxu3bID+kNLqZDBLRhx6srfxeWlHtWNnmrDDIi6cbKCskZXqL/wtJhNmjspP8/M8TDa8z7lH9e4bgrsHxlbRB2I+SmCa57SQ8K3tt/oC1VbF5UKxLsyQwrAuZOdEy6TkMrDhabqvoV2IVAEBatTN/Weeztwpz+ak9HPofcE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341212; c=relaxed/simple; bh=C9DZpUsr5+nPT9sdI2/PSUVPK/7UqB3t6+3qwk5oMyA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mHP9gq8fwtTKsssJmfp8wuEY6Z5Sf0Pt9eBmnvtZI64T3uCrK+RUwe9zL2mCTxe3jmxP+oF4I6hgN1d+tFHh8WoGhQPNxAGYjascbF4diweMVdbhlj5di2Db7C2TZ0UkJAH0sU76FBZPBfc2mtzsSwUs0iW3+qafgrvU3u9A9Mw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=mXOm8I5p; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="mXOm8I5p" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 27F2620B7171; Fri, 17 Jul 2026 19:19:58 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 27F2620B7171 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341199; bh=ce2HuU7oEU7lEaYhN091X/dnP9F03FTBpp/4clev1CI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mXOm8I5pv/PxhySGQOULPlrfJpwoBjNQ3Q7ME4koeRn4gjHOYnr/meF9P7J/FbWAz m6DpH2tYz6v+0WjzW2VuWUaGW70WU6EvvLbdw197GzhV8QU7vkX5ZCbixxk7eVQCAb L9i5lr/Y11rn7ahdOkeaEr3fX3/7LcPe+4mbMzH4= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 3/9] mshv: Introduce basic mshv bridge device for VFIO to build upon Date: Fri, 17 Jul 2026 19:19:43 -0700 Message-ID: <20260718021949.926306-4-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Add a new file to implement basic VFIO-MSHV bridge pseudo device. These functions are called in the VFIO framework, and credits to kvm/vfio.c as this file was adapted from it. This is a basic version to build upon. Co-developed-by: Wei Liu Signed-off-by: Wei Liu Signed-off-by: Mukesh R --- drivers/hv/Makefile | 3 +- drivers/hv/mshv_vfio.c | 211 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/mshv.h | 1 + 3 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 drivers/hv/mshv_vfio.c diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index 888a748cc7cb..9ab6fc254c38 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -14,7 +14,8 @@ hv_vmbus-y :=3D vmbus_drv.o \ hv_vmbus-$(CONFIG_HYPERV_TESTING) +=3D hv_debugfs.o hv_utils-y :=3D hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o mshv_root-y :=3D mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \ - mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o + mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o \ + mshv_vfio.o mshv_root-$(CONFIG_DEBUG_FS) +=3D mshv_debugfs.o mshv_root-$(CONFIG_TRACEPOINTS) +=3D mshv_trace.o mshv_vtl-y :=3D mshv_vtl_main.o diff --git a/drivers/hv/mshv_vfio.c b/drivers/hv/mshv_vfio.c new file mode 100644 index 000000000000..a770b508f080 --- /dev/null +++ b/drivers/hv/mshv_vfio.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VFIO-MSHV bridge pseudo device + * + * Heavily inspired by the VFIO-KVM bridge pseudo device. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mshv.h" +#include "mshv_root.h" + +struct mshv_vfio_file { + struct list_head node; + struct file *file; /* list of struct mshv_vfio_file */ +}; + +struct mshv_vfio { + struct list_head file_list; + struct mutex lock; +}; + +static bool mshv_vfio_file_is_valid(struct file *file) +{ + bool (*fn)(struct file *file); + bool ret; + + fn =3D symbol_get(vfio_file_is_valid); + if (!fn) + return false; + + ret =3D fn(file); + + symbol_put(vfio_file_is_valid); + + return ret; +} + +static long mshv_vfio_file_add(struct mshv_device *mshvdev, unsigned int f= d) +{ + struct mshv_vfio *mshv_vfio =3D mshvdev->device_private; + struct mshv_vfio_file *mvf; + struct file *filp; + long ret =3D 0; + + filp =3D fget(fd); + if (!filp) + return -EBADF; + + /* Ensure the FD is a vfio FD. */ + if (!mshv_vfio_file_is_valid(filp)) { + ret =3D -EINVAL; + goto out_fput; + } + + mutex_lock(&mshv_vfio->lock); + + list_for_each_entry(mvf, &mshv_vfio->file_list, node) { + if (mvf->file =3D=3D filp) { + ret =3D -EEXIST; + goto out_unlock; + } + } + + mvf =3D kzalloc(sizeof(*mvf), GFP_KERNEL_ACCOUNT); + if (!mvf) { + ret =3D -ENOMEM; + goto out_unlock; + } + + mvf->file =3D get_file(filp); + list_add_tail(&mvf->node, &mshv_vfio->file_list); + +out_unlock: + mutex_unlock(&mshv_vfio->lock); +out_fput: + fput(filp); + return ret; +} + +static long mshv_vfio_file_del(struct mshv_device *mshvdev, unsigned int f= d) +{ + struct mshv_vfio *mshv_vfio =3D mshvdev->device_private; + struct mshv_vfio_file *mvf; + long ret; + + CLASS(fd, f)(fd); + + if (fd_empty(f)) + return -EBADF; + + ret =3D -ENOENT; + mutex_lock(&mshv_vfio->lock); + + list_for_each_entry(mvf, &mshv_vfio->file_list, node) { + if (mvf->file !=3D fd_file(f)) + continue; + + list_del(&mvf->node); + fput(mvf->file); + kfree(mvf); + ret =3D 0; + break; + } + + mutex_unlock(&mshv_vfio->lock); + return ret; +} + +static long mshv_vfio_set_file(struct mshv_device *mshvdev, long attr, + void __user *arg) +{ + int32_t __user *argp =3D arg; + int32_t fd; + + switch (attr) { + case MSHV_DEV_VFIO_FILE_ADD: + if (get_user(fd, argp)) + return -EFAULT; + return mshv_vfio_file_add(mshvdev, fd); + + case MSHV_DEV_VFIO_FILE_DEL: + if (get_user(fd, argp)) + return -EFAULT; + return mshv_vfio_file_del(mshvdev, fd); + } + + return -ENXIO; +} + +static long mshv_vfio_set_attr(struct mshv_device *mshvdev, + struct mshv_device_attr *attr) +{ + switch (attr->group) { + case MSHV_DEV_VFIO_FILE: + return mshv_vfio_set_file(mshvdev, attr->attr, + u64_to_user_ptr(attr->addr)); + } + + return -ENXIO; +} + +static long mshv_vfio_has_attr(struct mshv_device *mshvdev, + struct mshv_device_attr *attr) +{ + switch (attr->group) { + case MSHV_DEV_VFIO_FILE: + switch (attr->attr) { + case MSHV_DEV_VFIO_FILE_ADD: + case MSHV_DEV_VFIO_FILE_DEL: + return 0; + } + + break; + } + + return -ENXIO; +} + +static long mshv_vfio_create_device(struct mshv_device *mshvdev) +{ + struct mshv_device *tmp; + struct mshv_vfio *mshv_vfio; + + /* Only one VFIO "device" per VM */ + hlist_for_each_entry(tmp, &mshvdev->device_pt->pt_devices, + device_ptnode) + if (tmp->device_ops =3D=3D &mshv_vfio_device_ops) + return -EBUSY; + + mshv_vfio =3D kzalloc_obj(*mshv_vfio); + if (mshv_vfio =3D=3D NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&mshv_vfio->file_list); + mutex_init(&mshv_vfio->lock); + + mshvdev->device_private =3D mshv_vfio; + + return 0; +} + +/* This is called from mshv_device_fop_release() */ +static void mshv_vfio_release_device(struct mshv_device *mshvdev) +{ + struct mshv_vfio *mv =3D mshvdev->device_private; + struct mshv_vfio_file *mvf, *tmp; + + list_for_each_entry_safe(mvf, tmp, &mv->file_list, node) { + fput(mvf->file); + list_del(&mvf->node); + kfree(mvf); + } + + kfree(mv); + kfree(mshvdev); +} + +struct mshv_device_ops mshv_vfio_device_ops =3D { + .device_name =3D "mshv-vfio", + .device_create =3D mshv_vfio_create_device, + .device_release =3D mshv_vfio_release_device, + .device_set_attr =3D mshv_vfio_set_attr, + .device_has_attr =3D mshv_vfio_has_attr, +}; diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index be6fe3ee8707..b038a79786d2 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -254,6 +254,7 @@ struct mshv_root_hvcall { #define MSHV_GET_GPAP_ACCESS_BITMAP _IOWR(MSHV_IOCTL, 0x06, struct mshv_gp= ap_access_bitmap) /* Generic hypercall */ #define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall) +#define MSHV_CREATE_DEVICE _IOWR(MSHV_IOCTL, 0x08, struct mshv= _create_device) =20 /* ******************************** --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5E1DD23393D; Sat, 18 Jul 2026 02:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; cv=none; b=XPC65Nmy/f2VHIUFpdrGfOJmbiQeW7Coaj7N2Zhv2zj2+c6gEailk01HtvPPiRYHJffQVgp+FtQnPbLwRm9WqsCxeT4QoQ3/qIG3f/Gj4CrCi+R/aD2bUERul6Uy9ERDlQkB8tnBegm7nA6R0GUoCbY0WUorpld39JKFrte3JhM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; c=relaxed/simple; bh=zJ3pn3bxME5JwSB5aK1OrtBWQvH5JhVENL3qlc66ejE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IlND+YW8AFOVgcwjqFL21kfb8uJbF7HPaGdVq1MbnF5fnc7zJKojwrCPOnzKJqXECMx7G4RYj0Gmd3Prit6FnIgD+G4izpehaGMihbCHbrTq88ufnnwe1icGWlvrJDaBDz2n5XQku1FySjd8yAINhIwMFkElmdk1HXwKYFtGNmw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=e7OO4xiF; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="e7OO4xiF" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 96FA020B716D; Fri, 17 Jul 2026 19:20:00 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 96FA020B716D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341201; bh=O5TgxXPIGID/9HT6Lm1wyXVi3Vi9JkEyO50J+jeuGzI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e7OO4xiFSi66/p/LIxGbMhjR3iRBU2tHCRC1MgSKiMRAzzjfc3RnBOKvpo9DcnW/c UdBz0xMz4/FcS5BUMF9VVyYYPYFStQr3DtEhVdZFIfcdZnJ60RbQzunuN0oSE5Fcjt zr/Rz2+B5HbgV3p2zNSibpnyLHEuBUQzbxXQeTkw= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 4/9] mshv: Add ioctl support for MSHV-VFIO bridge device Date: Fri, 17 Jul 2026 19:19:44 -0700 Message-ID: <20260718021949.926306-5-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Add ioctl support for MSHV devices for a partition. At present only VFIO device types are supported, but more could be added. At a high level, a partition ioctl to create device verifies it is of type VFIO and does some setup for bridge code in mshv_vfio.c. Adapted from KVM device ioctls. Co-developed-by: Wei Liu Signed-off-by: Wei Liu Signed-off-by: Mukesh R --- drivers/hv/mshv_root_main.c | 119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 02c107458be9..a36e54bfa064 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1625,6 +1625,122 @@ mshv_partition_ioctl_initialize(struct mshv_partiti= on *partition) return ret; } =20 +static long mshv_device_attr_ioctl(struct mshv_device *mshv_dev, int cmd, + ulong uarg) +{ + struct mshv_device_attr attr; + const struct mshv_device_ops *devops =3D mshv_dev->device_ops; + + if (copy_from_user(&attr, (void __user *)uarg, sizeof(attr))) + return -EFAULT; + + switch (cmd) { + case MSHV_SET_DEVICE_ATTR: + if (devops->device_set_attr) + return devops->device_set_attr(mshv_dev, &attr); + break; + case MSHV_HAS_DEVICE_ATTR: + if (devops->device_has_attr) + return devops->device_has_attr(mshv_dev, &attr); + break; + } + + return -EPERM; +} + +static long mshv_device_fop_ioctl(struct file *filp, unsigned int cmd, + ulong uarg) +{ + struct mshv_device *mshv_dev =3D filp->private_data; + + switch (cmd) { + case MSHV_SET_DEVICE_ATTR: + case MSHV_HAS_DEVICE_ATTR: + return mshv_device_attr_ioctl(mshv_dev, cmd, uarg); + } + + return -ENOTTY; +} + +static int mshv_device_fop_release(struct inode *inode, struct file *filp) +{ + struct mshv_device *mshv_dev =3D filp->private_data; + struct mshv_partition *partition =3D mshv_dev->device_pt; + + if (mshv_dev->device_ops->device_release) { + mutex_lock(&partition->pt_mutex); + hlist_del(&mshv_dev->device_ptnode); + mshv_dev->device_ops->device_release(mshv_dev); + mutex_unlock(&partition->pt_mutex); + } + + mshv_partition_put(partition); + return 0; +} + +static const struct file_operations mshv_device_fops =3D { + .owner =3D THIS_MODULE, + .unlocked_ioctl =3D mshv_device_fop_ioctl, + .release =3D mshv_device_fop_release, +}; + +static long mshv_partition_ioctl_create_device(struct mshv_partition *part= ition, + void __user *uarg) +{ + long rc; + struct mshv_create_device devargk; + struct mshv_device *mshv_dev; + const struct mshv_device_ops *vfio_ops; + + if (copy_from_user(&devargk, uarg, sizeof(devargk))) + return -EFAULT; + + /* At present, only VFIO is supported */ + if (devargk.type !=3D MSHV_DEV_TYPE_VFIO) + return -ENODEV; + + if (devargk.flags & MSHV_CREATE_DEVICE_TEST) + return 0; + + if (devargk.flags) /* currently, only MSHV_CREATE_DEVICE_TEST */ + return -EINVAL; + + /* This is freed later by mshv_vfio_release_device() */ + mshv_dev =3D kzalloc_obj(*mshv_dev); + if (mshv_dev =3D=3D NULL) + return -ENOMEM; + + vfio_ops =3D &mshv_vfio_device_ops; + mshv_dev->device_ops =3D vfio_ops; + mshv_dev->device_pt =3D partition; + + rc =3D vfio_ops->device_create(mshv_dev); + if (rc < 0) { + kfree(mshv_dev); + return rc; + } + + hlist_add_head(&mshv_dev->device_ptnode, &partition->pt_devices); + + mshv_partition_get(partition); + rc =3D anon_inode_getfd(vfio_ops->device_name, &mshv_device_fops, + mshv_dev, O_RDWR | O_CLOEXEC); + if (rc < 0) + goto undo_out; + + devargk.fd =3D rc; + if (copy_to_user(uarg, &devargk, sizeof(devargk))) + return -EFAULT; /* cleanup in mshv_device_fop_release() */ + + return 0; + +undo_out: + hlist_del(&mshv_dev->device_ptnode); + vfio_ops->device_release(mshv_dev); /* will kfree(mshv_dev) */ + mshv_partition_put(partition); + return rc; +} + static long mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long = arg) { @@ -1661,6 +1777,9 @@ mshv_partition_ioctl(struct file *filp, unsigned int = ioctl, unsigned long arg) case MSHV_ROOT_HVCALL: ret =3D mshv_ioctl_passthru_hvcall(partition, true, uarg); break; + case MSHV_CREATE_DEVICE: + ret =3D mshv_partition_ioctl_create_device(partition, uarg); + break; default: ret =3D -ENOTTY; } --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5E30924887E; Sat, 18 Jul 2026 02:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; cv=none; b=tlBjXerT9LDy7G4Xf9qviBoU8fbdUheJcLzHIx1MR5ECaxb2aHmLlewNcNxUbwx/99ld9/kbySHmrO/PAmcVoJeD4LuhIQFKBSUELDDSWuHyt9msmRfgDZMJ/WSuwQv2gozdajQemoVURdiJVXwwcQUZQ1xUncyHHaRqMsIQ36w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; c=relaxed/simple; bh=QinLjdrHG7tpTexgN5LTAi8FMgIWi0QZadkzwgkc6qw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=keqF1i+ZVC7XjSiegTxCu43ysRVtvXrFb2EZ9+G9cmcRh3EDZQ30Huj0veME/mMqevXI+XAs9nhjCMSpkNkLilrQSiEeHCASXxFjejrGWoZql97tcISGmQaqY8AW48lzW7n4eumTs+QuEYCS/h1pj16Jjfw2jx9s268dn03mpik= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=Ew4dV2gq; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="Ew4dV2gq" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id E293120B716F; Fri, 17 Jul 2026 19:20:02 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com E293120B716F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341203; bh=1I2WdTUqOuf9JU5Q89Ev0MAW1iXFtnGBGr5MppYA0aU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ew4dV2gq8hvgx09OU8SpHqKOlzsF0GSPl7liKgcxPqb2JF1VnUo8ebewPHAjJMiVn jIRkdT73a/CS5AUAbf/MR0zkRsuCFKKUy18yE15BDIe5AoQTQ8MR5i31XM+NnnZFIn FBAgK12OgjdZGurc/LCpY93nnp/GVW6jCsp9oNJA= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 5/9] mshv: Import data structs around device passthru from hyperv headers Date: Fri, 17 Jul 2026 19:19:45 -0700 Message-ID: <20260718021949.926306-6-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Copy/import from Hyper-V public headers, definitions and declarations that are related to creating domains in the hypervisor, attaching devices to them, etc. Signed-off-by: Mukesh R --- include/hyperv/hvgdk_mini.h | 8 +++++ include/hyperv/hvhdk_mini.h | 67 +++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 6a4e8b9d570f..e62f8d708e8e 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -326,6 +326,8 @@ union hv_hypervisor_version_info { /* stimer Direct Mode is available */ #define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(19) =20 +#define HV_DEVICE_DOMAIN_AVAILABLE BIT(24) + /* * Implementation recommendations. Indicates which behaviors the hypervisor * recommends the OS implement for optimal performance. @@ -486,9 +488,15 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_A= SSIST_PAGE */ #define HVCALL_GET_VP_INDEX_FROM_APIC_ID 0x009a #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0 +#define HVCALL_CREATE_DEVICE_DOMAIN 0x00b1 +#define HVCALL_ATTACH_DEVICE_DOMAIN 0x00b2 +#define HVCALL_MAP_DEVICE_GPA_PAGES 0x00b3 +#define HVCALL_UNMAP_DEVICE_GPA_PAGES 0x00b4 #define HVCALL_SIGNAL_EVENT_DIRECT 0x00c0 #define HVCALL_POST_MESSAGE_DIRECT 0x00c1 #define HVCALL_DISPATCH_VP 0x00c2 +#define HVCALL_DETACH_DEVICE_DOMAIN 0x00c4 +#define HVCALL_DELETE_DEVICE_DOMAIN 0x00c5 #define HVCALL_GET_GPA_PAGES_ACCESS_STATES 0x00c9 #define HVCALL_ACQUIRE_SPARSE_SPA_PAGE_HOST_ACCESS 0x00d7 #define HVCALL_RELEASE_SPARSE_SPA_PAGE_HOST_ACCESS 0x00d8 diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index b4cb2fa26e9b..90497635c3cb 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -547,4 +547,71 @@ union hv_device_id { /* HV_DEVICE_ID */ } acpi; } __packed; =20 +/* 3 domain types: stage 1, stage 2, and SOC */ +#define HV_DEVICE_DOMAIN_TYPE_S2 0 /* HV_DEVICE_DOMAIN_ID_TYPE_S2 */ +#define HV_DEVICE_DOMAIN_TYPE_S1 1 /* HV_DEVICE_DOMAIN_ID_TYPE_S1 */ +#define HV_DEVICE_DOMAIN_TYPE_SOC 2 /* HV_DEVICE_DOMAIN_ID_TYPE_SOC */ + +/* ID for stage 2 default domain and NULL domain */ +#define HV_DEVICE_DOMAIN_ID_S2_DEFAULT 0 +#define HV_DEVICE_DOMAIN_ID_S2_NULL 0xFFFFFFFFULL + +union hv_device_domain_id { + u64 as_uint64; + struct { + u32 type : 4; + u32 reserved : 28; + u32 id; + }; +} __packed; + +struct hv_input_device_domain { /* HV_INPUT_DEVICE_DOMAIN */ + u64 partition_id; + union hv_input_vtl owner_vtl; + u8 padding[7]; + union hv_device_domain_id domain_id; +} __packed; + +union hv_create_device_domain_flags { /* HV_CREATE_DEVICE_DOMAIN_FLAGS */ + u32 as_uint32; + struct { + u32 forward_progress_required : 1; + u32 inherit_owning_vtl : 1; + u32 reserved : 30; + } __packed; +} __packed; + +struct hv_input_create_device_domain { /* HV_INPUT_CREATE_DEVICE_DOMAIN */ + struct hv_input_device_domain device_domain; + union hv_create_device_domain_flags create_device_domain_flags; +} __packed; + +struct hv_input_delete_device_domain { /* HV_INPUT_DELETE_DEVICE_DOMAIN */ + struct hv_input_device_domain device_domain; +} __packed; + +struct hv_input_attach_device_domain { /* HV_INPUT_ATTACH_DEVICE_DOMAIN */ + struct hv_input_device_domain device_domain; + union hv_device_id device_id; +} __packed; + +struct hv_input_detach_device_domain { /* HV_INPUT_DETACH_DEVICE_DOMAIN */ + u64 partition_id; + union hv_device_id device_id; +} __packed; + +struct hv_input_map_device_gpa_pages { /* HV_INPUT_MAP_DEVICE_GPA_PAGES */ + struct hv_input_device_domain device_domain; + union hv_input_vtl target_vtl; + u8 padding[3]; + u32 map_flags; + u64 target_device_va_base; + u64 gpa_page_list[]; +} __packed; + +struct hv_input_unmap_device_gpa_pages { /* HV_INPUT_UNMAP_DEVICE_GPA_PAG= ES */ + struct hv_input_device_domain device_domain; + u64 target_device_va_base; +} __packed; + #endif /* _HV_HVHDK_MINI_H */ --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5E244242D9D; Sat, 18 Jul 2026 02:20:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; cv=none; b=AoUywtlkv+XcEVrrhiskPtLLb9MZ47EqoZHj+uhX4XDJsaVQyc+AhkreBzYk0kgmOcqVzEjvjG18iGPwVMXRIpsyFJfX8TGfmz0trrWvaDwjgGNykUceEXppt31V+AbrWjFNduTukCMEukTIX29BBjRa5TNUm4PGr5THGJ3uIwA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341217; c=relaxed/simple; bh=HteEkaSp4wVOVK9nw0E0kiL5yJI37zhJ2LZk6JsNuX8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BkizfGYIDSeCs9xcLV4fvD05HhbfuxXK7JLGoWozv/QSiVuLVKtUIN9NfAXNh4W+RJMl6AHaRcuMDG+67swnQKPd40uynHmUnciIdNYsS2SYOXGuEWmowh8SuZyRgW1NrZ6IkG2iG/DaZug0U5PIbiJsbnaiUTKnq5/LrrlNklM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=dSc2/Wzw; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="dSc2/Wzw" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 6E66420B716C; Fri, 17 Jul 2026 19:20:04 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 6E66420B716C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341205; bh=hSQHhkvihXETyUD20dRGf5jzacsSTIZgqTCFlZVNEAI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dSc2/Wzwv52COrskPRmfHSb6gFoCIkvamBfZcOOh3PYM4NoZHa0tGN5lnlPNxsfhG euoKKerSVTcgrBXhR7nk6py0bCLi7U/qcxyCnJTI4GR5ZYLK4+66CY0v7GxUDO3XOP ZLf+GUNvwl6vGA0p3ufbpK2FhsvRt8C5y1cQQztI= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 6/9] PCI: hv: Export hv_build_devid_type_pci() and change return type Date: Fri, 17 Jul 2026 19:19:46 -0700 Message-ID: <20260718021949.926306-7-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" On Hyper-V, most hypercalls related to PCI passthru to map/unmap regions, interrupts, etc need a device ID as a parameter. This device ID refers to that specific device during the lifetime of passthru. So, make hv_build_devid_type_pci() public and change return type to u64 to enforce it's size. Signed-off-by: Mukesh R Reviewed-by: Souradeep Chakrabarti --- arch/x86/hyperv/irqdomain.c | 9 +++++---- arch/x86/include/asm/mshyperv.h | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index b3ad50a874dc..8780573a4332 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -112,7 +112,7 @@ static int get_rid_cb(struct pci_dev *pdev, u16 alias, = void *data) return 0; } =20 -static union hv_device_id hv_build_devid_type_pci(struct pci_dev *pdev) +u64 hv_build_devid_type_pci(struct pci_dev *pdev) { int pos; union hv_device_id hv_devid; @@ -172,8 +172,9 @@ static union hv_device_id hv_build_devid_type_pci(struc= t pci_dev *pdev) } =20 out: - return hv_devid; + return hv_devid.as_uint64; } +EXPORT_SYMBOL_GPL(hv_build_devid_type_pci); =20 /* * hv_map_msi_interrupt() - Map the MSI IRQ in the hypervisor. @@ -196,7 +197,7 @@ int hv_map_msi_interrupt(struct irq_data *data, =20 msidesc =3D irq_data_get_msi_desc(data); pdev =3D msi_desc_to_pci_dev(msidesc); - hv_devid =3D hv_build_devid_type_pci(pdev); + hv_devid.as_uint64 =3D hv_build_devid_type_pci(pdev); cpu =3D cpumask_first(irq_data_get_effective_affinity_mask(data)); =20 return hv_map_interrupt(hv_devid, false, cpu, cfg->vector, @@ -271,7 +272,7 @@ static int hv_unmap_msi_interrupt(struct pci_dev *pdev, { union hv_device_id hv_devid; =20 - hv_devid =3D hv_build_devid_type_pci(pdev); + hv_devid.as_uint64 =3D hv_build_devid_type_pci(pdev); return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry); } =20 diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyper= v.h index f64393e853ee..8ebbd1cb7c8c 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -248,6 +248,12 @@ void hv_crash_asm_end(void); static inline void hv_root_crash_init(void) {} #endif /* CONFIG_MSHV_ROOT && CONFIG_CRASH_DUMP */ =20 +#ifdef CONFIG_PCI_MSI +u64 hv_build_devid_type_pci(struct pci_dev *pdev); +#else +static inline u64 hv_build_devid_type_pci(struct pci_dev *pdev) { return 0= ; } +#endif + #else /* CONFIG_HYPERV */ static inline void hyperv_init(void) {} static inline void hyperv_setup_mmu_ops(void) {} --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B1EDD271468; Sat, 18 Jul 2026 02:20:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341228; cv=none; b=FCEEPU8u72OIIiEYKmiS34aw2XrlvxyK0sz9EWli43Ex/owmMyfx7O1OPBRXIbcToL1JyNgpoCtXWdiuDt0fWMnXOIJ+TEdRDq2bJEtib8PLbzsfhtpIM5JkddV/aaDiVIiFfGTiQ4dYAxFDynkKVN3vG+4EEqO7sLevcU0kd4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341228; c=relaxed/simple; bh=vBlzOo4pGCVw0kDwJvKj3J43A+t14+sakilFS6RB+Hk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZtPSAQLY/HvcWqAtYoPhuKxoGYynxQ14vICtgqQLB71jrP5MbuiAE7gmV+E4fPrP0m+SmZG+nbnK4zy8gLm26wA0uzWOG2AhVaqMFTE/7sjkxN89uw54QExQ2mWjMV2zUmU/yeOcZdRRn/NU0tmhTWv5o9aQ8x70HZU+xt6qlsg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=JGa8jffz; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="JGa8jffz" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 269F320B716E; Fri, 17 Jul 2026 19:20:06 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 269F320B716E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341207; bh=IclXI6nPu7dMVsaBA2s3NoAnF7rr944Fi5cRTjFhPJc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JGa8jffzbSNB9hLWgd23+eajNlLBD1z0UrcM9RZiliXUqfmezyyiyOjlQkDlwkBZb JrtjqLCecA5trHLuYRgyFXcXTcpDBLP5wFlC4DmVRuxs5v4FN3CG7oszpmLROOCFNt 3HLL9uicZllDiCtqSjwJSZjb4WB+fyhyBjymJTYY= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 7/9] x86/hyperv: Implement Hyper-V virtual IOMMU Date: Fri, 17 Jul 2026 19:19:47 -0700 Message-ID: <20260718021949.926306-8-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Add a new file to implement a virtual IOMMU that works with Microsoft Hyper-V hypervisor. The hypervisor claims the IOMMU upon boot, and this driver communicates with it for creating and deleting paging domains, mapping and unmapping of pages, etc. The default domain type is IDENTITY. Lastly, this is only supported on baremetal or nested root/dom0, and not L1VH root/dom0. Signed-off-by: Mukesh R --- arch/x86/kernel/pci-dma.c | 2 + drivers/iommu/Kconfig | 1 + drivers/iommu/hyperv/Kconfig | 15 + drivers/iommu/hyperv/Makefile | 1 + drivers/iommu/hyperv/hv-iommu-root.c | 704 +++++++++++++++++++++++++++ include/asm-generic/mshyperv.h | 3 + include/linux/hyperv.h | 6 + 7 files changed, 732 insertions(+) create mode 100644 drivers/iommu/hyperv/Kconfig create mode 100644 drivers/iommu/hyperv/hv-iommu-root.c diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 6267363e0189..37fed8c7a8c2 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -8,6 +8,7 @@ #include #include #include +#include =20 #include #include @@ -103,6 +104,7 @@ void __init pci_iommu_alloc(void) } pci_swiotlb_detect(); gart_iommu_hole_init(); + hv_iommu_detect(); amd_iommu_detect(); detect_intel_iommu(); swiotlb_init(x86_swiotlb_enable, x86_swiotlb_flags); diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 1becc0f20222..e95a56a6e075 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -197,6 +197,7 @@ source "drivers/iommu/arm/Kconfig" source "drivers/iommu/intel/Kconfig" source "drivers/iommu/iommufd/Kconfig" source "drivers/iommu/riscv/Kconfig" +source "drivers/iommu/hyperv/Kconfig" =20 config IRQ_REMAP bool "Support for Interrupt Remapping" diff --git a/drivers/iommu/hyperv/Kconfig b/drivers/iommu/hyperv/Kconfig new file mode 100644 index 000000000000..512b154b2f59 --- /dev/null +++ b/drivers/iommu/hyperv/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Hyper-V IOMMU support + +config HYPERV_IOMMU + bool "Hyper-V IOMMU Device" + depends on HYPERV && X86 + select IOMMU_API + default HYPERV + help + This enables Hyper-V pseudo IOMMU device. When running as privileged + VM aka root on Microsoft Hyper-V hypervisor, this must be enabled + for doing any PCI passthru of devices to guest VMs. This applies= to + both PFs and VFs. When enabling this, it is best to disable amd/= intel + iommus via: intel_iommu=3Doff amd_iommu=3Doff as the hypervisor = really + owns the iommu. diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile index 6ef0ef97f3dd..aaced370a94f 100644 --- a/drivers/iommu/hyperv/Makefile +++ b/drivers/iommu/hyperv/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IRQ_REMAP) +=3D hv-irq-remap-x86.o +obj-$(CONFIG_HYPERV_IOMMU) +=3D hv-iommu-root.o diff --git a/drivers/iommu/hyperv/hv-iommu-root.c b/drivers/iommu/hyperv/hv= -iommu-root.c new file mode 100644 index 000000000000..374fa0c177c6 --- /dev/null +++ b/drivers/iommu/hyperv/hv-iommu-root.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hyper-V root vIOMMU driver. + * Copyright (C) 2026, Microsoft, Inc. + */ +#include +#include +#include +#include +#include +#include +#include "../dma-iommu.h" + +/* We will not claim these PCI devices, eg hypervisor needs it for debugge= r */ +static char *pci_devs_to_skip; +static int __init hv_iommu_setup_skip(char *str) +{ + pci_devs_to_skip =3D str; + + return 0; +} +/* hv_iommu_skip=3D(SSSS:BB:DD.F)(SSSS:BB:DD.F) */ +__setup("hv_iommu_skip=3D", hv_iommu_setup_skip); + +/* IOMMU device that we export to the world. HyperV supports max of one */ +static struct iommu_device hv_virt_iommu; + +struct hv_domain { + struct iommu_domain iommu_dom; + u32 domid_num; /* as opposed to domain_id.type */ + u64 partid; /* partition id */ + spinlock_t mappings_lock; /* protects mappings_tree */ + struct rb_root_cached mappings_tree; /* iova to pa lookup tree */ +}; + +#define to_hv_domain(d) container_of(d, struct hv_domain, iommu_dom) + +struct hv_iommu_mapping { + phys_addr_t paddr; + struct interval_tree_node iova; + u32 flags; +}; + +/* + * By default, during boot the hypervisor creates one Stage 2 (S2) default + * domain. Stage 2 means that the page table is controlled by the hypervis= or. + * It has two types: + * S2 default: access to entire root partition memory. This for us easily + * maps to IOMMU_DOMAIN_IDENTITY in the iommu subsystem, and + * is called HV_DEVICE_DOMAIN_ID_S2_DEFAULT in the hypervisor. + * S2 NULL: Blocks everything except RMRR + */ + +/* + * Create dummy domains to correspond to hypervisor prebuilt default ident= ity + * and null domains (dummy because we do not make hypercalls to create the= m). + */ +static struct hv_domain hv_def_identity_dom; +static struct hv_domain hv_null_dom; + +static bool hv_special_domain(struct hv_domain *hvdom) +{ + return hvdom =3D=3D &hv_def_identity_dom || hvdom =3D=3D &hv_null_dom; +} + +struct iommu_domain_geometry default_geometry =3D (struct iommu_domain_geo= metry) { + .aperture_start =3D 0, + .aperture_end =3D -1UL, + .force_aperture =3D true, +}; + +#define HV_IOMMU_PGSIZES SZ_4K /* for now, to be enhanced */ + +static atomic_t hv_unique_id; /* unique numeric id for a new domain */ + +/* + * If the current thread is a VMM thread, return the partition id of the V= M it + * is managing, else return HV_PARTITION_ID_INVALID. + */ +static u64 hv_get_current_partid(void) +{ + u64 (*fn)(void); + u64 ptid; + + fn =3D symbol_get(mshv_current_partid); + if (!fn) + return HV_PARTITION_ID_INVALID; + + ptid =3D fn(); + symbol_put(mshv_current_partid); + + return ptid; +} + +static bool hv_iommu_capable(struct device *dev, enum iommu_cap cap) +{ + switch (cap) { + case IOMMU_CAP_CACHE_COHERENCY: + return true; + default: + return false; + } +} + +/* Create a new device domain in the hypervisor */ +static int hv_iommu_create_hyp_devdom(struct hv_domain *hvdom) +{ + u64 status; + struct hv_input_device_domain *ddp; + struct hv_input_create_device_domain *input; + unsigned long flags; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + ddp =3D &input->device_domain; + ddp->partition_id =3D HV_PARTITION_ID_SELF; + ddp->domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + ddp->domain_id.id =3D hvdom->domid_num; + + input->create_device_domain_flags.forward_progress_required =3D 1; + input->create_device_domain_flags.inherit_owning_vtl =3D 0; + + status =3D hv_do_hypercall(HVCALL_CREATE_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); +} + +static struct iommu_domain *hv_iommu_domain_alloc_paging(struct device *de= v) +{ + struct hv_domain *hvdom; + int rc; + u32 unique_id; + u64 ptid =3D hv_get_current_partid(); + + if (ptid =3D=3D HV_PARTITION_ID_INVALID) + return NULL; + + hvdom =3D kzalloc_obj(struct hv_domain); + if (hvdom =3D=3D NULL) + return NULL; + + spin_lock_init(&hvdom->mappings_lock); + hvdom->mappings_tree =3D RB_ROOT_CACHED; + + unique_id =3D (u32)atomic_inc_return(&hv_unique_id); + if (unique_id =3D=3D HV_DEVICE_DOMAIN_ID_S2_NULL) /* ie, UINTMAX */ + goto out_err; + + hvdom->domid_num =3D unique_id; + hvdom->partid =3D ptid; + hvdom->iommu_dom.geometry =3D default_geometry; + hvdom->iommu_dom.pgsize_bitmap =3D HV_IOMMU_PGSIZES; + + rc =3D hv_iommu_create_hyp_devdom(hvdom); + if (rc) + goto out_err; + + return &hvdom->iommu_dom; + +out_err: + kfree(hvdom); + return NULL; +} + +static void hv_iommu_domain_free(struct iommu_domain *immdom) +{ + unsigned long flags; + u64 status; + struct hv_input_delete_device_domain *input; + struct hv_input_device_domain *ddp; + struct hv_domain *hvdom =3D to_hv_domain(immdom); + + if (hv_special_domain(hvdom)) + return; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + ddp =3D &input->device_domain; + memset(input, 0, sizeof(*input)); + + ddp->partition_id =3D HV_PARTITION_ID_SELF; + ddp->domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + ddp->domain_id.id =3D hvdom->domid_num; + + status =3D hv_do_hypercall(HVCALL_DELETE_DEVICE_DOMAIN, input, + NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + kfree(hvdom); +} + +static void hv_iommu_det_dev_from_dom(struct pci_dev *pdev) +{ + u64 status; + unsigned long flags; + struct hv_input_detach_device_domain *input; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + input->partition_id =3D HV_PARTITION_ID_SELF; + input->device_id.as_uint64 =3D hv_build_devid_type_pci(pdev); + status =3D hv_do_hypercall(HVCALL_DETACH_DEVICE_DOMAIN, input, NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); +} + +static void hv_iommu_detach_dev(struct hv_domain *hvdom, struct device *de= v) +{ + struct pci_dev *pdev; + + /* See the attach function, only PCI devices for now */ + if (!dev_is_pci(dev)) + return; + + pdev =3D to_pci_dev(dev); + hv_iommu_det_dev_from_dom(pdev); +} + +/* + * Attach a device to the default domain, or the null domain, or to a doma= in + * previously created in the hypervisor. + */ +static int hv_iommu_att_dev2dom(struct hv_domain *hvdom, struct pci_dev *p= dev) +{ + unsigned long flags; + u64 status; + struct hv_input_attach_device_domain *input; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + /* For null domain, hvdom->domid_num =3D=3D HV_DEVICE_DOMAIN_ID_S2_NULL */ + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D hvdom->domid_num; + + /* NB: Upon guest shutdown, device is re-attached to the default domain + * without explicit detach. + */ + input->device_id.as_uint64 =3D hv_build_devid_type_pci(pdev); + + status =3D hv_do_hypercall(HVCALL_ATTACH_DEVICE_DOMAIN, input, NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); +} + +/* Attach a device for passthru to guest VMs, host apps like SPDK, etc */ +static int hv_iommu_attach_dev(struct iommu_domain *immdom, struct device = *dev, + struct iommu_domain *old) +{ + struct pci_dev *pdev; + int rc; + struct hv_domain *hvdom_new =3D to_hv_domain(immdom); + struct hv_domain *hvdom_prev =3D to_hv_domain(old); + + /* Only allow PCI devices for now */ + if (!dev_is_pci(dev)) + return -EINVAL; + + pdev =3D to_pci_dev(dev); + + /* There are no explicit detach calls, hence check first if we need + * to detach first. Also, in case of guest shutdown, it's the VMM + * thread that attaches it back to the hv_def_identity_dom, and + * hvdom_prev will not be null then. It is null during boot. + */ + if (hvdom_prev && !hv_special_domain(hvdom_prev)) + hv_iommu_detach_dev(hvdom_prev, dev); + + rc =3D hv_iommu_att_dev2dom(hvdom_new, pdev); + + if (rc =3D=3D 0) + dev_iommu_priv_set(dev, hvdom_new); /* sets "private" field */ + else + dev_iommu_priv_set(dev, NULL); + + return rc; +} + +static int hv_iommu_add_tree_mapping(struct hv_domain *hvdom, + unsigned long iova, phys_addr_t paddr, + size_t size, u32 flags) +{ + unsigned long irqflags; + struct hv_iommu_mapping *mapping; + + mapping =3D kzalloc_obj(struct hv_iommu_mapping); + if (!mapping) + return -ENOMEM; + + mapping->paddr =3D paddr; + mapping->iova.start =3D iova; + mapping->iova.last =3D iova + size - 1; + mapping->flags =3D flags; + + spin_lock_irqsave(&hvdom->mappings_lock, irqflags); + interval_tree_insert(&mapping->iova, &hvdom->mappings_tree); + spin_unlock_irqrestore(&hvdom->mappings_lock, irqflags); + + return 0; +} + +static size_t hv_iommu_del_tree_mappings(struct hv_domain *hvdom, + unsigned long iova, size_t size) +{ + unsigned long flags; + size_t unmapped =3D 0; + unsigned long last =3D iova + size - 1; + struct hv_iommu_mapping *mapping =3D NULL; + struct interval_tree_node *node, *next; + + spin_lock_irqsave(&hvdom->mappings_lock, flags); + next =3D interval_tree_iter_first(&hvdom->mappings_tree, iova, last); + while (next) { + node =3D next; + mapping =3D container_of(node, struct hv_iommu_mapping, iova); + next =3D interval_tree_iter_next(node, iova, last); + + /* Trying to split a mapping? Not supported for now. */ + if (mapping->iova.start < iova) + break; + + unmapped +=3D mapping->iova.last - mapping->iova.start + 1; + + interval_tree_remove(node, &hvdom->mappings_tree); + kfree(mapping); + } + spin_unlock_irqrestore(&hvdom->mappings_lock, flags); + + return unmapped; +} + +static u64 hv_iommu_unmap_batch(u32 domid_num, ulong iova, u16 count) +{ + ulong flags; + struct hv_input_unmap_device_gpa_pages *input; + u64 status; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D domid_num; + input->target_device_va_base =3D iova; + + status =3D hv_do_rep_hypercall(HVCALL_UNMAP_DEVICE_GPA_PAGES, count, + 0, input, NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "iova:0x%lx count:0x%x\n", iova, count); + + return status; +} + +static size_t hv_iommu_unmap_pages(struct iommu_domain *immdom, ulong iova, + size_t pgsize, size_t pgcount, + struct iommu_iotlb_gather *gather) +{ + unsigned long npages; + u64 status; + struct hv_domain *hvdom =3D to_hv_domain(immdom); + size_t unmapped, tot_done =3D 0, size =3D pgsize * pgcount; + + unmapped =3D hv_iommu_del_tree_mappings(hvdom, iova, size); + if (unmapped < size) { + pr_err("%s: could not delete all mappings (%lx:%lx/%lx)\n", + __func__, iova, unmapped, size); + size =3D unmapped; + } + + npages =3D size >> HV_HYP_PAGE_SHIFT; + + while (npages) { + int done, count =3D min(npages, HV_REP_COUNT_MAX); + + status =3D hv_iommu_unmap_batch(hvdom->domid_num, iova, count); + + done =3D hv_repcomp(status); + tot_done +=3D done; + npages -=3D done; + iova +=3D done << HV_HYP_PAGE_SHIFT; + + if (!hv_result_success(status)) + break; + } + + return tot_done << HV_HYP_PAGE_SHIFT; +} + +/* Return: must return exact status from the hypercall without changes */ +static u64 hv_iommu_map_pgs(struct hv_domain *hvdom, + unsigned long iova, phys_addr_t paddr, + unsigned long npages, u32 map_flags) +{ + u64 status; + int i; + struct hv_input_map_device_gpa_pages *input; + unsigned long flags, pfn; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D hvdom->domid_num; + input->map_flags =3D map_flags; + input->target_device_va_base =3D iova; + + pfn =3D paddr >> HV_HYP_PAGE_SHIFT; + for (i =3D 0; i < npages; i++, pfn++) + input->gpa_page_list[i] =3D pfn; + + status =3D hv_do_rep_hypercall(HVCALL_MAP_DEVICE_GPA_PAGES, npages, 0, + input, NULL); + local_irq_restore(flags); + + return status; +} + +#define HV_MAP_DEVICE_GPA_BATCH_SIZE \ + ((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_map_device_gpa_pages)) \ + / sizeof(u64)) + +/* + * The core VFIO code loops over memory ranges calling this function with = the + * largest pgsize from HV_IOMMU_PGSIZES. cond_resched() is in vfio_iommu_m= ap. + */ +static int hv_iommu_map_pages(struct iommu_domain *immdom, ulong iova, + phys_addr_t paddr, size_t pgsize, size_t pgcount, + int prot, gfp_t gfp, size_t *mapped) +{ + u32 map_flags; + int ret; + u64 status; + unsigned long npages, done =3D 0; + struct hv_domain *hvdom =3D to_hv_domain(immdom); + size_t size =3D pgsize * pgcount; + + map_flags =3D HV_MAP_GPA_READABLE; /* required */ + map_flags |=3D prot & IOMMU_WRITE ? HV_MAP_GPA_WRITABLE : 0; + + ret =3D hv_iommu_add_tree_mapping(hvdom, iova, paddr, size, map_flags); + if (ret) + return ret; + + npages =3D size >> HV_HYP_PAGE_SHIFT; + while (done < npages) { + ulong completed, remain =3D npages - done; + + remain =3D min(remain, HV_MAP_DEVICE_GPA_BATCH_SIZE); + + status =3D hv_iommu_map_pgs(hvdom, iova, paddr, remain, + map_flags); + + completed =3D hv_repcomp(status); + done =3D done + completed; + iova =3D iova + (completed << HV_HYP_PAGE_SHIFT); + paddr =3D paddr + (completed << HV_HYP_PAGE_SHIFT); + + if (hv_result(status) =3D=3D HV_STATUS_INSUFFICIENT_MEMORY) { + ret =3D hv_call_deposit_pages(NUMA_NO_NODE, + hv_current_partition_id, + 256); + if (ret) + break; + continue; + } + if (!hv_result_success(status)) + break; + } + + if (!hv_result_success(status)) { + size_t done_size =3D done << HV_HYP_PAGE_SHIFT; + + hv_status_err(status, "pgs:%lx/%lx iova:%lx\n", + done, npages, iova); + /* + * lookup tree has all mappings [0 - size-1]. Below unmap will + * only remove from [0 - done], we need to remove second chunk + * [done+1 - size-1]. + */ + hv_iommu_del_tree_mappings(hvdom, iova, size - done_size); + hv_iommu_unmap_pages(immdom, iova - done_size, HV_HYP_PAGE_SIZE, + done, NULL); + if (mapped) + *mapped =3D 0; + } else + if (mapped) + *mapped =3D size; + + return hv_result_to_errno(status); +} + +static phys_addr_t hv_iommu_iova_to_phys(struct iommu_domain *immdom, + dma_addr_t iova) +{ + unsigned long flags; + struct hv_iommu_mapping *mapping; + struct interval_tree_node *node; + u64 paddr =3D 0; + struct hv_domain *hvdom =3D to_hv_domain(immdom); + + spin_lock_irqsave(&hvdom->mappings_lock, flags); + node =3D interval_tree_iter_first(&hvdom->mappings_tree, iova, iova); + if (node) { + mapping =3D container_of(node, struct hv_iommu_mapping, iova); + paddr =3D mapping->paddr + (iova - mapping->iova.start); + } + spin_unlock_irqrestore(&hvdom->mappings_lock, flags); + + return paddr; +} + +/* + * Currently, hypervisor does not provide list of devices it is using + * dynamically. So use this to allow users to manually specify devices that + * should be skipped. (eg. hypervisor debugger using some network device). + */ +static struct iommu_device *hv_iommu_probe_device(struct device *dev) +{ + if (!dev_is_pci(dev)) + return ERR_PTR(-ENODEV); + + if (pci_devs_to_skip && *pci_devs_to_skip) { + int rc, pos =3D 0; + int parsed; + int segment, bus, slot, func; + struct pci_dev *pdev =3D to_pci_dev(dev); + + do { + parsed =3D 0; + + rc =3D sscanf(pci_devs_to_skip + pos, " (%x:%x:%x.%x) %n", + &segment, &bus, &slot, &func, &parsed); + if (rc) + break; + if (parsed <=3D 0) + break; + + if (pci_domain_nr(pdev->bus) =3D=3D segment && + pdev->bus->number =3D=3D bus && + PCI_SLOT(pdev->devfn) =3D=3D slot && + PCI_FUNC(pdev->devfn) =3D=3D func) { + + dev_info(dev, "skipped by Hyper-V IOMMU\n"); + return ERR_PTR(-ENODEV); + } + pos +=3D parsed; + + } while (pci_devs_to_skip[pos]); + } + + /* Device will be explicitly attached to the default domain, so no need + * to do dev_iommu_priv_set() here. + */ + + return &hv_virt_iommu; +} + +static void hv_iommu_probe_finalize(struct device *dev) +{ + struct iommu_domain *immdom =3D iommu_get_domain_for_dev(dev); + + if (immdom && immdom->type =3D=3D IOMMU_DOMAIN_DMA) + iommu_setup_dma_ops(dev, immdom); + else + set_dma_ops(dev, NULL); +} + +static void hv_iommu_release_device(struct device *dev) +{ + struct hv_domain *hvdom =3D dev_iommu_priv_get(dev); + + /* Need to detach device from device domain if necessary. */ + if (hvdom) + hv_iommu_detach_dev(hvdom, dev); + + dev_iommu_priv_set(dev, NULL); + set_dma_ops(dev, NULL); +} + +static struct iommu_group *hv_iommu_device_group(struct device *dev) +{ + if (dev_is_pci(dev)) + return pci_device_group(dev); + else + return generic_device_group(dev); +} + +static int hv_iommu_def_domain_type(struct device *dev) +{ + /* The hypervisor always creates this by default during boot */ + return IOMMU_DOMAIN_IDENTITY; +} + +static struct iommu_ops hv_iommu_ops =3D { + .capable =3D hv_iommu_capable, + .domain_alloc_paging =3D hv_iommu_domain_alloc_paging, + .probe_device =3D hv_iommu_probe_device, + .probe_finalize =3D hv_iommu_probe_finalize, + .release_device =3D hv_iommu_release_device, + .def_domain_type =3D hv_iommu_def_domain_type, + .device_group =3D hv_iommu_device_group, + .default_domain_ops =3D &(const struct iommu_domain_ops) { + .attach_dev =3D hv_iommu_attach_dev, + .map_pages =3D hv_iommu_map_pages, + .unmap_pages =3D hv_iommu_unmap_pages, + .iova_to_phys =3D hv_iommu_iova_to_phys, + .free =3D hv_iommu_domain_free, + }, + .owner =3D THIS_MODULE, + .identity_domain =3D &hv_def_identity_dom.iommu_dom, + .blocked_domain =3D &hv_null_dom.iommu_dom, +}; + +static const struct iommu_domain_ops hv_special_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, +}; + +static void __init hv_initialize_special_domains(void) +{ + hv_def_identity_dom.iommu_dom.type =3D IOMMU_DOMAIN_IDENTITY; + hv_def_identity_dom.iommu_dom.ops =3D &hv_special_domain_ops; + hv_def_identity_dom.iommu_dom.owner =3D &hv_iommu_ops; + hv_def_identity_dom.iommu_dom.geometry =3D default_geometry; + hv_def_identity_dom.domid_num =3D HV_DEVICE_DOMAIN_ID_S2_DEFAULT; /* 0 */ + + hv_null_dom.iommu_dom.type =3D IOMMU_DOMAIN_BLOCKED; + hv_null_dom.iommu_dom.ops =3D &hv_special_domain_ops; + hv_null_dom.iommu_dom.owner =3D &hv_iommu_ops; + hv_null_dom.iommu_dom.geometry =3D default_geometry; + hv_null_dom.domid_num =3D HV_DEVICE_DOMAIN_ID_S2_NULL; /* INTMAX */ +} + +static int __init hv_iommu_init(void) +{ + int ret; + struct iommu_device *iommup =3D &hv_virt_iommu; + + if (!hv_is_hyperv_initialized()) + return -ENODEV; + + ret =3D iommu_device_sysfs_add(iommup, NULL, NULL, "%s", "hyperv-iommu"); + if (ret) { + pr_err("Hyper-V: iommu_device_sysfs_add failed: %d\n", ret); + return ret; + } + + /* This must come before iommu_device_register() because the latter + * calls into the hooks. + */ + hv_initialize_special_domains(); + + ret =3D iommu_device_register(iommup, &hv_iommu_ops, NULL); + if (ret) { + pr_err("Hyper-V: iommu_device_register failed: %d\n", ret); + goto err_sysfs_remove; + } + + pr_info("Hyper-V IOMMU initialized\n"); + + return 0; + +err_sysfs_remove: + iommu_device_sysfs_remove(iommup); + return ret; +} + +void __init hv_iommu_detect(void) +{ + if (no_iommu || iommu_detected || hv_l1vh_partition()) + return; + + if (!(ms_hyperv.misc_features & HV_DEVICE_DOMAIN_AVAILABLE)) + return; + + iommu_detected =3D 1; + x86_init.iommu.iommu_init =3D hv_iommu_init; + + pci_request_acs(); +} diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index e8cbc4e3f7ad..2c5d0812f19d 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -28,6 +28,9 @@ =20 #define VTPM_BASE_ADDRESS 0xfed40000 =20 +#define HV_REP_COUNT_MAX \ + (HV_HYPERCALL_REP_COMP_MASK >> HV_HYPERCALL_REP_COMP_OFFSET) + enum hv_partition_type { HV_PARTITION_TYPE_GUEST, HV_PARTITION_TYPE_ROOT, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 964f1be8150c..f78645e8d776 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1769,4 +1769,10 @@ static inline unsigned long virt_to_hvpfn(void *addr) #define HVPFN_DOWN(x) ((x) >> HV_HYP_PAGE_SHIFT) #define page_to_hvpfn(page) (page_to_pfn(page) * NR_HV_HYP_PAGES_IN_PAGE) =20 +#ifdef CONFIG_HYPERV_IOMMU +void __init hv_iommu_detect(void); +#else +static inline void hv_iommu_detect(void) { } +#endif /* CONFIG_HYPERV_IOMMU */ + #endif /* _HYPERV_H */ --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 43E7726738B; Sat, 18 Jul 2026 02:20:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341223; cv=none; b=i0i3Sdp+ugaCUbkRAEbu3IKZ/XrzqSMRS66ijmEVsmAEUaUoJCfstOH6QdQgcLA4haCFYjxM6D9uZCC1iSwxrLmNjpMlq5VBWkIjoZ1Bqc/lIqKmKv8Il22A4GiYDVA0KukRZT5GXDb3/o9TKepgHpFKibnO4MK/5EYjkVihMpE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341223; c=relaxed/simple; bh=lhwgNMXZqp6KYOABag9ODDEc0Rsjc61UKXXm5b2TflU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Nl+4wGXoAHAaZqqikvK2dMOEx4p70+BmXRIJB9z8VhhDYlzW6F+6QTiv+73lJJ4l0G6H7L/jvKEgePyj8t4uvB26aBG/D5nnEkvMAzMXoz4/BB6a/wpvSoQNbuAbGowFPSnDDXwoRRc7U2dnYsmpufR178vVDAOhNqnUGJim/bQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=NfXw75BD; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="NfXw75BD" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 6800720B7172; Fri, 17 Jul 2026 19:20:08 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 6800720B7172 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341209; bh=FCYqsaK2mb6k+KVJi6/phleqEvnwWe1htuCXkMy0+Ns=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NfXw75BDN3RdX46zLo8+0PtVPyFxOqHAKLIIKyufCyi+lGEbSIt4kLMtunlkVgzJ8 96Fq9PZUH2jrCmD5TLqkkdS9sZWrggU72jZSU0oi05rjul1CdZ9Kvy0pOSdtULh2ce b8u4+y+QVYioB8o7BT9+2VyxCj6e09wEApOzqDoE= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 8/9] mshv: Populate mmio mappings for PCI passthru Date: Fri, 17 Jul 2026 19:19:48 -0700 Message-ID: <20260718021949.926306-9-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" Upon guest accesses, in case of missing mmio mappings, the hypervisor generates unmapped gpa intercepts. In this path, lookup the PCI resource pfn for the guest gpa, and ask the hypervisor to map it via hypercall. The PCI resource pfn is maintained by the VFIO driver, and obtained via fixup_user_fault() call (similar to KVM). Try to use 2M page size as much as possible for significant performance gains. Also, remove existing code that is using vma->vm_pgoff to map mmio space, it is broken and will cause panics. Signed-off-by: Mukesh R --- drivers/hv/mshv_root.h | 3 +- drivers/hv/mshv_root_hv_call.c | 101 ++++++++++++++++++++++++-------- drivers/hv/mshv_root_main.c | 103 +++++++++++++++++++++++++++------ 3 files changed, 165 insertions(+), 42 deletions(-) diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index b9880d0bdc4d..c97a1b5da9ad 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -301,7 +301,8 @@ int hv_call_create_partition(u64 flags, int hv_call_initialize_partition(u64 partition_id); int hv_call_finalize_partition(u64 partition_id); int hv_call_delete_partition(u64 partition_id); -int hv_call_map_mmio_pages(u64 partition_id, u64 gfn, u64 mmio_spa, u64 nu= mpgs); +int hv_map_mmio_pages(u64 partition_id, struct mshv_mem_region *reg, u64 g= fn, + u64 mmio_mfn); int hv_call_map_gpa_pages(u64 partition_id, u64 gpa_target, u64 page_count, u32 flags, struct page **pages); int hv_call_unmap_gpa_pages(u64 partition_id, u64 gpa_target, u64 page_cou= nt, diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index cb55d4d4be2e..937225c60473 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -189,31 +189,33 @@ int hv_call_delete_partition(u64 partition_id) } =20 /* Ask the hypervisor to map guest ram pages or the guest mmio space */ -static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_= count, - u32 flags, struct page **pages, u64 mmio_spa) +static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_count, + u32 flags, struct page **pages, u64 mmio_mfn) { struct hv_input_map_gpa_pages *input_page; u64 status, *pfnlist; unsigned long irq_flags, large_shift =3D 0; - int ret =3D 0, done =3D 0; - u64 page_count =3D page_struct_count; - - if (page_count =3D=3D 0 || (pages && mmio_spa)) - return -EINVAL; + int i, ret =3D 0, done =3D 0; + u64 adj_page_count =3D page_count; =20 - if (flags & HV_MAP_GPA_LARGE_PAGE) { - if (mmio_spa) + if (mmio_mfn) { + if (pages) return -EINVAL; + for (i =3D 0; i < page_count; i++) + if (page_is_ram(mmio_mfn + i)) + return -EINVAL; + } =20 + if (flags & HV_MAP_GPA_LARGE_PAGE) { if (!HV_PAGE_COUNT_2M_ALIGNED(page_count)) return -EINVAL; =20 large_shift =3D HV_HYP_LARGE_PAGE_SHIFT - HV_HYP_PAGE_SHIFT; - page_count >>=3D large_shift; + adj_page_count >>=3D large_shift; } =20 - while (done < page_count) { - ulong i, completed, remain =3D page_count - done; + while (done < adj_page_count) { + ulong i, completed, remain =3D adj_page_count - done; int rep_count =3D min(remain, HV_MAP_GPA_BATCH_SIZE); =20 local_irq_save(irq_flags); @@ -230,13 +232,14 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 = gfn, u64 page_struct_count, } else if (pages) { u64 index =3D (done + i) << large_shift; =20 - if (index >=3D page_struct_count) { + if (index >=3D page_count) { ret =3D -EINVAL; break; } pfnlist[i] =3D page_to_pfn(pages[index]); } else { - pfnlist[i] =3D mmio_spa + done + i; + pfnlist[i] =3D mmio_mfn + + ((done + i) << large_shift); } if (ret) break; @@ -254,6 +257,9 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gf= n, u64 page_struct_count, break; =20 } else if (!hv_result_success(status)) { + pr_err("%s: failed to map pages at gfn %#llx: completed %u/%llu, flags= =3D%#x, status=3D%#llx (%s)\n", + __func__, gfn, done, page_count, flags, status, + hv_result_to_string(hv_result(status))); ret =3D hv_result_to_errno(status); break; } @@ -280,19 +286,66 @@ int hv_call_map_gpa_pages(u64 partition_id, u64 gpa_t= arget, u64 page_count, flags, pages, 0); } =20 -/* Ask the hypervisor to map guest mmio space */ -int hv_call_map_mmio_pages(u64 partition_id, u64 gfn, u64 mmio_spa, u64 nu= mpgs) +/* + * Ask the hypervisor to map guest mmio space. Don't set HV_MAP_GPA_NOT_CA= CHED + * in hcall flags for slightly better performance, and in that case the gu= est + * stage 1 page table will control caching. + */ +int hv_map_mmio_pages(u64 partition_id, struct mshv_mem_region *reg, u64 g= fn, + u64 mmio_mfn) { - int i; - u32 flags =3D HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE | - HV_MAP_GPA_NOT_CACHED; + int rc; + u32 flags =3D HV_MAP_GPA_READABLE; + u64 hpages, numpgs, numpgs_in_hpage =3D HPAGE_SIZE / PAGE_SIZE; + + if (reg->hv_map_flags & HV_MAP_GPA_WRITABLE) + flags |=3D HV_MAP_GPA_WRITABLE; + if (reg->hv_map_flags & HV_MAP_GPA_EXECUTABLE) + flags |=3D HV_MAP_GPA_EXECUTABLE; + + mmio_mfn =3D mmio_mfn - (gfn - reg->start_gfn); /* start of the range */ + + numpgs =3D 0; + gfn =3D reg->start_gfn; + while (!HV_PAGE_COUNT_2M_ALIGNED(gfn) && + !HV_PAGE_COUNT_2M_ALIGNED(mmio_mfn) && + numpgs < reg->nr_pages) { + numpgs++; + gfn++; + } =20 - for (i =3D 0; i < numpgs; i++) - if (page_is_ram(mmio_spa + i)) - return -EINVAL; + if (numpgs) { + rc =3D hv_do_map_gpa_hcall(partition_id, reg->start_gfn, numpgs, + flags, NULL, mmio_mfn); + if (rc || numpgs =3D=3D reg->nr_pages) + return rc; + } + + mmio_mfn =3D mmio_mfn + numpgs; + numpgs =3D reg->nr_pages - numpgs; + + if (numpgs < numpgs_in_hpage) + return hv_do_map_gpa_hcall(partition_id, gfn, numpgs, flags, + NULL, mmio_mfn); + + for (hpages =3D 0; numpgs >=3D numpgs_in_hpage;) { + hpages++; + numpgs =3D numpgs - numpgs_in_hpage; + } + rc =3D hv_do_map_gpa_hcall(partition_id, gfn, hpages * numpgs_in_hpage, + flags | HV_MAP_GPA_LARGE_PAGE, NULL, mmio_mfn); + if (rc) + return rc; + + if (numpgs) { + gfn =3D gfn + hpages * numpgs_in_hpage; + mmio_mfn =3D mmio_mfn + hpages * numpgs_in_hpage; + + rc =3D hv_do_map_gpa_hcall(partition_id, gfn, numpgs, flags, NULL, + mmio_mfn); + } =20 - return hv_do_map_gpa_hcall(partition_id, gfn, numpgs, flags, NULL, - mmio_spa); + return rc; } =20 int hv_call_unmap_gpa_pages(u64 partition_id, u64 gfn, u64 page_count_4k, diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index a36e54bfa064..6bce4123c5ff 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -641,6 +641,87 @@ mshv_partition_region_by_gfn_get(struct mshv_partition= *p, u64 gfn) return region; } =20 +/* + * Check if uaddr is for mmio range. If yes, return 0 with mmio_pfn filled= in + * else just return -errno. + */ +static int mshv_chk_get_mmio_start_pfn(u64 uaddr, u64 *mmio_pfnp) +{ + struct vm_area_struct *vma; + bool is_mmio; + struct follow_pfnmap_args pfnmap_args; + int rc =3D -EINVAL; + + mmap_read_lock(current->mm); + vma =3D vma_lookup(current->mm, uaddr); + is_mmio =3D vma ? !!(vma->vm_flags & (VM_IO | VM_PFNMAP)) : 0; + if (!is_mmio) + goto unlock_mmap_out; + + pfnmap_args.vma =3D vma; + pfnmap_args.address =3D uaddr; + + rc =3D follow_pfnmap_start(&pfnmap_args); + if (rc) { + rc =3D fixup_user_fault(current->mm, uaddr, FAULT_FLAG_WRITE, + NULL); + if (rc) + goto unlock_mmap_out; + + rc =3D follow_pfnmap_start(&pfnmap_args); + if (rc) + goto unlock_mmap_out; + } + + *mmio_pfnp =3D pfnmap_args.pfn; + follow_pfnmap_end(&pfnmap_args); + +unlock_mmap_out: + mmap_read_unlock(current->mm); + return rc; +} + +/* + * Check if the unmapped gpa belongs to mmio space. If yes, resolve it. + * + * Returns: True if valid mmio intercept and handled, else false. + */ +static bool mshv_handle_unmapped_gpa(struct mshv_vp *vp) +{ + struct hv_message *hvmsg =3D vp->vp_intercept_msg_page; + u64 gfn, uaddr, mmio_mfn; + struct mshv_mem_region *rg; + int rc =3D -EINVAL; + struct mshv_partition *pt =3D vp->vp_partition; +#if defined(CONFIG_X86_64) + struct hv_x64_memory_intercept_message *msg =3D + (struct hv_x64_memory_intercept_message *)hvmsg->u.payload; +#elif defined(CONFIG_ARM64) + struct hv_arm64_memory_intercept_message *msg =3D + (struct hv_arm64_memory_intercept_message *)hvmsg->u.payload; +#endif + + gfn =3D msg->guest_physical_address >> HV_HYP_PAGE_SHIFT; + + rg =3D mshv_partition_region_by_gfn_get(pt, gfn); + if (rg =3D=3D NULL) + return false; + if (rg->mreg_type !=3D MSHV_REGION_TYPE_MMIO) + goto put_rg_out; + + uaddr =3D rg->start_uaddr + ((gfn - rg->start_gfn) << HV_HYP_PAGE_SHIFT); + + rc =3D mshv_chk_get_mmio_start_pfn(uaddr, &mmio_mfn); + if (rc) + goto put_rg_out; + + rc =3D hv_map_mmio_pages(pt->pt_id, rg, gfn, mmio_mfn); + +put_rg_out: + mshv_region_put(rg); + return rc =3D=3D 0; +} + /** * mshv_handle_gpa_intercept - Handle GPA (Guest Physical Address) interce= pts. * @vp: Pointer to the virtual processor structure. @@ -699,6 +780,8 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *v= p) static bool mshv_vp_handle_intercept(struct mshv_vp *vp) { switch (vp->vp_intercept_msg_page->header.message_type) { + case HVMSG_UNMAPPED_GPA: + return mshv_handle_unmapped_gpa(vp); case HVMSG_GPA_INTERCEPT: return mshv_handle_gpa_intercept(vp); } @@ -1322,16 +1405,8 @@ static int mshv_prepare_pinned_region(struct mshv_me= m_region *region) } =20 /* - * This maps two things: guest RAM and for pci passthru mmio space. - * - * mmio: - * - vfio overloads vm_pgoff to store the mmio start pfn/spa. - * - Two things need to happen for mapping mmio range: - * 1. mapped in the uaddr so VMM can access it. - * 2. mapped in the hwpt (gfn <-> mmio phys addr) so guest can access it. - * - * This function takes care of the second. The first one is managed by v= fio, - * and hence is taken care of via vfio_pci_mmap_fault(). + * This is called for both user ram and mmio space. The mmio space is not + * mapped here, but later during intercept on demand. */ static long mshv_map_user_memory(struct mshv_partition *partition, @@ -1340,7 +1415,6 @@ mshv_map_user_memory(struct mshv_partition *partition, struct mshv_mem_region *region; struct vm_area_struct *vma; bool is_mmio; - ulong mmio_pfn; long ret; =20 if (mem->flags & BIT(MSHV_SET_MEM_BIT_UNMAP) || @@ -1350,7 +1424,6 @@ mshv_map_user_memory(struct mshv_partition *partition, mmap_read_lock(current->mm); vma =3D vma_lookup(current->mm, mem->userspace_addr); is_mmio =3D vma ? !!(vma->vm_flags & (VM_IO | VM_PFNMAP)) : 0; - mmio_pfn =3D is_mmio ? vma->vm_pgoff : 0; mmap_read_unlock(current->mm); =20 if (!vma) @@ -1376,11 +1449,7 @@ mshv_map_user_memory(struct mshv_partition *partitio= n, region->nr_pages, HV_MAP_GPA_NO_ACCESS, NULL); break; - case MSHV_REGION_TYPE_MMIO: - ret =3D hv_call_map_mmio_pages(partition->pt_id, - region->start_gfn, - mmio_pfn, - region->nr_pages); + default: break; } =20 --=20 2.51.2.vfs.0.1 From nobody Sat Jul 25 04:53:33 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 94F2C1DF73A; Sat, 18 Jul 2026 02:20:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341227; cv=none; b=ArOyGD0RNXNiDYlN8a9RyaLqL2hmbE0raKaFVjJ3nnSAFeOmvYuiXuKh85mVT4Ieop5kXO5lwOOMSDFDkyOLikRW/T3/6bamDTyVzHfzD/Jyg+ajYGAL8iwlhMZvj5ml02Yppx56hIOkdLYa1ew/9Ylr4Yi/8MOyf45bJs72th0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784341227; c=relaxed/simple; bh=jyU2w0jeFYELp815bsCTGQ+aognhy4mbcl5lWzjtNb0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UZdKWqFcI+cMJ/6t1qa1i5IsreMfN5UFycFKjLZL4CXz8az3dbbOve7glqnr6+VpJmBlTuqM6udPtN0JKNvXCgCSUXXoxF2zNzGnI5OAE67iF1IJyMFFanHXmO6FOm5PXWsOFfbwf6YJYTGWQy8r8rIj8Z2xGYmXcejs34aQ1w0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=SkUKF79B; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="SkUKF79B" Received: from mrdev.corp.microsoft.com (unknown [40.86.181.13]) by linux.microsoft.com (Postfix) with ESMTPSA id 399EF20B7170; Fri, 17 Jul 2026 19:20:10 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 399EF20B7170 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1784341211; bh=l8wD5Vo36bk7TEZt0YFS7oU60KHn/BuclqHBWIjSUh4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SkUKF79B8bnHuYly2t8ucM7V+JDRfxf3QLVRQNlgwTAqTX1SrSbRuCzMYt98H4oP8 lzcyB3x+Fkom1XfZZ7qAu4K+vTql8QrzoM0EP2SCl5Is7oNVL3WN3W5qhf/ynLEFz7 JvVV8kM82NjZQE6Ql6iTQmuC6HGDB57PLm9rhHlc= From: Mukesh R To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org, wei.liu@kernel.org, mrathor@linux.microsoft.com, mhklinux@outlook.com, muislam@microsoft.com, namjain@linux.microsoft.com, magnuskulke@linux.microsoft.com, anbelski@linux.microsoft.com, linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org, linux-arch@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, decui@microsoft.com, longli@microsoft.com, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, joro@8bytes.org, will@kernel.org, lpieralisi@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com, arnd@arndb.de, jacob.pan@linux.microsoft.com Subject: [PATCH V4 9/9] mshv: Disable movable regions upfront if device passthru Date: Fri, 17 Jul 2026 19:19:49 -0700 Message-ID: <20260718021949.926306-10-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com> References: <20260718021949.926306-1-mrathor@linux.microsoft.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" If a VM is started with device attached, the mem regions must be marked non-movable as the device attach hypercall right away allows the use of SLAT for IOMMU. Marking them non-movable forces mapping of the entire guest RAM in the SLAT at the time of region creation along with the region pinned. Also, because a device could be dynamically attached much later in a VM, create a boot parameter to allow a user to pin them upfront if they anticipate such an action. It is understood that VFIO pins the regions also, so there will be double pinning. This will be enhanced in the near future with a third region type that is not movable nor pinned. Signed-off-by: Mukesh R --- drivers/hv/mshv_root.h | 1 + drivers/hv/mshv_root_main.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index c97a1b5da9ad..0282f489ad90 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -141,6 +141,7 @@ struct mshv_partition { pid_t pt_vmm_tgid; bool import_completed; bool pt_initialized; + bool pt_regions_pinned; #if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *pt_stats_dentry; struct dentry *pt_vp_dentry; diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 6bce4123c5ff..d7cc131652af 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -46,6 +46,10 @@ MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM= interface /dev/mshv"); #define HV_VP_COUNTER_ROOT_DISPATCH_THREAD_BLOCKED 95 #endif =20 +static uint hv_memreg; /* mshv_mem_region allocations type */ +module_param(hv_memreg, uint, 0644); +MODULE_PARM_DESC(hv_memreg, "0: movable (default), 1: pinned"); + struct mshv_root mshv_root; =20 enum hv_scheduler_type hv_scheduler_type; @@ -1293,6 +1297,12 @@ static void mshv_async_hvcall_handler(void *data, u6= 4 *status) *status =3D partition->async_hypercall_status; } =20 +static bool mshv_do_pt_regions_pinned(struct mshv_partition *pt) +{ + return pt->pt_regions_pinned || mshv_partition_encrypted(pt) || + hv_memreg =3D=3D 1; +} + /* * NB: caller checks and makes sure mem->size is page aligned * Returns: 0 with regionpp updated on success, or -errno @@ -1323,7 +1333,7 @@ static int mshv_partition_create_region(struct mshv_p= artition *partition, =20 if (is_mmio) rg->mreg_type =3D MSHV_REGION_TYPE_MMIO; - else if (mshv_partition_encrypted(partition) || + else if (mshv_do_pt_regions_pinned(partition) || !mshv_region_movable_init(rg)) rg->mreg_type =3D MSHV_REGION_TYPE_MEM_PINNED; else @@ -1801,6 +1811,9 @@ static long mshv_partition_ioctl_create_device(struct= mshv_partition *partition, if (copy_to_user(uarg, &devargk, sizeof(devargk))) return -EFAULT; /* cleanup in mshv_device_fop_release() */ =20 + /* For now, just mark regions pinned if there is device passthru. */ + partition->pt_regions_pinned =3D true; + return 0; =20 undo_out: --=20 2.51.2.vfs.0.1