Skip to content

Commit 42e025b

Browse files
author
Thomas Gleixner
committed
irqchip/sifive-plic: Handle number of hardware interrupts correctly
The driver is handling the number of hardware interrupts inconsistently. The reason is that the firmware enumerates the maximum number of device interrupts, but the actual number of hardware interrupts is one more because hardware interrupt 0 is reserved. There are two loop variants where this matters: 1) Iterating over the device interrupts for (irq = 1; irq < total_irqs; irq++) 2) Iterating over the number of interrupt register groups for (grp = 0; grp < irq_groups; grp++) The current code stores the number of device interrupts and that requires to write the loops as: 1) for (irq = 1; irq <= device_irqs; irq++) 2) for (grp = 0; grp < DIV_ROUND_UP(device_irqs + 1); grp++) But the code gets it wrong all over the place. Just fixing up the conditions and off by ones is not a sustainable solution as the next changes will reintroduce the same bugs over and over. Sanitize it by storing the total number of hardware interrupts during probe and precalculating the number of groups. To future proof it mark priv::total_irqs __private, provide a correct iterator macro and adjust the code to this. Marking it private allows sparse (C=1 build) to catch direct access to this member: drivers/irqchip/irq-sifive-plic.c:270:9: warning: dereference of noderef expression That should prevent at least the most obvious future damage in that area. Fixes: e80f0b6 ("irqchip/irq-sifive-plic: Add syscore callbacks for hibernation") Reported-by: Yangyu Chen <cyy@cyyself.name> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Tested-by: Yangyu Chen <cyy@cyyself.name> Link: https://patch.msgid.link/87ikcd36i9.ffs@tglx
1 parent e1f9466 commit 42e025b

1 file changed

Lines changed: 45 additions & 37 deletions

File tree

drivers/irqchip/irq-sifive-plic.c

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,17 @@
6868
#define PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM 1
6969

7070
struct plic_priv {
71-
struct fwnode_handle *fwnode;
72-
struct cpumask lmask;
73-
struct irq_domain *irqdomain;
74-
void __iomem *regs;
75-
unsigned long plic_quirks;
76-
unsigned int nr_irqs;
77-
unsigned long *prio_save;
78-
u32 gsi_base;
79-
int acpi_plic_id;
71+
struct fwnode_handle *fwnode;
72+
struct cpumask lmask;
73+
struct irq_domain *irqdomain;
74+
void __iomem *regs;
75+
unsigned long plic_quirks;
76+
/* device interrupts + 1 to compensate for the reserved hwirq 0 */
77+
unsigned int __private total_irqs;
78+
unsigned int irq_groups;
79+
unsigned long *prio_save;
80+
u32 gsi_base;
81+
int acpi_plic_id;
8082
};
8183

8284
struct plic_handler {
@@ -91,6 +93,12 @@ struct plic_handler {
9193
u32 *enable_save;
9294
struct plic_priv *priv;
9395
};
96+
97+
/*
98+
* Macro to deal with the insanity of hardware interrupt 0 being reserved */
99+
#define for_each_device_irq(iter, priv) \
100+
for (unsigned int iter = 1; iter < ACCESS_PRIVATE(priv, total_irqs); iter++)
101+
94102
static int plic_parent_irq __ro_after_init;
95103
static bool plic_global_setup_done __ro_after_init;
96104
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
@@ -257,33 +265,27 @@ static int plic_irq_set_type(struct irq_data *d, unsigned int type)
257265

258266
static int plic_irq_suspend(void *data)
259267
{
260-
struct plic_priv *priv;
261-
262-
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
268+
struct plic_priv *priv = this_cpu_ptr(&plic_handlers)->priv;
263269

264-
/* irq ID 0 is reserved */
265-
for (unsigned int i = 1; i < priv->nr_irqs; i++) {
266-
__assign_bit(i, priv->prio_save,
267-
readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID));
270+
for_each_device_irq(irq, priv) {
271+
__assign_bit(irq, priv->prio_save,
272+
readl(priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID));
268273
}
269274

270275
return 0;
271276
}
272277

273278
static void plic_irq_resume(void *data)
274279
{
275-
unsigned int i, index, cpu;
280+
struct plic_priv *priv = this_cpu_ptr(&plic_handlers)->priv;
281+
unsigned int index, cpu;
276282
unsigned long flags;
277283
u32 __iomem *reg;
278-
struct plic_priv *priv;
279-
280-
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
281284

282-
/* irq ID 0 is reserved */
283-
for (i = 1; i < priv->nr_irqs; i++) {
284-
index = BIT_WORD(i);
285-
writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0,
286-
priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID);
285+
for_each_device_irq(irq, priv) {
286+
index = BIT_WORD(irq);
287+
writel((priv->prio_save[index] & BIT_MASK(irq)) ? 1 : 0,
288+
priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID);
287289
}
288290

289291
for_each_present_cpu(cpu) {
@@ -293,7 +295,7 @@ static void plic_irq_resume(void *data)
293295
continue;
294296

295297
raw_spin_lock_irqsave(&handler->enable_lock, flags);
296-
for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
298+
for (unsigned int i = 0; i < priv->irq_groups; i++) {
297299
reg = handler->enable_base + i * sizeof(u32);
298300
writel(handler->enable_save[i], reg);
299301
}
@@ -431,7 +433,7 @@ static u32 cp100_isolate_pending_irq(int nr_irq_groups, struct plic_handler *han
431433

432434
static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void __iomem *claim)
433435
{
434-
int nr_irq_groups = DIV_ROUND_UP(handler->priv->nr_irqs, 32);
436+
int nr_irq_groups = handler->priv->irq_groups;
435437
u32 __iomem *enable = handler->enable_base;
436438
irq_hw_number_t hwirq = 0;
437439
u32 iso_mask;
@@ -614,7 +616,6 @@ static int plic_probe(struct fwnode_handle *fwnode)
614616
struct plic_handler *handler;
615617
u32 nr_irqs, parent_hwirq;
616618
struct plic_priv *priv;
617-
irq_hw_number_t hwirq;
618619
void __iomem *regs;
619620
int id, context_id;
620621
u32 gsi_base;
@@ -647,7 +648,16 @@ static int plic_probe(struct fwnode_handle *fwnode)
647648

648649
priv->fwnode = fwnode;
649650
priv->plic_quirks = plic_quirks;
650-
priv->nr_irqs = nr_irqs;
651+
/*
652+
* The firmware provides the number of device interrupts. As
653+
* hardware interrupt 0 is reserved, the number of total interrupts
654+
* is nr_irqs + 1.
655+
*/
656+
nr_irqs++;
657+
ACCESS_PRIVATE(priv, total_irqs) = nr_irqs;
658+
/* Precalculate the number of register groups */
659+
priv->irq_groups = DIV_ROUND_UP(nr_irqs, 32);
660+
651661
priv->regs = regs;
652662
priv->gsi_base = gsi_base;
653663
priv->acpi_plic_id = id;
@@ -686,7 +696,7 @@ static int plic_probe(struct fwnode_handle *fwnode)
686696
u32 __iomem *enable_base = priv->regs + CONTEXT_ENABLE_BASE +
687697
i * CONTEXT_ENABLE_SIZE;
688698

689-
for (int j = 0; j <= nr_irqs / 32; j++)
699+
for (int j = 0; j < priv->irq_groups; j++)
690700
writel(0, enable_base + j);
691701
}
692702
continue;
@@ -718,23 +728,21 @@ static int plic_probe(struct fwnode_handle *fwnode)
718728
context_id * CONTEXT_ENABLE_SIZE;
719729
handler->priv = priv;
720730

721-
handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
722-
sizeof(*handler->enable_save), GFP_KERNEL);
731+
handler->enable_save = kcalloc(priv->irq_groups, sizeof(*handler->enable_save),
732+
GFP_KERNEL);
723733
if (!handler->enable_save) {
724734
error = -ENOMEM;
725735
goto fail_cleanup_contexts;
726736
}
727737
done:
728-
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
738+
for_each_device_irq(hwirq, priv) {
729739
plic_toggle(handler, hwirq, 0);
730-
writel(1, priv->regs + PRIORITY_BASE +
731-
hwirq * PRIORITY_PER_ID);
740+
writel(1, priv->regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
732741
}
733742
nr_handlers++;
734743
}
735744

736-
priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs + 1,
737-
&plic_irqdomain_ops, priv);
745+
priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs, &plic_irqdomain_ops, priv);
738746
if (WARN_ON(!priv->irqdomain)) {
739747
error = -ENOMEM;
740748
goto fail_cleanup_contexts;

0 commit comments

Comments
 (0)