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-16-den@valinux.co.jp>
Date: Thu, 23 Oct 2025 16:19:06 +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 15/25] dmaengine: dw-edma: Add self-interrupt registration API

Introduce dw_edma_register_selfirq() and dw_edma_unregister_selfirq() to
register IRQ callbacks for emulated interrupts. These can be used for
testing purposes, or even as interrupt callbacks triggered by inbound
address translations where the IB iATU target is set to e.g.
DMA_READ_INT_STATUS_OFF. The latter case provides a practical workaround
for endpoint controllers that cannot directly access GIC ITS registers
due to security restrictions, e.g. on R-Car S4 Spider.

Signed-off-by: Koichiro Den <den@...inux.co.jp>
---
 drivers/dma/dw-edma/dw-edma-core.c    | 60 +++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h    | 16 +++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c | 15 +++++++
 include/linux/dma/edma.h              | 16 +++++++
 4 files changed, 107 insertions(+)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index b43255f914f3..7cf9e5e74a89 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -661,11 +661,22 @@ static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
 
 static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
 {
+	struct dw_edma_irq *dw_irq = data;
+	struct dw_edma *dw = dw_irq->dw;
 	irqreturn_t ret = IRQ_NONE;
+	struct dw_edma_selfirq *h;
 
 	ret |= dw_edma_interrupt_write(irq, data);
 	ret |= dw_edma_interrupt_read(irq, data);
 
+	if (ret == IRQ_NONE) {
+		dw_edma_core_ack_test(dw);
+		scoped_guard(spinlock_irqsave, &dw->selfirq_lock) {
+			list_for_each_entry(h, &dw->selfirq_handlers, node)
+				h->fn(dw, h->data);
+		}
+		ret = IRQ_HANDLED;
+	}
 	return ret;
 }
 
@@ -892,6 +903,44 @@ static int dw_edma_irq_request(struct dw_edma *dw,
 	return err;
 }
 
+int dw_edma_register_selfirq(struct dw_edma *dw,
+			     dw_edma_selfirq_fn fn, void *data)
+{
+	struct dw_edma_selfirq *h;
+
+	if (!dw || !fn)
+		return -EINVAL;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return -ENOMEM;
+	h->fn = fn;
+	h->data = data;
+	guard(spinlock_irqsave)(&dw->selfirq_lock);
+	list_add_tail(&h->node, &dw->selfirq_handlers);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dw_edma_register_selfirq);
+
+void dw_edma_unregister_selfirq(struct dw_edma *dw,
+				dw_edma_selfirq_fn fn, void *data)
+{
+	struct dw_edma_selfirq *h, *tmp;
+
+	if (!dw || !fn)
+		return;
+
+	guard(spinlock_irqsave)(&dw->selfirq_lock);
+	list_for_each_entry_safe(h, tmp, &dw->selfirq_handlers, node) {
+		if (h->fn == fn && h->data == data) {
+			list_del(&h->node);
+			kfree(h);
+			break;
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(dw_edma_unregister_selfirq);
+
 int dw_edma_probe(struct dw_edma_chip *chip)
 {
 	struct device *dev;
@@ -912,6 +961,8 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 		return -ENOMEM;
 
 	dw->chip = chip;
+	INIT_LIST_HEAD(&dw->selfirq_handlers);
+	spin_lock_init(&dw->selfirq_lock);
 
 	if (dw->chip->mf == EDMA_MF_HDMA_NATIVE)
 		dw_hdma_v0_core_register(dw);
@@ -974,6 +1025,7 @@ EXPORT_SYMBOL_GPL(dw_edma_probe);
 int dw_edma_remove(struct dw_edma_chip *chip)
 {
 	struct dw_edma_chan *chan, *_chan;
+	struct dw_edma_selfirq *h, *tmp;
 	struct device *dev = chip->dev;
 	struct dw_edma *dw = chip->dw;
 	int i;
@@ -985,6 +1037,14 @@ int dw_edma_remove(struct dw_edma_chip *chip)
 	/* Disable eDMA */
 	dw_edma_core_off(dw);
 
+	/* Free self-irq handlers */
+	scoped_guard(spinlock_irqsave, &dw->selfirq_lock) {
+		list_for_each_entry_safe(h, tmp, &dw->selfirq_handlers, node) {
+			list_del(&h->node);
+			kfree(h);
+		}
+	}
+
 	/* Free irqs */
 	for (i = (dw->nr_irqs - 1); i >= 0; i--)
 		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b15..7d7dd9f13863 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -95,6 +95,12 @@ struct dw_edma_irq {
 	struct dw_edma			*dw;
 };
 
+struct dw_edma_selfirq {
+	struct list_head		node;
+	dw_edma_selfirq_fn		fn;
+	void				*data;
+};
+
 struct dw_edma {
 	char				name[32];
 
@@ -113,6 +119,9 @@ struct dw_edma {
 	struct dw_edma_chip             *chip;
 
 	const struct dw_edma_core_ops	*core;
+
+	struct list_head selfirq_handlers;
+	spinlock_t selfirq_lock;
 };
 
 typedef void (*dw_edma_handler_t)(struct dw_edma_chan *);
@@ -126,6 +135,7 @@ struct dw_edma_core_ops {
 	void (*start)(struct dw_edma_chunk *chunk, bool first);
 	void (*ch_config)(struct dw_edma_chan *chan);
 	void (*debugfs_on)(struct dw_edma *dw);
+	void (*ack_test)(struct dw_edma *dw);
 };
 
 struct dw_edma_sg {
@@ -206,4 +216,10 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
 	dw->core->debugfs_on(dw);
 }
 
+static inline void dw_edma_core_ack_test(struct dw_edma *dw)
+{
+	if (dw->core->ack_test)
+		dw->core->ack_test(dw);
+}
+
 #endif /* _DW_EDMA_CORE_H */
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index b75fdaffad9a..67b0541f38c3 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -509,6 +509,20 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
 	dw_edma_v0_debugfs_on(dw);
 }
 
+static void dw_edma_v0_core_ack_test(struct dw_edma *dw)
+{
+	u32 wr_mask_all = (dw->wr_ch_cnt >= 32) ? ~0U : (BIT(dw->wr_ch_cnt) - 1);
+	u32 rd_mask_all = (dw->rd_ch_cnt >= 32) ? ~0U : (BIT(dw->rd_ch_cnt) - 1);
+
+	u32 wr_val = FIELD_PREP(EDMA_V0_DONE_INT_MASK, wr_mask_all) |
+		     FIELD_PREP(EDMA_V0_ABORT_INT_MASK, wr_mask_all);
+	u32 rd_val = FIELD_PREP(EDMA_V0_DONE_INT_MASK, rd_mask_all) |
+		     FIELD_PREP(EDMA_V0_ABORT_INT_MASK, rd_mask_all);
+
+	SET_32(dw, wr_int_clear, wr_val);
+	SET_32(dw, rd_int_clear, rd_val);
+}
+
 static const struct dw_edma_core_ops dw_edma_v0_core = {
 	.off = dw_edma_v0_core_off,
 	.ch_count = dw_edma_v0_core_ch_count,
@@ -517,6 +531,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
 	.start = dw_edma_v0_core_start,
 	.ch_config = dw_edma_v0_core_ch_config,
 	.debugfs_on = dw_edma_v0_core_debugfs_on,
+	.ack_test = dw_edma_v0_core_ack_test,
 };
 
 void dw_edma_v0_core_register(struct dw_edma *dw)
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3080747689f6..42daf9a76b56 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -101,10 +101,16 @@ struct dw_edma_chip {
 	struct dw_edma		*dw;
 };
 
+typedef void (*dw_edma_selfirq_fn)(struct dw_edma *dw, void *data);
+
 /* Export to the platform drivers */
 #if IS_REACHABLE(CONFIG_DW_EDMA)
 int dw_edma_probe(struct dw_edma_chip *chip);
 int dw_edma_remove(struct dw_edma_chip *chip);
+int dw_edma_register_selfirq(struct dw_edma *dw,
+			     dw_edma_selfirq_fn fn, void *data);
+void dw_edma_unregister_selfirq(struct dw_edma *dw,
+				dw_edma_selfirq_fn fn, void *data);
 #else
 static inline int dw_edma_probe(struct dw_edma_chip *chip)
 {
@@ -115,6 +121,16 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
 {
 	return 0;
 }
+static inline int dw_edma_register_selfirq(struct dw_edma *dw,
+					   dw_edma_selfirq_fn fn, void *data)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void dw_edma_unregister_selfirq(struct dw_edma *dw,
+					      dw_edma_selfirq_fn fn, void *data)
+{
+}
 #endif /* CONFIG_DW_EDMA */
 
 #endif /* _DW_EDMA_H */
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ