From nobody Sat Feb 7 07:49:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 52E4F2264BA; Tue, 9 Dec 2025 05:11:41 +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=1765257102; cv=none; b=AAQLes4IF1MUxFFod8ZgX1kXTD7A26jYytkqCjQTx1P+b49HtG5wOmRU63Jqsdk+xG6CDr2qjYdzY3s+izAtS42Uq/+YCuSKrxnSPnHrE1GjqTjtDcRHyX54rzAkHatABMrhmKOMJ59DWn9yP0ayUCiG+vsTKr4EoJJYNzZfP+w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765257102; c=relaxed/simple; bh=+3p143fMjSAppT2Qj7LdMPmEhdytqzIMC8YXkd/Gslo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Fmbdaq/SAFGaP9xWafdJ4U5TN1yrFsPcEAOMJbG34GLVtsnGjqPnlafJ+sez+ZNo7AVnGk2vi5EXEHjXcJTK39wtUzqQxqjWCBq6Se1eISzMVO3vq9eZPKgklKLM36EgkYPYlL4rTBv+zHaQeFh7vDQP/juD1FRu8NjU2iU80y8= 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=i3ChbI+4; 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="i3ChbI+4" Received: from zhangyu-hyperv.mshome.net (unknown [167.220.232.230]) by linux.microsoft.com (Postfix) with ESMTPSA id 3B0AB201568D; Mon, 8 Dec 2025 21:11:36 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 3B0AB201568D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1765257100; bh=UvO9DsqrHAa+vgyuca9m8lV/Z4f5gmA9KXFanQPw9dI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i3ChbI+4CtB4IYKTOGVQl/4hLhMCQy38sL9A+5KxkZ2VFTF+5GHh63mnUNj1LN66t /FqojnkhWLVQdb8KEqD62aUYJ+RgL8TpytZo2DGDUDa5evOPiJwPaT4SeRsvEL/ojB mMZSpfoz8yYQO768MaHBYSTPQdonf0LzUxL1n824= From: Yu Zhang To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org, bhelgaas@google.com, arnd@arndb.de, joro@8bytes.org, will@kernel.org, robin.murphy@arm.com, easwar.hariharan@linux.microsoft.com, jacob.pan@linux.microsoft.com, nunodasneves@linux.microsoft.com, mrathor@linux.microsoft.com, mhklinux@outlook.com, peterz@infradead.org, linux-arch@vger.kernel.org Subject: [RFC v1 1/5] PCI: hv: Create and export hv_build_logical_dev_id() Date: Tue, 9 Dec 2025 13:11:24 +0800 Message-ID: <20251209051128.76913-2-zhangyu1@linux.microsoft.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com> References: <20251209051128.76913-1-zhangyu1@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" From: Easwar Hariharan Hyper-V uses a logical device ID to identify a PCI endpoint device for child partitions. This ID will also be required for future hypercalls used by the Hyper-V IOMMU driver. Refactor the logic for building this logical device ID into a standalone helper function and export the interface for wider use. Signed-off-by: Easwar Hariharan Signed-off-by: Yu Zhang Acked-by: Bjorn Helgaas --- drivers/pci/controller/pci-hyperv.c | 28 ++++++++++++++++++++-------- include/asm-generic/mshyperv.h | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/p= ci-hyperv.c index 146b43981b27..4b82e06b5d93 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -598,15 +598,31 @@ static unsigned int hv_msi_get_int_vector(struct irq_= data *data) =20 #define hv_msi_prepare pci_msi_prepare =20 +/** + * Build a "Device Logical ID" out of this PCI bus's instance GUID and the + * function number of the device. + */ +u64 hv_build_logical_dev_id(struct pci_dev *pdev) +{ + struct pci_bus *pbus =3D pdev->bus; + struct hv_pcibus_device *hbus =3D container_of(pbus->sysdata, + struct hv_pcibus_device, sysdata); + + return (u64)((hbus->hdev->dev_instance.b[5] << 24) | + (hbus->hdev->dev_instance.b[4] << 16) | + (hbus->hdev->dev_instance.b[7] << 8) | + (hbus->hdev->dev_instance.b[6] & 0xf8) | + PCI_FUNC(pdev->devfn)); +} +EXPORT_SYMBOL_GPL(hv_build_logical_dev_id); + /** * hv_irq_retarget_interrupt() - "Unmask" the IRQ by setting its current * affinity. * @data: Describes the IRQ * * Build new a destination for the MSI and make a hypercall to - * update the Interrupt Redirection Table. "Device Logical ID" - * is built out of this PCI bus's instance GUID and the function - * number of the device. + * update the Interrupt Redirection Table. */ static void hv_irq_retarget_interrupt(struct irq_data *data) { @@ -642,11 +658,7 @@ static void hv_irq_retarget_interrupt(struct irq_data = *data) params->int_entry.source =3D HV_INTERRUPT_SOURCE_MSI; params->int_entry.msi_entry.address.as_uint32 =3D int_desc->address & 0xf= fffffff; params->int_entry.msi_entry.data.as_uint32 =3D int_desc->data; - params->device_id =3D (hbus->hdev->dev_instance.b[5] << 24) | - (hbus->hdev->dev_instance.b[4] << 16) | - (hbus->hdev->dev_instance.b[7] << 8) | - (hbus->hdev->dev_instance.b[6] & 0xf8) | - PCI_FUNC(pdev->devfn); + params->device_id =3D hv_build_logical_dev_id(pdev); params->int_target.vector =3D hv_msi_get_int_vector(data); =20 if (hbus->protocol_version >=3D PCI_PROTOCOL_VERSION_1_2) { diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 64ba6bc807d9..1a205ed69435 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -71,6 +71,8 @@ extern enum hv_partition_type hv_curr_partition_type; extern void * __percpu *hyperv_pcpu_input_arg; extern void * __percpu *hyperv_pcpu_output_arg; =20 +extern u64 hv_build_logical_dev_id(struct pci_dev *pdev); + u64 hv_do_hypercall(u64 control, void *inputaddr, void *outputaddr); u64 hv_do_fast_hypercall8(u16 control, u64 input8); u64 hv_do_fast_hypercall16(u16 control, u64 input1, u64 input2); --=20 2.49.0 From nobody Sat Feb 7 07:49:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2E84427AC28; Tue, 9 Dec 2025 05:11:46 +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=1765257107; cv=none; b=I5eeyRuRc3Z5A5QTwCh+MSiPUNPxrWXgUghLcZlIU9m9iwB+2u8q11N0+sjEO3XovHaiJ5ahYRU2wtvu+DBCGnl5zie5IPoXEaFBtOGLyIT+O2qzVDotdlw56X9zJrWYm8nzPjPwI66xOfmpa38OZu3/shzq3NOrr24mlhVwTQ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765257107; c=relaxed/simple; bh=yxkvay7ppUK7zCSPMJkz9Is50IBjl8OT4F5+03I2GCI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fYR1VNB3WyV9TpWpfmJY/rjvoh0o/d2GjJ1OtOoSv6BKd+g5EyYnkZeUO8XcsDQWImvgFR9wWbBEVXO4PrPF9SO+IWIEQ1yGjGZ+CM28S8GSG3O1wVx5Mvb8tB19psmo3mlMfszMrAQ8xfJYIna6BhKVdwEPS2j6+50vOJKi9YU= 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=I/o9M7ko; 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="I/o9M7ko" Received: from zhangyu-hyperv.mshome.net (unknown [167.220.232.230]) by linux.microsoft.com (Postfix) with ESMTPSA id 6B4D42015694; Mon, 8 Dec 2025 21:11:41 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 6B4D42015694 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1765257105; bh=bB0p607TmqfIyRyLHIvV0ENjbM1faRQmO8b1AjVjqs0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I/o9M7koGFvYC6fY5jwYqKhJMq2SyL3Db/EhdSGEwwwocxD2mun7RMkPKSc9n2ojk YGE4RxoQ/kZ8hEcm7S6GldWHlCvehzdycvft6bvd9PHnJfUVCsSyYCXMgB2cagFrvP jPYWwT2zhCNRcv48snn3FDU4q7UxoD9VukFzLInU= From: Yu Zhang To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org, bhelgaas@google.com, arnd@arndb.de, joro@8bytes.org, will@kernel.org, robin.murphy@arm.com, easwar.hariharan@linux.microsoft.com, jacob.pan@linux.microsoft.com, nunodasneves@linux.microsoft.com, mrathor@linux.microsoft.com, mhklinux@outlook.com, peterz@infradead.org, linux-arch@vger.kernel.org Subject: [RFC v1 2/5] iommu: Move Hyper-V IOMMU driver to its own subdirectory Date: Tue, 9 Dec 2025 13:11:25 +0800 Message-ID: <20251209051128.76913-3-zhangyu1@linux.microsoft.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com> References: <20251209051128.76913-1-zhangyu1@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" From: Easwar Hariharan The Hyper-V IOMMU driver currently only supports IRQ remapping. As it will be adding DMA remapping support, prepare a directory to contain all the different feature files. This is a simple rename commit and has no functional changes. Signed-off-by: Easwar Hariharan Signed-off-by: Yu Zhang --- drivers/iommu/Kconfig | 10 +--------- drivers/iommu/Makefile | 2 +- drivers/iommu/hyperv/Kconfig | 10 ++++++++++ drivers/iommu/hyperv/Makefile | 2 ++ .../iommu/{hyperv-iommu.c =3D> hyperv/irq_remapping.c} | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 drivers/iommu/hyperv/Kconfig create mode 100644 drivers/iommu/hyperv/Makefile rename drivers/iommu/{hyperv-iommu.c =3D> hyperv/irq_remapping.c} (99%) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index c9ae3221cd6f..661ff4e764cc 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -194,6 +194,7 @@ config MSM_IOMMU source "drivers/iommu/amd/Kconfig" source "drivers/iommu/arm/Kconfig" source "drivers/iommu/intel/Kconfig" +source "drivers/iommu/hyperv/Kconfig" source "drivers/iommu/iommufd/Kconfig" source "drivers/iommu/riscv/Kconfig" =20 @@ -350,15 +351,6 @@ config MTK_IOMMU_V1 =20 if unsure, say N here. =20 -config HYPERV_IOMMU - bool "Hyper-V IRQ Handling" - depends on HYPERV && X86 - select IOMMU_API - default HYPERV - help - Stub IOMMU driver to handle IRQs to support Hyper-V Linux - guest and root partitions. - config VIRTIO_IOMMU tristate "Virtio IOMMU driver" depends on VIRTIO diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index b17ef9818759..757dc377cb66 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AMD_IOMMU) +=3D amd/ obj-$(CONFIG_INTEL_IOMMU) +=3D intel/ obj-$(CONFIG_RISCV_IOMMU) +=3D riscv/ obj-$(CONFIG_GENERIC_PT) +=3D generic_pt/fmt/ +obj-$(CONFIG_HYPERV_IOMMU) +=3D hyperv/ obj-$(CONFIG_IOMMU_API) +=3D iommu.o obj-$(CONFIG_IOMMU_SUPPORT) +=3D iommu-pages.o obj-$(CONFIG_IOMMU_API) +=3D iommu-traces.o @@ -29,7 +30,6 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) +=3D tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) +=3D exynos-iommu.o obj-$(CONFIG_FSL_PAMU) +=3D fsl_pamu.o fsl_pamu_domain.o obj-$(CONFIG_S390_IOMMU) +=3D s390-iommu.o -obj-$(CONFIG_HYPERV_IOMMU) +=3D hyperv-iommu.o obj-$(CONFIG_VIRTIO_IOMMU) +=3D virtio-iommu.o obj-$(CONFIG_IOMMU_SVA) +=3D iommu-sva.o obj-$(CONFIG_IOMMU_IOPF) +=3D io-pgfault.o diff --git a/drivers/iommu/hyperv/Kconfig b/drivers/iommu/hyperv/Kconfig new file mode 100644 index 000000000000..30f40d867036 --- /dev/null +++ b/drivers/iommu/hyperv/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# HyperV paravirtualized IOMMU support +config HYPERV_IOMMU + bool "Hyper-V IRQ Handling" + depends on HYPERV && X86 + select IOMMU_API + default HYPERV + help + Stub IOMMU driver to handle IRQs to support Hyper-V Linux + guest and root partitions. diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile new file mode 100644 index 000000000000..9f557bad94ff --- /dev/null +++ b/drivers/iommu/hyperv/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_HYPERV_IOMMU) +=3D irq_remapping.o diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv/irq_remapp= ing.c similarity index 99% rename from drivers/iommu/hyperv-iommu.c rename to drivers/iommu/hyperv/irq_remapping.c index 0961ac805944..f2c4c7d67302 100644 --- a/drivers/iommu/hyperv-iommu.c +++ b/drivers/iommu/hyperv/irq_remapping.c @@ -22,7 +22,7 @@ #include #include =20 -#include "irq_remapping.h" +#include "../irq_remapping.h" =20 #ifdef CONFIG_IRQ_REMAP =20 --=20 2.49.0 From nobody Sat Feb 7 07:49:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1FC3C2D2388; Tue, 9 Dec 2025 05:11:51 +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=1765257112; cv=none; b=kXjgs3z4mjJdJBKftp6YRSpyQnN3ZDt3A/DVpIyg3lWOR9kz6xwAZgvJC+8oZ4zieig3aw0Hz5gecd1WMBpsDgOt2lWZsKnFg2W+R4EVNYtdSZmwKQhyEv53ahUJ9xE0Oq1EuqIaW0wr0JLT4PJY+cTQKq66AoDxDcddwFSGkeY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765257112; c=relaxed/simple; bh=uZ8FQkbuP89JtXvhpoK2W8cWEh971YnxUtfH6onDaCY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uhnPjVy07zHCtCZTGw70UavgdnUiZQ23dHILVMtYLtXwm4ELpGfBfnDpGjdv2NNh45DScfrNphCoFHfU3hXgG2REh1eGpxFYuLE/GMNgn0HL0bVDtH63lUN6u+ZtTdzkNmxfqS2PAa3Ap0S2/7UMhO3gVN+d3Nl0JZv1nc7dd0E= 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=d0x6js15; 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="d0x6js15" Received: from zhangyu-hyperv.mshome.net (unknown [167.220.232.230]) by linux.microsoft.com (Postfix) with ESMTPSA id 473AC2015683; Mon, 8 Dec 2025 21:11:46 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 473AC2015683 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1765257110; bh=+fjEPwwpqVi0fhjnBnuNHGftSR6V5/vEADJlaXLDSuM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=d0x6js15MsRA31ZawMH/Pw56iJebVrKE14dWIK4rE0H2wLUjUyCvd/CoY/AjPOl9W An/dwv/Bhtj2XXwtPQHLMWQWt7cLRbOFgaT3cKyN8ff0/5pTkWHoto2nWRyYWHn4TR p0gUjbiVaxFDScUr88qYPD8f43LA8bpW2hadDV0s= From: Yu Zhang To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org, bhelgaas@google.com, arnd@arndb.de, joro@8bytes.org, will@kernel.org, robin.murphy@arm.com, easwar.hariharan@linux.microsoft.com, jacob.pan@linux.microsoft.com, nunodasneves@linux.microsoft.com, mrathor@linux.microsoft.com, mhklinux@outlook.com, peterz@infradead.org, linux-arch@vger.kernel.org Subject: [RFC v1 3/5] hyperv: Introduce new hypercall interfaces used by Hyper-V guest IOMMU Date: Tue, 9 Dec 2025 13:11:26 +0800 Message-ID: <20251209051128.76913-4-zhangyu1@linux.microsoft.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com> References: <20251209051128.76913-1-zhangyu1@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" From: Wei Liu Hyper-V guest IOMMU is a para-virtualized IOMMU based on hypercalls. Introduce the hypercalls used by the child partition to interact with this facility. These hypercalls fall into below categories: - Detection and capability: HVCALL_GET_IOMMU_CAPABILITIES is used to detect the existence and capabilities of the guest IOMMU. - Device management: HVCALL_GET_LOGICAL_DEVICE_PROPERTY is used to check whether an endpoint device is managed by the guest IOMMU. - Domain management: A set of hypercalls is provided to handle the creation, configuration, and deletion of guest domains, as well as the attachment/detachment of endpoint devices to/from those domains. - IOTLB flushing: HVCALL_FLUSH_DEVICE_DOMAIN is used to ask Hyper-V for a domain-selective IOTLB flush(which in its handler may flush the device TLB as well). Page-selective IOTLB flushes will be offered by new hypercalls in future patches. Signed-off-by: Wei Liu Co-developed-by: Jacob Pan Signed-off-by: Jacob Pan Co-developed-by: Easwar Hariharan Signed-off-by: Easwar Hariharan Co-developed-by: Yu Zhang Signed-off-by: Yu Zhang --- include/hyperv/hvgdk_mini.h | 8 +++ include/hyperv/hvhdk_mini.h | 123 ++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 77abddfc750e..e5b302bbfe14 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -478,10 +478,16 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_= ASSIST_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_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_CONFIGURE_DEVICE_DOMAIN 0x00ce +#define HVCALL_FLUSH_DEVICE_DOMAIN 0x00d0 #define HVCALL_ACQUIRE_SPARSE_SPA_PAGE_HOST_ACCESS 0x00d7 #define HVCALL_RELEASE_SPARSE_SPA_PAGE_HOST_ACCESS 0x00d8 #define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db @@ -492,6 +498,8 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_AS= SIST_PAGE */ #define HVCALL_GET_VP_CPUID_VALUES 0x00f4 #define HVCALL_MMIO_READ 0x0106 #define HVCALL_MMIO_WRITE 0x0107 +#define HVCALL_GET_IOMMU_CAPABILITIES 0x0125 +#define HVCALL_GET_LOGICAL_DEVICE_PROPERTY 0x0127 =20 /* HV_HYPERCALL_INPUT */ #define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0) diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index 858f6a3925b3..ba6b91746b13 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -400,4 +400,127 @@ union hv_device_id { /* HV_DEVICE_ID */ } acpi; } __packed; =20 +/* Device domain types */ +#define HV_DEVICE_DOMAIN_TYPE_S1 1 /* Stage 1 domain */ + +/* ID for default domain and NULL domain */ +#define HV_DEVICE_DOMAIN_ID_DEFAULT 0 +#define HV_DEVICE_DOMAIN_ID_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 { + 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 { + u32 as_uint32; + struct { + u32 forward_progress_required: 1; + u32 inherit_owning_vtl: 1; + u32 reserved: 30; + } __packed; +}; + +struct 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 { + struct hv_input_device_domain device_domain; +} __packed; + +struct 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 { + u64 partition_id; + union hv_device_id device_id; +} __packed; + +struct hv_device_domain_settings { + struct { + /* + * Enable translations. If not enabled, all transaction bypass + * S1 translations. + */ + u64 translation_enabled: 1; + u64 blocked: 1; + /* + * First stage address translation paging mode: + * 0: 4-level paging (default) + * 1: 5-level paging + */ + u64 first_stage_paging_mode: 1; + u64 reserved: 61; + } flags; + + /* Address of translation table */ + u64 page_table_root; +} __packed; + +struct hv_input_configure_device_domain { + struct hv_input_device_domain device_domain; + struct hv_device_domain_settings settings; +} __packed; + +struct hv_input_get_iommu_capabilities { + u64 partition_id; + u64 reserved; +} __packed; + +struct hv_output_get_iommu_capabilities { + u32 size; + u16 reserved; + u8 max_iova_width; + u8 max_pasid_width; + +#define HV_IOMMU_CAP_PRESENT (1ULL << 0) +#define HV_IOMMU_CAP_S2 (1ULL << 1) +#define HV_IOMMU_CAP_S1 (1ULL << 2) +#define HV_IOMMU_CAP_S1_5LVL (1ULL << 3) +#define HV_IOMMU_CAP_PASID (1ULL << 4) +#define HV_IOMMU_CAP_ATS (1ULL << 5) +#define HV_IOMMU_CAP_PRI (1ULL << 6) + + u64 iommu_cap; + u64 pgsize_bitmap; +} __packed; + +enum hv_logical_device_property_code { + HV_LOGICAL_DEVICE_PROPERTY_PVIOMMU =3D 10, +}; + +struct hv_input_get_logical_device_property { + u64 partition_id; + u64 logical_device_id; + enum hv_logical_device_property_code code; + u32 reserved; +} __packed; + +struct hv_output_get_logical_device_property { +#define HV_DEVICE_IOMMU_ENABLED (1ULL << 0) + u64 device_iommu; + u64 reserved; +} __packed; + +struct hv_input_flush_device_domain { + struct hv_input_device_domain device_domain; + u32 flags; + u32 reserved; +} __packed; + #endif /* _HV_HVHDK_MINI_H */ --=20 2.49.0 From nobody Sat Feb 7 07:49:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id EB17627935F; Tue, 9 Dec 2025 05:11:55 +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=1765257118; cv=none; b=XBnvJnHitfLPflx1CeCu0y1jGn0gc7OnfO5x++9EGveAOcAHAd2wPs719mnC6rFeP9ODIhluG9xWroobntXblmoeJGkHgvEJsFLJYz13SPsnnu02ZCNL/yH+/udtWgN5Wl4a2n/osObhr2AB4ua8G2NcCcC83W9ncCJsCp+fLZ0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765257118; c=relaxed/simple; bh=V//6OM2Jz+dzKFoZiudlThjDt6NL+I1qWnnI6UTQ2Js=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JwpKN7S5MW5x4Wxi0GyJn5MNJ1qkcfzwBPd20s+Gh/6ASMcTKy5Vb7rFyyl5No+ONmgVRepQlGAeJ9OGJVBIeoLUc2UcSidb9Cj98fJZvKLyXjcoSQZEXMqPaQ8pTmwzu7AW5ogcxKRaMGqsyyuiI34ZlLi8kw+7r/sMJIZXaiY= 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=sLrPF2Jp; 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="sLrPF2Jp" Received: from zhangyu-hyperv.mshome.net (unknown [167.220.232.230]) by linux.microsoft.com (Postfix) with ESMTPSA id 2F0142015692; Mon, 8 Dec 2025 21:11:51 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 2F0142015692 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1765257115; bh=kgOzUp6enPiadf1QwJ7kHsJyJDKUdcrrZkezB6/Q3dU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sLrPF2JpxoHOxPm0XW1E8Wpq00u2QbQbf1Gtjk4U7DRPE30OcdnHCxgAkSE5/xnIH d5rqFLixR4Y9+bNH60Hd7WtP06IjJq10D3hBW1cS+jn0vx4/7jxDCqKl2es+CsWD6y 6Wk4iCJsvaIeWqbCZO5lae+m9SHkiQTJdALB/Kc4= From: Yu Zhang To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org, bhelgaas@google.com, arnd@arndb.de, joro@8bytes.org, will@kernel.org, robin.murphy@arm.com, easwar.hariharan@linux.microsoft.com, jacob.pan@linux.microsoft.com, nunodasneves@linux.microsoft.com, mrathor@linux.microsoft.com, mhklinux@outlook.com, peterz@infradead.org, linux-arch@vger.kernel.org Subject: [RFC v1 4/5] hyperv: allow hypercall output pages to be allocated for child partitions Date: Tue, 9 Dec 2025 13:11:27 +0800 Message-ID: <20251209051128.76913-5-zhangyu1@linux.microsoft.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com> References: <20251209051128.76913-1-zhangyu1@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" Previously, the allocation of per-CPU output argument pages was restricted to root partitions or those operating in VTL mode. Remove this restriction to support guest IOMMU related hypercalls, which require valid output pages to function correctly. While unconditionally allocating per-CPU output pages scales with the number of vCPUs, and potentially adding overhead for guests that may not utilize t= he IOMMU, this change anticipates that future hypercalls from child partitions may also require these output pages. Signed-off-by: Yu Zhang --- drivers/hv/hv_common.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index e109a620c83f..034fb2592884 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -255,11 +255,6 @@ static void hv_kmsg_dump_register(void) } } =20 -static inline bool hv_output_page_exists(void) -{ - return hv_parent_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE); -} - void __init hv_get_partition_id(void) { struct hv_output_get_partition_id *output; @@ -371,11 +366,9 @@ int __init hv_common_init(void) hyperv_pcpu_input_arg =3D alloc_percpu(void *); BUG_ON(!hyperv_pcpu_input_arg); =20 - /* Allocate the per-CPU state for output arg for root */ - if (hv_output_page_exists()) { - hyperv_pcpu_output_arg =3D alloc_percpu(void *); - BUG_ON(!hyperv_pcpu_output_arg); - } + /* Allocate the per-CPU state for output arg*/ + hyperv_pcpu_output_arg =3D alloc_percpu(void *); + BUG_ON(!hyperv_pcpu_output_arg); =20 if (hv_parent_partition()) { hv_synic_eventring_tail =3D alloc_percpu(u8 *); @@ -473,7 +466,7 @@ int hv_common_cpu_init(unsigned int cpu) u8 **synic_eventring_tail; u64 msr_vp_index; gfp_t flags; - const int pgcount =3D hv_output_page_exists() ? 2 : 1; + const int pgcount =3D 2; void *mem; int ret =3D 0; =20 @@ -491,10 +484,8 @@ int hv_common_cpu_init(unsigned int cpu) if (!mem) return -ENOMEM; =20 - if (hv_output_page_exists()) { - outputarg =3D (void **)this_cpu_ptr(hyperv_pcpu_output_arg); - *outputarg =3D (char *)mem + HV_HYP_PAGE_SIZE; - } + outputarg =3D (void **)this_cpu_ptr(hyperv_pcpu_output_arg); + *outputarg =3D (char *)mem + HV_HYP_PAGE_SIZE; =20 if (!ms_hyperv.paravisor_present && (hv_isolation_type_snp() || hv_isolation_type_tdx())) { --=20 2.49.0 From nobody Sat Feb 7 07:49:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DB5B127935F; Tue, 9 Dec 2025 05:12:01 +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=1765257124; cv=none; b=KIGcxYQUnd4VgbHhxByv8r+C8PM1GJt8xZ8tnZaSZiF/HQebSZsRZKEfoJAryke96Lhbi6Ox5hyUAeU9sP0leBOKgtZTbPd916MzoUG+oDsz/m/SRiWqthm3ag7iMW1XAJH6AzexN3LSZlAltX27roy/mfh3wFqKmK1S25loLoI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765257124; c=relaxed/simple; bh=L82HXe73SVx67NNaZXv8amAvz07p+NaULya+Vyvg4bY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cWP1BeNIlmvpT/O8GOPoSctUo8RFV4UQibc44iqIhWMzoRdmHvX4fEkstJcEo7ZgPhXHp5RziI58uHRazeO4weFGBxg2IXMrsbTpgZcBrRon4ikJiygwyz4uzDZqJ4gtORVrXSy/spjRd4qfTYo1Y2qwoAenA5kYgHbIo/XwQTg= 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=b69JYPQz; 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="b69JYPQz" Received: from zhangyu-hyperv.mshome.net (unknown [167.220.232.230]) by linux.microsoft.com (Postfix) with ESMTPSA id E63C4201568D; Mon, 8 Dec 2025 21:11:55 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com E63C4201568D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1765257120; bh=21lCcfM5o1iG7jk9CsmQjcy+0iwTDq8Q7ftlfWHO2ng=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=b69JYPQzv5EY8vInUrxi7ujYELxgc23q2zxOeEWw+nNGU3GLa8YXVT9qVJm9gIW7B Q0jqIsAQtaCKWIay1yKV1wrJ7nSVpLch8GeiclmlQJIkg8cdnIgxX13b/IS9I7aVOQ cpD3uDgSA5xyPbhe+rfKgEuyyTCM0HB8/4C238J4= From: Yu Zhang To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org, iommu@lists.linux.dev, linux-pci@vger.kernel.org Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org, bhelgaas@google.com, arnd@arndb.de, joro@8bytes.org, will@kernel.org, robin.murphy@arm.com, easwar.hariharan@linux.microsoft.com, jacob.pan@linux.microsoft.com, nunodasneves@linux.microsoft.com, mrathor@linux.microsoft.com, mhklinux@outlook.com, peterz@infradead.org, linux-arch@vger.kernel.org Subject: [RFC v1 5/5] iommu/hyperv: Add para-virtualized IOMMU support for Hyper-V guest Date: Tue, 9 Dec 2025 13:11:28 +0800 Message-ID: <20251209051128.76913-6-zhangyu1@linux.microsoft.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com> References: <20251209051128.76913-1-zhangyu1@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 para-virtualized IOMMU driver for Linux guests running on Hyper-V. This driver implements stage-1 IO translation within the guest OS. It integrates with the Linux IOMMU core, utilizing Hyper-V hypercalls for: - Capability discovery - Domain allocation, configuration, and deallocation - Device attachment and detachment - IOTLB invalidation The driver constructs x86-compatible stage-1 IO page tables in the guest memory using consolidated IO page table helpers. This allows the guest to manage stage-1 translations independently of vendor- specific drivers (like Intel VT-d or AMD IOMMU). Hyper-v consumes this stage-1 IO page table, when a device domain is created and configured, and nests it with the host's stage-2 IO page tables, therefore elemenating the VM exits for guest IOMMU mapping operations. For guest IOMMU unmapping operations, VM exits to perform the IOTLB flush(and possibly the device TLB flush) is still unavoidable. For now, HVCALL_FLUSH_DEVICE_DOMAIN is used to implement a domain-selective IOTLB flush. New hypercalls for finer-grained hypercall will be provided in future patches. Co-developed-by: Wei Liu Signed-off-by: Wei Liu Co-developed-by: Jacob Pan Signed-off-by: Jacob Pan Co-developed-by: Easwar Hariharan Signed-off-by: Easwar Hariharan Signed-off-by: Yu Zhang --- drivers/iommu/hyperv/Kconfig | 14 + drivers/iommu/hyperv/Makefile | 1 + drivers/iommu/hyperv/iommu.c | 608 ++++++++++++++++++++++++++++++++++ drivers/iommu/hyperv/iommu.h | 53 +++ 4 files changed, 676 insertions(+) create mode 100644 drivers/iommu/hyperv/iommu.c create mode 100644 drivers/iommu/hyperv/iommu.h diff --git a/drivers/iommu/hyperv/Kconfig b/drivers/iommu/hyperv/Kconfig index 30f40d867036..fa3c77752d7b 100644 --- a/drivers/iommu/hyperv/Kconfig +++ b/drivers/iommu/hyperv/Kconfig @@ -8,3 +8,17 @@ config HYPERV_IOMMU help Stub IOMMU driver to handle IRQs to support Hyper-V Linux guest and root partitions. + +if HYPERV_IOMMU +config HYPERV_PVIOMMU + bool "Microsoft Hypervisor para-virtualized IOMMU support" + depends on X86 && HYPERV && PCI_HYPERV + depends on IOMMU_PT + select IOMMU_API + select IOMMU_DMA + select DMA_OPS + select IOMMU_IOVA + default HYPERV + help + A para-virtualized IOMMU for Microsoft Hypervisor guest. +endif diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile index 9f557bad94ff..8669741c0a51 100644 --- a/drivers/iommu/hyperv/Makefile +++ b/drivers/iommu/hyperv/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_HYPERV_IOMMU) +=3D irq_remapping.o +obj-$(CONFIG_HYPERV_PVIOMMU) +=3D iommu.o diff --git a/drivers/iommu/hyperv/iommu.c b/drivers/iommu/hyperv/iommu.c new file mode 100644 index 000000000000..3d0aff868e16 --- /dev/null +++ b/drivers/iommu/hyperv/iommu.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Hyper-V IOMMU driver. + * + * Copyright (C) 2019, 2024-2025 Microsoft, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iommu.h" +#include "../dma-iommu.h" +#include "../iommu-pages.h" + +static void hv_iommu_detach_dev(struct iommu_domain *domain, struct device= *dev); +static void hv_flush_device_domain(struct hv_iommu_domain *hv_domain); +struct hv_iommu_dev *hv_iommu_device; +static struct hv_iommu_domain hv_identity_domain; +static struct hv_iommu_domain hv_blocking_domain; +static const struct iommu_domain_ops hv_iommu_identity_domain_ops; +static const struct iommu_domain_ops hv_iommu_blocking_domain_ops; +static struct iommu_ops hv_iommu_ops; + +#define hv_iommu_present(iommu_cap) (iommu_cap & HV_IOMMU_CAP_PRESENT) +#define hv_iommu_s1_domain_supported(iommu_cap) (iommu_cap & HV_IOMMU_CAP_= S1) +#define hv_iommu_5lvl_supported(iommu_cap) (iommu_cap & HV_IOMMU_CAP_S1_5L= VL) +#define hv_iommu_ats_supported(iommu_cap) (iommu_cap & HV_IOMMU_CAP_ATS) + +static int hv_create_device_domain(struct hv_iommu_domain *hv_domain, u32 = domain_stage) +{ + int ret; + u64 status; + unsigned long flags; + struct hv_input_create_device_domain *input; + + ret =3D ida_alloc_range(&hv_iommu_device->domain_ids, + hv_iommu_device->first_domain, hv_iommu_device->last_domain, + GFP_KERNEL); + if (ret < 0) + return ret; + + hv_domain->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + hv_domain->device_domain.domain_id.type =3D domain_stage; + hv_domain->device_domain.domain_id.id =3D ret; + hv_domain->hv_iommu =3D hv_iommu_device; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + input->device_domain =3D hv_domain->device_domain; + 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)) { + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + ida_free(&hv_iommu_device->domain_ids, hv_domain->device_domain.domain_i= d.id); + } + + return hv_result_to_errno(status); +} + +static void hv_delete_device_domain(struct hv_iommu_domain *hv_domain) +{ + u64 status; + unsigned long flags; + struct hv_input_delete_device_domain *input; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + input->device_domain =3D hv_domain->device_domain; + status =3D hv_do_hypercall(HVCALL_DELETE_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + + ida_free(&hv_domain->hv_iommu->domain_ids, hv_domain->device_domain.domai= n_id.id); +} + +static bool hv_iommu_capable(struct device *dev, enum iommu_cap cap) +{ + switch (cap) { + case IOMMU_CAP_CACHE_COHERENCY: + return true; + case IOMMU_CAP_DEFERRED_FLUSH: + return true; + default: + return false; + } +} + +static int hv_iommu_attach_dev(struct iommu_domain *domain, struct device = *dev) +{ + u64 status; + unsigned long flags; + struct pci_dev *pdev; + struct hv_input_attach_device_domain *input; + struct hv_iommu_endpoint *vdev =3D dev_iommu_priv_get(dev); + struct hv_iommu_domain *hv_domain =3D to_hv_iommu_domain(domain); + + /* Only allow PCI devices for now */ + if (!dev_is_pci(dev)) + return -EINVAL; + + if (vdev->hv_domain =3D=3D hv_domain) + return 0; + + if (vdev->hv_domain) + hv_iommu_detach_dev(&vdev->hv_domain->domain, dev); + + pdev =3D to_pci_dev(dev); + dev_dbg(dev, "Attaching (%strusted) to %d\n", pdev->untrusted ? "un" : "", + hv_domain->device_domain.domain_id.id); + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + input->device_domain =3D hv_domain->device_domain; + input->device_id.as_uint64 =3D hv_build_logical_dev_id(pdev); + status =3D hv_do_hypercall(HVCALL_ATTACH_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) { + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + } else { + vdev->hv_domain =3D hv_domain; + spin_lock_irqsave(&hv_domain->lock, flags); + list_add(&vdev->list, &hv_domain->dev_list); + spin_unlock_irqrestore(&hv_domain->lock, flags); + } + + return hv_result_to_errno(status); +} + +static void hv_iommu_detach_dev(struct iommu_domain *domain, struct device= *dev) +{ + u64 status; + unsigned long flags; + struct hv_input_detach_device_domain *input; + struct pci_dev *pdev; + struct hv_iommu_domain *hv_domain =3D to_hv_iommu_domain(domain); + struct hv_iommu_endpoint *vdev =3D dev_iommu_priv_get(dev); + + /* See the attach function, only PCI devices for now */ + if (!dev_is_pci(dev) || vdev->hv_domain !=3D hv_domain) + return; + + pdev =3D to_pci_dev(dev); + + dev_dbg(dev, "Detaching from %d\n", hv_domain->device_domain.domain_id.id= ); + + 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_logical_dev_id(pdev); + status =3D hv_do_hypercall(HVCALL_DETACH_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + + spin_lock_irqsave(&hv_domain->lock, flags); + hv_flush_device_domain(hv_domain); + list_del(&vdev->list); + spin_unlock_irqrestore(&hv_domain->lock, flags); + + vdev->hv_domain =3D NULL; +} + +static int hv_iommu_get_logical_device_property(struct device *dev, + enum hv_logical_device_property_code code, + struct hv_output_get_logical_device_property *property) +{ + u64 status; + unsigned long flags; + struct hv_input_get_logical_device_property *input; + struct hv_output_get_logical_device_property *output; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + output =3D *this_cpu_ptr(hyperv_pcpu_output_arg); + memset(input, 0, sizeof(*input)); + memset(output, 0, sizeof(*output)); + input->partition_id =3D HV_PARTITION_ID_SELF; + input->logical_device_id =3D hv_build_logical_dev_id(to_pci_dev(dev)); + input->code =3D code; + status =3D hv_do_hypercall(HVCALL_GET_LOGICAL_DEVICE_PROPERTY, input, out= put); + *property =3D *output; + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + + return hv_result_to_errno(status); +} + +static struct iommu_device *hv_iommu_probe_device(struct device *dev) +{ + struct pci_dev *pdev; + struct hv_iommu_endpoint *vdev; + struct hv_output_get_logical_device_property device_iommu_property =3D {0= }; + + if (!dev_is_pci(dev)) + return ERR_PTR(-ENODEV); + + if (hv_iommu_get_logical_device_property(dev, + HV_LOGICAL_DEVICE_PROPERTY_PVIOMMU, + &device_iommu_property) || + !(device_iommu_property.device_iommu & HV_DEVICE_IOMMU_ENABLED)) + return ERR_PTR(-ENODEV); + + pdev =3D to_pci_dev(dev); + vdev =3D kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return ERR_PTR(-ENOMEM); + + vdev->dev =3D dev; + vdev->hv_iommu =3D hv_iommu_device; + dev_iommu_priv_set(dev, vdev); + + if (hv_iommu_ats_supported(hv_iommu_device->cap) && + pci_ats_supported(pdev)) + pci_enable_ats(pdev, __ffs(hv_iommu_device->pgsize_bitmap)); + + return &vdev->hv_iommu->iommu; +} + +static void hv_iommu_release_device(struct device *dev) +{ + struct hv_iommu_endpoint *vdev =3D dev_iommu_priv_get(dev); + + if (vdev->hv_domain) + hv_iommu_detach_dev(&vdev->hv_domain->domain, dev); + + dev_iommu_priv_set(dev, NULL); + set_dma_ops(dev, NULL); + + kfree(vdev); +} + +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_configure_device_domain(struct hv_iommu_domain *hv_domain, u= 32 domain_type) +{ + u64 status; + unsigned long flags; + struct pt_iommu_x86_64_hw_info pt_info; + struct hv_input_configure_device_domain *input; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + input->device_domain =3D hv_domain->device_domain; + input->settings.flags.blocked =3D (domain_type =3D=3D IOMMU_DOMAIN_BLOCKE= D); + input->settings.flags.translation_enabled =3D (domain_type !=3D IOMMU_DOM= AIN_IDENTITY); + + if (domain_type & __IOMMU_DOMAIN_PAGING) { + pt_iommu_x86_64_hw_info(&hv_domain->pt_iommu_x86_64, &pt_info); + input->settings.page_table_root =3D pt_info.gcr3_pt; + input->settings.flags.first_stage_paging_mode =3D + pt_info.levels =3D=3D 5; + } + status =3D hv_do_hypercall(HVCALL_CONFIGURE_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + + return hv_result_to_errno(status); +} + +static int __init hv_initialize_static_domains(void) +{ + int ret; + struct hv_iommu_domain *hv_domain; + + /* Default stage-1 identity domain */ + hv_domain =3D &hv_identity_domain; + memset(hv_domain, 0, sizeof(*hv_domain)); + + ret =3D hv_create_device_domain(hv_domain, HV_DEVICE_DOMAIN_TYPE_S1); + if (ret) + return ret; + + ret =3D hv_configure_device_domain(hv_domain, IOMMU_DOMAIN_IDENTITY); + if (ret) + goto delete_identity_domain; + + hv_domain->domain.type =3D IOMMU_DOMAIN_IDENTITY; + hv_domain->domain.ops =3D &hv_iommu_identity_domain_ops; + hv_domain->domain.owner =3D &hv_iommu_ops; + hv_domain->domain.geometry =3D hv_iommu_device->geometry; + hv_domain->domain.pgsize_bitmap =3D hv_iommu_device->pgsize_bitmap; + INIT_LIST_HEAD(&hv_domain->dev_list); + + /* Default stage-1 blocked domain */ + hv_domain =3D &hv_blocking_domain; + memset(hv_domain, 0, sizeof(*hv_domain)); + + ret =3D hv_create_device_domain(hv_domain, HV_DEVICE_DOMAIN_TYPE_S1); + if (ret) + goto delete_identity_domain; + + ret =3D hv_configure_device_domain(hv_domain, IOMMU_DOMAIN_BLOCKED); + if (ret) + goto delete_blocked_domain; + + hv_domain->domain.type =3D IOMMU_DOMAIN_BLOCKED; + hv_domain->domain.ops =3D &hv_iommu_blocking_domain_ops; + hv_domain->domain.owner =3D &hv_iommu_ops; + hv_domain->domain.geometry =3D hv_iommu_device->geometry; + hv_domain->domain.pgsize_bitmap =3D hv_iommu_device->pgsize_bitmap; + INIT_LIST_HEAD(&hv_domain->dev_list); + + return 0; + +delete_blocked_domain: + hv_delete_device_domain(&hv_blocking_domain); +delete_identity_domain: + hv_delete_device_domain(&hv_identity_domain); + return ret; +} + +#define INTERRUPT_RANGE_START (0xfee00000) +#define INTERRUPT_RANGE_END (0xfeefffff) +static void hv_iommu_get_resv_regions(struct device *dev, + struct list_head *head) +{ + struct iommu_resv_region *region; + + region =3D iommu_alloc_resv_region(INTERRUPT_RANGE_START, + INTERRUPT_RANGE_END - INTERRUPT_RANGE_START + 1, + 0, IOMMU_RESV_MSI, GFP_KERNEL); + if (!region) + return; + + list_add_tail(®ion->list, head); +} + +static void hv_flush_device_domain(struct hv_iommu_domain *hv_domain) +{ + u64 status; + unsigned long flags; + struct hv_input_flush_device_domain *input; + + 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_domain->device_domain.partition_= id; + input->device_domain.owner_vtl =3D hv_domain->device_domain.owner_vtl; + input->device_domain.domain_id.type =3D hv_domain->device_domain.domain_i= d.type; + input->device_domain.domain_id.id =3D hv_domain->device_domain.domain_id.= id; + status =3D hv_do_hypercall(HVCALL_FLUSH_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); +} + +static void hv_iommu_flush_iotlb_all(struct iommu_domain *domain) +{ + hv_flush_device_domain(to_hv_iommu_domain(domain)); +} + +static void hv_iommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *iotlb_gather) +{ + hv_flush_device_domain(to_hv_iommu_domain(domain)); + + iommu_put_pages_list(&iotlb_gather->freelist); +} + +static void hv_iommu_paging_domain_free(struct iommu_domain *domain) +{ + struct hv_iommu_domain *hv_domain =3D to_hv_iommu_domain(domain); + + /* Free all remaining mappings */ + pt_iommu_deinit(&hv_domain->pt_iommu); + + hv_delete_device_domain(hv_domain); + + kfree(hv_domain); +} + +static const struct iommu_domain_ops hv_iommu_identity_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, +}; + +static const struct iommu_domain_ops hv_iommu_blocking_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, +}; + +static const struct iommu_domain_ops hv_iommu_paging_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, + IOMMU_PT_DOMAIN_OPS(x86_64), + .flush_iotlb_all =3D hv_iommu_flush_iotlb_all, + .iotlb_sync =3D hv_iommu_iotlb_sync, + .free =3D hv_iommu_paging_domain_free, +}; + +static struct iommu_domain *hv_iommu_domain_alloc_paging(struct device *de= v) +{ + int ret; + struct hv_iommu_domain *hv_domain; + struct pt_iommu_x86_64_cfg cfg =3D {}; + + hv_domain =3D kzalloc(sizeof(*hv_domain), GFP_KERNEL); + if (!hv_domain) + return ERR_PTR(-ENOMEM); + + ret =3D hv_create_device_domain(hv_domain, HV_DEVICE_DOMAIN_TYPE_S1); + if (ret) { + kfree(hv_domain); + return ERR_PTR(ret); + } + + hv_domain->domain.pgsize_bitmap =3D hv_iommu_device->pgsize_bitmap; + hv_domain->domain.geometry =3D hv_iommu_device->geometry; + hv_domain->pt_iommu.nid =3D dev_to_node(dev); + INIT_LIST_HEAD(&hv_domain->dev_list); + spin_lock_init(&hv_domain->lock); + + cfg.common.hw_max_vasz_lg2 =3D hv_iommu_device->max_iova_width; + cfg.common.hw_max_oasz_lg2 =3D 52; + + ret =3D pt_iommu_x86_64_init(&hv_domain->pt_iommu_x86_64, &cfg, GFP_KERNE= L); + if (ret) { + hv_delete_device_domain(hv_domain); + return ERR_PTR(ret); + } + + hv_domain->domain.ops =3D &hv_iommu_paging_domain_ops; + + ret =3D hv_configure_device_domain(hv_domain, __IOMMU_DOMAIN_PAGING); + if (ret) { + pt_iommu_deinit(&hv_domain->pt_iommu); + hv_delete_device_domain(hv_domain); + return ERR_PTR(ret); + } + + return &hv_domain->domain; +} + +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, + .release_device =3D hv_iommu_release_device, + .device_group =3D hv_iommu_device_group, + .get_resv_regions =3D hv_iommu_get_resv_regions, + .owner =3D THIS_MODULE, + .identity_domain =3D &hv_identity_domain.domain, + .blocked_domain =3D &hv_blocking_domain.domain, + .release_domain =3D &hv_blocking_domain.domain, +}; + +static void hv_iommu_shutdown(void) +{ + iommu_device_sysfs_remove(&hv_iommu_device->iommu); + + kfree(hv_iommu_device); +} + +static struct syscore_ops hv_iommu_syscore_ops =3D { + .shutdown =3D hv_iommu_shutdown, +}; + +static int hv_iommu_detect(struct hv_output_get_iommu_capabilities *hv_iom= mu_cap) +{ + u64 status; + unsigned long flags; + struct hv_input_get_iommu_capabilities *input; + struct hv_output_get_iommu_capabilities *output; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + output =3D *this_cpu_ptr(hyperv_pcpu_output_arg); + memset(input, 0, sizeof(*input)); + memset(output, 0, sizeof(*output)); + input->partition_id =3D HV_PARTITION_ID_SELF; + status =3D hv_do_hypercall(HVCALL_GET_IOMMU_CAPABILITIES, input, output); + *hv_iommu_cap =3D *output; + + local_irq_restore(flags); + + if (!hv_result_success(status)) + pr_err("%s: hypercall failed, status %lld\n", __func__, status); + + return hv_result_to_errno(status); +} + +static void __init hv_init_iommu_device(struct hv_iommu_dev *hv_iommu, + struct hv_output_get_iommu_capabilities *hv_iommu_cap) +{ + ida_init(&hv_iommu->domain_ids); + + hv_iommu->cap =3D hv_iommu_cap->iommu_cap; + hv_iommu->max_iova_width =3D hv_iommu_cap->max_iova_width; + if (!hv_iommu_5lvl_supported(hv_iommu->cap) && + hv_iommu->max_iova_width > 48) { + pr_err("5-level paging not supported, limiting iova width to 48.\n"); + hv_iommu->max_iova_width =3D 48; + } + + hv_iommu->geometry =3D (struct iommu_domain_geometry) { + .aperture_start =3D 0, + .aperture_end =3D (((u64)1) << hv_iommu_cap->max_iova_width) - 1, + .force_aperture =3D true, + }; + + hv_iommu->first_domain =3D HV_DEVICE_DOMAIN_ID_DEFAULT + 1; + hv_iommu->last_domain =3D HV_DEVICE_DOMAIN_ID_NULL - 1; + hv_iommu->pgsize_bitmap =3D hv_iommu_cap->pgsize_bitmap; + hv_iommu_device =3D hv_iommu; +} + +static int __init hv_iommu_init(void) +{ + int ret =3D 0; + struct hv_iommu_dev *hv_iommu =3D NULL; + struct hv_output_get_iommu_capabilities hv_iommu_cap =3D {0}; + + if (no_iommu || iommu_detected) + return -ENODEV; + + if (!hv_is_hyperv_initialized()) + return -ENODEV; + + if (hv_iommu_detect(&hv_iommu_cap) || + !hv_iommu_present(hv_iommu_cap.iommu_cap) || + !hv_iommu_s1_domain_supported(hv_iommu_cap.iommu_cap)) + return -ENODEV; + + iommu_detected =3D 1; + pci_request_acs(); + + hv_iommu =3D kzalloc(sizeof(*hv_iommu), GFP_KERNEL); + if (!hv_iommu) + return -ENOMEM; + + hv_init_iommu_device(hv_iommu, &hv_iommu_cap); + + ret =3D hv_initialize_static_domains(); + if (ret) { + pr_err("hv_initialize_static_domains failed: %d\n", ret); + goto err_sysfs_remove; + } + + ret =3D iommu_device_sysfs_add(&hv_iommu->iommu, NULL, NULL, "%s", "hv-io= mmu"); + if (ret) { + pr_err("iommu_device_sysfs_add failed: %d\n", ret); + goto err_free; + } + + + ret =3D iommu_device_register(&hv_iommu->iommu, &hv_iommu_ops, NULL); + if (ret) { + pr_err("iommu_device_register failed: %d\n", ret); + goto err_sysfs_remove; + } + + register_syscore_ops(&hv_iommu_syscore_ops); + + pr_info("Microsoft Hypervisor IOMMU initialized\n"); + return 0; + +err_sysfs_remove: + iommu_device_sysfs_remove(&hv_iommu->iommu); +err_free: + kfree(hv_iommu); + return ret; +} + +device_initcall(hv_iommu_init); diff --git a/drivers/iommu/hyperv/iommu.h b/drivers/iommu/hyperv/iommu.h new file mode 100644 index 000000000000..c8657e791a6e --- /dev/null +++ b/drivers/iommu/hyperv/iommu.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Hyper-V IOMMU driver. + * + * Copyright (C) 2024-2025, Microsoft, Inc. + * + */ + +#ifndef _HYPERV_IOMMU_H +#define _HYPERV_IOMMU_H + +struct hv_iommu_dev { + struct iommu_device iommu; + struct ida domain_ids; + + /* Device configuration */ + u8 max_iova_width; + u8 max_pasid_width; + u64 cap; + u64 pgsize_bitmap; + + struct iommu_domain_geometry geometry; + u64 first_domain; + u64 last_domain; +}; + +struct hv_iommu_domain { + union { + struct iommu_domain domain; + struct pt_iommu pt_iommu; + struct pt_iommu_x86_64 pt_iommu_x86_64; + }; + struct hv_iommu_dev *hv_iommu; + struct hv_input_device_domain device_domain; + u64 pgsize_bitmap; + + spinlock_t lock; /* protects dev_list and TLB flushes */ + /* List of devices in this DMA domain */ + struct list_head dev_list; +}; + +struct hv_iommu_endpoint { + struct device *dev; + struct hv_iommu_dev *hv_iommu; + struct hv_iommu_domain *hv_domain; + struct list_head list; /* For domain->dev_list */ +}; + +#define to_hv_iommu_domain(d) \ + container_of(d, struct hv_iommu_domain, domain) + +#endif /* _HYPERV_IOMMU_H */ --=20 2.49.0