Commit a1558727 authored by Anup Patel's avatar Anup Patel Committed by Thomas Gleixner

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: default avatarAnup Patel <apatel@ventanamicro.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20240222094006.1030709-6-apatel@ventanamicro.com
parent 6c725f33
...@@ -417,17 +417,45 @@ static const struct of_device_id plic_match[] = { ...@@ -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) 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; struct device *dev = &pdev->dev;
unsigned long plic_quirks = 0; unsigned long plic_quirks = 0;
struct plic_handler *handler; struct plic_handler *handler;
u32 nr_irqs, parent_hwirq;
struct irq_domain *domain; struct irq_domain *domain;
struct plic_priv *priv; struct plic_priv *priv;
irq_hw_number_t hwirq;
bool cpuhp_setup; bool cpuhp_setup;
unsigned int cpu;
u32 nr_irqs;
if (is_of_node(dev->fwnode)) { if (is_of_node(dev->fwnode)) {
const struct of_device_id *id; const struct of_device_id *id;
...@@ -463,13 +491,9 @@ static int plic_probe(struct platform_device *pdev) ...@@ -463,13 +491,9 @@ static int plic_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
for (i = 0; i < nr_contexts; i++) { for (i = 0; i < nr_contexts; i++) {
struct of_phandle_args parent; error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
irq_hw_number_t hwirq; if (error) {
int cpu; dev_warn(dev, "hwirq for context%d not found\n", i);
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);
continue; continue;
} }
...@@ -477,7 +501,7 @@ static int plic_probe(struct platform_device *pdev) ...@@ -477,7 +501,7 @@ static int plic_probe(struct platform_device *pdev)
* Skip contexts other than external interrupts for our * Skip contexts other than external interrupts for our
* privilege level. * 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. */ /* Disable S-mode enable bits if running in M-mode. */
if (IS_ENABLED(CONFIG_RISCV_M_MODE)) { if (IS_ENABLED(CONFIG_RISCV_M_MODE)) {
void __iomem *enable_base = priv->regs + void __iomem *enable_base = priv->regs +
...@@ -490,13 +514,6 @@ static int plic_probe(struct platform_device *pdev) ...@@ -490,13 +514,6 @@ static int plic_probe(struct platform_device *pdev)
continue; 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) { if (cpu < 0) {
dev_warn(dev, "Invalid cpuid for context %d\n", i); dev_warn(dev, "Invalid cpuid for context %d\n", i);
continue; continue;
...@@ -534,7 +551,7 @@ static int plic_probe(struct platform_device *pdev) ...@@ -534,7 +551,7 @@ static int plic_probe(struct platform_device *pdev)
handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32), handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
sizeof(*handler->enable_save), GFP_KERNEL); sizeof(*handler->enable_save), GFP_KERNEL);
if (!handler->enable_save) if (!handler->enable_save)
return -ENOMEM; goto fail_cleanup_contexts;
done: done:
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) { for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
plic_toggle(handler, hwirq, 0); plic_toggle(handler, hwirq, 0);
...@@ -547,7 +564,7 @@ static int plic_probe(struct platform_device *pdev) ...@@ -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, priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
&plic_irqdomain_ops, priv); &plic_irqdomain_ops, priv);
if (WARN_ON(!priv->irqdomain)) if (WARN_ON(!priv->irqdomain))
return -ENOMEM; goto fail_cleanup_contexts;
/* /*
* We can have multiple PLIC instances so setup cpuhp state * We can have multiple PLIC instances so setup cpuhp state
...@@ -575,6 +592,22 @@ static int plic_probe(struct platform_device *pdev) ...@@ -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", dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
nr_irqs, nr_handlers, nr_contexts); nr_irqs, nr_handlers, nr_contexts);
return 0; 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 = { static struct platform_driver plic_driver = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment