diff options
Diffstat (limited to 'drivers/of/address.c')
| -rw-r--r-- | drivers/of/address.c | 130 |
1 files changed, 68 insertions, 62 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index d669ce25b5f9..4034d798c55a 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -8,6 +8,7 @@ #include <linux/logic_pio.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/overflow.h> #include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/sizes.h> @@ -15,25 +16,12 @@ #include <linux/string.h> #include <linux/dma-direct.h> /* for bus_dma_region */ -#include "of_private.h" +#include <kunit/visibility.h> -/* Max address size we deal with */ -#define OF_MAX_ADDR_CELLS 4 -#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) -#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0) +/* Uncomment me to enable of_dump_addr() debugging output */ +// #define DEBUG -/* Debug utility */ -#ifdef DEBUG -static void of_dump_addr(const char *s, const __be32 *addr, int na) -{ - pr_debug("%s", s); - while (na--) - pr_cont(" %08x", be32_to_cpu(*(addr++))); - pr_cont("\n"); -} -#else -static void of_dump_addr(const char *s, const __be32 *addr, int na) { } -#endif +#include "of_private.h" /* Callbacks for bus specific translators */ struct of_bus { @@ -146,7 +134,7 @@ static unsigned int of_bus_pci_get_flags(const __be32 *addr) * PCI bus specific translator */ -static bool of_node_is_pcie(struct device_node *np) +static bool of_node_is_pcie(const struct device_node *np) { bool is_pcie = of_node_name_eq(np, "pcie"); @@ -197,6 +185,22 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, #endif /* CONFIG_PCI */ +VISIBLE_IF_KUNIT int __of_address_resource_bounds(struct resource *r, u64 start, u64 size) +{ + if (overflows_type(start, r->start)) + return -EOVERFLOW; + + r->start = start; + + if (!size) + r->end = wrapping_sub(typeof(r->end), r->start, 1); + else if (size && check_add_overflow(r->start, size - 1, &r->end)) + return -EOVERFLOW; + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(__of_address_resource_bounds); + /* * of_pci_range_to_resource - Create a resource from an of_pci_range * @range: the PCI range that describes the resource @@ -212,9 +216,10 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, * To guard against that we try to register the IO range first. * If that fails we know that pci_address_to_pio() will do too. */ -int of_pci_range_to_resource(struct of_pci_range *range, - struct device_node *np, struct resource *res) +int of_pci_range_to_resource(const struct of_pci_range *range, + const struct device_node *np, struct resource *res) { + u64 start; int err; res->flags = range->flags; res->parent = res->child = res->sibling = NULL; @@ -231,18 +236,11 @@ int of_pci_range_to_resource(struct of_pci_range *range, err = -EINVAL; goto invalid_range; } - res->start = port; + start = port; } else { - if ((sizeof(resource_size_t) < 8) && - upper_32_bits(range->cpu_addr)) { - err = -EINVAL; - goto invalid_range; - } - - res->start = range->cpu_addr; + start = range->cpu_addr; } - res->end = res->start + range->size - 1; - return 0; + return __of_address_resource_bounds(res, start, range->size); invalid_range: res->start = (resource_size_t)OF_BAD_ADDR; @@ -258,8 +256,8 @@ EXPORT_SYMBOL(of_pci_range_to_resource); * @res: pointer to a valid resource that will be updated to * reflect the values contained in the range. * - * Returns ENOENT if the entry is not found or EINVAL if the range cannot be - * converted to resource. + * Returns -ENOENT if the entry is not found or -EOVERFLOW if the range + * cannot be converted to resource. */ int of_range_to_resource(struct device_node *np, int index, struct resource *res) { @@ -321,14 +319,23 @@ static unsigned int of_bus_isa_get_flags(const __be32 *addr) static int of_bus_default_flags_match(struct device_node *np) { - return of_bus_n_addr_cells(np) == 3; + /* + * Check for presence first since of_bus_n_addr_cells() will warn when + * walking parent nodes. + */ + return of_property_present(np, "#address-cells") && (of_bus_n_addr_cells(np) == 3); +} + +static int of_bus_default_match(struct device_node *np) +{ + return of_property_present(np, "#address-cells"); } /* * Array of bus specific translators */ -static struct of_bus of_busses[] = { +static const struct of_bus of_busses[] = { #ifdef CONFIG_PCI /* PCI */ { @@ -368,7 +375,7 @@ static struct of_bus of_busses[] = { { .name = "default", .addresses = "reg", - .match = NULL, + .match = of_bus_default_match, .count_cells = of_bus_default_count_cells, .map = of_bus_default_map, .translate = of_bus_default_translate, @@ -376,18 +383,17 @@ static struct of_bus of_busses[] = { }, }; -static struct of_bus *of_match_bus(struct device_node *np) +static const struct of_bus *of_match_bus(struct device_node *np) { int i; for (i = 0; i < ARRAY_SIZE(of_busses); i++) if (!of_busses[i].match || of_busses[i].match(np)) return &of_busses[i]; - BUG(); return NULL; } -static int of_empty_ranges_quirk(struct device_node *np) +static int of_empty_ranges_quirk(const struct device_node *np) { if (IS_ENABLED(CONFIG_PPC)) { /* To save cycles, we cache the result for global "Mac" setting */ @@ -407,8 +413,8 @@ static int of_empty_ranges_quirk(struct device_node *np) return false; } -static int of_translate_one(struct device_node *parent, struct of_bus *bus, - struct of_bus *pbus, __be32 *addr, +static int of_translate_one(const struct device_node *parent, const struct of_bus *bus, + const struct of_bus *pbus, __be32 *addr, int na, int ns, int pna, const char *rprop) { const __be32 *ranges; @@ -443,7 +449,8 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, } if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); - memset(addr, 0, pna * 4); + /* set address to zero, pass flags through */ + memset(addr + pbus->flag_cells, 0, (pna - pbus->flag_cells) * 4); pr_debug("empty ranges; 1:1 translation\n"); goto finish; } @@ -493,7 +500,7 @@ static u64 __of_translate_address(struct device_node *node, { struct device_node *dev __free(device_node) = of_node_get(node); struct device_node *parent __free(device_node) = get_parent(dev); - struct of_bus *bus, *pbus; + const struct of_bus *bus, *pbus; __be32 addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; @@ -504,6 +511,8 @@ static u64 __of_translate_address(struct device_node *node, if (parent == NULL) return OF_BAD_ADDR; bus = of_match_bus(parent); + if (!bus) + return OF_BAD_ADDR; /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); @@ -547,6 +556,8 @@ static u64 __of_translate_address(struct device_node *node, /* Get new parent bus and counts */ pbus = of_match_bus(parent); + if (!pbus) + return OF_BAD_ADDR; pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { pr_err("Bad cell count for %pOF\n", dev); @@ -603,7 +614,7 @@ struct device_node *__of_get_dma_parent(const struct device_node *np) if (ret < 0) return of_get_parent(np); - return of_node_get(args.np); + return args.np; } #endif @@ -678,7 +689,7 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, const __be32 *prop; unsigned int psize; struct device_node *parent __free(device_node) = of_get_parent(dev); - struct of_bus *bus; + const struct of_bus *bus; int onesize, i, na, ns; if (parent == NULL) @@ -686,11 +697,7 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, /* match the parent's bus type */ bus = of_match_bus(parent); - if (strcmp(bus->name, "pci") && (bar_no >= 0)) - return NULL; - - bus->count_cells(dev, &na, &ns); - if (!OF_CHECK_ADDR_COUNT(na)) + if (!bus || (strcmp(bus->name, "pci") && (bar_no >= 0))) return NULL; /* Get "reg" or "assigned-addresses" property */ @@ -699,6 +706,10 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, return NULL; psize /= 4; + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_ADDR_COUNT(na)) + return NULL; + onesize = na + ns; for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) { u32 val = be32_to_cpu(prop[0]); @@ -799,6 +810,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, else range->cpu_addr = of_translate_address(parser->node, parser->range + na); + + range->parent_bus_addr = of_read_number(parser->range + na, parser->pna); range->size = of_read_number(parser->range + parser->pna + na, ns); parser->range += np; @@ -1014,20 +1027,15 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent); * * Returns true if the "nonposted-mmio" property was found for * the device's bus. - * - * This is currently only enabled on builds that support Apple ARM devices, as - * an optimization. */ -static bool of_mmio_is_nonposted(struct device_node *np) +static bool of_mmio_is_nonposted(const struct device_node *np) { - if (!IS_ENABLED(CONFIG_ARCH_APPLE)) - return false; - struct device_node *parent __free(device_node) = of_get_parent(np); - if (!parent) - return false; - return of_property_read_bool(parent, "nonposted-mmio"); + if (of_property_read_bool(np, "nonposted-mmio")) + return true; + + return parent && of_property_read_bool(parent, "nonposted-mmio"); } static int __of_address_to_resource(struct device_node *dev, int index, int bar_no, @@ -1061,12 +1069,10 @@ static int __of_address_to_resource(struct device_node *dev, int index, int bar_ if (of_mmio_is_nonposted(dev)) flags |= IORESOURCE_MEM_NONPOSTED; - r->start = taddr; - r->end = taddr + size - 1; r->flags = flags; r->name = name ? name : dev->full_name; - return 0; + return __of_address_resource_bounds(r, taddr, size); } /** |
