checks: Add an interrupt-map check

Add a check for parsing 'interrupt-map' properties. The check primarily
tests parsing 'interrupt-map' properties which depends on and the parent
interrupt controller (or another map) node.

Note that this does not require '#address-cells' in the interrupt-map
parent, but treats missing '#address-cells' as 0 which is how the Linux
kernel parses it. There's numerous cases that expect this behavior.

Cc: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Message-Id: <20211015213527.2237774-1-robh@kernel.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
Rob Herring 2021-10-15 16:35:26 -05:00 committed by David Gibson
parent 8fd24744e3
commit 0a3a9d3449
5 changed files with 144 additions and 0 deletions

View file

@ -1590,6 +1590,90 @@ static void check_interrupt_provider(struct check *c,
} }
WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell); WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell);
static void check_interrupt_map(struct check *c,
struct dt_info *dti,
struct node *node)
{
struct node *root = dti->dt;
struct property *prop, *irq_map_prop;
size_t cellsize, cell, map_cells;
irq_map_prop = get_property(node, "interrupt-map");
if (!irq_map_prop)
return;
if (node->addr_cells < 0) {
FAIL(c, dti, node,
"Missing '#address-cells' in interrupt-map provider");
return;
}
cellsize = node_addr_cells(node);
cellsize += propval_cell(get_property(node, "#interrupt-cells"));
prop = get_property(node, "interrupt-map-mask");
if (prop && (prop->val.len != (cellsize * sizeof(cell_t))))
FAIL_PROP(c, dti, node, prop,
"property size (%d) is invalid, expected %zu",
prop->val.len, cellsize * sizeof(cell_t));
if (!is_multiple_of(irq_map_prop->val.len, sizeof(cell_t))) {
FAIL_PROP(c, dti, node, irq_map_prop,
"property size (%d) is invalid, expected multiple of %zu",
irq_map_prop->val.len, sizeof(cell_t));
return;
}
map_cells = irq_map_prop->val.len / sizeof(cell_t);
for (cell = 0; cell < map_cells; ) {
struct node *provider_node;
struct property *cellprop;
int phandle;
size_t parent_cellsize;
if ((cell + cellsize) >= map_cells) {
FAIL_PROP(c, dti, node, irq_map_prop,
"property size (%d) too small, expected > %zu",
irq_map_prop->val.len, (cell + cellsize) * sizeof(cell_t));
break;
}
cell += cellsize;
phandle = propval_cell_n(irq_map_prop, cell);
if (!phandle_is_valid(phandle)) {
/* Give up if this is an overlay with external references */
if (!(dti->dtsflags & DTSF_PLUGIN))
FAIL_PROP(c, dti, node, irq_map_prop,
"Cell %zu is not a phandle(%d)",
cell, phandle);
break;
}
provider_node = get_node_by_phandle(root, phandle);
if (!provider_node) {
FAIL_PROP(c, dti, node, irq_map_prop,
"Could not get phandle(%d) node for (cell %zu)",
phandle, cell);
break;
}
cellprop = get_property(provider_node, "#interrupt-cells");
if (cellprop) {
parent_cellsize = propval_cell(cellprop);
} else {
FAIL(c, dti, node, "Missing property '#interrupt-cells' in node %s or bad phandle (referred from interrupt-map[%zu])",
provider_node->fullpath, cell);
break;
}
cellprop = get_property(provider_node, "#address-cells");
if (cellprop)
parent_cellsize += propval_cell(cellprop);
cell += 1 + parent_cellsize;
}
}
WARNING(interrupt_map, check_interrupt_map, NULL, &phandle_references, &addr_size_cells, &interrupt_provider);
static void check_interrupts_property(struct check *c, static void check_interrupts_property(struct check *c,
struct dt_info *dti, struct dt_info *dti,
struct node *node) struct node *node)
@ -1888,6 +1972,7 @@ static struct check *check_table[] = {
&gpios_property, &gpios_property,
&interrupts_property, &interrupts_property,
&interrupt_provider, &interrupt_provider,
&interrupt_map,
&alias_paths, &alias_paths,

View file

@ -0,0 +1,20 @@
/dts-v1/;
/ {
interrupt-parent = <&intc>;
intc: interrupt-controller {
#interrupt-cells = <3>;
interrupt-controller;
};
node {
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-map = <1 &intc 1 2 3>;
interrupt-map-mask = <0 0>;
child {
interrupts = <1>;
};
};
};

View file

@ -0,0 +1,17 @@
/dts-v1/;
/ {
interrupt-parent = <&intc>;
intc: interrupt-controller {
};
node {
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-map = <1 &intc 1 2 3>;
child {
interrupts = <1>;
};
};
};

View file

@ -0,0 +1,19 @@
/dts-v1/;
/ {
interrupt-parent = <&intc>;
intc: interrupt-controller {
#interrupt-cells = <3>;
interrupt-controller;
};
node {
/* Missing #address-cells = <0>; */
#interrupt-cells = <1>;
interrupt-map = <1 &intc 1 2 3>;
child {
interrupts = <1>;
};
};
};

View file

@ -717,6 +717,9 @@ dtc_tests () {
run_sh_test "$SRCDIR/dtc-checkfails.sh" -n deprecated_gpio_property -- -Wdeprecated_gpio_property -I dts -O dtb "$SRCDIR/good-gpio.dts" run_sh_test "$SRCDIR/dtc-checkfails.sh" -n deprecated_gpio_property -- -Wdeprecated_gpio_property -I dts -O dtb "$SRCDIR/good-gpio.dts"
check_tests "$SRCDIR/bad-interrupt-cells.dts" interrupts_property check_tests "$SRCDIR/bad-interrupt-cells.dts" interrupts_property
check_tests "$SRCDIR/bad-interrupt-controller.dts" interrupt_provider check_tests "$SRCDIR/bad-interrupt-controller.dts" interrupt_provider
check_tests "$SRCDIR/bad-interrupt-map.dts" interrupt_map
check_tests "$SRCDIR/bad-interrupt-map-parent.dts" interrupt_map
check_tests "$SRCDIR/bad-interrupt-map-mask.dts" interrupt_map
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_chars -- -I dtb -O dtb bad_node_char.dtb run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_chars -- -I dtb -O dtb bad_node_char.dtb
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_format -- -I dtb -O dtb bad_node_format.dtb run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_format -- -I dtb -O dtb bad_node_format.dtb
run_sh_test "$SRCDIR/dtc-checkfails.sh" property_name_chars -- -I dtb -O dtb bad_prop_char.dtb run_sh_test "$SRCDIR/dtc-checkfails.sh" property_name_chars -- -I dtb -O dtb bad_prop_char.dtb