summaryrefslogtreecommitdiff
path: root/drivers/of/address.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/address.c')
-rw-r--r--drivers/of/address.c130
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);
}
/**