From: Tianyu Lan <ltykernel@gmail.com>
To: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com
Cc: Tianyu Lan <tiala@microsoft.com>,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
aik@amd.com, hch@infradead.org, robin.murphy@arm.com,
vdso@hexbites.dev, aneesh.kumar@kernel.org, mhklinux@outlook.com
Subject: [Update RFC V2 PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM
Date: Thu, 3 Sep 2026 23:16:34 +0800 [thread overview]
Message-ID: <20260903151635.125034-1-tiala@microsoft.com> (raw)
In CoCo VMs, system memory is encrypted by default.
Device drivers typically rely on the DMA core's
SWIOTLB as a bounce buffer for DMA operations, providing
decrypted memory that can be shared between the guest and
host.
For PCI devices with TDisp support and Confidential
VMBus devices (https://lkml.org/lkml/2026/7/27/1733) can
perform DMA transfers directly with private/encrypted
memory in a CoCo VM.
To support DMA transfer with encrypted memory, Hyper-V
DMA ops are introduced and bypass some API which may
use swiotlb as bounce buffer.
The DMA ops used is global data structure(see get_arch_
dma_ops() and get_dma_ops() for details). There is no
need to set up for each device individually.
Signed-off-by: Tianyu Lan <tiala@microsoft.com>
---
Update change:
* s/T-Disp/TDisp
* Remove redundant check code
Change since RFC v1:
* Fix warning, code style issues and make the code
of reference dma ops under CONFIG_ARCH_HAS_DMA_OPS.
---
drivers/hv/Kconfig | 9 +++
drivers/hv/Makefile | 1 +
drivers/hv/hv_dma_ops.c | 134 ++++++++++++++++++++++++++++++++++++++
drivers/hv/hyperv_vmbus.h | 1 +
drivers/hv/vmbus_drv.c | 19 +++++-
5 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 drivers/hv/hv_dma_ops.c
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 2d0b3fcb0ff87..fbbeb67cb2cab 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -7,6 +7,7 @@ config HYPERV
depends on (X86 && X86_LOCAL_APIC && HYPERVISOR_GUEST) \
|| (ARM64 && !CPU_BIG_ENDIAN)
select PARAVIRT
+ select HYPERV_DMA_OPS
select X86_HV_CALLBACK_VECTOR if X86
select OF_EARLY_FLATTREE if OF
select IRQ_MSI_LIB if X86
@@ -14,6 +15,13 @@ config HYPERV
Select this option to run Linux as a Hyper-V client operating
system.
+config HYPERV_DMA_OPS
+ bool "Enable Microsoft Hyper-V DMA ops support"
+ depends on ARCH_HAS_DMA_OPS
+ help
+ Select this option to enable DMA with encrypt
+ memory in CoCo VM.
+
config HYPERV_VTL_MODE
bool "Enable Linux to boot in VTL context"
depends on (X86_64 && HAVE_STATIC_CALL) || ARM64
@@ -62,6 +70,7 @@ config HYPERV_VMBUS
depends on HYPERV
default HYPERV
select SYSFB if EFI && !HYPERV_VTL_MODE
+ default y if HYPERV_DMA_OPS
help
Select this option to enable Hyper-V Vmbus driver.
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 888a748cc7cb9..b4d23c88b9eb0 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_HYPERV_VMBUS) += hv_vmbus.o
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
+obj-$(CONFIG_HYPERV_DMA_OPS) += hv_dma_ops.o
obj-$(CONFIG_MSHV_ROOT) += mshv_root.o
obj-$(CONFIG_MSHV_VTL) += mshv_vtl.o
diff --git a/drivers/hv/hv_dma_ops.c b/drivers/hv/hv_dma_ops.c
new file mode 100644
index 0000000000000..9b0fb76a98747
--- /dev/null
+++ b/drivers/hv/hv_dma_ops.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026, Microsoft Corporation.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hyperv.h>
+#include <linux/smpboot.h>
+
+#include <linux/kernel.h>
+#include <linux/dma-map-ops.h>
+#include <linux/export.h>
+#include <asm/mshyperv.h>
+#include "hyperv_vmbus.h"
+#include "../../kernel/dma/direct.h"
+
+const struct dma_map_ops *dma_ops;
+bool is_vmbus_dev(struct device *dev);
+
+static bool hyperv_private_memory_dma(struct device *dev)
+{
+ struct hv_device *hv_dev = device_to_hv_device(dev);
+
+ if (is_vmbus_dev(dev) && hv_dev && hv_dev->channel
+ && hv_dev->channel->co_external_memory)
+ return true;
+
+ /* Todo: Check TDisp capability of PCI device here */
+
+ return false;
+}
+
+static int hyperv_dma_map_sg(struct device *dev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ struct scatterlist *sg;
+ dma_addr_t dma_addr;
+ int i;
+
+ if (hyperv_private_memory_dma(dev)) {
+ for_each_sg(sgl, sg, nelems, i) {
+ dma_addr = phys_to_dma(dev, sg_phys(sg));
+ sg_dma_address(sg) = dma_addr;
+ sg_dma_len(sg) = sg->length;
+ }
+
+ return nelems;
+ } else {
+ return dma_direct_map_sg(dev, sgl, nelems, dir, attrs);
+ }
+}
+
+static void hyperv_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir, unsigned long attrs)
+{
+ if (!hyperv_private_memory_dma(dev))
+ dma_direct_unmap_sg(dev, sgl, nelems, dir, attrs);
+}
+
+static int hyperv_dma_supported(struct device *dev, u64 mask)
+{
+ return 1;
+}
+
+static size_t hyperv_dma_max_mapping_size(struct device *dev)
+{
+ if (hyperv_private_memory_dma(dev))
+ return SIZE_MAX;
+ else
+ return swiotlb_max_mapping_size(dev);
+}
+
+/* allocate and map a coherent mapping */
+static void *
+hyperv_dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
+ gfp_t flag, unsigned long attrs)
+{
+ phys_addr_t phys;
+ void *ret;
+
+ if (!hyperv_private_memory_dma(dev))
+ return dma_alloc_coherent(dev, size, dma_handle, flag);
+
+ size = ALIGN(size, PAGE_SIZE);
+ ret = (void *)__get_free_pages(flag, get_order(size));
+ if (!ret)
+ return ret;
+ phys = virt_to_phys(ret);
+
+ *dma_handle = dma_addr_encrypted(__phys_to_dma(dev, phys));
+ memset(ret, 0, size);
+ return ret;
+}
+
+/* free a coherent mapping */
+static void
+hyperv_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_addr, unsigned long attrs)
+{
+ if (hyperv_private_memory_dma(dev))
+ dmam_free_coherent(dev, size, vaddr, dma_addr);
+ else
+ free_pages((unsigned long)vaddr, get_order(size));
+}
+
+static dma_addr_t hyperv_dma_map_phys(struct device *dev, phys_addr_t phys,
+ size_t size, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ if (hyperv_private_memory_dma(dev))
+ return __phys_to_dma(dev, phys);
+ else
+ return dma_direct_map_phys(dev, phys, size, dir, attrs, true);
+}
+
+static void hyperv_dma_unmap_phys(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction dir, unsigned long attrs)
+{
+ if (!hyperv_private_memory_dma(dev))
+ dma_direct_unmap_phys(dev, dma_handle, size, dir, attrs, true);
+}
+
+const struct dma_map_ops hyperv_dma_ops = {
+ .alloc = hyperv_dma_alloc_coherent,
+ .free = hyperv_dma_free_coherent,
+ .map_phys = hyperv_dma_map_phys,
+ .unmap_phys = hyperv_dma_unmap_phys,
+ .map_sg = hyperv_dma_map_sg,
+ .unmap_sg = hyperv_dma_unmap_sg,
+ .dma_supported = hyperv_dma_supported,
+ .max_mapping_size = hyperv_dma_max_mapping_size,
+};
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 05a36854389af..94f16382bba7b 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -356,6 +356,7 @@ extern const struct vmbus_channel_message_table_entry
/* General vmbus interface */
bool vmbus_is_confidential(void);
+bool is_vmbus_dev(struct device *dev);
#if IS_ENABLED(CONFIG_HYPERV_VMBUS)
/* Free the message slot and signal end-of-message if required */
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index d28ff45d4cfdd..a45a1d010a1cd 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -40,6 +40,12 @@
#include <clocksource/hyperv_timer.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
+#include "../../kernel/dma/direct.h"
+
+#ifdef CONFIG_ARCH_HAS_DMA_OPS
+const struct dma_map_ops *dma_ops;
+const struct dma_map_ops hyperv_dma_ops;
+#endif
struct vmbus_dynid {
struct list_head node;
@@ -62,6 +68,7 @@ int vmbus_interrupt;
* visible to either the host or the hypervisor.
*/
static bool is_confidential;
+static const struct bus_type hv_bus;
bool vmbus_is_confidential(void)
{
@@ -69,6 +76,11 @@ bool vmbus_is_confidential(void)
}
EXPORT_SYMBOL_GPL(vmbus_is_confidential);
+bool is_vmbus_dev(struct device *dev)
+{
+ return dev->bus == &hv_bus;
+}
+
/*
* The panic notifier below is responsible solely for unloading the
* vmbus connection, which is necessary in a panic event.
@@ -1523,8 +1535,13 @@ static int vmbus_bus_init(void)
* doing that on each VP while initializing SynIC's wastes time.
*/
is_confidential = ms_hyperv.confidential_vmbus_available;
- if (is_confidential)
+ if (is_confidential) {
+#ifdef CONFIG_ARCH_HAS_DMA_OPS
+ dma_ops = &hyperv_dma_ops;
+#endif
pr_info("Establishing connection to the confidential VMBus\n");
+ }
+
hv_para_set_sint_proxy(!is_confidential);
ret = vmbus_alloc_synic_and_connect();
if (ret)
--
2.53.0
reply other threads:[~2026-09-03 15:37 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260903151635.125034-1-tiala@microsoft.com \
--to=ltykernel@gmail.com \
--cc=aik@amd.com \
--cc=aneesh.kumar@kernel.org \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=hch@infradead.org \
--cc=kys@microsoft.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=mhklinux@outlook.com \
--cc=robin.murphy@arm.com \
--cc=tiala@microsoft.com \
--cc=vdso@hexbites.dev \
--cc=wei.liu@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.