diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/.kunitconfig | 1 | ||||
-rw-r--r-- | drivers/of/Kconfig | 10 | ||||
-rw-r--r-- | drivers/of/Makefile | 3 | ||||
-rw-r--r-- | drivers/of/address.c | 153 | ||||
-rw-r--r-- | drivers/of/base.c | 34 | ||||
-rw-r--r-- | drivers/of/device.c | 72 | ||||
-rw-r--r-- | drivers/of/dynamic.c | 64 | ||||
-rw-r--r-- | drivers/of/fdt.c | 32 | ||||
-rw-r--r-- | drivers/of/irq.c | 197 | ||||
-rw-r--r-- | drivers/of/kunit_overlay_test.dtso | 9 | ||||
-rw-r--r-- | drivers/of/module.c | 7 | ||||
-rw-r--r-- | drivers/of/of_kunit_helpers.c | 77 | ||||
-rw-r--r-- | drivers/of/of_numa.c | 5 | ||||
-rw-r--r-- | drivers/of/of_private.h | 4 | ||||
-rw-r--r-- | drivers/of/of_reserved_mem.c | 22 | ||||
-rw-r--r-- | drivers/of/of_test.c | 1 | ||||
-rw-r--r-- | drivers/of/overlay.c | 23 | ||||
-rw-r--r-- | drivers/of/overlay_test.c | 115 | ||||
-rw-r--r-- | drivers/of/platform.c | 25 | ||||
-rw-r--r-- | drivers/of/property.c | 122 | ||||
-rw-r--r-- | drivers/of/resolver.c | 47 | ||||
-rw-r--r-- | drivers/of/unittest.c | 188 |
22 files changed, 748 insertions, 463 deletions
diff --git a/drivers/of/.kunitconfig b/drivers/of/.kunitconfig index 5a8fee11978c..4c53d2c7a275 100644 --- a/drivers/of/.kunitconfig +++ b/drivers/of/.kunitconfig @@ -1,3 +1,4 @@ CONFIG_KUNIT=y CONFIG_OF=y CONFIG_OF_KUNIT_TEST=y +CONFIG_OF_OVERLAY_KUNIT_TEST=y diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index dd726c7056bf..0e2d608c3e20 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -107,6 +107,16 @@ config OF_OVERLAY While this option is selected automatically when needed, you can enable it manually to improve device tree unit test coverage. +config OF_OVERLAY_KUNIT_TEST + tristate "Device Tree overlay KUnit tests" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + select OF_OVERLAY + help + This option builds KUnit unit tests for the device tree overlay code. + + If unsure, say N here, but this option is safe to enable. + config OF_NUMA bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 251d33532148..379a0afcbdc0 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -19,6 +19,9 @@ obj-y += kexec.o endif endif +obj-$(CONFIG_KUNIT) += of_kunit_helpers.o obj-$(CONFIG_OF_KUNIT_TEST) += of_test.o +obj-$(CONFIG_OF_OVERLAY_KUNIT_TEST) += overlay-test.o +overlay-test-y := overlay_test.o kunit_overlay_test.dtbo.o obj-$(CONFIG_OF_UNITTEST) += unittest-data/ diff --git a/drivers/of/address.c b/drivers/of/address.c index ae46a3605904..286f0c161e33 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> @@ -197,6 +198,23 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, #endif /* CONFIG_PCI */ +static int __of_address_resource_bounds(struct resource *r, u64 start, u64 size) +{ + u64 end = start; + + if (overflows_type(start, r->start)) + return -EOVERFLOW; + if (size && check_add_overflow(end, size - 1, &end)) + return -EOVERFLOW; + if (overflows_type(end, r->end)) + return -EOVERFLOW; + + r->start = start; + r->end = end; + + return 0; +} + /* * of_pci_range_to_resource - Create a resource from an of_pci_range * @range: the PCI range that describes the resource @@ -215,6 +233,7 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, int of_pci_range_to_resource(struct of_pci_range *range, struct device_node *np, struct resource *res) { + u64 start; int err; res->flags = range->flags; res->parent = res->child = res->sibling = NULL; @@ -231,18 +250,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 +270,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) { @@ -486,34 +498,30 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * device that had registered logical PIO mapping, and the return code is * relative to that node. */ -static u64 __of_translate_address(struct device_node *dev, +static u64 __of_translate_address(struct device_node *node, struct device_node *(*get_parent)(const struct device_node *), const __be32 *in_addr, const char *rprop, struct device_node **host) { - struct device_node *parent = NULL; + 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; __be32 addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; - u64 result = OF_BAD_ADDR; pr_debug("** translation for device %pOF **\n", dev); - /* Increase refcount at current level */ - of_node_get(dev); - *host = NULL; - /* Get parent & match bus type */ - parent = get_parent(dev); + if (parent == NULL) - goto bail; + return OF_BAD_ADDR; bus = of_match_bus(parent); /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { pr_debug("Bad cell count for %pOF\n", dev); - goto bail; + return OF_BAD_ADDR; } memcpy(addr, in_addr, na * 4); @@ -533,8 +541,7 @@ static u64 __of_translate_address(struct device_node *dev, /* If root, we have finished */ if (parent == NULL) { pr_debug("reached root node\n"); - result = of_read_number(addr, na); - break; + return of_read_number(addr, na); } /* @@ -543,11 +550,11 @@ static u64 __of_translate_address(struct device_node *dev, */ iorange = find_io_range_by_fwnode(&dev->fwnode); if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) { - result = of_read_number(addr + 1, na - 1); + u64 result = of_read_number(addr + 1, na - 1); pr_debug("indirectIO matched(%pOF) 0x%llx\n", dev, result); - *host = of_node_get(dev); - break; + *host = no_free_ptr(dev); + return result; } /* Get new parent bus and counts */ @@ -555,7 +562,7 @@ static u64 __of_translate_address(struct device_node *dev, pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { pr_err("Bad cell count for %pOF\n", dev); - break; + return OF_BAD_ADDR; } pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n", @@ -563,7 +570,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) - break; + return OF_BAD_ADDR; /* Complete the move up one level */ na = pna; @@ -572,11 +579,8 @@ static u64 __of_translate_address(struct device_node *dev, of_dump_addr("one level translation:", addr, na); } - bail: - of_node_put(parent); - of_node_put(dev); - return result; + unreachable(); } u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) @@ -654,19 +658,16 @@ EXPORT_SYMBOL(of_translate_dma_address); const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop, phys_addr_t *start, size_t *length) { - struct device_node *parent; + struct device_node *parent __free(device_node) = __of_get_dma_parent(dev); u64 address, size; int na, ns; - parent = __of_get_dma_parent(dev); if (!parent) return NULL; na = of_bus_n_addr_cells(parent); ns = of_bus_n_size_cells(parent); - of_node_put(parent); - address = of_translate_dma_address(dev, prop); if (address == OF_BAD_ADDR) return NULL; @@ -688,21 +689,19 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, { const __be32 *prop; unsigned int psize; - struct device_node *parent; + struct device_node *parent __free(device_node) = of_get_parent(dev); struct of_bus *bus; int onesize, i, na, ns; - /* Get parent & match bus type */ - parent = of_get_parent(dev); if (parent == NULL) return NULL; + + /* match the parent's bus type */ bus = of_match_bus(parent); - if (strcmp(bus->name, "pci") && (bar_no >= 0)) { - of_node_put(parent); + if (strcmp(bus->name, "pci") && (bar_no >= 0)) return NULL; - } + bus->count_cells(dev, &na, &ns); - of_node_put(parent); if (!OF_CHECK_ADDR_COUNT(na)) return NULL; @@ -888,14 +887,13 @@ static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, */ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) { - struct device_node *node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); const __be32 *ranges = NULL; bool found_dma_ranges = false; struct of_range_parser parser; struct of_range range; struct bus_dma_region *r; int len, num_ranges = 0; - int ret = 0; while (node) { ranges = of_get_property(node, "dma-ranges", &len); @@ -905,10 +903,9 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) break; /* Once we find 'dma-ranges', then a missing one is an error */ - if (found_dma_ranges && !ranges) { - ret = -ENODEV; - goto out; - } + if (found_dma_ranges && !ranges) + return -ENODEV; + found_dma_ranges = true; node = of_get_next_dma_parent(node); @@ -916,10 +913,8 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) if (!node || !ranges) { pr_debug("no dma-ranges found for node(%pOF)\n", np); - ret = -ENODEV; - goto out; + return -ENODEV; } - of_dma_range_parser_init(&parser, node); for_each_of_range(&parser, &range) { if (range.cpu_addr == OF_BAD_ADDR) { @@ -930,16 +925,12 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) num_ranges++; } - if (!num_ranges) { - ret = -EINVAL; - goto out; - } + if (!num_ranges) + return -EINVAL; r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL); - if (!r) { - ret = -ENOMEM; - goto out; - } + if (!r) + return -ENOMEM; /* * Record all info in the generic DMA ranges array for struct device, @@ -957,9 +948,7 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) r->size = range.size; r++; } -out: - of_node_put(node); - return ret; + return 0; } #endif /* CONFIG_HAS_DMA */ @@ -1016,24 +1005,18 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) */ bool of_dma_is_coherent(struct device_node *np) { - struct device_node *node; - bool is_coherent = dma_default_coherent; - - node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); while (node) { - if (of_property_read_bool(node, "dma-coherent")) { - is_coherent = true; - break; - } - if (of_property_read_bool(node, "dma-noncoherent")) { - is_coherent = false; - break; - } + if (of_property_read_bool(node, "dma-coherent")) + return true; + + if (of_property_read_bool(node, "dma-noncoherent")) + return false; + node = of_get_next_dma_parent(node); } - of_node_put(node); - return is_coherent; + return dma_default_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); @@ -1049,20 +1032,14 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent); */ static bool of_mmio_is_nonposted(struct device_node *np) { - struct device_node *parent; - bool nonposted; - if (!IS_ENABLED(CONFIG_ARCH_APPLE)) return false; - parent = of_get_parent(np); + struct device_node *parent __free(device_node) = of_get_parent(np); if (!parent) return false; - nonposted = of_property_read_bool(parent, "nonposted-mmio"); - - of_node_put(parent); - return nonposted; + return of_property_read_bool(parent, "nonposted-mmio"); } static int __of_address_to_resource(struct device_node *dev, int index, int bar_no, @@ -1096,12 +1073,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); } /** diff --git a/drivers/of/base.c b/drivers/of/base.c index 8856c67c466a..20603d3c9931 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> @@ -1393,8 +1394,10 @@ int of_parse_phandle_with_args_map(const struct device_node *np, const char *stem_name, int index, struct of_phandle_args *out_args) { - char *cells_name, *map_name = NULL, *mask_name = NULL; - char *pass_name = NULL; + char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); + char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name); + char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); + char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); struct device_node *cur, *new = NULL; const __be32 *map, *mask, *pass; static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; @@ -1407,27 +1410,13 @@ int of_parse_phandle_with_args_map(const struct device_node *np, if (index < 0) return -EINVAL; - cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); - if (!cells_name) + if (!cells_name || !map_name || !mask_name || !pass_name) return -ENOMEM; - ret = -ENOMEM; - map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name); - if (!map_name) - goto free; - - mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); - if (!mask_name) - goto free; - - pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); - if (!pass_name) - goto free; - ret = __of_parse_phandle_with_args(np, list_name, cells_name, -1, index, out_args); if (ret) - goto free; + return ret; /* Get the #<list>-cells property */ cur = out_args->np; @@ -1444,8 +1433,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np, /* Get the <list>-map property */ map = of_get_property(cur, map_name, &map_len); if (!map) { - ret = 0; - goto free; + return 0; } map_len /= sizeof(u32); @@ -1521,12 +1509,6 @@ int of_parse_phandle_with_args_map(const struct device_node *np, put: of_node_put(cur); of_node_put(new); -free: - kfree(mask_name); - kfree(map_name); - kfree(cells_name); - kfree(pass_name); - return ret; } EXPORT_SYMBOL(of_parse_phandle_with_args_map); diff --git a/drivers/of/device.c b/drivers/of/device.c index de89f9906375..edf3be197265 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -95,10 +95,8 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, { const struct bus_dma_region *map = NULL; struct device_node *bus_np; - u64 dma_start = 0; - u64 mask, end, size = 0; - bool coherent; - int iommu_ret; + u64 mask, end = 0; + bool coherent, set_map = false; int ret; if (np == dev->of_node) @@ -117,34 +115,9 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, if (!force_dma) return ret == -ENODEV ? 0 : ret; } else { - const struct bus_dma_region *r = map; - u64 dma_end = 0; - /* Determine the overall bounds of all DMA regions */ - for (dma_start = ~0; r->size; r++) { - /* Take lower and upper limits */ - if (r->dma_start < dma_start) - dma_start = r->dma_start; - if (r->dma_start + r->size > dma_end) - dma_end = r->dma_start + r->size; - } - size = dma_end - dma_start; - - /* - * Add a work around to treat the size as mask + 1 in case - * it is defined in DT as a mask. - */ - if (size & 1) { - dev_warn(dev, "Invalid size 0x%llx for dma-range(s)\n", - size); - size = size + 1; - } - - if (!size) { - dev_err(dev, "Adjusted size 0x%llx invalid\n", size); - kfree(map); - return -EINVAL; - } + end = dma_range_map_max(map); + set_map = true; } /* @@ -158,21 +131,20 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, dev->dma_mask = &dev->coherent_dma_mask; } - if (!size && dev->coherent_dma_mask) - size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); - else if (!size) - size = 1ULL << 32; + if (!end && dev->coherent_dma_mask) + end = dev->coherent_dma_mask; + else if (!end) + end = (1ULL << 32) - 1; /* * Limit coherent and dma mask based on size and default mask * set by the driver. */ - end = dma_start + size - 1; mask = DMA_BIT_MASK(ilog2(end) + 1); dev->coherent_dma_mask &= mask; *dev->dma_mask &= mask; /* ...but only set bus limit and range map if we found valid dma-ranges earlier */ - if (!ret) { + if (set_map) { dev->bus_dma_limit = end; dev->dma_range_map = map; } @@ -181,29 +153,21 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, dev_dbg(dev, "device is%sdma coherent\n", coherent ? " " : " not "); - iommu_ret = of_iommu_configure(dev, np, id); - if (iommu_ret == -EPROBE_DEFER) { + ret = of_iommu_configure(dev, np, id); + if (ret == -EPROBE_DEFER) { /* Don't touch range map if it wasn't set from a valid dma-ranges */ - if (!ret) + if (set_map) dev->dma_range_map = NULL; kfree(map); return -EPROBE_DEFER; - } else if (iommu_ret == -ENODEV) { - dev_dbg(dev, "device is not behind an iommu\n"); - } else if (iommu_ret) { - dev_err(dev, "iommu configuration for device failed with %pe\n", - ERR_PTR(iommu_ret)); - - /* - * Historically this routine doesn't fail driver probing - * due to errors in of_iommu_configure() - */ - } else - dev_dbg(dev, "device is behind an iommu\n"); + } + /* Take all other IOMMU errors to mean we'll just carry on without it */ + dev_dbg(dev, "device is%sbehind an iommu\n", + !ret ? " " : " not "); - arch_setup_dma_ops(dev, dma_start, size, coherent); + arch_setup_dma_ops(dev, coherent); - if (iommu_ret) + if (ret) of_dma_set_restricted_buffer(dev, np); return 0; diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 4d57a4e34105..110104a936d9 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/of.h> #include <linux/spinlock.h> @@ -306,15 +307,20 @@ int of_detach_node(struct device_node *np) } EXPORT_SYMBOL_GPL(of_detach_node); +void __of_prop_free(struct property *prop) +{ + kfree(prop->name); + kfree(prop->value); + kfree(prop); +} + static void property_list_free(struct property *prop_list) { struct property *prop, *next; for (prop = prop_list; prop != NULL; prop = next) { next = prop->next; - kfree(prop->name); - kfree(prop->value); - kfree(prop); + __of_prop_free(prop); } } @@ -427,9 +433,7 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) return new; err_free: - kfree(new->name); - kfree(new->value); - kfree(new); + __of_prop_free(new); return NULL; } @@ -471,9 +475,7 @@ struct device_node *__of_node_dup(const struct device_node *np, if (!new_pp) goto err_prop; if (__of_add_property(node, new_pp)) { - kfree(new_pp->name); - kfree(new_pp->value); - kfree(new_pp); + __of_prop_free(new_pp); goto err_prop; } } @@ -933,11 +935,8 @@ static int of_changeset_add_prop_helper(struct of_changeset *ocs, return -ENOMEM; ret = of_changeset_add_property(ocs, np, new_pp); - if (ret) { - kfree(new_pp->name); - kfree(new_pp->value); - kfree(new_pp); - } + if (ret) + __of_prop_free(new_pp); return ret; } @@ -985,7 +984,7 @@ EXPORT_SYMBOL_GPL(of_changeset_add_prop_string); int of_changeset_add_prop_string_array(struct of_changeset *ocs, struct device_node *np, const char *prop_name, - const char **str_array, size_t sz) + const char * const *str_array, size_t sz) { struct property prop; int i, ret; @@ -1033,10 +1032,9 @@ int of_changeset_add_prop_u32_array(struct of_changeset *ocs, const u32 *array, size_t sz) { struct property prop; - __be32 *val; - int i, ret; + __be32 *val __free(kfree) = kcalloc(sz, sizeof(__be32), GFP_KERNEL); + int i; - val = kcalloc(sz, sizeof(__be32), GFP_KERNEL); if (!val) return -ENOMEM; @@ -1046,9 +1044,31 @@ int of_changeset_add_prop_u32_array(struct of_changeset *ocs, prop.length = sizeof(u32) * sz; prop.value = (void *)val; - ret = of_changeset_add_prop_helper(ocs, np, &prop); - kfree(val); - - return ret; + return of_changeset_add_prop_helper(ocs, np, &prop); } EXPORT_SYMBOL_GPL(of_changeset_add_prop_u32_array); + +/** + * of_changeset_add_prop_bool - Add a boolean property (i.e. a property without + * any values) to a changeset. + * + * @ocs: changeset pointer + * @np: device node pointer + * @prop_name: name of the property to be added + * + * Create a boolean property and add it to a changeset. + * + * Return: 0 on success, a negative error value in case of an error. + */ +int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np, + const char *prop_name) +{ + struct property prop; + + prop.name = (char *)prop_name; + prop.length = 0; + prop.value = NULL; + + return of_changeset_add_prop_helper(ocs, np, &prop); +} +EXPORT_SYMBOL_GPL(of_changeset_add_prop_bool); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a8a04f27915b..4d528c10df3a 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -34,7 +34,7 @@ /* * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by - * cmd_dt_S_dtb in scripts/Makefile.lib + * cmd_wrap_S_dtb in scripts/Makefile.dtbs */ extern uint8_t __dtb_empty_root_begin[]; extern uint8_t __dtb_empty_root_end[]; @@ -52,28 +52,7 @@ void __init of_fdt_limit_memory(int limit) int memory; int len; const void *val; - int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; - int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; - const __be32 *addr_prop; - const __be32 *size_prop; - int root_offset; - int cell_size; - - root_offset = fdt_path_offset(initial_boot_params, "/"); - if (root_offset < 0) - return; - - addr_prop = fdt_getprop(initial_boot_params, root_offset, - "#address-cells", NULL); - if (addr_prop) - nr_address_cells = fdt32_to_cpu(*addr_prop); - - size_prop = fdt_getprop(initial_boot_params, root_offset, - "#size-cells", NULL); - if (size_prop) - nr_size_cells = fdt32_to_cpu(*size_prop); - - cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells); + int cell_size = sizeof(uint32_t)*(dt_root_addr_cells + dt_root_size_cells); memory = fdt_path_offset(initial_boot_params, "/memory"); if (memory > 0) { @@ -1170,6 +1149,10 @@ bool __init early_init_dt_verify(void *params) initial_boot_params = params; of_fdt_crc32 = crc32_be(~0, initial_boot_params, fdt_totalsize(initial_boot_params)); + + /* Initialize {size,address}-cells info */ + early_init_dt_scan_root(); + return true; } @@ -1178,9 +1161,6 @@ void __init early_init_dt_scan_nodes(void) { int rc; - /* Initialize {size,address}-cells info */ - early_init_dt_scan_root(); - /* Retrieve various information from the /chosen node */ rc = early_init_dt_scan_chosen(boot_command_line); if (rc) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 174900072c18..a494f56a0d0e 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -25,6 +25,8 @@ #include <linux/string.h> #include <linux/slab.h> +#include "of_private.h" + /** * 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 @@ -79,7 +81,8 @@ EXPORT_SYMBOL_GPL(of_irq_find_parent); /* * These interrupt controllers abuse interrupt-map for unspeakable * reasons and rely on the core code to *ignore* it (the drivers do - * their own parsing of the property). + * their own parsing of the property). The PAsemi entry covers a + * non-sensical interrupt-map that is better left ignored. * * If you think of adding to the list for something *new*, think * again. There is a high chance that you will be sent back to the @@ -93,9 +96,61 @@ static const char * const of_irq_imap_abusers[] = { "fsl,ls1043a-extirq", "fsl,ls1088a-extirq", "renesas,rza1-irqc", + "pasemi,rootbus", 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 * @addr: address specifier (start of "reg" property of the device) in be32 format @@ -112,12 +167,12 @@ static const char * const of_irq_imap_abusers[] = { */ 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]; const __be32 *match_array = initial_match_array; - const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i, rc = -EINVAL; + const __be32 *tmp, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; + u32 intsize = 1, addrsize; + int i, rc = -EINVAL; #ifdef DEBUG of_print_phandle_args("of_irq_parse_raw: ", out_irq); @@ -176,6 +231,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 */ while (ipar != NULL) { + int imaplen, match; + const __be32 *imap, *oldimap, *imask; + struct device_node *newpar; /* * Now check if cursor is an interrupt-controller and * if it is then we are done, unless there is an @@ -216,7 +274,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Parse interrupt-map */ match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { + while (imaplen > (addrsize + intsize + 1)) { /* Compare specifiers */ match = 1; for (i = 0; i < (addrsize + intsize); i++, imaplen--) @@ -224,74 +282,31 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - 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"); + oldimap = imap; + imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq); + if (!imap) goto fail; - } - - if (!of_device_is_available(newpar)) - match = 0; - - /* 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; + match &= of_device_is_available(out_irq->np); + if (match) + break; + of_node_put(out_irq->np); + imaplen -= imap - oldimap; pr_debug(" -> imaplen=%d\n", imaplen); } - if (!match) { - if (intc) { - /* - * The PASEMI Nemo is a known offender, so - * let's only warn for anyone else. - */ - WARN(!IS_ENABLED(CONFIG_PPC_PASEMI), - "%pOF interrupt-map failed, using interrupt-controller\n", - ipar); - return 0; - } - + if (!match) goto fail; - } /* * Successfully parsed an interrupt-map translation; copy new * interrupt specifier into the out_irq structure */ - match_array = imap - newaddrsize - newintsize; - for (i = 0; i < newintsize; i++) - out_irq->args[i] = be32_to_cpup(imap - newintsize + i); - out_irq->args_count = intsize = newintsize; - addrsize = newaddrsize; + match_array = oldimap + 1; + + newpar = out_irq->np; + intsize = out_irq->args_count; + addrsize = (imap - match_array) - intsize; if (ipar == newpar) { pr_debug("%pOF interrupt-map entry to self\n", ipar); @@ -300,7 +315,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) skiplevel: /* Iterate again with new parent */ - out_irq->np = newpar; pr_debug(" -> new parent: %pOF\n", newpar); of_node_put(ipar); ipar = newpar; @@ -310,7 +324,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) fail: of_node_put(ipar); - of_node_put(newpar); return rc; } @@ -331,7 +344,8 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar struct device_node *p; const __be32 *addr; u32 intsize; - int i, res; + int i, res, addr_len; + __be32 addr_buf[3] = { 0 }; pr_debug("of_irq_parse_one: dev=%pOF, index=%d\n", device, index); @@ -340,13 +354,19 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar return of_irq_parse_oldworld(device, index, out_irq); /* Get the reg property (if any) */ - addr = of_get_property(device, "reg", NULL); + addr = of_get_property(device, "reg", &addr_len); + + /* Prevent out-of-bounds read in case of longer interrupt parent address size */ + if (addr_len > sizeof(addr_buf)) + addr_len = sizeof(addr_buf); + if (addr) + memcpy(addr_buf, addr, addr_len); /* Try the new-style interrupts-extended first */ res = of_parse_phandle_with_args(device, "interrupts-extended", "#interrupt-cells", index, out_irq); if (!res) - return of_irq_parse_raw(addr, out_irq); + return of_irq_parse_raw(addr_buf, out_irq); /* Look for the interrupt parent. */ p = of_irq_find_parent(device); @@ -376,7 +396,7 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Check if there are any interrupt-map translations to process */ - res = of_irq_parse_raw(addr, out_irq); + res = of_irq_parse_raw(addr_buf, out_irq); out: of_node_put(p); return res; @@ -409,9 +429,8 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) of_property_read_string_index(dev, "interrupt-names", index, &name); - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq)); - r->name = name ? name : of_node_full_name(dev); + *r = DEFINE_RES_IRQ_NAMED(irq, name ?: of_node_full_name(dev)); + r->flags |= irq_get_trigger_type(irq); } return irq; @@ -696,8 +715,7 @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id, * @np: device node for @dev * @token: bus type for this domain * - * Parse the msi-parent property (both the simple and the complex - * versions), and returns the corresponding MSI domain. + * Parse the msi-parent property and returns the corresponding MSI domain. * * Returns: the MSI domain for this device (or NULL on failure). */ @@ -705,33 +723,14 @@ struct irq_domain *of_msi_get_domain(struct device *dev, struct device_node *np, enum irq_domain_bus_token token) { - struct device_node *msi_np; + struct of_phandle_iterator it; struct irq_domain *d; + int err; - /* Check for a single msi-parent property */ - msi_np = of_parse_phandle(np, "msi-parent", 0); - if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) { - d = irq_find_matching_host(msi_np, token); - if (!d) - of_node_put(msi_np); - return d; - } - - if (token == DOMAIN_BUS_PLATFORM_MSI) { - /* Check for the complex msi-parent version */ - struct of_phandle_args args; - int index = 0; - - while (!of_parse_phandle_with_args(np, "msi-parent", - "#msi-cells", - index, &args)) { - d = irq_find_matching_host(args.np, token); - if (d) - return d; - - of_node_put(args.np); - index++; - } + of_for_each_phandle(&it, err, np, "msi-parent", "#msi-cells", 0) { + d = irq_find_matching_host(it.node, token); + if (d) + return d; } return NULL; diff --git a/drivers/of/kunit_overlay_test.dtso b/drivers/of/kunit_overlay_test.dtso new file mode 100644 index 000000000000..85f20b4b4c16 --- /dev/null +++ b/drivers/of/kunit_overlay_test.dtso @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + kunit-test { + compatible = "test,empty"; + }; +}; diff --git a/drivers/of/module.c b/drivers/of/module.c index f58e624953a2..780fd82a7ecc 100644 --- a/drivers/of/module.c +++ b/drivers/of/module.c @@ -29,14 +29,15 @@ ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len) csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', of_node_get_device_type(np)); tsize = csize; + if (csize >= len) + csize = len > 0 ? len - 1 : 0; len -= csize; - if (str) - str += csize; + str += csize; of_property_for_each_string(np, "compatible", p, compat) { csize = strlen(compat) + 1; tsize += csize; - if (csize > len) + if (csize >= len) continue; csize = snprintf(str, len, "C%s", compat); diff --git a/drivers/of/of_kunit_helpers.c b/drivers/of/of_kunit_helpers.c new file mode 100644 index 000000000000..287d6c91bb37 --- /dev/null +++ b/drivers/of/of_kunit_helpers.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test managed DeviceTree APIs + */ + +#include <linux/of.h> +#include <linux/of_fdt.h> + +#include <kunit/of.h> +#include <kunit/test.h> +#include <kunit/resource.h> + +#if defined(CONFIG_OF_OVERLAY) && defined(CONFIG_OF_EARLY_FLATTREE) + +static void of_overlay_fdt_apply_kunit_exit(void *ovcs_id) +{ + of_overlay_remove(ovcs_id); +} + +/** + * of_overlay_fdt_apply_kunit() - Test managed of_overlay_fdt_apply() + * @test: test context + * @overlay_fdt: device tree overlay to apply + * @overlay_fdt_size: size in bytes of @overlay_fdt + * @ovcs_id: identifier of overlay, used to remove the overlay + * + * Just like of_overlay_fdt_apply(), except the overlay is managed by the test + * case and is automatically removed with of_overlay_remove() after the test + * case concludes. + * + * Return: 0 on success, negative errno on failure + */ +int of_overlay_fdt_apply_kunit(struct kunit *test, void *overlay_fdt, + u32 overlay_fdt_size, int *ovcs_id) +{ + int ret; + int *copy_id; + + copy_id = kunit_kmalloc(test, sizeof(*copy_id), GFP_KERNEL); + if (!copy_id) + return -ENOMEM; + + ret = of_overlay_fdt_apply(overlay_fdt, overlay_fdt_size, + ovcs_id, NULL); + if (ret) + return ret; + + *copy_id = *ovcs_id; + + return kunit_add_action_or_reset(test, of_overlay_fdt_apply_kunit_exit, + copy_id); +} +EXPORT_SYMBOL_GPL(of_overlay_fdt_apply_kunit); + +#endif + +KUNIT_DEFINE_ACTION_WRAPPER(of_node_put_wrapper, of_node_put, struct device_node *); + +/** + * of_node_put_kunit() - Test managed of_node_put() + * @test: test context + * @node: node to pass to `of_node_put()` + * + * Just like of_node_put(), except the node is managed by the test case and is + * automatically put with of_node_put() after the test case concludes. + */ +void of_node_put_kunit(struct kunit *test, struct device_node *node) +{ + if (kunit_add_action(test, of_node_put_wrapper, node)) { + KUNIT_FAIL(test, + "Can't allocate a kunit resource to put of_node\n"); + } +} +EXPORT_SYMBOL_GPL(of_node_put_kunit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Test managed DeviceTree APIs"); diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c index 5949829a1b00..2ec20886d176 100644 --- a/drivers/of/of_numa.c +++ b/drivers/of/of_numa.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/nodemask.h> +#include <linux/numa_memblks.h> #include <asm/numa.h> @@ -44,7 +45,7 @@ static int __init of_numa_parse_memory_nodes(void) struct device_node *np = NULL; struct resource rsrc; u32 nid; - int i, r; + int i, r = -EINVAL; for_each_node_by_type(np, "memory") { r = of_property_read_u32(np, "numa-node-id", &nid); @@ -71,7 +72,7 @@ static int __init of_numa_parse_memory_nodes(void) } } - return 0; + return r; } static int __init of_numa_parse_distance_map_v1(struct device_node *map) diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 485483524b7f..04aa2a91f851 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -123,6 +123,7 @@ extern void *__unflatten_device_tree(const void *blob, * own the devtree lock or work on detached trees only. */ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); +void __of_prop_free(struct property *prop); struct device_node *__of_node_dup(const struct device_node *np, const char *full_name); @@ -158,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_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; #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA) int of_dma_get_range(struct device_node *np, diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 8236ecae2953..46e1c3fbc769 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -437,17 +437,10 @@ void __init fdt_init_reserved_mem(void) for (i = 0; i < reserved_mem_count; i++) { struct reserved_mem *rmem = &reserved_mem[i]; unsigned long node = rmem->fdt_node; - int len; - const __be32 *prop; int err = 0; bool nomap; nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - prop = of_get_flat_dt_prop(node, "phandle", &len); - if (!prop) - prop = of_get_flat_dt_prop(node, "linux,phandle", &len); - if (prop) - rmem->phandle = of_read_number(prop, len/4); if (rmem->size == 0) err = __reserved_mem_alloc_size(node, rmem->name, @@ -477,19 +470,6 @@ void __init fdt_init_reserved_mem(void) } } -static inline struct reserved_mem *__find_rmem(struct device_node *node) -{ - unsigned int i; - - if (!node->phandle) - return NULL; - - for (i = 0; i < reserved_mem_count; i++) - if (reserved_mem[i].phandle == node->phandle) - return &reserved_mem[i]; - return NULL; -} - struct rmem_assigned_device { struct device *dev; struct reserved_mem *rmem; @@ -534,7 +514,7 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, return 0; } - rmem = __find_rmem(target); + rmem = of_reserved_mem_lookup(target); of_node_put(target); if (!rmem || !rmem->ops || !rmem->ops->device_init) diff --git a/drivers/of/of_test.c b/drivers/of/of_test.c index a9301d293f01..c85a258bc6ae 100644 --- a/drivers/of/of_test.c +++ b/drivers/of/of_test.c @@ -54,4 +54,5 @@ static struct kunit_suite of_dtb_suite = { kunit_test_suites( &of_dtb_suite, ); +MODULE_DESCRIPTION("KUnit tests for OF APIs"); MODULE_LICENSE("GPL"); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 2ae7e9d24a64..cbdecccca097 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -262,9 +262,7 @@ static struct property *dup_and_fixup_symbol_prop( return new_prop; err_free_new_prop: - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); + __of_prop_free(new_prop); err_free_target_path: kfree(target_path); @@ -361,11 +359,8 @@ static int add_changeset_property(struct overlay_changeset *ovcs, pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n", target->np, new_prop->name); - if (ret) { - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); - } + if (ret) + __of_prop_free(new_prop); return ret; } @@ -477,7 +472,6 @@ static int add_changeset_node(struct overlay_changeset *ovcs, static int build_changeset_next_level(struct overlay_changeset *ovcs, struct target *target, const struct device_node *overlay_node) { - struct device_node *child; struct property *prop; int ret; @@ -490,12 +484,11 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs, } } - for_each_child_of_node(overlay_node, child) { + for_each_child_of_node_scoped(overlay_node, child) { ret = add_changeset_node(ovcs, target, child); if (ret) { pr_debug("Failed to apply node @%pOF/%pOFn, err=%d\n", target->np, child, ret); - of_node_put(child); return ret; } } @@ -1083,16 +1076,12 @@ EXPORT_SYMBOL_GPL(of_overlay_fdt_apply); */ static int find_node(struct device_node *tree, struct device_node *np) { - struct device_node *child; - if (tree == np) return 1; - for_each_child_of_node(tree, child) { - if (find_node(child, np)) { - of_node_put(child); + for_each_child_of_node_scoped(tree, child) { + if (find_node(child, np)) return 1; - } } return 0; diff --git a/drivers/of/overlay_test.c b/drivers/of/overlay_test.c new file mode 100644 index 000000000000..19a292cdeee3 --- /dev/null +++ b/drivers/of/overlay_test.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for device tree overlays + */ +#include <linux/device/bus.h> +#include <linux/kconfig.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +#include <kunit/of.h> +#include <kunit/test.h> + +static const char * const kunit_node_name = "kunit-test"; +static const char * const kunit_compatible = "test,empty"; + +/* Test that of_overlay_apply_kunit() adds a node to the live tree */ +static void of_overlay_apply_kunit_apply(struct kunit *test) +{ + struct device_node *np; + + KUNIT_ASSERT_EQ(test, 0, + of_overlay_apply_kunit(test, kunit_overlay_test)); + + np = of_find_node_by_name(NULL, kunit_node_name); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, np); + of_node_put(np); +} + +/* + * Test that of_overlay_apply_kunit() creates platform devices with the + * expected device_node + */ +static void of_overlay_apply_kunit_platform_device(struct kunit *test) +{ + struct platform_device *pdev; + struct device_node *np; + + KUNIT_ASSERT_EQ(test, 0, + of_overlay_apply_kunit(test, kunit_overlay_test)); + + np = of_find_node_by_name(NULL, kunit_node_name); + of_node_put_kunit(test, np); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + + pdev = of_find_device_by_node(np); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, pdev); + if (pdev) + put_device(&pdev->dev); +} + +static int of_overlay_bus_match_compatible(struct device *dev, const void *data) +{ + return of_device_is_compatible(dev->of_node, data); +} + +/* Test that of_overlay_apply_kunit() cleans up after the test is finished */ +static void of_overlay_apply_kunit_cleanup(struct kunit *test) +{ + struct kunit fake; + struct platform_device *pdev; + struct device *dev; + struct device_node *np; + + if (!IS_ENABLED(CONFIG_OF_EARLY_FLATTREE)) + kunit_skip(test, "requires CONFIG_OF_EARLY_FLATTREE for root node"); + + kunit_init_test(&fake, "fake test", NULL); + KUNIT_ASSERT_EQ(test, fake.status, KUNIT_SUCCESS); + + KUNIT_ASSERT_EQ(test, 0, + of_overlay_apply_kunit(&fake, kunit_overlay_test)); + + np = of_find_node_by_name(NULL, kunit_node_name); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + of_node_put_kunit(test, np); + + pdev = of_find_device_by_node(np); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + put_device(&pdev->dev); /* Not derefing 'pdev' after this */ + + /* Remove overlay */ + kunit_cleanup(&fake); + + /* The node and device should be removed */ + np = of_find_node_by_name(NULL, kunit_node_name); + KUNIT_EXPECT_PTR_EQ(test, NULL, np); + of_node_put(np); + + dev = bus_find_device(&platform_bus_type, NULL, kunit_compatible, + of_overlay_bus_match_compatible); + KUNIT_EXPECT_PTR_EQ(test, NULL, dev); + put_device(dev); +} + +static struct kunit_case of_overlay_apply_kunit_test_cases[] = { + KUNIT_CASE(of_overlay_apply_kunit_apply), + KUNIT_CASE(of_overlay_apply_kunit_platform_device), + KUNIT_CASE(of_overlay_apply_kunit_cleanup), + {} +}; + +/* + * Test suite for test managed device tree overlays. + */ +static struct kunit_suite of_overlay_apply_kunit_suite = { + .name = "of_overlay_apply_kunit", + .test_cases = of_overlay_apply_kunit_test_cases, +}; + +kunit_test_suites( + &of_overlay_apply_kunit_suite, +); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for device tree overlays"); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 389d4ea6bfc1..9bafcff3e628 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -338,7 +338,6 @@ static int of_platform_bus_create(struct device_node *bus, struct device *parent, bool strict) { const struct of_dev_auxdata *auxdata; - struct device_node *child; struct platform_device *dev; const char *bus_id = NULL; void *platform_data = NULL; @@ -382,13 +381,11 @@ static int of_platform_bus_create(struct device_node *bus, if (!dev || !of_match_node(matches, bus)) return 0; - for_each_child_of_node(bus, child) { + for_each_child_of_node_scoped(bus, child) { pr_debug(" create child: %pOF\n", child); rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); - if (rc) { - of_node_put(child); + if (rc) break; - } } of_node_set_flag(bus, OF_POPULATED_BUS); return rc; @@ -459,7 +456,6 @@ int of_platform_populate(struct device_node *root, const struct of_dev_auxdata *lookup, struct device *parent) { - struct device_node *child; int rc = 0; root = root ? of_node_get(root) : of_find_node_by_path("/"); @@ -470,12 +466,10 @@ int of_platform_populate(struct device_node *root, pr_debug(" starting at: %pOF\n", root); device_links_supplier_sync_state_pause(); - for_each_child_of_node(root, child) { + for_each_child_of_node_scoped(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); - if (rc) { - of_node_put(child); + if (rc) break; - } } device_links_supplier_sync_state_resume(); @@ -592,7 +586,7 @@ static int __init of_platform_default_populate_init(void) * This can happen for example on DT systems that do EFI * booting and may provide a GOP handle to the EFI stub. */ - sysfb_disable(); + sysfb_disable(NULL); of_platform_device_create(node, NULL, NULL); of_node_put(node); } @@ -732,11 +726,14 @@ static int of_platform_notify(struct notifier_block *nb, struct of_reconfig_data *rd = arg; struct platform_device *pdev_parent, *pdev; bool children_left; + struct device_node *parent; switch (of_reconfig_get_state_change(action, rd)) { case OF_RECONFIG_CHANGE_ADD: - /* verify that the parent is a bus */ - if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) + parent = rd->dn->parent; + /* verify that the parent is a bus (or the root node) */ + if (!of_node_is_root(parent) && + !of_node_check_flag(parent, OF_POPULATED_BUS)) return NOTIFY_OK; /* not for us */ /* already populated? (driver using of_populate manually) */ @@ -749,7 +746,7 @@ static int of_platform_notify(struct notifier_block *nb, */ rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; /* pdev_parent may be NULL when no bus platform device */ - pdev_parent = of_find_device_by_node(rd->dn->parent); + pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, pdev_parent ? &pdev_parent->dev : NULL); platform_device_put(pdev_parent); diff --git a/drivers/of/property.c b/drivers/of/property.c index a6358ee99b74..11b922fde7af 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -40,15 +40,12 @@ */ bool of_graph_is_present(const struct device_node *node) { - struct device_node *ports, *port; + struct device_node *ports __free(device_node) = of_get_child_by_name(node, "ports"); - ports = of_get_child_by_name(node, "ports"); if (ports) node = ports; - port = of_get_child_by_name(node, "port"); - of_node_put(ports); - of_node_put(port); + struct device_node *port __free(device_node) = of_get_child_by_name(node, "port"); return !!port; } @@ -455,12 +452,17 @@ EXPORT_SYMBOL_GPL(of_property_read_string); /** * of_property_match_string() - Find string in a list and return index - * @np: pointer to node containing string list property + * @np: pointer to the node containing the string list property * @propname: string list property name - * @string: pointer to string to search for in string list + * @string: pointer to the string to search for in the string list * - * This function searches a string list property and returns the index - * of a specific string value. + * Search for an exact match of string in a device node property which is a + * string of lists. + * + * Return: the index of the first occurrence of the string on success, -EINVAL + * if the property does not exist, -ENODATA if the property does not have a + * value, and -EILSEQ if the string is not null-terminated within the length of + * the property data. */ int of_property_match_string(const struct device_node *np, const char *propname, const char *string) @@ -579,7 +581,8 @@ EXPORT_SYMBOL_GPL(of_prop_next_string); int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint) { - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = + of_get_parent(node); WARN_ONCE(!port_node, "%s(): endpoint %pOF has no parent node\n", __func__, node); @@ -594,8 +597,6 @@ int of_graph_parse_endpoint(const struct device_node *node, of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } EXPORT_SYMBOL(of_graph_parse_endpoint); @@ -610,25 +611,22 @@ EXPORT_SYMBOL(of_graph_parse_endpoint); */ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id) { - struct device_node *node, *port; + struct device_node *node __free(device_node) = of_get_child_by_name(parent, "ports"); - node = of_get_child_by_name(parent, "ports"); if (node) parent = node; - for_each_child_of_node(parent, port) { + for_each_child_of_node_scoped(parent, port) { u32 port_id = 0; if (!of_node_name_eq(port, "port")) continue; of_property_read_u32(port, "reg", &port_id); if (id == port_id) - break; + return_ptr(port); } - of_node_put(node); - - return port; + return NULL; } EXPORT_SYMBOL(of_graph_get_port_by_id); @@ -655,15 +653,13 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, * parent port node. */ if (!prev) { - struct device_node *node; + struct device_node *node __free(device_node) = + of_get_child_by_name(parent, "ports"); - node = of_get_child_by_name(parent, "ports"); if (node) parent = node; port = of_get_child_by_name(parent, "port"); - of_node_put(node); - if (!port) { pr_debug("graph: no port node found in %pOF\n", parent); return NULL; @@ -782,16 +778,11 @@ EXPORT_SYMBOL(of_graph_get_port_parent); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { - struct device_node *np, *pp; - /* Get remote endpoint node. */ - np = of_graph_get_remote_endpoint(node); - - pp = of_graph_get_port_parent(np); + struct device_node *np __free(device_node) = + of_graph_get_remote_endpoint(node); - of_node_put(np); - - return pp; + return of_graph_get_port_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port_parent); @@ -1052,15 +1043,13 @@ static int of_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint) { const struct device_node *node = to_of_node(fwnode); - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = of_get_parent(node); endpoint->local_fwnode = fwnode; of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } @@ -1075,19 +1064,15 @@ static void of_link_to_phandle(struct device_node *con_np, struct device_node *sup_np, u8 flags) { - struct device_node *tmp_np = of_node_get(sup_np); + struct device_node *tmp_np __free(device_node) = of_node_get(sup_np); /* Check that sup_np and its ancestors are available. */ while (tmp_np) { - if (of_fwnode_handle(tmp_np)->dev) { - of_node_put(tmp_np); + if (of_fwnode_handle(tmp_np)->dev) break; - } - if (!of_device_is_available(tmp_np)) { - of_node_put(tmp_np); + if (!of_device_is_available(tmp_np)) return; - } tmp_np = of_get_next_parent(tmp_np); } @@ -1252,6 +1237,9 @@ DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) DEFINE_SIMPLE_PROP(panel, "panel", NULL) DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells") DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL) +DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells") +DEFINE_SIMPLE_PROP(pses, "pses", "#pse-cells") +DEFINE_SIMPLE_PROP(power_supplies, "power-supplies", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1311,6 +1299,47 @@ static struct device_node *parse_interrupts(struct device_node *np, return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np; } +static struct device_node *parse_interrupt_map(struct device_node *np, + const char *prop_name, int index) +{ + const __be32 *imap, *imap_end; + struct of_phandle_args sup_args; + u32 addrcells, intcells; + int imaplen; + + if (!IS_ENABLED(CONFIG_OF_IRQ)) + return NULL; + + if (strcmp(prop_name, "interrupt-map")) + return NULL; + + if (of_property_read_u32(np, "#interrupt-cells", &intcells)) + return NULL; + addrcells = of_bus_n_addr_cells(np); + + imap = of_get_property(np, "interrupt-map", &imaplen); + imaplen /= sizeof(*imap); + if (!imap) + return NULL; + + imap_end = imap + imaplen; + + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; + + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) + return NULL; + + if (i == index) + return sup_args.np; + + of_node_put(sup_args.np); + } + + return NULL; +} + static struct device_node *parse_remote_endpoint(struct device_node *np, const char *prop_name, int index) @@ -1357,8 +1386,12 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_backlight, }, { .parse_prop = parse_panel, }, { .parse_prop = parse_msi_parent, }, + { .parse_prop = parse_pses, }, + { .parse_prop = parse_power_supplies, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, + { .parse_prop = parse_interrupt_map, }, + { .parse_prop = parse_access_controllers, }, { .parse_prop = parse_regulators, }, { .parse_prop = parse_gpio, }, { .parse_prop = parse_gpios, }, @@ -1403,16 +1436,13 @@ static int of_link_property(struct device_node *con_np, const char *prop_name) } while ((phandle = s->parse_prop(con_np, prop_name, i))) { - struct device_node *con_dev_np; + struct device_node *con_dev_np __free(device_node) = + s->get_con_dev ? s->get_con_dev(con_np) : of_node_get(con_np); - con_dev_np = s->get_con_dev - ? s->get_con_dev(con_np) - : of_node_get(con_np); matched = true; i++; of_link_to_phandle(con_dev_np, phandle, s->fwlink_flags); of_node_put(phandle); - of_node_put(con_dev_np); } s++; } diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index b278ab4338ce..5cf96776dd7d 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "OF: resolver: " fmt +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -74,11 +75,11 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, { struct device_node *refnode; struct property *prop; - char *value, *cur, *end, *node_path, *prop_name, *s; + char *value __free(kfree) = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); + char *cur, *end, *node_path, *prop_name, *s; int offset, len; int err = 0; - value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); if (!value) return -ENOMEM; @@ -89,23 +90,19 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, node_path = cur; s = strchr(cur, ':'); - if (!s) { - err = -EINVAL; - goto err_fail; - } + if (!s) + return -EINVAL; *s++ = '\0'; prop_name = s; s = strchr(s, ':'); - if (!s) { - err = -EINVAL; - goto err_fail; - } + if (!s) + return -EINVAL; *s++ = '\0'; err = kstrtoint(s, 10, &offset); if (err) - goto err_fail; + return err; refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path); if (!refnode) @@ -117,22 +114,16 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, } of_node_put(refnode); - if (!prop) { - err = -ENOENT; - goto err_fail; - } + if (!prop) + return -ENOENT; - if (offset < 0 || offset + sizeof(__be32) > prop->length) { - err = -EINVAL; - goto err_fail; - } + if (offset < 0 || offset + sizeof(__be32) > prop->length) + return -EINVAL; *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); } -err_fail: - kfree(value); - return err; + return 0; } /* compare nodes taking into account that 'name' strips out the @ part */ @@ -159,7 +150,7 @@ static int node_name_cmp(const struct device_node *dn1, static int adjust_local_phandle_references(struct device_node *local_fixups, struct device_node *overlay, int phandle_delta) { - struct device_node *child, *overlay_child; + struct device_node *overlay_child; struct property *prop_fix, *prop; int err, i, count; unsigned int off; @@ -203,7 +194,7 @@ static int adjust_local_phandle_references(struct device_node *local_fixups, * The roots of the subtrees are the overlay's __local_fixups__ node * and the overlay's root node. */ - for_each_child_of_node(local_fixups, child) { + for_each_child_of_node_scoped(local_fixups, child) { for_each_child_of_node(overlay, overlay_child) if (!node_name_cmp(child, overlay_child)) { @@ -211,17 +202,13 @@ static int adjust_local_phandle_references(struct device_node *local_fixups, break; } - if (!overlay_child) { - of_node_put(child); + if (!overlay_child) return -EINVAL; - } err = adjust_local_phandle_references(child, overlay_child, phandle_delta); - if (err) { - of_node_put(child); + if (err) return err; - } } return 0; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 6b5c36b6a758..daf9a2dddd7e 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -795,15 +795,11 @@ static void __init of_unittest_property_copy(void) new = __of_prop_dup(&p1, GFP_KERNEL); unittest(new && propcmp(&p1, new), "empty property didn't copy correctly\n"); - kfree(new->value); - kfree(new->name); - kfree(new); + __of_prop_free(new); new = __of_prop_dup(&p2, GFP_KERNEL); unittest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n"); - kfree(new->value); - kfree(new->name); - kfree(new); + __of_prop_free(new); #endif } @@ -904,8 +900,8 @@ static void __init of_unittest_changeset(void) unittest(!of_find_node_by_path("/testcase-data/changeset/n2/n21"), "'%pOF' still present after revert\n", n21); - ppremove = of_find_property(parent, "prop-remove", NULL); - unittest(ppremove, "failed to find removed prop after revert\n"); + unittest(of_property_present(parent, "prop-remove"), + "failed to find removed prop after revert\n"); ret = of_property_read_string(parent, "prop-update", &propstr); unittest(!ret, "failed to find updated prop after revert\n"); @@ -921,6 +917,171 @@ static void __init of_unittest_changeset(void) #endif } +static void __init __maybe_unused changeset_check_string(struct device_node *np, + const char *prop_name, + const char *expected_str) +{ + const char *str; + int ret; + + ret = of_property_read_string(np, prop_name, &str); + if (unittest(ret == 0, "failed to read %s\n", prop_name)) + return; + + unittest(strcmp(str, expected_str) == 0, + "%s value mismatch (read '%s', exp '%s')\n", + prop_name, str, expected_str); +} + +static void __init __maybe_unused changeset_check_string_array(struct device_node *np, + const char *prop_name, + const char * const *expected_array, + unsigned int count) +{ + const char *str; + unsigned int i; + int ret; + int cnt; + + cnt = of_property_count_strings(np, prop_name); + if (unittest(cnt >= 0, "failed to get %s count\n", prop_name)) + return; + + if (unittest(cnt == count, + "%s count mismatch (read %d, exp %u)\n", + prop_name, cnt, count)) + return; + + for (i = 0; i < count; i++) { + ret = of_property_read_string_index(np, prop_name, i, &str); + if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i)) + continue; + + unittest(strcmp(str, expected_array[i]) == 0, + "%s[%d] value mismatch (read '%s', exp '%s')\n", + prop_name, i, str, expected_array[i]); + } +} + +static void __init __maybe_unused changeset_check_u32(struct device_node *np, + const char *prop_name, + u32 expected_u32) +{ + u32 val32; + int ret; + + ret = of_property_read_u32(np, prop_name, &val32); + if (unittest(ret == 0, "failed to read %s\n", prop_name)) + return; + + unittest(val32 == expected_u32, + "%s value mismatch (read '%u', exp '%u')\n", + prop_name, val32, expected_u32); +} + +static void __init __maybe_unused changeset_check_u32_array(struct device_node *np, + const char *prop_name, + const u32 *expected_array, + unsigned int count) +{ + unsigned int i; + u32 val32; + int ret; + int cnt; + + cnt = of_property_count_u32_elems(np, prop_name); + if (unittest(cnt >= 0, "failed to get %s count\n", prop_name)) + return; + + if (unittest(cnt == count, + "%s count mismatch (read %d, exp %u)\n", + prop_name, cnt, count)) + return; + + for (i = 0; i < count; i++) { + ret = of_property_read_u32_index(np, prop_name, i, &val32); + if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i)) + continue; + + unittest(val32 == expected_array[i], + "%s[%d] value mismatch (read '%u', exp '%u')\n", + prop_name, i, val32, expected_array[i]); + } +} + +static void __init __maybe_unused changeset_check_bool(struct device_node *np, + const char *prop_name) +{ + unittest(of_property_read_bool(np, prop_name), + "%s value mismatch (read 'false', exp 'true')\n", prop_name); +} + +static void __init of_unittest_changeset_prop(void) +{ +#ifdef CONFIG_OF_DYNAMIC + static const char * const str_array[] = { "abc", "defg", "hij" }; + static const u32 u32_array[] = { 123, 4567, 89, 10, 11 }; + struct device_node *nchangeset, *np; + struct of_changeset chgset; + int ret; + + nchangeset = of_find_node_by_path("/testcase-data/changeset"); + if (!nchangeset) { + pr_err("missing testcase data\n"); + return; + } + + of_changeset_init(&chgset); + + np = of_changeset_create_node(&chgset, nchangeset, "test-prop"); + if (unittest(np, "failed to create test-prop node\n")) + goto end_changeset_destroy; + + ret = of_changeset_add_prop_string(&chgset, np, "prop-string", "abcde"); + unittest(ret == 0, "failed to add prop-string\n"); + + ret = of_changeset_add_prop_string_array(&chgset, np, "prop-string-array", + str_array, ARRAY_SIZE(str_array)); + unittest(ret == 0, "failed to add prop-string-array\n"); + + ret = of_changeset_add_prop_u32(&chgset, np, "prop-u32", 1234); + unittest(ret == 0, "failed to add prop-u32\n"); + + ret = of_changeset_add_prop_u32_array(&chgset, np, "prop-u32-array", + u32_array, ARRAY_SIZE(u32_array)); + unittest(ret == 0, "failed to add prop-u32-array\n"); + + ret = of_changeset_add_prop_bool(&chgset, np, "prop-bool"); + unittest(ret == 0, "failed to add prop-bool\n"); + + of_node_put(np); + + ret = of_changeset_apply(&chgset); + if (unittest(ret == 0, "failed to apply changeset\n")) + goto end_changeset_destroy; + + np = of_find_node_by_path("/testcase-data/changeset/test-prop"); + if (unittest(np, "failed to find test-prop node\n")) + goto end_revert_changeset; + + changeset_check_string(np, "prop-string", "abcde"); + changeset_check_string_array(np, "prop-string-array", str_array, ARRAY_SIZE(str_array)); + changeset_check_u32(np, "prop-u32", 1234); + changeset_check_u32_array(np, "prop-u32-array", u32_array, ARRAY_SIZE(u32_array)); + changeset_check_bool(np, "prop-bool"); + + of_node_put(np); + +end_revert_changeset: + ret = of_changeset_revert(&chgset); + unittest(ret == 0, "failed to revert changeset\n"); + +end_changeset_destroy: + of_changeset_destroy(&chgset); + of_node_put(nchangeset); +#endif +} + static void __init of_unittest_dma_get_max_cpu_address(void) { struct device_node *np; @@ -1700,7 +1861,7 @@ static int __init unittest_data_add(void) struct device_node *unittest_data_node = NULL, *np; /* * __dtbo_testcases_begin[] and __dtbo_testcases_end[] are magically - * created by cmd_dt_S_dtbo in scripts/Makefile.lib + * created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs */ extern uint8_t __dtbo_testcases_begin[]; extern uint8_t __dtbo_testcases_end[]; @@ -2815,7 +2976,7 @@ static int unittest_i2c_mux_probe(struct i2c_client *client) if (!muxc) return -ENOMEM; for (i = 0; i < nchans; i++) { - if (i2c_mux_add_adapter(muxc, 0, i, 0)) { + if (i2c_mux_add_adapter(muxc, 0, i)) { dev_err(dev, "Failed to register mux #%d\n", i); i2c_mux_del_adapters(muxc); return -ENODEV; @@ -3364,7 +3525,7 @@ out_skip_tests: /* * __dtbo_##overlay_name##_begin[] and __dtbo_##overlay_name##_end[] are - * created by cmd_dt_S_dtbo in scripts/Makefile.lib + * created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs */ #define OVERLAY_INFO_EXTERN(overlay_name) \ @@ -3718,9 +3879,7 @@ static __init void of_unittest_overlay_high_level(void) goto err_unlock; } if (__of_add_property(of_symbols, new_prop)) { - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); + __of_prop_free(new_prop); /* "name" auto-generated by unflatten */ if (!strcmp(prop->name, "name")) continue; @@ -4107,6 +4266,7 @@ static int __init of_unittest(void) of_unittest_property_string(); of_unittest_property_copy(); of_unittest_changeset(); + of_unittest_changeset_prop(); of_unittest_parse_interrupts(); of_unittest_parse_interrupts_extended(); of_unittest_dma_get_max_cpu_address(); |