[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <tencent_3927462924B7319F8F4F90447BDC110FB705@qq.com>
Date: Wed, 4 Feb 2026 01:21:16 +0800
From: Yangyu Chen <cyy@...self.name>
To: linux-riscv@...ts.infradead.org
Cc: linux-kernel@...r.kernel.org,
Anup Patel <anup.patel@....qualcomm.com>,
Samuel Holland <samuel.holland@...ive.com>,
Charles Mirabile <cmirabil@...hat.com>,
Lucas Zampieri <lzampier@...hat.com>,
Thomas Gleixner <tglx@...nel.org>,
Paul Walmsley <pjw@...nel.org>,
Palmer Dabbelt <palmer@...belt.com>,
Mason Huo <mason.huo@...rfivetech.com>,
Zhang Xincheng <zhangxincheng@...rarisc.com>,
Charlie Jenkins <charlie@...osinc.com>,
Marc Zyngier <maz@...nel.org>,
Sia Jee Heng <jeeheng.sia@...rfivetech.com>,
Ley Foon Tan <leyfoon.tan@...rfivetech.com>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Rob Herring <robh@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Alexandre Ghiti <alex@...ti.fr>,
devicetree@...r.kernel.org,
Jia Wang <wangjia@...rarisc.com>,
Yangyu Chen <cyy@...self.name>
Subject: [PATCH v3 1/2] irqchip/sifive-plic: Fix wrong nr_irqs handling
Since the first irq source is 1 instead of 0, when the number of
irqs is multiple of 32, the last irq group will be ignored during
allocation, saving, and restoring. This lead to memory corruption
when accessing enable_save beyond allocated memory after commit
14ff9e54dd14 ("irqchip/sifive-plic: Cache the interrupt enable state")
which will access enable_save for all sources during plic_probe.
Thus, we should allocate irq_groups based on (nr_irqs + 1) instead of
nr_irqs to avoid this issue. This commit also fixes related loops
to have all consumer of nr_irqs consistent.
This is an long standing bug since Linux v5.6 but since the last irq
source is rarely used, it may not be triggered in practice until commit
14ff9e54dd14 ("irqchip/sifive-plic: Cache the interrupt enable state").
Fixes: 466008f98435 ("irqchip/sifive-plic: Support irq domain hierarchy")
Fixes: e80f0b6a2cf3 ("irqchip/irq-sifive-plic: Add syscore callbacks for hibernation")
Fixes: 4d936f10ff80 ("irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform")
Fixes: f75e07bf5226 ("irqchip/sifive-plic: Avoid interrupt ID 0 handling during suspend/resume")
Fixes: 14ff9e54dd14 ("irqchip/sifive-plic: Cache the interrupt enable state")
Fixes: 539d147ef69c ("irqchip/sifive-plic: Add support for UltraRISC DP1000 PLIC")
Fixes: a045359e7245 ("irqchip/sifive-plic: Fix call to __plic_toggle() in M-Mode code path")
Signed-off-by: Yangyu Chen <cyy@...self.name>
---
drivers/irqchip/irq-sifive-plic.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
index 210a57959637..4658cad0d502 100644
--- a/drivers/irqchip/irq-sifive-plic.c
+++ b/drivers/irqchip/irq-sifive-plic.c
@@ -262,7 +262,7 @@ static int plic_irq_suspend(void *data)
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
/* irq ID 0 is reserved */
- for (unsigned int i = 1; i < priv->nr_irqs; i++) {
+ for (unsigned int i = 1; i <= priv->nr_irqs; i++) {
__assign_bit(i, priv->prio_save,
readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID));
}
@@ -280,7 +280,7 @@ static void plic_irq_resume(void *data)
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
/* irq ID 0 is reserved */
- for (i = 1; i < priv->nr_irqs; i++) {
+ for (i = 1; i <= priv->nr_irqs; i++) {
index = BIT_WORD(i);
writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0,
priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID);
@@ -293,7 +293,7 @@ static void plic_irq_resume(void *data)
continue;
raw_spin_lock_irqsave(&handler->enable_lock, flags);
- for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
+ for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs + 1, 32); i++) {
reg = handler->enable_base + i * sizeof(u32);
writel(handler->enable_save[i], reg);
}
@@ -351,7 +351,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
if (ret)
return ret;
- for (i = 0; i < nr_irqs; i++) {
+ for (i = 1; i <= nr_irqs; i++) {
ret = plic_irqdomain_map(domain, virq + i, hwirq + i);
if (ret)
return ret;
@@ -431,7 +431,7 @@ static u32 cp100_isolate_pending_irq(int nr_irq_groups, struct plic_handler *han
static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void __iomem *claim)
{
- int nr_irq_groups = DIV_ROUND_UP(handler->priv->nr_irqs, 32);
+ int nr_irq_groups = DIV_ROUND_UP(handler->priv->nr_irqs + 1, 32);
u32 __iomem *enable = handler->enable_base;
irq_hw_number_t hwirq = 0;
u32 iso_mask;
@@ -652,7 +652,7 @@ static int plic_probe(struct fwnode_handle *fwnode)
priv->gsi_base = gsi_base;
priv->acpi_plic_id = id;
- priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
+ priv->prio_save = bitmap_zalloc(nr_irqs + 1, GFP_KERNEL);
if (!priv->prio_save) {
error = -ENOMEM;
goto fail_free_priv;
@@ -686,7 +686,7 @@ static int plic_probe(struct fwnode_handle *fwnode)
u32 __iomem *enable_base = priv->regs + CONTEXT_ENABLE_BASE +
i * CONTEXT_ENABLE_SIZE;
- for (int j = 0; j <= nr_irqs / 32; j++)
+ for (int j = 0; j <= (nr_irqs + 1) / 32; j++)
writel(0, enable_base + j);
}
continue;
@@ -718,7 +718,7 @@ static int plic_probe(struct fwnode_handle *fwnode)
context_id * CONTEXT_ENABLE_SIZE;
handler->priv = priv;
- handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+ handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs + 1, 32),
sizeof(*handler->enable_save), GFP_KERNEL);
if (!handler->enable_save) {
error = -ENOMEM;
--
2.51.0
Powered by blists - more mailing lists