* [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM
@ 2026-08-03 3:35 Tianyu Lan
2026-08-03 9:04 ` Aneesh Kumar K.V
0 siblings, 1 reply; 3+ messages in thread
From: Tianyu Lan @ 2026-08-03 3:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli
Cc: Tianyu Lan, linux-hyperv, linux-kernel, aik, hch, robin.murphy,
vdso, aneesh.kumar, mhklinux
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 T-Disp 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>
---
drivers/hv/Kconfig | 5 ++
drivers/hv/Makefile | 1 +
drivers/hv/hv_dma_ops.c | 140 ++++++++++++++++++++++++++++++++++++++++
drivers/hv/vmbus_drv.c | 14 +++-
4 files changed, 159 insertions(+), 1 deletion(-)
create mode 100644 drivers/hv/hv_dma_ops.c
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 2d0b3fcb0ff8..4b50a631fd57 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,10 @@ 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
+
config HYPERV_VTL_MODE
bool "Enable Linux to boot in VTL context"
depends on (X86_64 && HAVE_STATIC_CALL) || ARM64
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 888a748cc7cb..b4d23c88b9eb 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 000000000000..ef53a42c0450
--- /dev/null
+++ b/drivers/hv/hv_dma_ops.c
@@ -0,0 +1,140 @@
+// 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"
+
+extern const struct dma_map_ops *dma_ops;
+
+extern 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 T-Disp 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)
+{
+ dev->coherent_dma_mask = 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);
+
+ if (hyperv_private_memory_dma(dev))
+ *dma_handle = dma_addr_encrypted(__phys_to_dma(dev, phys));
+ else
+ *dma_handle = phys_to_dma_unencrypted(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/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index d28ff45d4cfd..d1ce5c107993 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -40,6 +40,10 @@
#include <clocksource/hyperv_timer.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
+#include "../../kernel/dma/direct.h"
+
+extern const struct dma_map_ops *dma_ops;
+extern const struct dma_map_ops hyperv_dma_ops;
struct vmbus_dynid {
struct list_head node;
@@ -1468,6 +1472,11 @@ static int vmbus_alloc_synic_and_connect(void)
return -ENOMEM;
}
+bool is_vmbus_dev(struct device *dev)
+{
+ return dev->bus == &hv_bus;
+}
+
/*
* vmbus_bus_init -Main vmbus driver initialization routine.
*
@@ -1523,8 +1532,11 @@ 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) {
+ dma_ops = &hyperv_dma_ops;
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM
2026-08-03 3:35 [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM Tianyu Lan
@ 2026-08-03 9:04 ` Aneesh Kumar K.V
2026-08-04 9:22 ` Tianyu Lan
0 siblings, 1 reply; 3+ messages in thread
From: Aneesh Kumar K.V @ 2026-08-03 9:04 UTC (permalink / raw)
To: Tianyu Lan, kys, haiyangz, wei.liu, decui, longli
Cc: Tianyu Lan, linux-hyperv, linux-kernel, aik, hch, robin.murphy,
vdso, mhklinux
Tianyu Lan <ltykernel@gmail.com> writes:
> 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 T-Disp 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.
>
Can we use __DMA_ATTR_ALLOC_CC_SHARED instead of checking
hyperv_private_memory_dma(dev) directly? Also, would it be possible to
encapsulate this logic in something like force_dma_encrypted(dev)?
Looking at functions such as hyperv_dma_alloc_coherent(), how does this
differ from dma_direct_alloc()?
-aneesh
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM
2026-08-03 9:04 ` Aneesh Kumar K.V
@ 2026-08-04 9:22 ` Tianyu Lan
0 siblings, 0 replies; 3+ messages in thread
From: Tianyu Lan @ 2026-08-04 9:22 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: kys, haiyangz, wei.liu, decui, longli, Tianyu Lan, linux-hyperv,
linux-kernel, aik, hch, robin.murphy, vdso, mhklinux
On Mon, Aug 3, 2026 at 5:04 PM Aneesh Kumar K.V <aneesh.kumar@kernel.org> wrote:
>
> Tianyu Lan <ltykernel@gmail.com> writes:
>
> > 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 T-Disp 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.
> >
>
> Can we use __DMA_ATTR_ALLOC_CC_SHARED instead of checking
> hyperv_private_memory_dma(dev) directly? Also, would it be possible to
> encapsulate this logic in something like force_dma_encrypted(dev)?
>
Hi Aneesh:
Thanks for your review. I go through your patchset “dma-mapping: Use
DMA_ATTR_CC_SHARED”(https://lkml.org/lkml/2026/6/4/588). This patch
is compatible with your DMA_ATTR_CC_SHARED attr work in dma-mapping
layer. Once dma ops callbacks get DMA_ATTR_CC_SHARED attr flag and
it may use the flag to replace hyperv_private_memory_dma().
> Looking at functions such as hyperv_dma_alloc_coherent(), how does this
> differ from dma_direct_alloc()?
>
hyperv_dma_alloc_coherent() is to allocate memory and returns encrypted
memory address directly when hyperv_private_memory_dma() is ture.
This change is to keep all changes under Hyper-V subsystem and it's
also compatible with existing solution which changes DMA core with minor
changes.
--
Thanks
Tianyu Lan
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-04 9:22 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 3:35 [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM Tianyu Lan
2026-08-03 9:04 ` Aneesh Kumar K.V
2026-08-04 9:22 ` Tianyu Lan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox