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: [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM
Date: Sun, 2 Aug 2026 23:35:20 -0400 [thread overview]
Message-ID: <20260803033520.12930-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 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
next reply other threads:[~2026-08-03 3:35 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-03 3:35 Tianyu Lan [this message]
2026-08-03 3:53 ` [RFC PATCH 1/1] x86/VMBus: DMA transfer with encrypted memory in Coco VM sashiko-bot
2026-08-03 9:04 ` Aneesh Kumar K.V
2026-08-04 9:22 ` Tianyu Lan
2026-08-05 10:11 ` Aneesh Kumar K.V
2026-08-06 14:21 ` Tianyu Lan
2026-08-06 15:14 ` Robin Murphy
2026-08-07 14:32 ` Tianyu Lan
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=20260803033520.12930-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.