of/irq: Factor out parsing of interrupt-map parent phandle+args from of_irq_parse_raw()
Factor out the parsing of interrupt-map interrupt parent phandle and its arg cells to a separate function, of_irq_parse_imap_parent(), so that it can be used in other parsing scenarios (e.g. fw_devlink). There was a refcount leak on non-matching entries when iterating thru "interrupt-map" which is fixed. Tested-by: Marc Zyngier <maz@kernel.org> Tested-by: Anup Patel <apatel@ventanamicro.com> Link: https://lore.kernel.org/r/20240529-dt-interrupt-map-fix-v2-1-ef86dc5bcd2a@kernel.org Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
This commit is contained in:
parent
321e4fa68c
commit
935df1bd40
125
drivers/of/irq.c
125
drivers/of/irq.c
@ -25,6 +25,8 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "of_private.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
|
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
|
||||||
* @dev: Device node of the device whose interrupt is to be mapped
|
* @dev: Device node of the device whose interrupt is to be mapped
|
||||||
@ -96,6 +98,57 @@ static const char * const of_irq_imap_abusers[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_phandle_args *out_irq)
|
||||||
|
{
|
||||||
|
u32 intsize, addrsize;
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
/* Get the interrupt parent */
|
||||||
|
if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
|
||||||
|
np = of_node_get(of_irq_dflt_pic);
|
||||||
|
else
|
||||||
|
np = of_find_node_by_phandle(be32_to_cpup(imap));
|
||||||
|
imap++;
|
||||||
|
|
||||||
|
/* Check if not found */
|
||||||
|
if (!np) {
|
||||||
|
pr_debug(" -> imap parent not found !\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get #interrupt-cells and #address-cells of new parent */
|
||||||
|
if (of_property_read_u32(np, "#interrupt-cells",
|
||||||
|
&intsize)) {
|
||||||
|
pr_debug(" -> parent lacks #interrupt-cells!\n");
|
||||||
|
of_node_put(np);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (of_property_read_u32(np, "#address-cells",
|
||||||
|
&addrsize))
|
||||||
|
addrsize = 0;
|
||||||
|
|
||||||
|
pr_debug(" -> intsize=%d, addrsize=%d\n",
|
||||||
|
intsize, addrsize);
|
||||||
|
|
||||||
|
/* Check for malformed properties */
|
||||||
|
if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)
|
||||||
|
|| (len < (addrsize + intsize))) {
|
||||||
|
of_node_put(np);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug(" -> imaplen=%d\n", len);
|
||||||
|
|
||||||
|
imap += addrsize + intsize;
|
||||||
|
|
||||||
|
out_irq->np = np;
|
||||||
|
for (int i = 0; i < intsize; i++)
|
||||||
|
out_irq->args[i] = be32_to_cpup(imap - intsize + i);
|
||||||
|
out_irq->args_count = intsize;
|
||||||
|
|
||||||
|
return imap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_irq_parse_raw - Low level interrupt tree parsing
|
* of_irq_parse_raw - Low level interrupt tree parsing
|
||||||
* @addr: address specifier (start of "reg" property of the device) in be32 format
|
* @addr: address specifier (start of "reg" property of the device) in be32 format
|
||||||
@ -112,12 +165,12 @@ static const char * const of_irq_imap_abusers[] = {
|
|||||||
*/
|
*/
|
||||||
int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
||||||
{
|
{
|
||||||
struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
|
struct device_node *ipar, *tnode, *old = NULL;
|
||||||
__be32 initial_match_array[MAX_PHANDLE_ARGS];
|
__be32 initial_match_array[MAX_PHANDLE_ARGS];
|
||||||
const __be32 *match_array = initial_match_array;
|
const __be32 *match_array = initial_match_array;
|
||||||
const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) };
|
const __be32 *tmp, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) };
|
||||||
u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
|
u32 intsize = 1, addrsize;
|
||||||
int imaplen, match, i, rc = -EINVAL;
|
int i, rc = -EINVAL;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
of_print_phandle_args("of_irq_parse_raw: ", out_irq);
|
of_print_phandle_args("of_irq_parse_raw: ", out_irq);
|
||||||
@ -176,6 +229,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
|
|
||||||
/* Now start the actual "proper" walk of the interrupt tree */
|
/* Now start the actual "proper" walk of the interrupt tree */
|
||||||
while (ipar != NULL) {
|
while (ipar != NULL) {
|
||||||
|
int imaplen, match;
|
||||||
|
const __be32 *imap, *oldimap, *imask;
|
||||||
|
struct device_node *newpar;
|
||||||
/*
|
/*
|
||||||
* Now check if cursor is an interrupt-controller and
|
* Now check if cursor is an interrupt-controller and
|
||||||
* if it is then we are done, unless there is an
|
* if it is then we are done, unless there is an
|
||||||
@ -216,7 +272,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
|
|
||||||
/* Parse interrupt-map */
|
/* Parse interrupt-map */
|
||||||
match = 0;
|
match = 0;
|
||||||
while (imaplen > (addrsize + intsize + 1) && !match) {
|
while (imaplen > (addrsize + intsize + 1)) {
|
||||||
/* Compare specifiers */
|
/* Compare specifiers */
|
||||||
match = 1;
|
match = 1;
|
||||||
for (i = 0; i < (addrsize + intsize); i++, imaplen--)
|
for (i = 0; i < (addrsize + intsize); i++, imaplen--)
|
||||||
@ -224,48 +280,17 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
|
|
||||||
pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
|
pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
|
||||||
|
|
||||||
/* Get the interrupt parent */
|
oldimap = imap;
|
||||||
if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
|
imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq);
|
||||||
newpar = of_node_get(of_irq_dflt_pic);
|
if (!imap)
|
||||||
else
|
|
||||||
newpar = of_find_node_by_phandle(be32_to_cpup(imap));
|
|
||||||
imap++;
|
|
||||||
--imaplen;
|
|
||||||
|
|
||||||
/* Check if not found */
|
|
||||||
if (newpar == NULL) {
|
|
||||||
pr_debug(" -> imap parent not found !\n");
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!of_device_is_available(newpar))
|
match &= of_device_is_available(out_irq->np);
|
||||||
match = 0;
|
if (match)
|
||||||
|
break;
|
||||||
/* Get #interrupt-cells and #address-cells of new
|
|
||||||
* parent
|
|
||||||
*/
|
|
||||||
if (of_property_read_u32(newpar, "#interrupt-cells",
|
|
||||||
&newintsize)) {
|
|
||||||
pr_debug(" -> parent lacks #interrupt-cells!\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (of_property_read_u32(newpar, "#address-cells",
|
|
||||||
&newaddrsize))
|
|
||||||
newaddrsize = 0;
|
|
||||||
|
|
||||||
pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
|
|
||||||
newintsize, newaddrsize);
|
|
||||||
|
|
||||||
/* Check for malformed properties */
|
|
||||||
if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS)
|
|
||||||
|| (imaplen < (newaddrsize + newintsize))) {
|
|
||||||
rc = -EFAULT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
imap += newaddrsize + newintsize;
|
|
||||||
imaplen -= newaddrsize + newintsize;
|
|
||||||
|
|
||||||
|
of_node_put(out_irq->np);
|
||||||
|
imaplen -= imap - oldimap;
|
||||||
pr_debug(" -> imaplen=%d\n", imaplen);
|
pr_debug(" -> imaplen=%d\n", imaplen);
|
||||||
}
|
}
|
||||||
if (!match) {
|
if (!match) {
|
||||||
@ -287,11 +312,11 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
* Successfully parsed an interrupt-map translation; copy new
|
* Successfully parsed an interrupt-map translation; copy new
|
||||||
* interrupt specifier into the out_irq structure
|
* interrupt specifier into the out_irq structure
|
||||||
*/
|
*/
|
||||||
match_array = imap - newaddrsize - newintsize;
|
match_array = oldimap + 1;
|
||||||
for (i = 0; i < newintsize; i++)
|
|
||||||
out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
|
newpar = out_irq->np;
|
||||||
out_irq->args_count = intsize = newintsize;
|
intsize = out_irq->args_count;
|
||||||
addrsize = newaddrsize;
|
addrsize = (imap - match_array) - intsize;
|
||||||
|
|
||||||
if (ipar == newpar) {
|
if (ipar == newpar) {
|
||||||
pr_debug("%pOF interrupt-map entry to self\n", ipar);
|
pr_debug("%pOF interrupt-map entry to self\n", ipar);
|
||||||
@ -300,7 +325,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
|
|
||||||
skiplevel:
|
skiplevel:
|
||||||
/* Iterate again with new parent */
|
/* Iterate again with new parent */
|
||||||
out_irq->np = newpar;
|
|
||||||
pr_debug(" -> new parent: %pOF\n", newpar);
|
pr_debug(" -> new parent: %pOF\n", newpar);
|
||||||
of_node_put(ipar);
|
of_node_put(ipar);
|
||||||
ipar = newpar;
|
ipar = newpar;
|
||||||
@ -310,7 +334,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
of_node_put(ipar);
|
of_node_put(ipar);
|
||||||
of_node_put(newpar);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,9 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np,
|
|||||||
extern int of_bus_n_addr_cells(struct device_node *np);
|
extern int of_bus_n_addr_cells(struct device_node *np);
|
||||||
extern int of_bus_n_size_cells(struct device_node *np);
|
extern int of_bus_n_size_cells(struct device_node *np);
|
||||||
|
|
||||||
|
const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len,
|
||||||
|
struct of_phandle_args *out_irq);
|
||||||
|
|
||||||
struct bus_dma_region;
|
struct bus_dma_region;
|
||||||
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA)
|
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA)
|
||||||
int of_dma_get_range(struct device_node *np,
|
int of_dma_get_range(struct device_node *np,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user