summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 11:54:57 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 11:54:57 -0700
commit09fb6892f34abdb6d9b50ae7337b7b7b56dc82d6 (patch)
tree698df622366101bf536f6e3a4c37a6deb0e95153 /drivers/of
parentd44ade05aa21468bd30652bc4492891b854a400a (diff)
parentfaa25db0892135c97a0bfd48d79173db0dd25ab2 (diff)
downloadlinux-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.c270
-rw-r--r--drivers/of/cpu.c2
-rw-r--r--drivers/of/dynamic.c20
-rw-r--r--drivers/of/fdt.c4
-rw-r--r--drivers/of/irq.c32
-rw-r--r--drivers/of/of_reserved_mem.c79
-rw-r--r--drivers/of/overlay.c4
-rw-r--r--drivers/of/property.c27
-rw-r--r--drivers/of/unittest.c32
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) && \