Skip to content

Commit a155872

Browse files
avpatelKAGA-KOKO
authored andcommitted
irqchip/sifive-plic: Cleanup PLIC contexts upon irqdomain creation failure
The SiFive PLIC contexts should not be left dangling if irqdomain creation fails because plic_starting_cpu() can crash accessing unmapped registers. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/r/20240222094006.1030709-6-apatel@ventanamicro.com
1 parent 6c725f3 commit a155872

File tree

1 file changed

+53
-20
lines changed

1 file changed

+53
-20
lines changed

drivers/irqchip/irq-sifive-plic.c

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -417,17 +417,45 @@ static const struct of_device_id plic_match[] = {
417417
{}
418418
};
419419

420+
static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
421+
u32 *parent_hwirq, int *parent_cpu)
422+
{
423+
struct device *dev = &pdev->dev;
424+
struct of_phandle_args parent;
425+
unsigned long hartid;
426+
int rc;
427+
428+
/*
429+
* Currently, only OF fwnode is supported so extend this
430+
* function for ACPI support.
431+
*/
432+
if (!is_of_node(dev->fwnode))
433+
return -EINVAL;
434+
435+
rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
436+
if (rc)
437+
return rc;
438+
439+
rc = riscv_of_parent_hartid(parent.np, &hartid);
440+
if (rc)
441+
return rc;
442+
443+
*parent_hwirq = parent.args[0];
444+
*parent_cpu = riscv_hartid_to_cpuid(hartid);
445+
return 0;
446+
}
447+
420448
static int plic_probe(struct platform_device *pdev)
421449
{
422-
int error = 0, nr_contexts, nr_handlers = 0, i;
450+
int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
423451
struct device *dev = &pdev->dev;
424452
unsigned long plic_quirks = 0;
425453
struct plic_handler *handler;
454+
u32 nr_irqs, parent_hwirq;
426455
struct irq_domain *domain;
427456
struct plic_priv *priv;
457+
irq_hw_number_t hwirq;
428458
bool cpuhp_setup;
429-
unsigned int cpu;
430-
u32 nr_irqs;
431459

432460
if (is_of_node(dev->fwnode)) {
433461
const struct of_device_id *id;
@@ -463,21 +491,17 @@ static int plic_probe(struct platform_device *pdev)
463491
return -EINVAL;
464492

465493
for (i = 0; i < nr_contexts; i++) {
466-
struct of_phandle_args parent;
467-
irq_hw_number_t hwirq;
468-
int cpu;
469-
unsigned long hartid;
470-
471-
if (of_irq_parse_one(to_of_node(dev->fwnode), i, &parent)) {
472-
dev_err(dev, "failed to parse parent for context %d.\n", i);
494+
error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
495+
if (error) {
496+
dev_warn(dev, "hwirq for context%d not found\n", i);
473497
continue;
474498
}
475499

476500
/*
477501
* Skip contexts other than external interrupts for our
478502
* privilege level.
479503
*/
480-
if (parent.args[0] != RV_IRQ_EXT) {
504+
if (parent_hwirq != RV_IRQ_EXT) {
481505
/* Disable S-mode enable bits if running in M-mode. */
482506
if (IS_ENABLED(CONFIG_RISCV_M_MODE)) {
483507
void __iomem *enable_base = priv->regs +
@@ -490,13 +514,6 @@ static int plic_probe(struct platform_device *pdev)
490514
continue;
491515
}
492516

493-
error = riscv_of_parent_hartid(parent.np, &hartid);
494-
if (error < 0) {
495-
dev_warn(dev, "failed to parse hart ID for context %d.\n", i);
496-
continue;
497-
}
498-
499-
cpu = riscv_hartid_to_cpuid(hartid);
500517
if (cpu < 0) {
501518
dev_warn(dev, "Invalid cpuid for context %d\n", i);
502519
continue;
@@ -534,7 +551,7 @@ static int plic_probe(struct platform_device *pdev)
534551
handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
535552
sizeof(*handler->enable_save), GFP_KERNEL);
536553
if (!handler->enable_save)
537-
return -ENOMEM;
554+
goto fail_cleanup_contexts;
538555
done:
539556
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
540557
plic_toggle(handler, hwirq, 0);
@@ -547,7 +564,7 @@ static int plic_probe(struct platform_device *pdev)
547564
priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
548565
&plic_irqdomain_ops, priv);
549566
if (WARN_ON(!priv->irqdomain))
550-
return -ENOMEM;
567+
goto fail_cleanup_contexts;
551568

552569
/*
553570
* We can have multiple PLIC instances so setup cpuhp state
@@ -575,6 +592,22 @@ static int plic_probe(struct platform_device *pdev)
575592
dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
576593
nr_irqs, nr_handlers, nr_contexts);
577594
return 0;
595+
596+
fail_cleanup_contexts:
597+
for (i = 0; i < nr_contexts; i++) {
598+
if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
599+
continue;
600+
if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
601+
continue;
602+
603+
handler = per_cpu_ptr(&plic_handlers, cpu);
604+
handler->present = false;
605+
handler->hart_base = NULL;
606+
handler->enable_base = NULL;
607+
handler->enable_save = NULL;
608+
handler->priv = NULL;
609+
}
610+
return -ENOMEM;
578611
}
579612

580613
static struct platform_driver plic_driver = {

0 commit comments

Comments
 (0)