lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251023071916.901355-24-den@valinux.co.jp>
Date: Thu, 23 Oct 2025 16:19:14 +0900
From: Koichiro Den <den@...inux.co.jp>
To: ntb@...ts.linux.dev,
	linux-pci@...r.kernel.org,
	dmaengine@...r.kernel.org,
	linux-kernel@...r.kernel.org
Cc: mani@...nel.org,
	kwilczynski@...nel.org,
	kishon@...nel.org,
	bhelgaas@...gle.com,
	corbet@....net,
	vkoul@...nel.org,
	jdmason@...zu.us,
	dave.jiang@...el.com,
	allenbh@...il.com,
	Basavaraj.Natikar@....com,
	Shyam-sundar.S-k@....com,
	kurt.schwemmer@...rosemi.com,
	logang@...tatee.com,
	jingoohan1@...il.com,
	lpieralisi@...nel.org,
	robh@...nel.org,
	jbrunet@...libre.com,
	Frank.Li@....com,
	fancer.lancer@...il.com,
	arnd@...db.de,
	pstanner@...hat.com,
	elfring@...rs.sourceforge.net
Subject: [RFC PATCH 23/25] NTB: intr_dw_edma: Add DW eDMA emulated interrupt backend

Introduce a new NTB interrupt backend using DW eDMA's emulated interrupt
mechanism. Enables interrupt-based signaling from RC to EP where MSI is
impossible due to security restrictions on the platform.

Signed-off-by: Koichiro Den <den@...inux.co.jp>
---
 drivers/ntb/Kconfig        |  10 ++
 drivers/ntb/Makefile       |   1 +
 drivers/ntb/intr_common.c  |   8 +-
 drivers/ntb/intr_dw_edma.c | 253 +++++++++++++++++++++++++++++++++++++
 include/linux/ntb.h        |  10 ++
 5 files changed, 281 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ntb/intr_dw_edma.c

diff --git a/drivers/ntb/Kconfig b/drivers/ntb/Kconfig
index 2f22f44245b3..5b7e1563e639 100644
--- a/drivers/ntb/Kconfig
+++ b/drivers/ntb/Kconfig
@@ -29,6 +29,16 @@ config NTB_MSI
 
 	 If unsure, say N.
 
+config NTB_DW_EDMA
+	bool "DW eDMA test-interrupt backend"
+	depends on PCI_ENDPOINT && PCIE_DW_EP && DW_EDMA
+	select NTB_INTR_COMMON
+	help
+	 Use DW eDMA v0 test interrupt as a doorbell-like backend
+	 for NTB transports when MSI is not available on EPF side.
+
+	 If unsure, say N.
+
 source "drivers/ntb/hw/Kconfig"
 
 source "drivers/ntb/test/Kconfig"
diff --git a/drivers/ntb/Makefile b/drivers/ntb/Makefile
index feaa2a77cbf6..cae84d132b78 100644
--- a/drivers/ntb/Makefile
+++ b/drivers/ntb/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_NTB_TRANSPORT) += ntb_transport.o
 ntb-y				:= core.o
 ntb-$(CONFIG_NTB_INTR_COMMON)	+= intr_common.o
 ntb-$(CONFIG_NTB_MSI)		+= msi.o
+ntb-$(CONFIG_NTB_DW_EDMA)	+= intr_dw_edma.o
diff --git a/drivers/ntb/intr_common.c b/drivers/ntb/intr_common.c
index e0e296fd3e3c..41b2752c6d03 100644
--- a/drivers/ntb/intr_common.c
+++ b/drivers/ntb/intr_common.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
 
-#include <linux/ntb.h>
 #include <linux/module.h>
+#include <linux/ntb.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 
@@ -13,6 +13,12 @@ int ntb_intr_init(struct ntb_dev *ntb,
 		ntb->intr_backend = ntb_intr_msi_backend();
 		dev_info(&ntb->dev, "NTB interrupt MSI backend selected.\n");
 	}
+#endif
+#ifdef CONFIG_NTB_DW_EDMA
+	if (!ntb->intr_backend) {
+		ntb->intr_backend = ntb_intr_dw_edma_backend();
+		dev_info(&ntb->dev, "NTB interrupt DW eDMA backend selected.\n");
+	}
 #endif
 	if (!ntb->intr_backend)
 		return -ENODEV;
diff --git a/drivers/ntb/intr_dw_edma.c b/drivers/ntb/intr_dw_edma.c
new file mode 100644
index 000000000000..0e408ecfaf61
--- /dev/null
+++ b/drivers/ntb/intr_dw_edma.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+
+#include <linux/dma/edma.h>
+#include <linux/ntb.h>
+#include <linux/pci-epc.h>
+
+struct ntb_intr_dw {
+	u64 base_addr;
+	u64 end_addr;
+
+	struct dw_edma *edma;
+	resource_size_t rd_status_off;
+	resource_size_t rd_clear_off;
+
+	u32 __iomem *peer_mws[];
+};
+
+struct ntb_intr_dw_ctx {
+	irq_handler_t handler;
+	void *dev;
+	struct dw_edma *edma;
+};
+
+static void dw_edma_selfirq_handler(struct dw_edma *dw, void *data)
+{
+	struct ntb_intr_dw_ctx *ctx = data;
+
+	ctx->handler(0, ctx->dev);
+}
+
+static int dw_edma_find_backend_for_ntb(struct ntb_dev *ntb, struct ntb_intr_dw *intr_dw)
+{
+	struct pci_epc *epc = NULL;
+
+	epc = ntb_get_pci_epc(ntb);
+	if (!epc)
+		return -ENODEV;
+	intr_dw->edma = dw_edma_find_by_child(&epc->dev);
+	if (!intr_dw->edma)
+		return -ENODEV;
+	dw_edma_selfirq_offsets(intr_dw->edma, &intr_dw->rd_status_off, &intr_dw->rd_clear_off);
+	return 0;
+}
+
+static int dw_intr_init(struct ntb_dev *ntb, void (*desc_changed)(void *ctx))
+{
+	struct ntb_intr_dw *intr_dw;
+	phys_addr_t mw_phys_addr;
+	resource_size_t mw_size;
+	int peer_widx;
+	int peers;
+	int ret;
+	int i;
+
+	peers = ntb_peer_port_count(ntb);
+	if (peers <= 0)
+		return -EINVAL;
+
+	intr_dw = devm_kzalloc(&ntb->dev, struct_size(intr_dw, peer_mws, peers),
+			       GFP_KERNEL);
+	if (!intr_dw)
+		return -ENOMEM;
+
+	ret = dw_edma_find_backend_for_ntb(ntb, intr_dw);
+	if (ret) {
+		devm_kfree(&ntb->dev, intr_dw);
+		return ret;
+	}
+
+	for (i = 0; i < peers; i++) {
+		peer_widx = ntb_peer_mw_count(ntb) - 1 - i;
+
+		ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr,
+					   &mw_size);
+		if (ret)
+			goto unroll;
+
+		intr_dw->peer_mws[i] = devm_ioremap(&ntb->dev, mw_phys_addr,
+						    mw_size);
+		if (!intr_dw->peer_mws[i]) {
+			ret = -EFAULT;
+			goto unroll;
+		}
+	}
+
+	ntb->intr_priv = intr_dw;
+
+	return 0;
+
+unroll:
+	for (i = 0; i < peers; i++)
+		if (intr_dw->peer_mws[i])
+			devm_iounmap(&ntb->dev, intr_dw->peer_mws[i]);
+
+	devm_kfree(&ntb->dev, intr_dw);
+	return ret;
+}
+
+static int dw_intr_setup_mws(struct ntb_dev *ntb)
+{
+	struct ntb_intr_dw *dwc = ntb->intr_priv;
+	resource_size_t addr_align, size_align, offset;
+	resource_size_t mw_size = SZ_32K;
+	resource_size_t mw_min_size = mw_size;
+	u64 addr = dwc->rd_status_off;
+	int peer, peer_widx, ret;
+	int i;
+
+	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+		if (peer_widx < 0)
+			return peer_widx;
+
+		ret = ntb_mw_get_align(ntb, peer, peer_widx, &addr_align,
+				       NULL, NULL, NULL);
+		if (ret)
+			return ret;
+
+		addr &= ~(addr_align - 1);
+	}
+
+	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+		if (peer_widx < 0) {
+			ret = peer_widx;
+			goto error_out;
+		}
+
+		ret = ntb_mw_get_align(ntb, peer, peer_widx, NULL,
+				       &size_align, NULL, &offset);
+		if (ret)
+			goto error_out;
+
+		mw_size = round_up(mw_size, size_align);
+		if (mw_size < mw_min_size)
+			mw_min_size = mw_size;
+
+		ret = ntb_mw_set_trans(ntb, peer, peer_widx,
+				       addr, mw_size, offset);
+		if (ret)
+			goto error_out;
+	}
+
+	dwc->base_addr = addr;
+	dwc->end_addr = addr + mw_min_size;
+
+	return 0;
+
+error_out:
+	for (i = 0; i < peer; i++) {
+		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+		if (peer_widx < 0)
+			continue;
+
+		ntb_mw_clear_trans(ntb, i, peer_widx);
+	}
+
+	return ret;
+}
+
+static void dw_intr_clear_mws(struct ntb_dev *ntb)
+{
+	int peer, peer_widx;
+
+	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+		if (peer_widx < 0)
+			continue;
+
+		ntb_mw_clear_trans(ntb, peer, peer_widx);
+	}
+}
+
+static void dw_intr_release_irq(void *data)
+{
+	struct ntb_intr_dw_ctx *ctx = data;
+
+	dw_edma_unregister_selfirq(ctx->edma, dw_edma_selfirq_handler, ctx);
+	kfree(ctx);
+}
+
+static int dw_intr_request_irq(struct ntb_dev *ntb, irq_handler_t h,
+			       const char *name, void *dev_id,
+			       struct ntb_intr_desc *intr_desc)
+{
+	struct ntb_intr_dw *dwc = ntb->intr_priv;
+	struct dw_edma *edma = dwc->edma;
+	int ret;
+
+	if (intr_desc->ctx)
+		return 1;
+
+	struct ntb_intr_dw_ctx *ctx __free(kfree) = kzalloc(
+						sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	ctx->handler = h;
+	ctx->dev = dev_id;
+	ctx->edma = edma;
+
+	ret = dw_edma_register_selfirq(edma, dw_edma_selfirq_handler, ctx);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&ntb->dev, dw_intr_release_irq, ctx);
+	if (ret)
+		return ret;
+
+	intr_desc->addr_offset = dwc->rd_status_off - dwc->base_addr;
+	intr_desc->data = 0x0;
+	intr_desc->ctx = no_free_ptr(ctx);
+	return 1;
+}
+
+static void dw_intr_free_irq(struct ntb_dev *ntb, int irq, void *dev_id,
+			     struct ntb_intr_desc *intr_desc)
+{
+	struct ntb_intr_dw *dwc = ntb->intr_priv;
+	struct dw_edma *edma = dwc->edma;
+	struct ntb_intr_dw_ctx *ctx;
+
+	ctx = intr_desc->ctx;
+	dw_edma_unregister_selfirq(edma, dw_edma_selfirq_handler, ctx);
+	devm_remove_action(&ntb->dev, dw_intr_release_irq, ctx);
+	kfree(ctx);
+}
+
+static int dw_intr_peer_trigger(struct ntb_dev *ntb, int peer, struct ntb_intr_desc *desc)
+{
+	struct ntb_intr_dw *intr_dw = ntb->intr_priv;
+	int idx;
+
+	idx = desc->addr_offset / sizeof(*intr_dw->peer_mws[peer]);
+
+	iowrite32(desc->data, &intr_dw->peer_mws[peer][idx]);
+
+	return 0;
+}
+
+static const struct ntb_intr_backend ntb_intr_backend_dw_edma = {
+	.name = "dw-edma-testirq",
+	.init = dw_intr_init,
+	.setup_mws = dw_intr_setup_mws,
+	.clear_mws = dw_intr_clear_mws,
+	.request_irq = dw_intr_request_irq,
+	.free_irq = dw_intr_free_irq,
+	.peer_trigger = dw_intr_peer_trigger,
+};
+
+const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void)
+{
+	return &ntb_intr_backend_dw_edma;
+}
diff --git a/include/linux/ntb.h b/include/linux/ntb.h
index 1a88fe45471e..7daba67928e9 100644
--- a/include/linux/ntb.h
+++ b/include/linux/ntb.h
@@ -1664,6 +1664,7 @@ struct ntb_intr_desc {
 	u32 addr_offset;
 	u32 data;
 	u16 vector_offset;
+	void *ctx;
 };
 
 struct ntb_intr_backend {
@@ -1734,4 +1735,13 @@ static inline const struct ntb_intr_backend *ntb_intr_msi_backend(void)
 }
 #endif /* CONFIG_NTB_MSI */
 
+#ifdef CONFIG_NTB_DW_EDMA
+extern const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void);
+#else
+static inline const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void)
+{
+	return NULL;
+}
+#endif /* CONFIG_NTB_DW_EDMA */
+
 #endif
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ