diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 11:54:57 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 11:54:57 -0700 |
| commit | 09fb6892f34abdb6d9b50ae7337b7b7b56dc82d6 (patch) | |
| tree | 698df622366101bf536f6e3a4c37a6deb0e95153 /drivers/of | |
| parent | d44ade05aa21468bd30652bc4492891b854a400a (diff) | |
| parent | faa25db0892135c97a0bfd48d79173db0dd25ab2 (diff) | |
| download | linux-next-09fb6892f34abdb6d9b50ae7337b7b7b56dc82d6.tar.gz linux-next-09fb6892f34abdb6d9b50ae7337b7b7b56dc82d6.zip | |
Merge tag 'devicetree-for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
Pull devicetree updates from Rob Herring:
"DT core:
- Add support for handling multiple cells in "iommu-map" entries
- Support only 1 entry in /reserved-memory "reg" entries. Support for
more than 1 entry has been broken
- Fix a UAF on alloc_reserved_mem_array() failure
- Make "ibm,phandle" handling logic specific to PPC
- Use memcpy() instead of strcpy() for known length strings
- Ensure __of_find_n_match_cpu_property() handles malformed "reg"
entries
- Add various checks that expected strings are strings before
accessing them
- Drop redundant memset() when unflattening DT
DT bindings:
- Add a DTS style checker. Currently hooked up to dt_binding_check to
check examples
- Convert st,nomadik platform, ti,omap-dmm, and ti,irq-crossbar
bindings to DT schema
- Add Apple System Management Controller hwmon, Qualcomm Hamoa
Embedded Controller, Qualcomm IPQ6018 PWM controller, fsl,mc1323,
Samsung SOFEF01-M DDIC panel, Freescale i.MX53 Television Encoder,
Samsung S2M series PMIC extcon, and MT6365 PMIC AuxADC schemas
- Extend bindings for QCom Maili and Nord PDC, QCom Hali fastrpc,
qcom,eliza-imem, qcom,oryon-1-5 CPU, and MT6365 Keys
- Consolidate "sram" property definitions
- Fix constraints on "nvmem" properties which only contain phandles
and no arg cells
- Another pass of fixing "phandle-array" constraints
- Add Gira vendor prefix"
* tag 'devicetree-for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (50 commits)
dt-bindings: interrupt-controller: qcom,pdc: Add Maili compatible string
dt-bindings: interrupt-controller: ti,irq-crossbar: Convert to DT schema
dt-bindings: vendor-prefixes: add Gira
dt-bindings: embedded-controller: Add Qualcomm reference device EC description
dt-bindings: pwm: add IPQ6018 binding
dt-bindings: hwmon: Add Apple System Management Controller hwmon schema
docs: dt: writing-schema: Clarify what is required in a schema
of: Respect #{iommu,msi}-cells in maps
of: Factor arguments passed to of_map_id() into a struct
of: Add convenience wrappers for of_map_id()
of: reserved_mem: zero total_reserved_mem_cnt if no valid /reserved-memory entry
of: reserved_mem: handle NULL name in of_reserved_mem_lookup()
dt-bindings: cache: l2c2x0: Add missing power-domains
dt-bindings: interrupt-controller: renesas,r9a09g077-icu: Fix reg size in example
dt-bindings: nvmem: consumer: Make 'nvmem' an array of one-item entries
drivers/of/overlay: Use memcpy() to copy known length strings
dt-bindings: add self-test fixtures for style checker
dt-bindings: wire style checker into dt_binding_check
scripts/jobserver-exec: propagate child exit status
dt-bindings: add DTS style checker
...
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/base.c | 270 | ||||
| -rw-r--r-- | drivers/of/cpu.c | 2 | ||||
| -rw-r--r-- | drivers/of/dynamic.c | 20 | ||||
| -rw-r--r-- | drivers/of/fdt.c | 4 | ||||
| -rw-r--r-- | drivers/of/irq.c | 32 | ||||
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 79 | ||||
| -rw-r--r-- | drivers/of/overlay.c | 4 | ||||
| -rw-r--r-- | drivers/of/property.c | 27 | ||||
| -rw-r--r-- | drivers/of/unittest.c | 32 |
9 files changed, 355 insertions, 115 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index a650c91897cc..7cb0d7e88247 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -82,9 +82,17 @@ 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); + const char *match; + int len; + + if (!np || !type) + return false; + + match = __of_get_property(np, "device_type", &len); + if (!match || len <= 0 || strnlen(match, len) >= len) + return false; - return np && match && type && !strcmp(match, type); + return !strcmp(match, type); } #define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ @@ -511,22 +519,22 @@ static bool __of_device_is_status(const struct device_node *device, return false; status = __of_get_property(device, "status", &statlen); - if (status == NULL) + if (!status || statlen <= 0) + return false; + if (strnlen(status, statlen) >= statlen) return false; - if (statlen > 0) { - while (*strings) { - unsigned int len = strlen(*strings); + while (*strings) { + unsigned int len = strlen(*strings); - if ((*strings)[len - 1] == '-') { - if (!strncmp(status, *strings, len)) - return true; - } else { - if (!strcmp(status, *strings)) - return true; - } - strings++; + if ((*strings)[len - 1] == '-') { + if (!strncmp(status, *strings, len)) + return true; + } else { + if (!strcmp(status, *strings)) + return true; } + strings++; } return false; @@ -1237,10 +1245,11 @@ EXPORT_SYMBOL(of_find_matching_node_and_match); int of_alias_from_compatible(const struct device_node *node, char *alias, int len) { const char *compatible, *p; - int cplen; + int ret; - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible || strlen(compatible) > cplen) + ret = of_property_read_string_index(node, "compatible", 0, + &compatible); + if (ret) return -ENODEV; p = strchr(compatible, ','); strscpy(alias, p ? p + 1 : compatible, len); @@ -2116,50 +2125,84 @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } +/* + * Some DTs have an iommu-map targeting a 2-cell IOMMU node while + * specifying only 1 cell. Fortunately they all consist of value '1' + * as the 2nd cell entry with the same target, so check for that pattern. + * + * Example: + * IOMMU node: + * #iommu-cells = <2>; + * + * Device node: + * iommu-map = <0x0000 &smmu 0x0000 0x1>, + * <0x0100 &smmu 0x0100 0x1>; + */ +static bool of_check_bad_map(const __be32 *map, int len) +{ + __be32 phandle = map[1]; + + if (len % 4) + return false; + for (int i = 0; i < len; i += 4) { + if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1)) + return false; + } + return true; +} + /** * of_map_id - Translate an ID through a downstream mapping. * @np: root complex device node. * @id: device ID to map. * @map_name: property name of the map to use. + * @cells_name: property name of target specifier cells. * @map_mask_name: optional property name of the mask to use. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: pointer to an optional filter node, or NULL to allow bypass. + * If non-NULL, the map property must exist (-ENODEV if absent). If + * *filter_np is also non-NULL, only entries targeting that node match. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args_count will be set to the number of output specifier cells + * as defined by @cells_name in the target node, and + * @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np will be set + * to the target node with a reference held that the caller must release + * with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that - * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or - * @id_out may be NULL if only the other is required. If @target points to - * a non-NULL device node pointer, only entries targeting that node will be - * matched; if it points to a NULL value, it will receive the device node of - * the first matching target phandle, with a reference held. + * ID, as per the "iommu-map" and "msi-map" bindings. * * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + const char *map_name, const char *cells_name, + const char *map_mask_name, + struct device_node * const *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; - int map_len; + int map_bytes, map_len, offset = 0; + bool bad_map = false; const __be32 *map = NULL; - if (!np || !map_name || (!target && !id_out)) + if (!np || !map_name || !cells_name || !arg) return -EINVAL; + /* Ensure bypass/no-match success never returns a stale target node. */ + arg->np = NULL; - map = of_get_property(np, map_name, &map_len); + map = of_get_property(np, map_name, &map_bytes); if (!map) { - if (target) + if (filter_np) return -ENODEV; /* Otherwise, no map implies no translation */ - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } - if (!map_len || map_len % (4 * sizeof(*map))) { - pr_err("%pOF: Error: Bad %s length: %d\n", np, - map_name, map_len); - return -EINVAL; - } + if (map_bytes % sizeof(*map)) + goto err_map_len; + map_len = map_bytes / sizeof(*map); /* The default is to select all bits. */ map_mask = 0xffffffff; @@ -2172,52 +2215,157 @@ int of_map_id(const struct device_node *np, u32 id, of_property_read_u32(np, map_mask_name, &map_mask); masked_id = map_mask & id; - for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + + while (offset < map_len) { struct device_node *phandle_node; - u32 id_base = be32_to_cpup(map + 0); - u32 phandle = be32_to_cpup(map + 1); - u32 out_base = be32_to_cpup(map + 2); - u32 id_len = be32_to_cpup(map + 3); + u32 id_base, phandle, id_len, id_off, cells = 0; + const __be32 *out_base; + + if (map_len - offset < 2) + goto err_map_len; + + id_base = be32_to_cpup(map + offset); if (id_base & ~map_mask) { - pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n", - np, map_name, map_name, - map_mask, id_base); + pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n", + np, map_name, map_mask_name, map_mask, id_base); return -EFAULT; } - if (masked_id < id_base || masked_id >= id_base + id_len) - continue; - + phandle = be32_to_cpup(map + offset + 1); phandle_node = of_find_node_by_phandle(phandle); if (!phandle_node) return -ENODEV; - if (target) { - if (*target) - of_node_put(phandle_node); - else - *target = phandle_node; + /* + * Assume 1-cell output specifier if the target node lacks the + * #cells property, for backward compatibility with controllers + * that predate the property (e.g. arm,gic-v2m-frame). + */ + if (bad_map || of_property_read_u32(phandle_node, cells_name, &cells)) + cells = 1; + + if (cells > MAX_PHANDLE_ARGS) { + pr_err("%pOF: %s cell count %d exceeds maximum\n", + phandle_node, cells_name, cells); + of_node_put(phandle_node); + return -EINVAL; + } + + if (offset == 0 && cells == 2) { + bad_map = of_check_bad_map(map, map_len); + if (bad_map) { + pr_warn_once("%pOF: %s has 1-cell entries targeting 2-cell %s, treating as 1-cell output\n", + np, map_name, cells_name); + cells = 1; + } + } - if (*target != phandle_node) - continue; + if (map_len - offset < 3 + cells) { + of_node_put(phandle_node); + goto err_map_len; } - if (id_out) - *id_out = masked_id - id_base + out_base; + out_base = map + offset + 2; + offset += 3 + cells; + + id_len = be32_to_cpup(map + offset - 1); + id_off = masked_id - id_base; + if (masked_id < id_base || id_off >= id_len) { + of_node_put(phandle_node); + continue; + } + if (id_len > 1 && cells > 1) { + /* + * With 1 output cell we reasonably assume its value + * has a linear relationship to the input; with more, + * we'd need help from the provider to know what to do. + */ + pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n", + np, map_name, id_len, cells); + of_node_put(phandle_node); + return -EINVAL; + } + + if (filter_np && *filter_np && *filter_np != phandle_node) { + of_node_put(phandle_node); + continue; + } + + arg->np = phandle_node; + for (int i = 0; i < cells; i++) + arg->args[i] = id_off + be32_to_cpu(out_base[i]); + arg->args_count = cells; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", - np, map_name, map_mask, id_base, out_base, - id_len, id, masked_id - id_base + out_base); + np, map_name, map_mask, id_base, + cells ? be32_to_cpup(out_base) : 0, + id_len, id, + cells ? id_off + be32_to_cpup(out_base) : id_off); return 0; } pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name, - id, target && *target ? *target : NULL); + id, filter_np && *filter_np ? *filter_np : NULL); /* Bypasses translation */ - if (id_out) - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; + +err_map_len: + pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes); + return -EINVAL; } EXPORT_SYMBOL_GPL(of_map_id); + +/** + * of_map_iommu_id - Translate an ID using "iommu-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the iommu-map table. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). + * + * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells", + * and "iommu-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_iommu_id(const struct device_node *np, u32 id, + struct of_phandle_args *arg) +{ + return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg); +} +EXPORT_SYMBOL_GPL(of_map_iommu_id); + +/** + * of_map_msi_id - Translate an ID using "msi-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the msi-map table. + * @filter_np: pointer to an optional filter node, or NULL to allow bypass. + * If non-NULL, the map property must exist (-ENODEV if absent). If + * *filter_np is also non-NULL, only entries targeting that node match. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). + * + * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells", + * and "msi-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node * const *filter_np, struct of_phandle_args *arg) +{ + return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg); +} +EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/cpu.c b/drivers/of/cpu.c index 5214dc3d05ae..bd0e918d6f29 100644 --- a/drivers/of/cpu.c +++ b/drivers/of/cpu.c @@ -60,7 +60,7 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, cell = of_get_property(cpun, prop_name, &prop_len); if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0)) return true; - if (!cell || !ac) + if (!cell || !ac || ac > 2) return false; prop_len /= sizeof(*cell) * ac; for (tid = 0; tid < prop_len; tid++) { diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index aa450425ec1e..744ce0e1eb24 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -74,6 +74,20 @@ static const char *action_names[] = { [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY", }; +static bool of_property_status_ok(const struct property *prop) +{ + const char *status; + + if (!prop || !prop->value || prop->length <= 0) + return false; + + status = prop->value; + if (strnlen(status, prop->length) >= prop->length) + return false; + + return !strcmp(status, "okay") || !strcmp(status, "ok"); +} + #define _do_print(func, prefix, action, node, prop, ...) ({ \ func("changeset: " prefix "%-15s %pOF%s%s\n", \ ##__VA_ARGS__, action_names[action], node, \ @@ -135,11 +149,9 @@ int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data * if (prop && !strcmp(prop->name, "status")) { is_status = 1; - status_state = !strcmp(prop->value, "okay") || - !strcmp(prop->value, "ok"); + status_state = of_property_status_ok(prop); if (old_prop) - old_status_state = !strcmp(old_prop->value, "okay") || - !strcmp(old_prop->value, "ok"); + old_status_state = of_property_status_ok(old_prop); } switch (action) { diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 82f7327c59ea..26f66046cc32 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -145,7 +145,7 @@ static void populate_properties(const void *blob, * used in pSeries dynamic device tree * stuff */ - if (!strcmp(pname, "ibm,phandle")) + if (IS_ENABLED(CONFIG_PPC_PSERIES) && !strcmp(pname, "ibm,phandle")) np->phandle = be32_to_cpup(val); pp->name = (char *)pname; @@ -391,8 +391,6 @@ void *__unflatten_device_tree(const void *blob, if (!mem) return NULL; - memset(mem, 0, size); - *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); pr_debug(" unflattening %p...\n", mem); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6367c67732d2..967c19100879 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -796,14 +796,15 @@ static int of_check_msi_parent(struct device_node *dev_node, struct device_node /** * of_msi_xlate - map a MSI ID and find relevant MSI controller node * @dev: device for which the mapping is to be done. - * @msi_np: Pointer to target MSI controller node + * @msi_np: Pointer to target MSI controller node, or NULL if the caller + * only needs the translated ID without receiving the controller node. + * If non-NULL and pointing to a non-NULL node, only entries targeting + * that node will be matched. If non-NULL and pointing to NULL, it will + * receive the first matching target node with a reference held. * @id_in: Device ID. * * Walk up the device hierarchy looking for devices with a "msi-map" * or "msi-parent" property. If found, apply the mapping to @id_in. - * If @msi_np points to a non-NULL device node pointer, only entries targeting - * that node will be matched; if it points to a NULL value, it will receive the - * device node of the first matching target phandle, with a reference held. * * Returns: The mapped MSI id. */ @@ -817,10 +818,22 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_id(parent_dev->of_node, id_in, "msi-map", - "msi-map-mask", msi_np, &id_out)) + struct of_phandle_args msi_spec = {}; + + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &msi_spec)) { + if (msi_spec.np) { + /* msi-map matched: use the translated ID and target node */ + if (msi_spec.args_count > 0) + id_out = msi_spec.args[0]; + if (msi_np && !*msi_np) + *msi_np = of_node_get(msi_spec.np); + of_node_put(msi_spec.np); + } + /* msi-map present but no match → stop walking */ break; - if (!of_check_msi_parent(parent_dev->of_node, msi_np)) + } + /* -ENODEV: msi-map absent → check for msi-parent */ + if (msi_np && !of_check_msi_parent(parent_dev->of_node, msi_np)) break; } return id_out; @@ -842,9 +855,12 @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id, u32 bus_token) { struct device_node *np = NULL; + struct irq_domain *d; of_msi_xlate(dev, &np, id); - return irq_find_matching_host(np, bus_token); + d = irq_find_matching_host(np, bus_token); + of_node_put(np); + return d; } /** diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 8d5777cb5d1b..82222bd45ac6 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -69,29 +69,32 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, * the initial static array is copied over to this new array and * the new array is used from this point on. */ -static void __init alloc_reserved_mem_array(void) +static int __init alloc_reserved_mem_array(void) { struct reserved_mem *new_array; size_t alloc_size, copy_size, memset_size; + int ret; + + if (!total_reserved_mem_cnt) + return 0; alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); if (alloc_size == SIZE_MAX) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); if (!new_array) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); - return; + ret = -ENOMEM; + goto fail; } copy_size = array_size(reserved_mem_count, sizeof(*new_array)); if (copy_size == SIZE_MAX) { memblock_free(new_array, alloc_size); - total_reserved_mem_cnt = MAX_RESERVED_REGIONS; - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } memset_size = alloc_size - copy_size; @@ -100,6 +103,12 @@ static void __init alloc_reserved_mem_array(void) memset(new_array + reserved_mem_count, 0, memset_size); reserved_mem = new_array; + return 0; + +fail: + pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret); + reserved_mem_count = 0; + return ret; } static void fdt_init_reserved_mem_node(unsigned long node, const char *uname, @@ -128,42 +137,43 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base, } /* - * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property + * __reserved_mem_reserve_reg() - reserve memory described in the + * first entry in 'reg' property */ static int __init __reserved_mem_reserve_reg(unsigned long node, const char *uname) { phys_addr_t base, size; - int i, len, err; + int len, err; const __be32 *prop; bool nomap; + u64 b, s; prop = of_flat_dt_get_addr_size_prop(node, "reg", &len); - if (!prop) + if (!prop || !len) return -ENOENT; + if (len > 1) + pr_warn("Reserved memory: node '%s' has %d <base size> entries, only the first is used\n", + uname, len); + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; err = fdt_validate_reserved_mem_node(node, NULL); if (err && err != -ENODEV) return err; - for (i = 0; i < len; i++) { - u64 b, s; - - of_flat_dt_read_addr_size(prop, i, &b, &s); - - base = b; - size = s; + of_flat_dt_read_addr_size(prop, 0, &b, &s); + base = b; + size = s; - if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) { - fdt_fixup_reserved_mem_node(node, base, size); - pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", - uname, &base, (unsigned long)(size / SZ_1M)); - } else { - pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", - uname, &base, (unsigned long)(size / SZ_1M)); - } + if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) { + fdt_fixup_reserved_mem_node(node, base, size); + pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", + uname, &base, (unsigned long)(size / SZ_1M)); + } else { + pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", + uname, &base, (unsigned long)(size / SZ_1M)); } return 0; } @@ -266,7 +276,8 @@ void __init fdt_scan_reserved_mem_late(void) } /* Attempt dynamic allocation of a new reserved_mem array */ - alloc_reserved_mem_array(); + if (alloc_reserved_mem_array()) + return; if (__reserved_mem_check_root(node)) { pr_err("Reserved memory: unsupported node format, ignoring\n"); @@ -274,20 +285,24 @@ void __init fdt_scan_reserved_mem_late(void) } fdt_for_each_subnode(child, fdt, node) { + const __be32 *prop; const char *uname; u64 b, s; int ret; + int len; if (!of_fdt_device_is_available(fdt, child)) continue; - if (!of_flat_dt_get_addr_size(child, "reg", &b, &s)) + prop = of_flat_dt_get_addr_size_prop(child, "reg", &len); + if (!prop || !len) continue; ret = fdt_validate_reserved_mem_node(child, NULL); if (ret && ret != -ENODEV) continue; + of_flat_dt_read_addr_size(prop, 0, &b, &s); base = b; size = s; @@ -321,11 +336,14 @@ int __init fdt_scan_reserved_mem(void) const void *fdt = initial_boot_params; node = fdt_path_offset(fdt, "/reserved-memory"); - if (node < 0) + if (node < 0) { + total_reserved_mem_cnt = 0; return -ENODEV; + } if (__reserved_mem_check_root(node) != 0) { pr_err("Reserved memory: unsupported node format, ignoring\n"); + total_reserved_mem_cnt = 0; return -EINVAL; } @@ -788,7 +806,8 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) name = kbasename(np->full_name); for (i = 0; i < reserved_mem_count; i++) - if (!strcmp(reserved_mem[i].name, name)) + if (reserved_mem[i].name && + !strcmp(reserved_mem[i].name, name)) return &reserved_mem[i]; return NULL; diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 4e45f3414c2c..08d5351746be 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -267,8 +267,8 @@ static struct property *dup_and_fixup_symbol_prop( if (!new_prop->name || !new_prop->value) goto err_free_new_prop; - strcpy(new_prop->value, target_path); - strcpy(new_prop->value + target_path_len, path_tail); + memcpy(new_prop->value, target_path, target_path_len); + memcpy(new_prop->value + target_path_len, path_tail, path_tail_len); of_property_set_flag(new_prop, OF_DYNAMIC); diff --git a/drivers/of/property.c b/drivers/of/property.c index 136946f8b746..b276d1de3222 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -648,16 +648,31 @@ EXPORT_SYMBOL_GPL(of_prop_next_u32); const char *of_prop_next_string(const struct property *prop, const char *cur) { - const void *curv = cur; + const char *curv; + const char *end; + size_t len; - if (!prop) + if (!prop || !prop->value || !prop->length) return NULL; - if (!cur) - return prop->value; + curv = cur ? cur : prop->value; + end = prop->value + prop->length; - curv += strlen(cur) + 1; - if (curv >= prop->value + prop->length) + if (curv < (const char *)prop->value || curv >= end) + return NULL; + + if (cur) { + len = strnlen(curv, end - curv); + if (len >= end - curv) + return NULL; + + curv += len + 1; + if (curv >= end) + return NULL; + } + + len = strnlen(curv, end - curv); + if (len >= end - curv) return NULL; return curv; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 4078569a0f96..e255f54f4d76 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -713,6 +713,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void) static void __init of_unittest_property_string(void) { const char *strings[4]; + const struct property *prop; struct device_node *np; int rc; @@ -789,6 +790,37 @@ static void __init of_unittest_property_string(void) strings[1] = NULL; rc = of_property_read_string_array(np, "phandle-list-names", strings, 1); unittest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]); + + /* of_prop_next_string() tests */ + prop = of_find_property(np, "phandle-list-names", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "third"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should return NULL at end of list\n"); + + prop = of_find_property(np, "unterminated-string", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated first string\n"); + + prop = of_find_property(np, "unterminated-string-list", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated trailing string\n"); } #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ |
