Skip to content

Commit

Permalink
irqchip/sifive-plic: Cleanup PLIC contexts upon irqdomain creation fa…
Browse files Browse the repository at this point in the history
…ilure

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
  • Loading branch information
Anup Patel authored and Thomas Gleixner committed Feb 23, 2024
1 parent 6c725f3 commit a155872
Showing 1 changed file with 53 additions and 20 deletions.
73 changes: 53 additions & 20 deletions drivers/irqchip/irq-sifive-plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,17 +417,45 @@ static const struct of_device_id plic_match[] = {
{}
};

static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
u32 *parent_hwirq, int *parent_cpu)
{
struct device *dev = &pdev->dev;
struct of_phandle_args parent;
unsigned long hartid;
int rc;

/*
* Currently, only OF fwnode is supported so extend this
* function for ACPI support.
*/
if (!is_of_node(dev->fwnode))
return -EINVAL;

rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
if (rc)
return rc;

rc = riscv_of_parent_hartid(parent.np, &hartid);
if (rc)
return rc;

*parent_hwirq = parent.args[0];
*parent_cpu = riscv_hartid_to_cpuid(hartid);
return 0;
}

static int plic_probe(struct platform_device *pdev)
{
int error = 0, nr_contexts, nr_handlers = 0, i;
int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
struct device *dev = &pdev->dev;
unsigned long plic_quirks = 0;
struct plic_handler *handler;
u32 nr_irqs, parent_hwirq;
struct irq_domain *domain;
struct plic_priv *priv;
irq_hw_number_t hwirq;
bool cpuhp_setup;
unsigned int cpu;
u32 nr_irqs;

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

for (i = 0; i < nr_contexts; i++) {
struct of_phandle_args parent;
irq_hw_number_t hwirq;
int cpu;
unsigned long hartid;

if (of_irq_parse_one(to_of_node(dev->fwnode), i, &parent)) {
dev_err(dev, "failed to parse parent for context %d.\n", i);
error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
if (error) {
dev_warn(dev, "hwirq for context%d not found\n", i);
continue;
}

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

error = riscv_of_parent_hartid(parent.np, &hartid);
if (error < 0) {
dev_warn(dev, "failed to parse hart ID for context %d.\n", i);
continue;
}

cpu = riscv_hartid_to_cpuid(hartid);
if (cpu < 0) {
dev_warn(dev, "Invalid cpuid for context %d\n", i);
continue;
Expand Down Expand Up @@ -534,7 +551,7 @@ static int plic_probe(struct platform_device *pdev)
handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
sizeof(*handler->enable_save), GFP_KERNEL);
if (!handler->enable_save)
return -ENOMEM;
goto fail_cleanup_contexts;
done:
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
plic_toggle(handler, hwirq, 0);
Expand All @@ -547,7 +564,7 @@ static int plic_probe(struct platform_device *pdev)
priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
&plic_irqdomain_ops, priv);
if (WARN_ON(!priv->irqdomain))
return -ENOMEM;
goto fail_cleanup_contexts;

/*
* We can have multiple PLIC instances so setup cpuhp state
Expand Down Expand Up @@ -575,6 +592,22 @@ static int plic_probe(struct platform_device *pdev)
dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
nr_irqs, nr_handlers, nr_contexts);
return 0;

fail_cleanup_contexts:
for (i = 0; i < nr_contexts; i++) {
if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
continue;
if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
continue;

handler = per_cpu_ptr(&plic_handlers, cpu);
handler->present = false;
handler->hart_base = NULL;
handler->enable_base = NULL;
handler->enable_save = NULL;
handler->priv = NULL;
}
return -ENOMEM;
}

static struct platform_driver plic_driver = {
Expand Down

0 comments on commit a155872

Please sign in to comment.