diff options
author | Linus Torvalds | 2018-12-28 20:08:34 -0800 |
---|---|---|
committer | Linus Torvalds | 2018-12-28 20:08:34 -0800 |
commit | 030672aea826adf3dee9100ee8ac303b62c8fe7f (patch) | |
tree | c9815fcd276e4c61b864790e81e5cbca16b80142 /drivers/of | |
parent | 24dc83635ffe3c93d8122099a83ee228c9b7e4f7 (diff) | |
parent | 5801169a2ed20003f771acecf3ac00574cf10a38 (diff) |
Merge tag 'devicetree-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
Pull Devicetree updates from Rob Herring:
"The biggest highlight here is the start of using json-schema for DT
bindings. Being able to validate bindings has been discussed for years
with little progress.
- Initial support for DT bindings using json-schema language. This is
the start of converting DT bindings from free-form text to a
structured format.
- Reworking of initrd address initialization. This moves to using the
phys address instead of virt addr in the DT parsing code. This
rework was motivated by CONFIG_DEV_BLK_INITRD causing unnecessary
rebuilding of lots of files.
- Fix stale phandle entries in phandle cache
- DT overlay validation improvements. This exposed several memory
leak bugs which have been fixed.
- Use node name and device_type helper functions in DT code
- Last remaining conversions to using %pOFn printk specifier instead
of device_node.name directly
- Create new common RTC binding doc and move all trivial RTC devices
out of trivial-devices.txt.
- New bindings for Freescale MAG3110 magnetometer, Cadence Sierra
PHY, and Xen shared memory
- Update dtc to upstream version v1.4.7-57-gf267e674d145"
* tag 'devicetree-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (68 commits)
of: __of_detach_node() - remove node from phandle cache
of: of_node_get()/of_node_put() nodes held in phandle cache
gpio-omap.txt: add reg and interrupts properties
dt-bindings: mrvl,intc: fix a trivial typo
dt-bindings: iio: magnetometer: add dt-bindings for freescale mag3110
dt-bindings: Convert trivial-devices.txt to json-schema
dt-bindings: arm: mrvl: amend Browstone compatible string
dt-bindings: arm: Convert Tegra board/soc bindings to json-schema
dt-bindings: arm: Convert ZTE board/soc bindings to json-schema
dt-bindings: arm: Add missing Xilinx boards
dt-bindings: arm: Convert Xilinx board/soc bindings to json-schema
dt-bindings: arm: Convert VIA board/soc bindings to json-schema
dt-bindings: arm: Convert ST STi board/soc bindings to json-schema
dt-bindings: arm: Convert SPEAr board/soc bindings to json-schema
dt-bindings: arm: Convert CSR SiRF board/soc bindings to json-schema
dt-bindings: arm: Convert QCom board/soc bindings to json-schema
dt-bindings: arm: Convert TI nspire board/soc bindings to json-schema
dt-bindings: arm: Convert TI davinci board/soc bindings to json-schema
dt-bindings: arm: Convert Calxeda board/soc bindings to json-schema
dt-bindings: arm: Convert Altera board/soc bindings to json-schema
...
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/address.c | 6 | ||||
-rw-r--r-- | drivers/of/base.c | 126 | ||||
-rw-r--r-- | drivers/of/device.c | 9 | ||||
-rw-r--r-- | drivers/of/dynamic.c | 62 | ||||
-rw-r--r-- | drivers/of/fdt.c | 23 | ||||
-rw-r--r-- | drivers/of/kobj.c | 4 | ||||
-rw-r--r-- | drivers/of/of_private.h | 4 | ||||
-rw-r--r-- | drivers/of/overlay.c | 292 | ||||
-rw-r--r-- | drivers/of/pdt.c | 5 | ||||
-rw-r--r-- | drivers/of/property.c | 10 | ||||
-rw-r--r-- | drivers/of/resolver.c | 4 | ||||
-rw-r--r-- | drivers/of/unittest-data/Makefile | 2 | ||||
-rw-r--r-- | drivers/of/unittest-data/overlay_bad_add_dup_node.dts | 28 | ||||
-rw-r--r-- | drivers/of/unittest-data/overlay_bad_add_dup_prop.dts | 24 | ||||
-rw-r--r-- | drivers/of/unittest-data/overlay_base.dts | 1 | ||||
-rw-r--r-- | drivers/of/unittest.c | 100 |
16 files changed, 546 insertions, 154 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index 7ddbf0a1ab86..2270373b30ab 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -110,8 +110,8 @@ static int of_bus_pci_match(struct device_node *np) * "vci" is for the /chaos bridge on 1st-gen PCI powermacs * "ht" is hypertransport */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex") || - !strcmp(np->type, "vci") || !strcmp(np->type, "ht"); + return of_node_is_type(np, "pci") || of_node_is_type(np, "pciex") || + of_node_is_type(np, "vci") || of_node_is_type(np, "ht"); } static void of_bus_pci_count_cells(struct device_node *np, @@ -371,7 +371,7 @@ EXPORT_SYMBOL(of_pci_range_to_resource); static int of_bus_isa_match(struct device_node *np) { - return !strcmp(np->name, "isa"); + return of_node_name_eq(np, "isa"); } static void of_bus_isa_count_cells(struct device_node *child, diff --git a/drivers/of/base.c b/drivers/of/base.c index 09692c9b32a7..5226e898476e 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -79,6 +79,13 @@ bool of_node_name_prefix(const struct device_node *np, const char *prefix) } EXPORT_SYMBOL(of_node_name_prefix); +static bool __of_node_is_type(const struct device_node *np, const char *type) +{ + const char *match = __of_get_property(np, "device_type", NULL); + + return np && match && type && !strcmp(match, type); +} + int of_n_addr_cells(struct device_node *np) { u32 cells; @@ -116,9 +123,6 @@ int __weak of_node_to_nid(struct device_node *np) } #endif -static struct device_node **phandle_cache; -static u32 phandle_cache_mask; - /* * Assumptions behind phandle_cache implementation: * - phandle property values are in a contiguous range of 1..n @@ -127,6 +131,66 @@ static u32 phandle_cache_mask; * - the phandle lookup overhead reduction provided by the cache * will likely be less */ + +static struct device_node **phandle_cache; +static u32 phandle_cache_mask; + +/* + * Caller must hold devtree_lock. + */ +static void __of_free_phandle_cache(void) +{ + u32 cache_entries = phandle_cache_mask + 1; + u32 k; + + if (!phandle_cache) + return; + + for (k = 0; k < cache_entries; k++) + of_node_put(phandle_cache[k]); + + kfree(phandle_cache); + phandle_cache = NULL; +} + +int of_free_phandle_cache(void) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); + + __of_free_phandle_cache(); + + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + return 0; +} +#if !defined(CONFIG_MODULES) +late_initcall_sync(of_free_phandle_cache); +#endif + +/* + * Caller must hold devtree_lock. + */ +void __of_free_phandle_cache_entry(phandle handle) +{ + phandle masked_handle; + struct device_node *np; + + if (!handle) + return; + + masked_handle = handle & phandle_cache_mask; + + if (phandle_cache) { + np = phandle_cache[masked_handle]; + if (np && handle == np->phandle) { + of_node_put(np); + phandle_cache[masked_handle] = NULL; + } + } +} + void of_populate_phandle_cache(void) { unsigned long flags; @@ -136,8 +200,7 @@ void of_populate_phandle_cache(void) raw_spin_lock_irqsave(&devtree_lock, flags); - kfree(phandle_cache); - phandle_cache = NULL; + __of_free_phandle_cache(); for_each_of_allnodes(np) if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) @@ -155,30 +218,15 @@ void of_populate_phandle_cache(void) goto out; for_each_of_allnodes(np) - if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) + if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) { + of_node_get(np); phandle_cache[np->phandle & phandle_cache_mask] = np; + } out: raw_spin_unlock_irqrestore(&devtree_lock, flags); } -int of_free_phandle_cache(void) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); - - kfree(phandle_cache); - phandle_cache = NULL; - - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - return 0; -} -#if !defined(CONFIG_MODULES) -late_initcall_sync(of_free_phandle_cache); -#endif - void __init of_core_init(void) { struct device_node *np; @@ -482,14 +530,14 @@ static int __of_device_is_compatible(const struct device_node *device, /* Matching type is better than matching name */ if (type && type[0]) { - if (!device->type || of_node_cmp(type, device->type)) + if (!__of_node_is_type(device, type)) return 0; score += 2; } /* Matching name is a bit better than not */ if (name && name[0]) { - if (!device->name || of_node_cmp(name, device->name)) + if (!of_node_name_eq(device, name)) return 0; score++; } @@ -775,7 +823,7 @@ struct device_node *of_get_next_cpu_node(struct device_node *prev) } for (; next; next = next->sibling) { if (!(of_node_name_eq(next, "cpu") || - (next->type && !of_node_cmp(next->type, "cpu")))) + __of_node_is_type(next, "cpu"))) continue; if (of_node_get(next)) break; @@ -828,7 +876,7 @@ struct device_node *of_get_child_by_name(const struct device_node *node, struct device_node *child; for_each_child_of_node(node, child) - if (child->name && (of_node_cmp(child->name, name) == 0)) + if (of_node_name_eq(child, name)) break; return child; } @@ -954,8 +1002,7 @@ struct device_node *of_find_node_by_name(struct device_node *from, raw_spin_lock_irqsave(&devtree_lock, flags); for_each_of_allnodes_from(from, np) - if (np->name && (of_node_cmp(np->name, name) == 0) - && of_node_get(np)) + if (of_node_name_eq(np, name) && of_node_get(np)) break; of_node_put(from); raw_spin_unlock_irqrestore(&devtree_lock, flags); @@ -983,8 +1030,7 @@ struct device_node *of_find_node_by_type(struct device_node *from, raw_spin_lock_irqsave(&devtree_lock, flags); for_each_of_allnodes_from(from, np) - if (np->type && (of_node_cmp(np->type, type) == 0) - && of_node_get(np)) + if (__of_node_is_type(np, type) && of_node_get(np)) break; of_node_put(from); raw_spin_unlock_irqrestore(&devtree_lock, flags); @@ -1190,13 +1236,23 @@ struct device_node *of_find_node_by_phandle(phandle handle) if (phandle_cache[masked_handle] && handle == phandle_cache[masked_handle]->phandle) np = phandle_cache[masked_handle]; + if (np && of_node_check_flag(np, OF_DETACHED)) { + WARN_ON(1); /* did not uncache np on node removal */ + of_node_put(np); + phandle_cache[masked_handle] = NULL; + np = NULL; + } } if (!np) { for_each_of_allnodes(np) - if (np->phandle == handle) { - if (phandle_cache) + if (np->phandle == handle && + !of_node_check_flag(np, OF_DETACHED)) { + if (phandle_cache) { + /* will put when removed from cache */ + of_node_get(np); phandle_cache[masked_handle] = np; + } break; } } @@ -2108,9 +2164,9 @@ struct device_node *of_find_next_cache_node(const struct device_node *np) /* OF on pmac has nodes instead of properties named "l2-cache" * beneath CPU nodes. */ - if (IS_ENABLED(CONFIG_PPC_PMAC) && !strcmp(np->type, "cpu")) + if (IS_ENABLED(CONFIG_PPC_PMAC) && of_node_is_type(np, "cpu")) for_each_child_of_node(np, child) - if (!strcmp(child->type, "cache")) + if (of_node_is_type(child, "cache")) return child; return NULL; diff --git a/drivers/of/device.c b/drivers/of/device.c index 5592437bb3d1..3717f2a20d0d 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -211,7 +211,7 @@ static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len /* Name & Type */ /* %p eats all alphanum characters, so %c must be used here */ csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node, 'T', - dev->of_node->type); + of_node_get_device_type(dev->of_node)); tsize = csize; len -= csize; if (str) @@ -281,7 +281,7 @@ EXPORT_SYMBOL_GPL(of_device_modalias); */ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - const char *compat; + const char *compat, *type; struct alias_prop *app; struct property *p; int seen = 0; @@ -291,8 +291,9 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) add_uevent_var(env, "OF_NAME=%pOFn", dev->of_node); add_uevent_var(env, "OF_FULLNAME=%pOF", dev->of_node); - if (dev->of_node->type && strcmp("<NULL>", dev->of_node->type) != 0) - add_uevent_var(env, "OF_TYPE=%s", dev->of_node->type); + type = of_node_get_device_type(dev->of_node); + if (type) + add_uevent_var(env, "OF_TYPE=%s", type); /* Since the compatible field can contain pretty much anything * it's not really legal to split it out with commas. We split it diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index f4f8ed9b5454..a09c1c3cf831 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -205,15 +205,24 @@ static void __of_attach_node(struct device_node *np) const __be32 *phandle; int sz; - np->name = __of_get_property(np, "name", NULL) ? : "<NULL>"; - np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>"; - - phandle = __of_get_property(np, "phandle", &sz); - if (!phandle) - phandle = __of_get_property(np, "linux,phandle", &sz); - if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle) - phandle = __of_get_property(np, "ibm,phandle", &sz); - np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; + if (!of_node_check_flag(np, OF_OVERLAY)) { + np->name = __of_get_property(np, "name", NULL); + np->type = __of_get_property(np, "device_type", NULL); + if (!np->name) + np->name = "<NULL>"; + if (!np->type) + np->type = "<NULL>"; + + phandle = __of_get_property(np, "phandle", &sz); + if (!phandle) + phandle = __of_get_property(np, "linux,phandle", &sz); + if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle) + phandle = __of_get_property(np, "ibm,phandle", &sz); + if (phandle && (sz >= 4)) + np->phandle = be32_to_cpup(phandle); + else + np->phandle = 0; + } np->child = NULL; np->sibling = np->parent->child; @@ -268,13 +277,13 @@ void __of_detach_node(struct device_node *np) } of_node_set_flag(np, OF_DETACHED); + + /* race with of_find_node_by_phandle() prevented by devtree_lock */ + __of_free_phandle_cache_entry(np->phandle); } /** * of_detach_node() - "Unplug" a node from the device tree. - * - * The caller must hold a reference to the node. The memory associated with - * the node is not freed until its refcount goes to zero. */ int of_detach_node(struct device_node *np) { @@ -330,6 +339,25 @@ void of_node_release(struct kobject *kobj) if (!of_node_check_flag(node, OF_DYNAMIC)) return; + if (of_node_check_flag(node, OF_OVERLAY)) { + + if (!of_node_check_flag(node, OF_OVERLAY_FREE_CSET)) { + /* premature refcount of zero, do not free memory */ + pr_err("ERROR: memory leak before free overlay changeset, %pOF\n", + node); + return; + } + + /* + * If node->properties non-empty then properties were added + * to this node either by different overlay that has not + * yet been removed, or by a non-overlay mechanism. + */ + if (node->properties) + pr_err("ERROR: %s(), unexpected properties in %pOF\n", + __func__, node); + } + property_list_free(node->properties); property_list_free(node->deadprops); @@ -434,6 +462,16 @@ struct device_node *__of_node_dup(const struct device_node *np, static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) { + if (ce->action == OF_RECONFIG_ATTACH_NODE && + of_node_check_flag(ce->np, OF_OVERLAY)) { + if (kref_read(&ce->np->kobj.kref) > 1) { + pr_err("ERROR: memory leak, expected refcount 1 instead of %d, of_node_get()/of_node_put() unbalanced - destroy cset entry: attach overlay node %pOF\n", + kref_read(&ce->np->kobj.kref), ce->np); + } else { + of_node_set_flag(ce->np, OF_OVERLAY_FREE_CSET); + } + } + of_node_put(ce->np); list_del(&ce->node); kfree(ce); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index bb532aae0d92..7099c652c6a5 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -891,15 +891,20 @@ const void * __init of_flat_dt_match_machine(const void *default_match, } #ifdef CONFIG_BLK_DEV_INITRD -#ifndef __early_init_dt_declare_initrd static void __early_init_dt_declare_initrd(unsigned long start, unsigned long end) { - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; + /* ARM64 would cause a BUG to occur here when CONFIG_DEBUG_VM is + * enabled since __va() is called too early. ARM64 does make use + * of phys_initrd_start/phys_initrd_size so we can skip this + * conversion. + */ + if (!IS_ENABLED(CONFIG_ARM64)) { + initrd_start = (unsigned long)__va(start); + initrd_end = (unsigned long)__va(end); + initrd_below_start_ok = 1; + } } -#endif /** * early_init_dt_check_for_initrd - Decode initrd location from flat tree @@ -924,6 +929,8 @@ static void __init early_init_dt_check_for_initrd(unsigned long node) end = of_read_number(prop, len/4); __early_init_dt_declare_initrd(start, end); + phys_initrd_start = start; + phys_initrd_size = end - start; pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", (unsigned long long)start, (unsigned long long)end); @@ -1200,8 +1207,12 @@ bool __init early_init_dt_verify(void *params) void __init early_init_dt_scan_nodes(void) { + int rc = 0; + /* Retrieve various information from the /chosen node */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + if (!rc) + pr_warn("No chosen node found, continuing without\n"); /* Initialize {size,address}-cells info */ of_scan_flat_dt(early_init_dt_scan_root, NULL); diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c index 7a0a18980b98..c72eef988041 100644 --- a/drivers/of/kobj.c +++ b/drivers/of/kobj.c @@ -133,6 +133,9 @@ int __of_attach_node_sysfs(struct device_node *np) } if (!name) return -ENOMEM; + + of_node_get(np); + rc = kobject_add(&np->kobj, parent, "%s", name); kfree(name); if (rc) @@ -159,6 +162,5 @@ void __of_detach_node_sysfs(struct device_node *np) kobject_del(&np->kobj); } - /* finally remove the kobj_init ref */ of_node_put(np); } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 5d1567025358..24786818e32e 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -84,6 +84,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {} int of_resolve_phandles(struct device_node *tree); #endif +#if defined(CONFIG_OF_DYNAMIC) +void __of_free_phandle_cache_entry(phandle handle); +#endif + #if defined(CONFIG_OF_OVERLAY) void of_overlay_mutex_lock(void); void of_overlay_mutex_unlock(void); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 42b1f73ac5f6..2b5ac43a5690 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -24,13 +24,33 @@ #include "of_private.h" /** + * struct target - info about current target node as recursing through overlay + * @np: node where current level of overlay will be applied + * @in_livetree: @np is a node in the live devicetree + * + * Used in the algorithm to create the portion of a changeset that describes + * an overlay fragment, which is a devicetree subtree. Initially @np is a node + * in the live devicetree where the overlay subtree is targeted to be grafted + * into. When recursing to the next level of the overlay subtree, the target + * also recurses to the next level of the live devicetree, as long as overlay + * subtree node also exists in the live devicetree. When a node in the overlay + * subtree does not exist at the same level in the live devicetree, target->np + * points to a newly allocated node, and all subsequent targets in the subtree + * will be newly allocated nodes. + */ +struct target { + struct device_node *np; + bool in_livetree; +}; + +/** * struct fragment - info about fragment nodes in overlay expanded device tree * @target: target of the overlay operation * @overlay: pointer to the __overlay__ node */ struct fragment { - struct device_node *target; struct device_node *overlay; + struct device_node *target; }; /** @@ -72,8 +92,7 @@ static int devicetree_corrupt(void) } static int build_changeset_next_level(struct overlay_changeset *ovcs, - struct device_node *target_node, - const struct device_node *overlay_node); + struct target *target, const struct device_node *overlay_node); /* * of_resolve_phandles() finds the largest phandle in the live tree. @@ -257,15 +276,23 @@ err_free_target_path: /** * add_changeset_property() - add @overlay_prop to overlay changeset * @ovcs: overlay changeset - * @target_node: where to place @overlay_prop in live tree + * @target: where @overlay_prop will be placed * @overlay_prop: property to add or update, from overlay tree * @is_symbols_prop: 1 if @overlay_prop is from node "/__symbols__" * - * If @overlay_prop does not already exist in @target_node, add changeset entry - * to add @overlay_prop in @target_node, else add changeset entry to update + * If @overlay_prop does not already exist in live devicetree, add changeset + * entry to add @overlay_prop in @target, else add changeset entry to update * value of @overlay_prop. * - * Some special properties are not updated (no error returned). + * @target may be either in the live devicetree or in a new subtree that + * is contained in the changeset. + * + * Some special properties are not added or updated (no error returned): + * "name", "phandle", "linux,phandle". + * + * Properties "#address-cells" and "#size-cells" are not updated if they + * are already in the live tree, but if present in the live tree, the values + * in the overlay must match the values in the live tree. * * Update of property in symbols node is not allowed. * @@ -273,19 +300,23 @@ err_free_target_path: * invalid @overlay. */ static int add_changeset_property(struct overlay_changeset *ovcs, - struct device_node *target_node, - struct property *overlay_prop, + struct target *target, struct property *overlay_prop, bool is_symbols_prop) { struct property *new_prop = NULL, *prop; int ret = 0; + bool check_for_non_overlay_node = false; - prop = of_find_property(target_node, overlay_prop->name, NULL); + if (target->in_livetree) + if (!of_prop_cmp(overlay_prop->name, "name") || + !of_prop_cmp(overlay_prop->name, "phandle") || + !of_prop_cmp(overlay_prop->name, "linux,phandle")) + return 0; - if (!of_prop_cmp(overlay_prop->name, "name") || - !of_prop_cmp(overlay_prop->name, "phandle") || - !of_prop_cmp(overlay_prop->name, "linux,phandle")) - return 0; + if (target->in_livetree) + prop = of_find_property(target->np, overlay_prop->name, NULL); + else + prop = NULL; if (is_symbols_prop) { if (prop) @@ -298,12 +329,36 @@ static int add_changeset_property(struct overlay_changeset *ovcs, if (!new_prop) return -ENOMEM; - if (!prop) - ret = of_changeset_add_property(&ovcs->cset, target_node, + if (!prop) { + check_for_non_overlay_node = true; + if (!target->in_livetree) { + new_prop->next = target->np->deadprops; + target->np->deadprops = new_prop; + } + ret = of_changeset_add_property(&ovcs->cset, target->np, new_prop); - else - ret = of_changeset_update_property(&ovcs->cset, target_node, + } else if (!of_prop_cmp(prop->name, "#address-cells")) { + if (!of_prop_val_eq(prop, new_prop)) { + pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n", + target->np); + ret = -EINVAL; + } + } else if (!of_prop_cmp(prop->name, "#size-cells")) { + if (!of_prop_val_eq(prop, new_prop)) { + pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n", + target->np); + ret = -EINVAL; + } + } else { + check_for_non_overlay_node = true; + ret = of_changeset_update_property(&ovcs->cset, target->np, new_prop); + } + + if (check_for_non_overlay_node && + !of_node_check_flag(target->np, OF_OVERLAY)) + 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); @@ -315,14 +370,14 @@ static int add_changeset_property(struct overlay_changeset *ovcs, /** * add_changeset_node() - add @node (and children) to overlay changeset - * @ovcs: overlay changeset - * @target_node: where to place @node in live tree - * @node: node from within overlay device tree fragment + * @ovcs: overlay changeset + * @target: where @node will be placed in live tree or changeset + * @node: node from within overlay device tree fragment * - * If @node does not already exist in @target_node, add changeset entry - * to add @node in @target_node. + * If @node does not already exist in @target, add changeset entry + * to add @node in @target. * - * If @node already exists in @target_node, and the existing node has + * If @node already exists in @target, and the existing node has * a phandle, the overlay node is not allowed to have a phandle. * * If @node has child nodes, add the children recursively via @@ -342,49 +397,65 @@ static int add_changeset_property(struct overlay_changeset *ovcs, * a live devicetree created from Open Firmware. * * NOTE_2: Multiple mods of created nodes not supported. - * If more than one fragment contains a node that does not already exist - * in the live tree, then for each fragment of_changeset_attach_node() - * will add a changeset entry to add the node. When the changeset is - * applied, __of_attach_node() will attach the node twice (once for - * each fragment). At this point the device tree will be corrupted. - * - * TODO: add integrity check to ensure that multiple fragments do not - * create the same node. * * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if * invalid @overlay. */ static int add_changeset_node(struct overlay_changeset *ovcs, - struct device_node *target_node, struct device_node *node) + struct target *target, struct device_node *node) { const char *node_kbasename; + const __be32 *phandle; struct device_node *tchild; - int ret = 0; + struct target target_child; + int ret = 0, size; node_kbasename = kbasename(node->full_name); - for_each_child_of_node(target_node, tchild) + for_each_child_of_node(target->np, tchild) if (!of_node_cmp(node_kbasename, kbasename(tchild->full_name))) break; if (!tchild) { - tchild = __of_node_dup(node, node_kbasename); + tchild = __of_node_dup(NULL, node_kbasename); if (!tchild) return -ENOMEM; - tchild->parent = target_node; + tchild->parent = target->np; + tchild->name = __of_get_property(node, "name", NULL); + tchild->type = __of_get_property(node, "device_type", NULL); + + if (!tchild->name) + tchild->name = "<NULL>"; + if (!tchild->type) + tchild->type = "<NULL>"; + + /* ignore obsolete "linux,phandle" */ + phandle = __of_get_property(node, "phandle", &size); + if (phandle && (size == 4)) + tchild->phandle = be32_to_cpup(phandle); + + of_node_set_flag(tchild, OF_OVERLAY); ret = of_changeset_attach_node(&ovcs->cset, tchild); if (ret) return ret; - return build_changeset_next_level(ovcs, tchild, node); + target_child.np = tchild; + target_child.in_livetree = false; + + ret = build_changeset_next_level(ovcs, &target_child, node); + of_node_put(tchild); + return ret; } - if (node->phandle && tchild->phandle) + if (node->phandle && tchild->phandle) { ret = -EINVAL; - else - ret = build_changeset_next_level(ovcs, tchild, node); + } else { + target_child.np = tchild; + target_child.in_livetree = target->in_livetree; + ret = build_changeset_next_level(ovcs, &target_child, node); + } of_node_put(tchild); return ret; @@ -393,7 +464,7 @@ static int add_changeset_node(struct overlay_changeset *ovcs, /** * build_changeset_next_level() - add level of overlay changeset * @ovcs: overlay changeset - * @target_node: where to place @overlay_node in live tree + * @target: where to place @overlay_node in live tree * @overlay_node: node from within an overlay device tree fragment * * Add the properties (if any) and nodes (if any) from @overlay_node to the @@ -406,27 +477,26 @@ static int add_changeset_node(struct overlay_changeset *ovcs, * invalid @overlay_node. */ static int build_changeset_next_level(struct overlay_changeset *ovcs, - struct device_node *target_node, - const struct device_node *overlay_node) + struct target *target, const struct device_node *overlay_node) { struct device_node *child; struct property *prop; int ret; for_each_property_of_node(overlay_node, prop) { - ret = add_changeset_property(ovcs, target_node, prop, 0); + ret = add_changeset_property(ovcs, target, prop, 0); if (ret) { pr_debug("Failed to apply prop @%pOF/%s, err=%d\n", - target_node, prop->name, ret); + target->np, prop->name, ret); return ret; } } for_each_child_of_node(overlay_node, child) { - ret = add_changeset_node(ovcs, target_node, child); + ret = add_changeset_node(ovcs, target, child); if (ret) { pr_debug("Failed to apply node @%pOF/%pOFn, err=%d\n", - target_node, child, ret); + target->np, child, ret); of_node_put(child); return ret; } @@ -439,17 +509,17 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs, * Add the properties from __overlay__ node to the @ovcs->cset changeset. */ static int build_changeset_symbols_node(struct overlay_changeset *ovcs, - struct device_node *target_node, + struct target *target, const struct device_node *overlay_symbols_node) { struct property *prop; int ret; for_each_property_of_node(overlay_symbols_node, prop) { - ret = add_changeset_property(ovcs, target_node, prop, 1); + ret = add_changeset_property(ovcs, target, prop, 1); if (ret) { - pr_debug("Failed to apply prop @%pOF/%s, err=%d\n", - target_node, prop->name, ret); + pr_debug("Failed to apply symbols prop @%pOF/%s, err=%d\n", + target->np, prop->name, ret); return ret; } } @@ -457,6 +527,98 @@ static int build_changeset_symbols_node(struct overlay_changeset *ovcs, return 0; } +static int find_dup_cset_node_entry(struct overlay_changeset *ovcs, + struct of_changeset_entry *ce_1) +{ + struct of_changeset_entry *ce_2; + char *fn_1, *fn_2; + int node_path_match; + + if (ce_1->action != OF_RECONFIG_ATTACH_NODE && + ce_1->action != OF_RECONFIG_DETACH_NODE) + return 0; + + ce_2 = ce_1; + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { + if ((ce_2->action != OF_RECONFIG_ATTACH_NODE && + ce_2->action != OF_RECONFIG_DETACH_NODE) || + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) + continue; + + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); + node_path_match = !strcmp(fn_1, fn_2); + kfree(fn_1); + kfree(fn_2); + if (node_path_match) { + pr_err("ERROR: multiple fragments add and/or delete node %pOF\n", + ce_1->np); + return -EINVAL; + } + } + + return 0; +} + +static int find_dup_cset_prop(struct overlay_changeset *ovcs, + struct of_changeset_entry *ce_1) +{ + struct of_changeset_entry *ce_2; + char *fn_1, *fn_2; + int node_path_match; + + if (ce_1->action != OF_RECONFIG_ADD_PROPERTY && + ce_1->action != OF_RECONFIG_REMOVE_PROPERTY && + ce_1->action != OF_RECONFIG_UPDATE_PROPERTY) + return 0; + + ce_2 = ce_1; + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { + if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY && + ce_2->action != OF_RECONFIG_REMOVE_PROPERTY && + ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) || + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) + continue; + + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); + node_path_match = !strcmp(fn_1, fn_2); + kfree(fn_1); + kfree(fn_2); + if (node_path_match && + !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) { + pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n", + ce_1->np, ce_1->prop->name); + return -EINVAL; + } + } + + return 0; +} + +/** + * changeset_dup_entry_check() - check for duplicate entries + * @ovcs: Overlay changeset + * + * Check changeset @ovcs->cset for multiple {add or delete} node entries for + * the same node or duplicate {add, delete, or update} properties entries + * for the same property. + * + * Returns 0 on success, or -EINVAL if duplicate changeset entry found. + */ +static int changeset_dup_entry_check(struct overlay_changeset *ovcs) +{ + struct of_changeset_entry *ce_1; + int dup_entry = 0; + + list_for_each_entry(ce_1, &ovcs->cset.entries, node) { + dup_entry |= find_dup_cset_node_entry(ovcs, ce_1); + dup_entry |= find_dup_cset_prop(ovcs, ce_1); + } + + return dup_entry ? -EINVAL : 0; +} + /** * build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments * @ovcs: Overlay changeset @@ -472,6 +634,7 @@ static int build_changeset_symbols_node(struct overlay_changeset *ovcs, static int build_changeset(struct overlay_changeset *ovcs) { struct fragment *fragment; + struct target target; int fragments_count, i, ret; /* @@ -486,25 +649,32 @@ static int build_changeset(struct overlay_changeset *ovcs) for (i = 0; i < fragments_count; i++) { fragment = &ovcs->fragments[i]; - ret = build_changeset_next_level(ovcs, fragment->target, + target.np = fragment->target; + target.in_livetree = true; + ret = build_changeset_next_level(ovcs, &target, fragment->overlay); if (ret) { - pr_debug("apply failed '%pOF'\n", fragment->target); + pr_debug("fragment apply failed '%pOF'\n", + fragment->target); return ret; } } if (ovcs->symbols_fragment) { fragment = &ovcs->fragments[ovcs->count - 1]; - ret = build_changeset_symbols_node(ovcs, fragment->target, + + target.np = fragment->target; + target.in_livetree = true; + ret = build_changeset_symbols_node(ovcs, &target, fragment->overlay); if (ret) { - pr_debug("apply failed '%pOF'\n", fragment->target); + pr_debug("symbols fragment apply failed '%pOF'\n", + fragment->target); return ret; } } - return 0; + return changeset_dup_entry_check(ovcs); } /* @@ -514,7 +684,7 @@ static int build_changeset(struct overlay_changeset *ovcs) * 1) "target" property containing the phandle of the target * 2) "target-path" property containing the path of the target */ -static struct device_node *find_target_node(struct device_node *info_node) +static struct device_node *find_target(struct device_node *info_node) { struct device_node *node; const char *path; @@ -620,7 +790,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, fragment = &fragments[cnt]; fragment->overlay = overlay_node; - fragment->target = find_target_node(node); + fragment->target = find_target(node); if (!fragment->target) { of_node_put(fragment->overlay); ret = -EINVAL; @@ -808,7 +978,7 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, ret = __of_changeset_apply_notify(&ovcs->cset); if (ret) - pr_err("overlay changeset entry notify error %d\n", ret); + pr_err("overlay apply changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ list_add_tail(&ovcs->ovcs_list, &ovcs_list); @@ -1067,7 +1237,7 @@ int of_overlay_remove(int *ovcs_id) ret = __of_changeset_revert_notify(&ovcs->cset); if (ret) - pr_err("overlay changeset entry notify error %d\n", ret); + pr_err("overlay remove changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ *ovcs_id = 0; diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index c1633041621d..d3185063d369 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -21,8 +21,6 @@ static struct of_pdt_ops *of_pdt_prom_ops __initdata; -void __initdata (*of_pdt_build_more)(struct device_node *dp); - #if defined(CONFIG_SPARC) unsigned int of_pdt_unique_id __initdata; @@ -189,9 +187,6 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); - if (of_pdt_build_more) - of_pdt_build_more(dp); - node = of_pdt_prom_ops->getsibling(node); } diff --git a/drivers/of/property.c b/drivers/of/property.c index f46828e3b082..08430031bd28 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -571,7 +571,7 @@ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id) for_each_child_of_node(parent, port) { u32 port_id = 0; - if (of_node_cmp(port->name, "port") != 0) + if (!of_node_name_eq(port, "port")) continue; of_property_read_u32(port, "reg", &port_id); if (id == port_id) @@ -646,7 +646,7 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, port = of_get_next_child(parent, port); if (!port) return NULL; - } while (of_node_cmp(port->name, "port")); + } while (!of_node_name_eq(port, "port")); } } EXPORT_SYMBOL(of_graph_get_next_endpoint); @@ -715,7 +715,7 @@ struct device_node *of_graph_get_port_parent(struct device_node *node) /* Walk 3 levels up only if there is 'ports' node. */ for (depth = 3; depth && node; depth--) { node = of_get_next_parent(node); - if (depth == 2 && of_node_cmp(node->name, "ports")) + if (depth == 2 && !of_node_name_eq(node, "ports")) break; } return node; @@ -893,7 +893,7 @@ of_fwnode_get_named_child_node(const struct fwnode_handle *fwnode, struct device_node *child; for_each_available_child_of_node(node, child) - if (!of_node_cmp(child->name, childname)) + if (of_node_name_eq(child, childname)) return of_fwnode_handle(child); return NULL; @@ -955,7 +955,7 @@ of_fwnode_graph_get_port_parent(struct fwnode_handle *fwnode) return NULL; /* Is this the "ports" node? If not, it's the port parent. */ - if (of_node_cmp(np->name, "ports")) + if (!of_node_name_eq(np, "ports")) return of_fwnode_handle(np); return of_fwnode_handle(of_get_next_parent(np)); diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index 7edfac6f1914..c1b67dd7cd6e 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -281,7 +281,7 @@ int of_resolve_phandles(struct device_node *overlay) adjust_overlay_phandles(overlay, phandle_delta); for_each_child_of_node(overlay, local_fixups) - if (!of_node_cmp(local_fixups->name, "__local_fixups__")) + if (of_node_name_eq(local_fixups, "__local_fixups__")) break; err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); @@ -291,7 +291,7 @@ int of_resolve_phandles(struct device_node *overlay) overlay_fixups = NULL; for_each_child_of_node(overlay, child) { - if (!of_node_cmp(child->name, "__fixups__")) + if (of_node_name_eq(child, "__fixups__")) overlay_fixups = child; } diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile index 013d85e694c6..9b6807065827 100644 --- a/drivers/of/unittest-data/Makefile +++ b/drivers/of/unittest-data/Makefile @@ -17,6 +17,8 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \ overlay_12.dtb.o \ overlay_13.dtb.o \ overlay_15.dtb.o \ + overlay_bad_add_dup_node.dtb.o \ + overlay_bad_add_dup_prop.dtb.o \ overlay_bad_phandle.dtb.o \ overlay_bad_symbol.dtb.o \ overlay_base.dtb.o diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_node.dts b/drivers/of/unittest-data/overlay_bad_add_dup_node.dts new file mode 100644 index 000000000000..145dfc3b1024 --- /dev/null +++ b/drivers/of/unittest-data/overlay_bad_add_dup_node.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +/* + * &electric_1/motor-1 and &spin_ctrl_1 are the same node: + * /testcase-data-2/substation@100/motor-1 + * + * Thus the new node "controller" in each fragment will + * result in an attempt to add the same node twice. + * This will result in an error and the overlay apply + * will fail. + */ + +&electric_1 { + + motor-1 { + controller { + power_bus = < 0x1 0x2 >; + }; + }; +}; + +&spin_ctrl_1 { + controller { + power_bus_emergency = < 0x101 0x102 >; + }; +}; diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts new file mode 100644 index 000000000000..c190da54f175 --- /dev/null +++ b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +/* + * &electric_1/motor-1 and &spin_ctrl_1 are the same node: + * /testcase-data-2/substation@100/motor-1 + * + * Thus the property "rpm_avail" in each fragment will + * result in an attempt to update the same property twice. + * This will result in an error and the overlay apply + * will fail. + */ + +&electric_1 { + + motor-1 { + rpm_avail = < 100 >; + }; +}; + +&spin_ctrl_1 { + rpm_avail = < 100 200 >; +}; diff --git a/drivers/of/unittest-data/overlay_base.dts b/drivers/of/unittest-data/overlay_base.dts index 820b79ca378a..99ab9d12d00b 100644 --- a/drivers/of/unittest-data/overlay_base.dts +++ b/drivers/of/unittest-data/overlay_base.dts @@ -30,6 +30,7 @@ spin_ctrl_1: motor-1 { compatible = "ot,ferris-wheel-motor"; spin = "clockwise"; + rpm_avail = < 50 >; }; spin_ctrl_2: motor-8 { diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 49ae2aa744d6..84427384654d 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -379,6 +379,7 @@ static void __init of_unittest_parse_phandle_with_args(void) for (i = 0; i < 8; i++) { bool passed = true; + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args(np, "phandle-list", "#phandle-cells", i, &args); @@ -432,6 +433,7 @@ static void __init of_unittest_parse_phandle_with_args(void) } /* Check for missing list property */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args(np, "phandle-list-missing", "#phandle-cells", 0, &args); unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); @@ -440,6 +442,7 @@ static void __init of_unittest_parse_phandle_with_args(void) unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); /* Check for missing cells property */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args(np, "phandle-list", "#phandle-cells-missing", 0, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); @@ -448,6 +451,7 @@ static void __init of_unittest_parse_phandle_with_args(void) unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* Check for bad phandle in list */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle", "#phandle-cells", 0, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); @@ -456,6 +460,7 @@ static void __init of_unittest_parse_phandle_with_args(void) unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* Check for incorrectly formed argument list */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args(np, "phandle-list-bad-args", "#phandle-cells", 1, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); @@ -506,6 +511,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void) for (i = 0; i < 8; i++) { bool passed = true; + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args_map(np, "phandle-list", "phandle", i, &args); @@ -563,21 +569,25 @@ static void __init of_unittest_parse_phandle_with_args_map(void) } /* Check for missing list property */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args_map(np, "phandle-list-missing", "phandle", 0, &args); unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); /* Check for missing cells,map,mask property */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args_map(np, "phandle-list", "phandle-missing", 0, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* Check for bad phandle in list */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle", "phandle", 0, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* Check for incorrectly formed argument list */ + memset(&args, 0, sizeof(args)); rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args", "phandle", 1, &args); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); @@ -787,7 +797,7 @@ static void __init of_unittest_parse_interrupts(void) for (i = 0; i < 4; i++) { bool passed = true; - args.args_count = 0; + memset(&args, 0, sizeof(args)); rc = of_irq_parse_one(np, i, &args); passed &= !rc; @@ -808,7 +818,7 @@ static void __init of_unittest_parse_interrupts(void) for (i = 0; i < 4; i++) { bool passed = true; - args.args_count = 0; + memset(&args, 0, sizeof(args)); rc = of_irq_parse_one(np, i, &args); /* Test the values from tests-phandle.dtsi */ @@ -864,6 +874,7 @@ static void __init of_unittest_parse_interrupts_extended(void) for (i = 0; i < 7; i++) { bool passed = true; + memset(&args, 0, sizeof(args)); rc = of_irq_parse_one(np, i, &args); /* Test the values from tests-phandle.dtsi */ @@ -1071,20 +1082,44 @@ static void __init of_unittest_platform_populate(void) * of np into dup node (present in live tree) and * updates parent of children of np to dup. * - * @np: node already present in live tree + * @np: node whose properties are being added to the live tree * @dup: node present in live tree to be updated */ static void update_node_properties(struct device_node *np, struct device_node *dup) { struct property *prop; + struct property *save_next; struct device_node *child; - - for_each_property_of_node(np, prop) - of_add_property(dup, prop); + int ret; for_each_child_of_node(np, child) child->parent = dup; + + /* + * "unittest internal error: unable to add testdata property" + * + * If this message reports a property in node '/__symbols__' then + * the respective unittest overlay contains a label that has the + * same name as a label in the live devicetree. The label will + * be in the live devicetree only if the devicetree source was + * compiled with the '-@' option. If you encounter this error, + * please consider renaming __all__ of the labels in the unittest + * overlay dts files with an odd prefix that is unlikely to be + * used in a real devicetree. + */ + + /* + * open code for_each_property_of_node() because of_add_property() + * sets prop->next to NULL + */ + for (prop = np->properties; prop != NULL; prop = save_next) { + save_next = prop->next; + ret = of_add_property(dup, prop); + if (ret) + pr_err("unittest internal error: unable to add testdata property %pOF/%s", + np, prop->name); + } } /** @@ -1093,18 +1128,23 @@ static void update_node_properties(struct device_node *np, * * @np: Node to attach to live tree */ -static int attach_node_and_children(struct device_node *np) +static void attach_node_and_children(struct device_node *np) { struct device_node *next, *dup, *child; unsigned long flags; const char *full_name; full_name = kasprintf(GFP_KERNEL, "%pOF", np); + + if (!strcmp(full_name, "/__local_fixups__") || + !strcmp(full_name, "/__fixups__")) + return; + dup = of_find_node_by_path(full_name); kfree(full_name); if (dup) { update_node_properties(np, dup); - return 0; + return; } child = np->child; @@ -1125,8 +1165,6 @@ static int attach_node_and_children(struct device_node *np) attach_node_and_children(child); child = next; } - - return 0; } /** @@ -1433,8 +1471,7 @@ static void of_unittest_destroy_tracked_overlays(void) } while (defers > 0); } -static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr, - int *overlay_id) +static int __init of_unittest_apply_overlay(int overlay_nr, int *overlay_id) { const char *overlay_name; @@ -1467,7 +1504,7 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr, } ovcs_id = 0; - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); + ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id); if (ret != 0) { /* of_unittest_apply_overlay already called unittest() */ return ret; @@ -1503,7 +1540,7 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr, /* apply the overlay */ ovcs_id = 0; - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); + ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id); if (ret != 0) { /* of_unittest_apply_overlay already called unittest() */ return ret; @@ -2161,10 +2198,12 @@ OVERLAY_INFO_EXTERN(overlay_11); OVERLAY_INFO_EXTERN(overlay_12); OVERLAY_INFO_EXTERN(overlay_13); OVERLAY_INFO_EXTERN(overlay_15); +OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node); +OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop); OVERLAY_INFO_EXTERN(overlay_bad_phandle); OVERLAY_INFO_EXTERN(overlay_bad_symbol); -/* order of entries is hard-coded into users of overlays[] */ +/* entries found by name */ static struct overlay_info overlays[] = { OVERLAY_INFO(overlay_base, -9999), OVERLAY_INFO(overlay, 0), @@ -2183,9 +2222,12 @@ static struct overlay_info overlays[] = { OVERLAY_INFO(overlay_12, 0), OVERLAY_INFO(overlay_13, 0), OVERLAY_INFO(overlay_15, 0), + OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL), + OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL), OVERLAY_INFO(overlay_bad_phandle, -EINVAL), OVERLAY_INFO(overlay_bad_symbol, -EINVAL), - {} + /* end marker */ + {.dtb_begin = NULL, .dtb_end = NULL, .expected_result = 0, .name = NULL} }; static struct device_node *overlay_base_root; @@ -2215,6 +2257,19 @@ void __init unittest_unflatten_overlay_base(void) u32 data_size; void *new_fdt; u32 size; + int found = 0; + const char *overlay_name = "overlay_base"; + + for (info = overlays; info && info->name; info++) { + if (!strcmp(overlay_name, info->name)) { + found = 1; + break; + } + } + if (!found) { + pr_err("no overlay data for %s\n", overlay_name); + return; + } info = &overlays[0]; @@ -2262,11 +2317,10 @@ static int __init overlay_data_apply(const char *overlay_name, int *overlay_id) { struct overlay_info *info; int found = 0; - int k; int ret; u32 size; - for (k = 0, info = overlays; info && info->name; info++, k++) { + for (info = overlays; info && info->name; info++) { if (!strcmp(overlay_name, info->name)) { found = 1; break; @@ -2339,7 +2393,7 @@ static __init void of_unittest_overlay_high_level(void) */ pprev = &overlay_base_root->child; for (np = overlay_base_root->child; np; np = np->sibling) { - if (!of_node_cmp(np->name, "__local_fixups__")) { + if (of_node_name_eq(np, "__local_fixups__")) { *pprev = np->sibling; break; } @@ -2352,7 +2406,7 @@ static __init void of_unittest_overlay_high_level(void) /* will have to graft properties from node into live tree */ pprev = &overlay_base_root->child; for (np = overlay_base_root->child; np; np = np->sibling) { - if (!of_node_cmp(np->name, "__symbols__")) { + if (of_node_name_eq(np, "__symbols__")) { overlay_base_symbols = np; *pprev = np->sibling; break; @@ -2430,6 +2484,12 @@ static __init void of_unittest_overlay_high_level(void) unittest(overlay_data_apply("overlay", NULL), "Adding overlay 'overlay' failed\n"); + unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL), + "Adding overlay 'overlay_bad_add_dup_node' failed\n"); + + unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL), + "Adding overlay 'overlay_bad_add_dup_prop' failed\n"); + unittest(overlay_data_apply("overlay_bad_phandle", NULL), "Adding overlay 'overlay_bad_phandle' failed\n"); |