summaryrefslogtreecommitdiffstats
path: root/lib/libfdt
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libfdt')
-rw-r--r--lib/libfdt/fdt.c335
-rw-r--r--lib/libfdt/fdt_addresses.c101
-rw-r--r--lib/libfdt/fdt_empty_tree.c38
-rw-r--r--lib/libfdt/fdt_overlay.c867
-rw-r--r--lib/libfdt/fdt_ro.c859
-rw-r--r--lib/libfdt/fdt_rw.c500
-rw-r--r--lib/libfdt/fdt_strerror.c60
-rw-r--r--lib/libfdt/fdt_sw.c384
-rw-r--r--lib/libfdt/fdt_wip.c94
-rw-r--r--lib/libfdt/libfdt.mk19
-rw-r--r--lib/libfdt/libfdt_internal.h192
11 files changed, 3449 insertions, 0 deletions
diff --git a/lib/libfdt/fdt.c b/lib/libfdt/fdt.c
new file mode 100644
index 0000000..9fe7cf4
--- /dev/null
+++ b/lib/libfdt/fdt.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+/*
+ * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
+ * that the given buffer contains what appears to be a flattened
+ * device tree with sane information in its header.
+ */
+int32_t fdt_ro_probe_(const void *fdt)
+{
+ uint32_t totalsize = fdt_totalsize(fdt);
+
+ if (can_assume(VALID_DTB))
+ return totalsize;
+
+ /* The device tree must be at an 8-byte aligned address */
+ if ((uintptr_t)fdt & 7)
+ return -FDT_ERR_ALIGNMENT;
+
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (!can_assume(LATEST)) {
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) >
+ FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ }
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ if (totalsize < INT32_MAX)
+ return totalsize;
+ else
+ return -FDT_ERR_TRUNCATED;
+}
+
+static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
+{
+ return (off >= hdrsize) && (off <= totalsize);
+}
+
+static int check_block_(uint32_t hdrsize, uint32_t totalsize,
+ uint32_t base, uint32_t size)
+{
+ if (!check_off_(hdrsize, totalsize, base))
+ return 0; /* block start out of bounds */
+ if ((base + size) < base)
+ return 0; /* overflow */
+ if (!check_off_(hdrsize, totalsize, base + size))
+ return 0; /* block end out of bounds */
+ return 1;
+}
+
+size_t fdt_header_size_(uint32_t version)
+{
+ if (version <= 1)
+ return FDT_V1_SIZE;
+ else if (version <= 2)
+ return FDT_V2_SIZE;
+ else if (version <= 3)
+ return FDT_V3_SIZE;
+ else if (version <= 16)
+ return FDT_V16_SIZE;
+ else
+ return FDT_V17_SIZE;
+}
+
+size_t fdt_header_size(const void *fdt)
+{
+ return can_assume(LATEST) ? FDT_V17_SIZE :
+ fdt_header_size_(fdt_version(fdt));
+}
+
+int fdt_check_header(const void *fdt)
+{
+ size_t hdrsize;
+
+ /* The device tree must be at an 8-byte aligned address */
+ if ((uintptr_t)fdt & 7)
+ return -FDT_ERR_ALIGNMENT;
+
+ if (fdt_magic(fdt) != FDT_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ if (!can_assume(LATEST)) {
+ if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ || (fdt_last_comp_version(fdt) >
+ FDT_LAST_SUPPORTED_VERSION))
+ return -FDT_ERR_BADVERSION;
+ if (fdt_version(fdt) < fdt_last_comp_version(fdt))
+ return -FDT_ERR_BADVERSION;
+ }
+ hdrsize = fdt_header_size(fdt);
+ if (!can_assume(VALID_DTB)) {
+
+ if ((fdt_totalsize(fdt) < hdrsize)
+ || (fdt_totalsize(fdt) > INT_MAX))
+ return -FDT_ERR_TRUNCATED;
+
+ /* Bounds check memrsv block */
+ if (!check_off_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_mem_rsvmap(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ if (!can_assume(VALID_DTB)) {
+ /* Bounds check structure block */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
+ if (!check_off_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ } else {
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ /* Bounds check strings block */
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
+{
+ unsigned int uoffset = offset;
+ unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
+
+ if (offset < 0)
+ return NULL;
+
+ if (!can_assume(VALID_INPUT))
+ if ((absoffset < uoffset)
+ || ((absoffset + len) < absoffset)
+ || (absoffset + len) > fdt_totalsize(fdt))
+ return NULL;
+
+ if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
+ if (((uoffset + len) < uoffset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ return fdt_offset_ptr_(fdt, offset);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
+{
+ const fdt32_t *tagp, *lenp;
+ uint32_t tag;
+ int offset = startoffset;
+ const char *p;
+
+ *nextoffset = -FDT_ERR_TRUNCATED;
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (!can_assume(VALID_DTB) && !tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ *nextoffset = -FDT_ERR_BADSTRUCTURE;
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+ break;
+
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (!can_assume(VALID_DTB) && !lenp)
+ return FDT_END; /* premature end */
+ /* skip-name offset, length and value */
+ offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ + fdt32_to_cpu(*lenp);
+ if (!can_assume(LATEST) &&
+ fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
+ ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
+ offset += 4;
+ break;
+
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return FDT_END;
+ }
+
+ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
+ return FDT_END; /* premature end */
+
+ *nextoffset = FDT_TAGALIGN(offset);
+ return tag;
+}
+
+int fdt_check_node_offset_(const void *fdt, int offset)
+{
+ if (!can_assume(VALID_INPUT)
+ && ((offset < 0) || (offset % FDT_TAGSIZE)))
+ return -FDT_ERR_BADOFFSET;
+
+ if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_check_prop_offset_(const void *fdt, int offset)
+{
+ if (!can_assume(VALID_INPUT)
+ && ((offset < 0) || (offset % FDT_TAGSIZE)))
+ return -FDT_ERR_BADOFFSET;
+
+ if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0)
+ if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
+ return nextoffset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth && ((--(*depth)) < 0))
+ return nextoffset;
+ break;
+
+ case FDT_END:
+ if ((nextoffset >= 0)
+ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
+ return -FDT_ERR_NOTFOUND;
+ else
+ return nextoffset;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+int fdt_first_subnode(const void *fdt, int offset)
+{
+ int depth = 0;
+
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth != 1)
+ return -FDT_ERR_NOTFOUND;
+
+ return offset;
+}
+
+int fdt_next_subnode(const void *fdt, int offset)
+{
+ int depth = 1;
+
+ /*
+ * With respect to the parent, the depth of the next subnode will be
+ * the same as the last.
+ */
+ do {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth < 1)
+ return -FDT_ERR_NOTFOUND;
+ } while (depth > 1);
+
+ return offset;
+}
+
+const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memcmp(p, s, len) == 0)
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ if (!can_assume(VALID_INPUT) && bufsize < 0)
+ return -FDT_ERR_NOSPACE;
+
+ FDT_RO_PROBE(fdt);
+
+ if (fdt_totalsize(fdt) > (unsigned int)bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/lib/libfdt/fdt_addresses.c b/lib/libfdt/fdt_addresses.c
new file mode 100644
index 0000000..c40ba09
--- /dev/null
+++ b/lib/libfdt/fdt_addresses.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
+ * Copyright (C) 2018 embedded brains GmbH
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
+{
+ const fdt32_t *c;
+ uint32_t val;
+ int len;
+
+ c = fdt_getprop(fdt, nodeoffset, name, &len);
+ if (!c)
+ return len;
+
+ if (len != sizeof(*c))
+ return -FDT_ERR_BADNCELLS;
+
+ val = fdt32_to_cpu(*c);
+ if (val > FDT_MAX_NCELLS)
+ return -FDT_ERR_BADNCELLS;
+
+ return (int)val;
+}
+
+int fdt_address_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#address-cells");
+ if (val == 0)
+ return -FDT_ERR_BADNCELLS;
+ if (val == -FDT_ERR_NOTFOUND)
+ return 2;
+ return val;
+}
+
+int fdt_size_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#size-cells");
+ if (val == -FDT_ERR_NOTFOUND)
+ return 1;
+ return val;
+}
+
+/* This function assumes that [address|size]_cells is 1 or 2 */
+int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
+ const char *name, uint64_t addr, uint64_t size)
+{
+ int addr_cells, size_cells, ret;
+ uint8_t data[sizeof(fdt64_t) * 2], *prop;
+
+ ret = fdt_address_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ addr_cells = ret;
+
+ ret = fdt_size_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ size_cells = ret;
+
+ /* check validity of address */
+ prop = data;
+ if (addr_cells == 1) {
+ if ((addr > UINT32_MAX) || (((uint64_t) UINT32_MAX + 1 - addr) < size))
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)addr);
+ } else if (addr_cells == 2) {
+ fdt64_st(prop, addr);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ /* check validity of size */
+ prop += addr_cells * sizeof(fdt32_t);
+ if (size_cells == 1) {
+ if (size > UINT32_MAX)
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)size);
+ } else if (size_cells == 2) {
+ fdt64_st(prop, size);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ return fdt_appendprop(fdt, nodeoffset, name, data,
+ (addr_cells + size_cells) * sizeof(fdt32_t));
+}
diff --git a/lib/libfdt/fdt_empty_tree.c b/lib/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000..49d54d4
--- /dev/null
+++ b/lib/libfdt/fdt_empty_tree.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2012 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_create_empty_tree(void *buf, int bufsize)
+{
+ int err;
+
+ err = fdt_create(buf, bufsize);
+ if (err)
+ return err;
+
+ err = fdt_finish_reservemap(buf);
+ if (err)
+ return err;
+
+ err = fdt_begin_node(buf, "");
+ if (err)
+ return err;
+
+ err = fdt_end_node(buf);
+ if (err)
+ return err;
+
+ err = fdt_finish(buf);
+ if (err)
+ return err;
+
+ return fdt_open_into(buf, buf, bufsize);
+}
diff --git a/lib/libfdt/fdt_overlay.c b/lib/libfdt/fdt_overlay.c
new file mode 100644
index 0000000..5c0c398
--- /dev/null
+++ b/lib/libfdt/fdt_overlay.c
@@ -0,0 +1,867 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2016 Free Electrons
+ * Copyright (C) 2016 NextThing Co.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+/**
+ * overlay_get_target_phandle - retrieves the target phandle of a fragment
+ * @fdto: pointer to the device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ *
+ * overlay_get_target_phandle() retrieves the target phandle of an
+ * overlay fragment when that fragment uses a phandle (target
+ * property) instead of a path (target-path property).
+ *
+ * returns:
+ * the phandle pointed by the target property
+ * 0, if the phandle was not found
+ * -1, if the phandle was malformed
+ */
+static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
+{
+ const fdt32_t *val;
+ int len;
+
+ val = fdt_getprop(fdto, fragment, "target", &len);
+ if (!val)
+ return 0;
+
+ if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
+ return (uint32_t)-1;
+
+ return fdt32_to_cpu(*val);
+}
+
+int fdt_overlay_target_offset(const void *fdt, const void *fdto,
+ int fragment_offset, char const **pathp)
+{
+ uint32_t phandle;
+ const char *path = NULL;
+ int path_len = 0, ret;
+
+ /* Try first to do a phandle based lookup */
+ phandle = overlay_get_target_phandle(fdto, fragment_offset);
+ if (phandle == (uint32_t)-1)
+ return -FDT_ERR_BADPHANDLE;
+
+ /* no phandle, try path */
+ if (!phandle) {
+ /* And then a path based lookup */
+ path = fdt_getprop(fdto, fragment_offset, "target-path", &path_len);
+ if (path)
+ ret = fdt_path_offset(fdt, path);
+ else
+ ret = path_len;
+ } else
+ ret = fdt_node_offset_by_phandle(fdt, phandle);
+
+ /*
+ * If we haven't found either a target or a
+ * target-path property in a node that contains a
+ * __overlay__ subnode (we wouldn't be called
+ * otherwise), consider it a improperly written
+ * overlay
+ */
+ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
+ ret = -FDT_ERR_BADOVERLAY;
+
+ /* return on error */
+ if (ret < 0)
+ return ret;
+
+ /* return pointer to path (if available) */
+ if (pathp)
+ *pathp = path ? path : NULL;
+
+ return ret;
+}
+
+/**
+ * overlay_phandle_add_offset - Increases a phandle by an offset
+ * @fdt: Base device tree blob
+ * @node: Device tree overlay blob
+ * @name: Name of the property to modify (phandle or linux,phandle)
+ * @delta: offset to apply
+ *
+ * overlay_phandle_add_offset() increments a node phandle by a given
+ * offset.
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int overlay_phandle_add_offset(void *fdt, int node,
+ const char *name, uint32_t delta)
+{
+ const fdt32_t *val;
+ uint32_t adj_val;
+ int len;
+
+ val = fdt_getprop(fdt, node, name, &len);
+ if (!val)
+ return len;
+
+ if (len != sizeof(*val))
+ return -FDT_ERR_BADPHANDLE;
+
+ adj_val = fdt32_to_cpu(*val);
+ if ((adj_val + delta) < adj_val)
+ return -FDT_ERR_NOPHANDLES;
+
+ adj_val += delta;
+ if (adj_val == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+
+ return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
+}
+
+/**
+ * overlay_adjust_node_phandles - Offsets the phandles of a node
+ * @fdto: Device tree overlay blob
+ * @node: Offset of the node we want to adjust
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_node_phandles() adds a constant to all the phandles
+ * of a given node. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_node_phandles(void *fdto, int node,
+ uint32_t delta)
+{
+ int child;
+ int ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ fdt_for_each_subnode(child, fdto, node) {
+ ret = overlay_adjust_node_phandles(fdto, child, delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_local_phandles() adds a constant to all the
+ * phandles of an overlay. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
+{
+ /*
+ * Start adjusting the phandles from the overlay root
+ */
+ return overlay_adjust_node_phandles(fdto, 0, delta);
+}
+
+/**
+ * overlay_update_local_node_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @tree_node: Node offset of the node to operate on
+ * @fixup_node: Node offset of the matching local fixups node
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_nodes_references() update the phandles
+ * pointing to a node within the device tree overlay by adding a
+ * constant delta.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_node_references(void *fdto,
+ int tree_node,
+ int fixup_node,
+ uint32_t delta)
+{
+ int fixup_prop;
+ int fixup_child;
+ int ret;
+
+ fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
+ const fdt32_t *fixup_val;
+ const char *tree_val;
+ const char *name;
+ int fixup_len;
+ int tree_len;
+ int i;
+
+ fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
+ &name, &fixup_len);
+ if (!fixup_val)
+ return fixup_len;
+
+ if (fixup_len % sizeof(uint32_t))
+ return -FDT_ERR_BADOVERLAY;
+ fixup_len /= sizeof(uint32_t);
+
+ tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
+ if (!tree_val) {
+ if (tree_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+
+ return tree_len;
+ }
+
+ for (i = 0; i < fixup_len; i++) {
+ fdt32_t adj_val;
+ uint32_t poffset;
+
+ poffset = fdt32_to_cpu(fixup_val[i]);
+
+ /*
+ * phandles to fixup can be unaligned.
+ *
+ * Use a memcpy for the architectures that do
+ * not support unaligned accesses.
+ */
+ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
+
+ adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
+
+ ret = fdt_setprop_inplace_namelen_partial(fdto,
+ tree_node,
+ name,
+ strlen(name),
+ poffset,
+ &adj_val,
+ sizeof(adj_val));
+ if (ret == -FDT_ERR_NOSPACE)
+ return -FDT_ERR_BADOVERLAY;
+
+ if (ret)
+ return ret;
+ }
+ }
+
+ fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
+ const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
+ NULL);
+ int tree_child;
+
+ tree_child = fdt_subnode_offset(fdto, tree_node,
+ fixup_child_name);
+ if (tree_child == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (tree_child < 0)
+ return tree_child;
+
+ ret = overlay_update_local_node_references(fdto,
+ tree_child,
+ fixup_child,
+ delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_update_local_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_references() update all the phandles pointing
+ * to a node within the device tree overlay by adding a constant
+ * delta to not conflict with the base overlay.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_references(void *fdto, uint32_t delta)
+{
+ int fixups;
+
+ fixups = fdt_path_offset(fdto, "/__local_fixups__");
+ if (fixups < 0) {
+ /* There's no local phandles to adjust, bail out */
+ if (fixups == -FDT_ERR_NOTFOUND)
+ return 0;
+
+ return fixups;
+ }
+
+ /*
+ * Update our local references from the root of the tree
+ */
+ return overlay_update_local_node_references(fdto, 0, fixups,
+ delta);
+}
+
+/**
+ * overlay_fixup_one_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @path: Path to a node holding a phandle in the overlay
+ * @path_len: number of path characters to consider
+ * @name: Name of the property holding the phandle reference in the overlay
+ * @name_len: number of name characters to consider
+ * @poffset: Offset within the overlay property where the phandle is stored
+ * @label: Label of the node referenced by the phandle
+ *
+ * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
+ * a node in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_one_phandle(void *fdt, void *fdto,
+ int symbols_off,
+ const char *path, uint32_t path_len,
+ const char *name, uint32_t name_len,
+ int poffset, const char *label)
+{
+ const char *symbol_path;
+ uint32_t phandle;
+ fdt32_t phandle_prop;
+ int symbol_off, fixup_off;
+ int prop_len;
+
+ if (symbols_off < 0)
+ return symbols_off;
+
+ symbol_path = fdt_getprop(fdt, symbols_off, label,
+ &prop_len);
+ if (!symbol_path)
+ return prop_len;
+
+ symbol_off = fdt_path_offset(fdt, symbol_path);
+ if (symbol_off < 0)
+ return symbol_off;
+
+ phandle = fdt_get_phandle(fdt, symbol_off);
+ if (!phandle)
+ return -FDT_ERR_NOTFOUND;
+
+ fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
+ if (fixup_off == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (fixup_off < 0)
+ return fixup_off;
+
+ phandle_prop = cpu_to_fdt32(phandle);
+ return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
+ name, name_len, poffset,
+ &phandle_prop,
+ sizeof(phandle_prop));
+};
+
+/**
+ * overlay_fixup_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @property: Property offset in the overlay holding the list of fixups
+ *
+ * overlay_fixup_phandle() resolves all the overlay phandles pointed
+ * to in a __fixups__ property, and updates them to match the phandles
+ * in use in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
+ int property)
+{
+ const char *value;
+ const char *label;
+ int len;
+
+ value = fdt_getprop_by_offset(fdto, property,
+ &label, &len);
+ if (!value) {
+ if (len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+
+ return len;
+ }
+
+ do {
+ const char *path, *name, *fixup_end;
+ const char *fixup_str = value;
+ uint32_t path_len, name_len;
+ uint32_t fixup_len;
+ char *sep, *endptr;
+ int poffset, ret;
+
+ fixup_end = memchr(value, '\0', len);
+ if (!fixup_end)
+ return -FDT_ERR_BADOVERLAY;
+ fixup_len = fixup_end - fixup_str;
+
+ len -= fixup_len + 1;
+ value += fixup_len + 1;
+
+ path = fixup_str;
+ sep = memchr(fixup_str, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ path_len = sep - path;
+ if (path_len == (fixup_len - 1))
+ return -FDT_ERR_BADOVERLAY;
+
+ fixup_len -= path_len + 1;
+ name = sep + 1;
+ sep = memchr(name, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ name_len = sep - name;
+ if (!name_len)
+ return -FDT_ERR_BADOVERLAY;
+
+ poffset = strtoul(sep + 1, &endptr, 10);
+ if ((*endptr != '\0') || (endptr <= (sep + 1)))
+ return -FDT_ERR_BADOVERLAY;
+
+ ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
+ path, path_len, name, name_len,
+ poffset, label);
+ if (ret)
+ return ret;
+ } while (len > 0);
+
+ return 0;
+}
+
+/**
+ * overlay_fixup_phandles - Resolve the overlay phandles to the base
+ * device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_fixup_phandles() resolves all the overlay phandles pointing
+ * to nodes in the base device tree.
+ *
+ * This is one of the steps of the device tree overlay application
+ * process, when you want all the phandles in the overlay to point to
+ * the actual base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandles(void *fdt, void *fdto)
+{
+ int fixups_off, symbols_off;
+ int property;
+
+ /* We can have overlays without any fixups */
+ fixups_off = fdt_path_offset(fdto, "/__fixups__");
+ if (fixups_off == -FDT_ERR_NOTFOUND)
+ return 0; /* nothing to do */
+ if (fixups_off < 0)
+ return fixups_off;
+
+ /* And base DTs without symbols */
+ symbols_off = fdt_path_offset(fdt, "/__symbols__");
+ if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
+ return symbols_off;
+
+ fdt_for_each_property_offset(property, fdto, fixups_off) {
+ int ret;
+
+ ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_apply_node - Merges a node into the base device tree
+ * @fdt: Base Device Tree blob
+ * @target: Node offset in the base device tree to apply the fragment to
+ * @fdto: Device tree overlay blob
+ * @node: Node offset in the overlay holding the changes to merge
+ *
+ * overlay_apply_node() merges a node into a target base device tree
+ * node pointed.
+ *
+ * This is part of the final step in the device tree overlay
+ * application process, when all the phandles have been adjusted and
+ * resolved and you just have to merge overlay into the base device
+ * tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_apply_node(void *fdt, int target,
+ void *fdto, int node)
+{
+ int property;
+ int subnode;
+
+ fdt_for_each_property_offset(property, fdto, node) {
+ const char *name;
+ const void *prop;
+ int prop_len;
+ int ret;
+
+ prop = fdt_getprop_by_offset(fdto, property, &name,
+ &prop_len);
+ if (prop_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ if (prop_len < 0)
+ return prop_len;
+
+ ret = fdt_setprop(fdt, target, name, prop, prop_len);
+ if (ret)
+ return ret;
+ }
+
+ fdt_for_each_subnode(subnode, fdto, node) {
+ const char *name = fdt_get_name(fdto, subnode, NULL);
+ int nnode;
+ int ret;
+
+ nnode = fdt_add_subnode(fdt, target, name);
+ if (nnode == -FDT_ERR_EXISTS) {
+ nnode = fdt_subnode_offset(fdt, target, name);
+ if (nnode == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ }
+
+ if (nnode < 0)
+ return nnode;
+
+ ret = overlay_apply_node(fdt, nnode, fdto, subnode);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_merge - Merge an overlay into its base device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_merge() merges an overlay into its base device tree.
+ *
+ * This is the next to last step in the device tree overlay application
+ * process, when all the phandles have been adjusted and resolved and
+ * you just have to merge overlay into the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_merge(void *fdt, void *fdto)
+{
+ int fragment;
+
+ fdt_for_each_subnode(fragment, fdto, 0) {
+ int overlay;
+ int target;
+ int ret;
+
+ /*
+ * Each fragments will have an __overlay__ node. If
+ * they don't, it's not supposed to be merged
+ */
+ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
+ if (overlay == -FDT_ERR_NOTFOUND)
+ continue;
+
+ if (overlay < 0)
+ return overlay;
+
+ target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
+ if (target < 0)
+ return target;
+
+ ret = overlay_apply_node(fdt, target, fdto, overlay);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_path_len(const void *fdt, int nodeoffset)
+{
+ int len = 0, namelen;
+ const char *name;
+
+ FDT_RO_PROBE(fdt);
+
+ for (;;) {
+ name = fdt_get_name(fdt, nodeoffset, &namelen);
+ if (!name)
+ return namelen;
+
+ /* root? we're done */
+ if (namelen == 0)
+ break;
+
+ nodeoffset = fdt_parent_offset(fdt, nodeoffset);
+ if (nodeoffset < 0)
+ return nodeoffset;
+ len += namelen + 1;
+ }
+
+ /* in case of root pretend it's "/" */
+ if (len == 0)
+ len++;
+ return len;
+}
+
+/**
+ * overlay_symbol_update - Update the symbols of base tree after a merge
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_symbol_update() updates the symbols of the base tree with the
+ * symbols of the applied overlay
+ *
+ * This is the last step in the device tree overlay application
+ * process, allowing the reference of overlay symbols by subsequent
+ * overlay operations.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_symbol_update(void *fdt, void *fdto)
+{
+ int root_sym, ov_sym, prop, path_len, fragment, target;
+ int len, frag_name_len, ret, rel_path_len;
+ const char *s, *e;
+ const char *path;
+ const char *name;
+ const char *frag_name;
+ const char *rel_path;
+ const char *target_path;
+ char *buf;
+ void *p;
+
+ ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
+
+ /* if no overlay symbols exist no problem */
+ if (ov_sym < 0)
+ return 0;
+
+ root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
+
+ /* it no root symbols exist we should create them */
+ if (root_sym == -FDT_ERR_NOTFOUND)
+ root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
+
+ /* any error is fatal now */
+ if (root_sym < 0)
+ return root_sym;
+
+ /* iterate over each overlay symbol */
+ fdt_for_each_property_offset(prop, fdto, ov_sym) {
+ path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
+ if (!path)
+ return path_len;
+
+ /* verify it's a string property (terminated by a single \0) */
+ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
+ return -FDT_ERR_BADVALUE;
+
+ /* keep end marker to avoid strlen() */
+ e = path + path_len;
+
+ if (*path != '/')
+ return -FDT_ERR_BADVALUE;
+
+ /* get fragment name first */
+ s = strchr(path + 1, '/');
+ if (!s) {
+ /* Symbol refers to something that won't end
+ * up in the target tree */
+ continue;
+ }
+
+ frag_name = path + 1;
+ frag_name_len = s - path - 1;
+
+ /* verify format; safe since "s" lies in \0 terminated prop */
+ len = sizeof("/__overlay__/") - 1;
+ if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) {
+ /* /<fragment-name>/__overlay__/<relative-subnode-path> */
+ rel_path = s + len;
+ rel_path_len = e - rel_path - 1;
+ } else if ((e - s) == len
+ && (memcmp(s, "/__overlay__", len - 1) == 0)) {
+ /* /<fragment-name>/__overlay__ */
+ rel_path = "";
+ rel_path_len = 0;
+ } else {
+ /* Symbol refers to something that won't end
+ * up in the target tree */
+ continue;
+ }
+
+ /* find the fragment index in which the symbol lies */
+ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
+ frag_name_len);
+ /* not found? */
+ if (ret < 0)
+ return -FDT_ERR_BADOVERLAY;
+ fragment = ret;
+
+ /* an __overlay__ subnode must exist */
+ ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
+ if (ret < 0)
+ return -FDT_ERR_BADOVERLAY;
+
+ /* get the target of the fragment */
+ ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
+ if (ret < 0)
+ return ret;
+ target = ret;
+
+ /* if we have a target path use */
+ if (!target_path) {
+ ret = get_path_len(fdt, target);
+ if (ret < 0)
+ return ret;
+ len = ret;
+ } else {
+ len = strlen(target_path);
+ }
+
+ ret = fdt_setprop_placeholder(fdt, root_sym, name,
+ len + (len > 1) + rel_path_len + 1, &p);
+ if (ret < 0)
+ return ret;
+
+ if (!target_path) {
+ /* again in case setprop_placeholder changed it */
+ ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
+ if (ret < 0)
+ return ret;
+ target = ret;
+ }
+
+ buf = p;
+ if (len > 1) { /* target is not root */
+ if (!target_path) {
+ ret = fdt_get_path(fdt, target, buf, len + 1);
+ if (ret < 0)
+ return ret;
+ } else
+ memcpy(buf, target_path, len + 1);
+
+ } else
+ len--;
+
+ buf[len] = '/';
+ memcpy(buf + len + 1, rel_path, rel_path_len);
+ buf[len + 1 + rel_path_len] = '\0';
+ }
+
+ return 0;
+}
+
+int fdt_overlay_apply(void *fdt, void *fdto)
+{
+ uint32_t delta;
+ int ret;
+
+ FDT_RO_PROBE(fdt);
+ FDT_RO_PROBE(fdto);
+
+ ret = fdt_find_max_phandle(fdt, &delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_adjust_local_phandles(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_update_local_references(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_fixup_phandles(fdt, fdto);
+ if (ret)
+ goto err;
+
+ ret = overlay_merge(fdt, fdto);
+ if (ret)
+ goto err;
+
+ ret = overlay_symbol_update(fdt, fdto);
+ if (ret)
+ goto err;
+
+ /*
+ * The overlay has been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ return 0;
+
+err:
+ /*
+ * The overlay might have been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ /*
+ * The base device tree might have been damaged, erase its
+ * magic.
+ */
+ fdt_set_magic(fdt, ~0);
+
+ return ret;
+}
diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c
new file mode 100644
index 0000000..9f6c551
--- /dev/null
+++ b/lib/libfdt/fdt_ro.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int fdt_nodename_eq_(const void *fdt, int offset,
+ const char *s, int len)
+{
+ int olen;
+ const char *p = fdt_get_name(fdt, offset, &olen);
+
+ if (!p || olen < len)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
+{
+ int32_t totalsize;
+ uint32_t absoffset;
+ size_t len;
+ int err;
+ const char *s, *n;
+
+ if (can_assume(VALID_INPUT)) {
+ s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+
+ if (lenp)
+ *lenp = strlen(s);
+ return s;
+ }
+ totalsize = fdt_ro_probe_(fdt);
+ err = totalsize;
+ if (totalsize < 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ absoffset = stroffset + fdt_off_dt_strings(fdt);
+ if (absoffset >= (unsigned)totalsize)
+ goto fail;
+ len = totalsize - absoffset;
+
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ if (stroffset < 0)
+ goto fail;
+ if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
+ if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
+ goto fail;
+ if ((fdt_size_dt_strings(fdt) - stroffset) < len)
+ len = fdt_size_dt_strings(fdt) - stroffset;
+ }
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ unsigned int sw_stroffset = -stroffset;
+
+ if ((stroffset >= 0) ||
+ (sw_stroffset > fdt_size_dt_strings(fdt)))
+ goto fail;
+ if (sw_stroffset < len)
+ len = sw_stroffset;
+ } else {
+ err = -FDT_ERR_INTERNAL;
+ goto fail;
+ }
+
+ s = (const char *)fdt + absoffset;
+ n = memchr(s, '\0', len);
+ if (!n) {
+ /* missing terminating NULL */
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+ }
+
+ if (lenp)
+ *lenp = n - s;
+ return s;
+
+fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return fdt_get_string(fdt, stroffset, NULL);
+}
+
+static int fdt_string_eq_(const void *fdt, int stroffset,
+ const char *s, int len)
+{
+ int slen;
+ const char *p = fdt_get_string(fdt, stroffset, &slen);
+
+ return p && (slen == len) && (memcmp(p, s, len) == 0);
+}
+
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
+{
+ uint32_t max = 0;
+ int offset = -1;
+
+ while (true) {
+ uint32_t value;
+
+ offset = fdt_next_node(fdt, offset, NULL);
+ if (offset < 0) {
+ if (offset == -FDT_ERR_NOTFOUND)
+ break;
+
+ return offset;
+ }
+
+ value = fdt_get_phandle(fdt, offset);
+
+ if (value > max)
+ max = value;
+ }
+
+ if (phandle)
+ *phandle = max;
+
+ return 0;
+}
+
+int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
+{
+ uint32_t max;
+ int err;
+
+ err = fdt_find_max_phandle(fdt, &max);
+ if (err < 0)
+ return err;
+
+ if (max == FDT_MAX_PHANDLE)
+ return -FDT_ERR_NOPHANDLES;
+
+ if (phandle)
+ *phandle = max + 1;
+
+ return 0;
+}
+
+static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
+{
+ unsigned int offset = n * sizeof(struct fdt_reserve_entry);
+ unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
+
+ if (!can_assume(VALID_INPUT)) {
+ if (absoffset < fdt_off_mem_rsvmap(fdt))
+ return NULL;
+ if (absoffset > fdt_totalsize(fdt) -
+ sizeof(struct fdt_reserve_entry))
+ return NULL;
+ }
+ return fdt_mem_rsv_(fdt, n);
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ const struct fdt_reserve_entry *re;
+
+ FDT_RO_PROBE(fdt);
+ re = fdt_mem_rsv(fdt, n);
+ if (!can_assume(VALID_INPUT) && !re)
+ return -FDT_ERR_BADOFFSET;
+
+ *address = fdt64_ld_(&re->address);
+ *size = fdt64_ld_(&re->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i;
+ const struct fdt_reserve_entry *re;
+
+ for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
+ if (fdt64_ld_(&re->size) == 0)
+ return i;
+ }
+ return -FDT_ERR_TRUNCATED;
+}
+
+static int nextprop_(const void *fdt, int offset)
+{
+ uint32_t tag;
+ int nextoffset;
+
+ do {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ if (nextoffset >= 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ else
+ return nextoffset;
+
+ case FDT_PROP:
+ return offset;
+ }
+ offset = nextoffset;
+ } while (tag == FDT_NOP);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ FDT_RO_PROBE(fdt);
+
+ for (depth = 0;
+ (offset >= 0) && (depth >= 0);
+ offset = fdt_next_node(fdt, offset, &depth))
+ if ((depth == 1)
+ && fdt_nodename_eq_(fdt, offset, name, namelen))
+ return offset;
+
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ return offset; /* error */
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
+{
+ const char *end = path + namelen;
+ const char *p = path;
+ int offset = 0;
+
+ FDT_RO_PROBE(fdt);
+
+ /* see if we have an alias */
+ if (*path != '/') {
+ const char *q = memchr(path, '/', end - p);
+
+ if (!q)
+ q = end;
+
+ p = fdt_get_alias_namelen(fdt, p, q - p);
+ if (!p)
+ return -FDT_ERR_BADPATH;
+ offset = fdt_path_offset(fdt, p);
+
+ p = q;
+ }
+
+ while (p < end) {
+ const char *q;
+
+ while (*p == '/') {
+ p++;
+ if (p == end)
+ return offset;
+ }
+ q = memchr(p, '/', end - p);
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
+ const char *nameptr;
+ int err;
+
+ if (((err = fdt_ro_probe_(fdt)) < 0)
+ || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
+ goto fail;
+
+ nameptr = nh->name;
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ /*
+ * For old FDT versions, match the naming conventions of V16:
+ * give only the leaf name (after all /). The actual tree
+ * contents are loosely checked.
+ */
+ const char *leaf;
+ leaf = strrchr(nameptr, '/');
+ if (leaf == NULL) {
+ err = -FDT_ERR_BADSTRUCTURE;
+ goto fail;
+ }
+ nameptr = leaf+1;
+ }
+
+ if (len)
+ *len = strlen(nameptr);
+
+ return nameptr;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+int fdt_first_property_offset(const void *fdt, int nodeoffset)
+{
+ int offset;
+
+ if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+ return offset;
+
+ return nextprop_(fdt, offset);
+}
+
+int fdt_next_property_offset(const void *fdt, int offset)
+{
+ if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
+ return offset;
+
+ return nextprop_(fdt, offset);
+}
+
+static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ int err;
+ const struct fdt_property *prop;
+
+ if (!can_assume(VALID_INPUT) &&
+ (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
+ if (lenp)
+ *lenp = err;
+ return NULL;
+ }
+
+ prop = fdt_offset_ptr_(fdt, offset);
+
+ if (lenp)
+ *lenp = fdt32_ld_(&prop->len);
+
+ return prop;
+}
+
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ /* Prior to version 16, properties may need realignment
+ * and this API does not work. fdt_getprop_*() will, however. */
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVERSION;
+ return NULL;
+ }
+
+ return fdt_get_property_by_offset_(fdt, offset, lenp);
+}
+
+static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen,
+ int *lenp,
+ int *poffset)
+{
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset_(fdt, offset, lenp);
+ if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+ if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
+ name, namelen)) {
+ if (poffset)
+ *poffset = offset;
+ return prop;
+ }
+ }
+
+ if (lenp)
+ *lenp = offset;
+ return NULL;
+}
+
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen, int *lenp)
+{
+ /* Prior to version 16, properties may need realignment
+ * and this API does not work. fdt_getprop_*() will, however. */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVERSION;
+ return NULL;
+ }
+
+ return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
+ NULL);
+}
+
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_get_property_namelen(fdt, nodeoffset, name,
+ strlen(name), lenp);
+}
+
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp)
+{
+ int poffset;
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
+ &poffset);
+ if (!prop)
+ return NULL;
+
+ /* Handle realignment */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
+ (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
+ return prop->data + 4;
+ return prop->data;
+}
+
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset_(fdt, offset, lenp);
+ if (!prop)
+ return NULL;
+ if (namep) {
+ const char *name;
+ int namelen;
+
+ if (!can_assume(VALID_INPUT)) {
+ name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
+ &namelen);
+ *namep = name;
+ if (!name) {
+ if (lenp)
+ *lenp = namelen;
+ return NULL;
+ }
+ } else {
+ *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
+ }
+ }
+
+ /* Handle realignment */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
+ (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
+ return prop->data + 4;
+ return prop->data;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *php;
+ int len;
+
+ /* FIXME: This is a bit sub-optimal, since we potentially scan
+ * over all the properties twice. */
+ php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
+ if (!php || (len != sizeof(*php))) {
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+ }
+
+ return fdt32_ld_(php);
+}
+
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen)
+{
+ int aliasoffset;
+
+ aliasoffset = fdt_path_offset(fdt, "/aliases");
+ if (aliasoffset < 0)
+ return NULL;
+
+ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
+}
+
+const char *fdt_get_alias(const void *fdt, const char *name)
+{
+ return fdt_get_alias_namelen(fdt, name, strlen(name));
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ FDT_RO_PROBE(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ if (pdepth >= depth) {
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return 0;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ FDT_RO_PROBE(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if (!can_assume(VALID_INPUT)) {
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
+ -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ int offset;
+
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we
+ * potentially scan each property of a node in
+ * fdt_get_phandle(), then if that didn't find what
+ * we want, we scan over them again making our way to the next
+ * node. Still it's the easiest to implement approach;
+ * performance can come later. */
+ for (offset = fdt_next_node(fdt, -1, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ if (fdt_get_phandle(fdt, offset) == phandle)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const char *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
+{
+ const char *list, *end;
+ int length, count = 0;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ list += length;
+ count++;
+ }
+
+ return count;
+}
+
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string)
+{
+ int length, len, idx = 0;
+ const char *list, *end;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ len = strlen(string) + 1;
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ if (length == len && memcmp(list, string, length) == 0)
+ return idx;
+
+ list += length;
+ idx++;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int idx,
+ int *lenp)
+{
+ const char *list, *end;
+ int length;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list) {
+ if (lenp)
+ *lenp = length;
+
+ return NULL;
+ }
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVALUE;
+
+ return NULL;
+ }
+
+ if (idx == 0) {
+ if (lenp)
+ *lenp = length - 1;
+
+ return list;
+ }
+
+ list += length;
+ idx--;
+ }
+
+ if (lenp)
+ *lenp = -FDT_ERR_NOTFOUND;
+
+ return NULL;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+
+ return !fdt_stringlist_contains(prop, len, compatible);
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/lib/libfdt/fdt_rw.c b/lib/libfdt/fdt_rw.c
new file mode 100644
index 0000000..3621d36
--- /dev/null
+++ b/lib/libfdt/fdt_rw.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int fdt_blocks_misordered_(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int fdt_rw_probe_(void *fdt)
+{
+ if (can_assume(VALID_DTB))
+ return 0;
+ FDT_RO_PROBE(fdt);
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (!can_assume(LATEST) && fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define FDT_RW_PROBE(fdt) \
+ { \
+ int err_; \
+ if ((err_ = fdt_rw_probe_(fdt)) != 0) \
+ return err_; \
+ }
+
+static inline unsigned int fdt_data_size_(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
+{
+ char *p = splicepoint;
+ unsigned int dsize = fdt_data_size_(fdt);
+ size_t soff = p - (char *)fdt;
+
+ if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
+ return -FDT_ERR_BADOFFSET;
+ if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
+ return -FDT_ERR_BADOFFSET;
+ if (dsize - oldlen + newlen > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
+ return 0;
+}
+
+static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int fdt_splice_struct_(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+/* Must only be used to roll back in case of error */
+static void fdt_del_last_string_(void *fdt, const char *s)
+{
+ int newlen = strlen(s) + 1;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
+}
+
+static int fdt_splice_string_(void *fdt, int newlen)
+{
+ void *p = (char *)fdt
+ + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = fdt_splice_(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+/**
+ * fdt_find_add_string_() - Find or allocate a string
+ *
+ * @fdt: pointer to the device tree to check/adjust
+ * @s: string to find/add
+ * @allocated: Set to 0 if the string was found, 1 if not found and so
+ * allocated. Ignored if can_assume(NO_ROLLBACK)
+ * @return offset of string in the string table (whether found or added)
+ */
+static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ if (!can_assume(NO_ROLLBACK))
+ *allocated = 0;
+
+ p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = fdt_splice_string_(fdt, len);
+ if (err)
+ return err;
+
+ if (!can_assume(NO_ROLLBACK))
+ *allocated = 1;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
+ err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
+
+ FDT_RW_PROBE(fdt);
+
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ return fdt_splice_mem_rsv_(fdt, re, 1, 0);
+}
+
+static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (!*prop)
+ return oldlen;
+
+ if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(len))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+ int allocated;
+
+ if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+ return nextoffset;
+
+ namestroff = fdt_find_add_string_(fdt, name, &allocated);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = fdt_offset_ptr_w_(fdt, nextoffset);
+ proplen = sizeof(**prop) + FDT_TAGALIGN(len);
+
+ err = fdt_splice_struct_(fdt, *prop, 0, proplen);
+ if (err) {
+ /* Delete the string if we failed to add it */
+ if (!can_assume(NO_ROLLBACK) && allocated)
+ fdt_del_last_string_(fdt, name);
+ return err;
+ }
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
+ FDT_TAGALIGN(newlen+1));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
+ int len, void **prop_data)
+{
+ struct fdt_property *prop;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ *prop_data = prop->data;
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ void *prop_data;
+ int err;
+
+ err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
+ if (err)
+ return err;
+
+ if (len)
+ memcpy(prop_data, val, len);
+ return 0;
+}
+
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err, oldlen, newlen;
+
+ FDT_RW_PROBE(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (prop) {
+ newlen = len + oldlen;
+ err = fdt_splice_struct_(fdt, prop->data,
+ FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(newlen));
+ if (err)
+ return err;
+ prop->len = cpu_to_fdt32(newlen);
+ memcpy(prop->data + oldlen, val, len);
+ } else {
+ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+ memcpy(prop->data, val, len);
+ }
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ FDT_RW_PROBE(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (!prop)
+ return len;
+
+ proplen = sizeof(*prop) + FDT_TAGALIGN(len);
+ return fdt_splice_struct_(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ fdt32_t *endtag;
+
+ FDT_RW_PROBE(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
+ /* the fdt_subnode_offset_namelen() should ensure this never hits */
+ if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
+ return -FDT_ERR_INTERNAL;
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while ((tag == FDT_PROP) || (tag == FDT_NOP));
+
+ nh = fdt_offset_ptr_w_(fdt, offset);
+ nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
+
+ err = fdt_splice_struct_(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
+ memcpy(nh->name, name, namelen);
+ endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ FDT_RW_PROBE(fdt);
+
+ endoffset = fdt_node_end_offset_(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void fdt_packblocks_(const char *old, char *new,
+ int mem_rsv_size,
+ int struct_size,
+ int strings_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(new, mem_rsv_off);
+
+ memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
+ fdt_set_off_dt_struct(new, struct_off);
+ fdt_set_size_dt_struct(new, struct_size);
+
+ memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
+ fdt_set_off_dt_strings(new, strings_off);
+ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ const char *fdtstart = fdt;
+ const char *fdtend = fdtstart + fdt_totalsize(fdt);
+ char *tmp;
+
+ FDT_RO_PROBE(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else if (fdt_version(fdt) == 16) {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ if (struct_size < 0)
+ return struct_size;
+ } else {
+ return -FDT_ERR_BADVERSION;
+ }
+
+ if (can_assume(LIBFDT_ORDER) ||
+ !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ /* First attempt to build converted tree at beginning of buffer */
+ tmp = buf;
+ /* But if that overlaps with the old tree... */
+ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
+ /* Try right after the old tree instead */
+ tmp = (char *)(uintptr_t)fdtend;
+ if ((tmp + newsize) > ((char *)buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
+ fdt_size_dt_strings(fdt));
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+
+ FDT_RW_PROBE(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
+ fdt_size_dt_strings(fdt));
+ fdt_set_totalsize(fdt, fdt_data_size_(fdt));
+
+ return 0;
+}
diff --git a/lib/libfdt/fdt_strerror.c b/lib/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..d852b77
--- /dev/null
+++ b/lib/libfdt/fdt_strerror.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+struct fdt_errtabent {
+ const char *str;
+};
+
+#define FDT_ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct fdt_errtabent fdt_errtable[] = {
+ FDT_ERRTABENT(FDT_ERR_NOTFOUND),
+ FDT_ERRTABENT(FDT_ERR_EXISTS),
+ FDT_ERRTABENT(FDT_ERR_NOSPACE),
+
+ FDT_ERRTABENT(FDT_ERR_BADOFFSET),
+ FDT_ERRTABENT(FDT_ERR_BADPATH),
+ FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
+ FDT_ERRTABENT(FDT_ERR_BADSTATE),
+
+ FDT_ERRTABENT(FDT_ERR_TRUNCATED),
+ FDT_ERRTABENT(FDT_ERR_BADMAGIC),
+ FDT_ERRTABENT(FDT_ERR_BADVERSION),
+ FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
+ FDT_ERRTABENT(FDT_ERR_INTERNAL),
+ FDT_ERRTABENT(FDT_ERR_BADNCELLS),
+ FDT_ERRTABENT(FDT_ERR_BADVALUE),
+ FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
+ FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
+ FDT_ERRTABENT(FDT_ERR_BADFLAGS),
+ FDT_ERRTABENT(FDT_ERR_ALIGNMENT),
+};
+#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (-errval < FDT_ERRTABSIZE) {
+ const char *s = fdt_errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/lib/libfdt/fdt_sw.c b/lib/libfdt/fdt_sw.c
new file mode 100644
index 0000000..4c569ee
--- /dev/null
+++ b/lib/libfdt/fdt_sw.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int fdt_sw_probe_(void *fdt)
+{
+ if (!can_assume(VALID_INPUT)) {
+ if (fdt_magic(fdt) == FDT_MAGIC)
+ return -FDT_ERR_BADSTATE;
+ else if (fdt_magic(fdt) != FDT_SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+#define FDT_SW_PROBE(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_(fdt)) != 0) \
+ return err; \
+ }
+
+/* 'memrsv' state: Initial state after fdt_create()
+ *
+ * Allowed functions:
+ * fdt_add_reservemap_entry()
+ * fdt_finish_reservemap() [moves to 'struct' state]
+ */
+static int fdt_sw_probe_memrsv_(void *fdt)
+{
+ int err = fdt_sw_probe_(fdt);
+ if (err)
+ return err;
+
+ if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
+ return -FDT_ERR_BADSTATE;
+ return 0;
+}
+
+#define FDT_SW_PROBE_MEMRSV(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
+ return err; \
+ }
+
+/* 'struct' state: Enter this state after fdt_finish_reservemap()
+ *
+ * Allowed functions:
+ * fdt_begin_node()
+ * fdt_end_node()
+ * fdt_property*()
+ * fdt_finish() [moves to 'complete' state]
+ */
+static int fdt_sw_probe_struct_(void *fdt)
+{
+ int err = fdt_sw_probe_(fdt);
+ if (err)
+ return err;
+
+ if (!can_assume(VALID_INPUT) &&
+ fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
+ return -FDT_ERR_BADSTATE;
+ return 0;
+}
+
+#define FDT_SW_PROBE_STRUCT(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
+ return err; \
+ }
+
+static inline uint32_t sw_flags(void *fdt)
+{
+ /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
+ return fdt_last_comp_version(fdt);
+}
+
+/* 'complete' state: Enter this state after fdt_finish()
+ *
+ * Allowed functions: none
+ */
+
+static void *fdt_grab_space_(void *fdt, size_t len)
+{
+ unsigned int offset = fdt_size_dt_struct(fdt);
+ unsigned int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return fdt_offset_ptr_w_(fdt, offset);
+}
+
+int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
+{
+ const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry));
+ void *fdt = buf;
+
+ if (bufsize < hdrsize)
+ return -FDT_ERR_NOSPACE;
+
+ if (flags & ~FDT_CREATE_FLAGS_ALL)
+ return -FDT_ERR_BADFLAGS;
+
+ memset(buf, 0, bufsize);
+
+ /*
+ * magic and last_comp_version keep intermediate state during the fdt
+ * creation process, which is replaced with the proper FDT format by
+ * fdt_finish().
+ *
+ * flags should be accessed with sw_flags().
+ */
+ fdt_set_magic(fdt, FDT_SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, flags);
+
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, hdrsize);
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, 0);
+
+ return 0;
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ return fdt_create_with_flags(buf, bufsize, 0);
+}
+
+int fdt_resize(void *fdt, void *buf, int bufsize)
+{
+ size_t headsize, tailsize;
+ char *oldtail, *newtail;
+
+ FDT_SW_PROBE(fdt);
+
+ if (bufsize < 0)
+ return -FDT_ERR_NOSPACE;
+
+ headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ tailsize = fdt_size_dt_strings(fdt);
+
+ if (!can_assume(VALID_DTB) &&
+ headsize + tailsize > fdt_totalsize(fdt))
+ return -FDT_ERR_INTERNAL;
+
+ if ((headsize + tailsize) > (unsigned)bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
+ newtail = (char *)buf + bufsize - tailsize;
+
+ /* Two cases to avoid clobbering data if the old and new
+ * buffers partially overlap */
+ if (buf <= fdt) {
+ memmove(buf, fdt, headsize);
+ memmove(newtail, oldtail, tailsize);
+ } else {
+ memmove(newtail, oldtail, tailsize);
+ memmove(buf, fdt, headsize);
+ }
+
+ fdt_set_totalsize(buf, bufsize);
+ if (fdt_off_dt_strings(buf))
+ fdt_set_off_dt_strings(buf, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int offset;
+
+ FDT_SW_PROBE_MEMRSV(fdt);
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)((char *)fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ int err = fdt_add_reservemap_entry(fdt, 0, 0);
+
+ if (err)
+ return err;
+
+ fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
+ return 0;
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int namelen;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ namelen = strlen(name) + 1;
+ nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ fdt32_t *en;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ en = fdt_grab_space_(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int fdt_add_string_(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ unsigned int strtabsize = fdt_size_dt_strings(fdt);
+ unsigned int len = strlen(s) + 1;
+ unsigned int struct_top, offset;
+
+ offset = strtabsize + len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) - offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab - offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return -offset;
+}
+
+/* Must only be used to roll back in case of error */
+static void fdt_del_last_string_(void *fdt, const char *s)
+{
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+
+ fdt_set_size_dt_strings(fdt, strtabsize - len);
+}
+
+static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ int strtabsize = fdt_size_dt_strings(fdt);
+ const char *p;
+
+ *allocated = 0;
+
+ p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ *allocated = 1;
+
+ return fdt_add_string_(fdt, s);
+}
+
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
+{
+ struct fdt_property *prop;
+ int nameoff;
+ int allocated;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
+ if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
+ allocated = 1;
+ nameoff = fdt_add_string_(fdt, name);
+ } else {
+ nameoff = fdt_find_add_string_(fdt, name, &allocated);
+ }
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
+ if (! prop) {
+ if (allocated)
+ fdt_del_last_string_(fdt, name);
+ return -FDT_ERR_NOSPACE;
+ }
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ *valp = prop->data;
+ return 0;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ void *ptr;
+ int ret;
+
+ ret = fdt_property_placeholder(fdt, name, len, &ptr);
+ if (ret)
+ return ret;
+ memcpy(ptr, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ char *p = (char *)fdt;
+ fdt32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ /* Add terminator */
+ end = fdt_grab_space_(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ fdt_offset_ptr_w_(fdt, offset);
+ int nameoff;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+ if (nextoffset < 0)
+ return nextoffset;
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+
+ /* And fix up fields that were keeping intermediate state. */
+ fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
+ fdt_set_magic(fdt, FDT_MAGIC);
+
+ return 0;
+}
diff --git a/lib/libfdt/fdt_wip.c b/lib/libfdt/fdt_wip.c
new file mode 100644
index 0000000..c2d7566
--- /dev/null
+++ b/lib/libfdt/fdt_wip.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
+ &proplen);
+ if (!propval)
+ return proplen;
+
+ if ((unsigned)proplen < (len + idx))
+ return -FDT_ERR_NOSPACE;
+
+ memcpy((char *)propval + idx, val, len);
+ return 0;
+}
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ const void *propval;
+ int proplen;
+
+ propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
+ if (!propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
+ strlen(name), 0,
+ val, len);
+}
+
+static void fdt_nop_region_(void *start, int len)
+{
+ fdt32_t *p;
+
+ for (p = start; (char *)p < ((char *)start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (!prop)
+ return len;
+
+ fdt_nop_region_(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int fdt_node_end_offset_(void *fdt, int offset)
+{
+ int depth = 0;
+
+ while ((offset >= 0) && (depth >= 0))
+ offset = fdt_next_node(fdt, offset, &depth);
+
+ return offset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = fdt_node_end_offset_(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0),
+ endoffset - nodeoffset);
+ return 0;
+}
diff --git a/lib/libfdt/libfdt.mk b/lib/libfdt/libfdt.mk
new file mode 100644
index 0000000..1cbbd78
--- /dev/null
+++ b/lib/libfdt/libfdt.mk
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+LIBFDT_SRCS := $(addprefix lib/libfdt/, \
+ fdt.c \
+ fdt_addresses.c \
+ fdt_empty_tree.c \
+ fdt_ro.c \
+ fdt_rw.c \
+ fdt_strerror.c \
+ fdt_sw.c \
+ fdt_wip.c) \
+
+INCLUDES += -Iinclude/lib/libfdt
+
+$(eval $(call MAKE_LIB,fdt))
diff --git a/lib/libfdt/libfdt_internal.h b/lib/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..16bda19
--- /dev/null
+++ b/lib/libfdt/libfdt_internal.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_INTERNAL_H
+#define LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <fdt.h>
+
+#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+
+int32_t fdt_ro_probe_(const void *fdt);
+#define FDT_RO_PROBE(fdt) \
+ { \
+ int32_t totalsize_; \
+ if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
+ return totalsize_; \
+ }
+
+int fdt_check_node_offset_(const void *fdt, int offset);
+int fdt_check_prop_offset_(const void *fdt, int offset);
+const char *fdt_find_string_(const char *strtab, int tabsize, const char *s);
+int fdt_node_end_offset_(void *fdt, int nodeoffset);
+
+static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
+{
+ return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *fdt_offset_ptr_w_(void *fdt, int offset)
+{
+ return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ (const struct fdt_reserve_entry *)
+ ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
+{
+ return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n);
+}
+
+/*
+ * Internal helpers to access tructural elements of the device tree
+ * blob (rather than for exaple reading integers from within property
+ * values). We assume that we are either given a naturally aligned
+ * address for the platform or if we are not, we are on a platform
+ * where unaligned memory reads will be handled in a graceful manner.
+ * If not the external helpers fdtXX_ld() from libfdt.h can be used
+ * instead.
+ */
+static inline uint32_t fdt32_ld_(const fdt32_t *p)
+{
+ return fdt32_to_cpu(*p);
+}
+
+static inline uint64_t fdt64_ld_(const fdt64_t *p)
+{
+ return fdt64_to_cpu(*p);
+}
+
+#define FDT_SW_MAGIC (~FDT_MAGIC)
+
+/**********************************************************************/
+/* Checking controls */
+/**********************************************************************/
+
+#ifndef FDT_ASSUME_MASK
+#define FDT_ASSUME_MASK 0
+#endif
+
+/*
+ * Defines assumptions which can be enabled. Each of these can be enabled
+ * individually. For maximum safety, don't enable any assumptions!
+ *
+ * For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
+ * You should have another method of validating the device tree, such as a
+ * signature or hash check before using libfdt.
+ *
+ * For situations where security is not a concern it may be safe to enable
+ * ASSUME_SANE.
+ */
+enum {
+ /*
+ * This does essentially no checks. Only the latest device-tree
+ * version is correctly handled. Inconsistencies or errors in the device
+ * tree may cause undefined behaviour or crashes. Invalid parameters
+ * passed to libfdt may do the same.
+ *
+ * If an error occurs when modifying the tree it may leave the tree in
+ * an intermediate (but valid) state. As an example, adding a property
+ * where there is insufficient space may result in the property name
+ * being added to the string table even though the property itself is
+ * not added to the struct section.
+ *
+ * Only use this if you have a fully validated device tree with
+ * the latest supported version and wish to minimise code size.
+ */
+ ASSUME_PERFECT = 0xff,
+
+ /*
+ * This assumes that the device tree is sane. i.e. header metadata
+ * and basic hierarchy are correct.
+ *
+ * With this assumption enabled, normal device trees produced by libfdt
+ * and the compiler should be handled safely. Malicious device trees and
+ * complete garbage may cause libfdt to behave badly or crash. Truncated
+ * device trees (e.g. those only partially loaded) can also cause
+ * problems.
+ *
+ * Note: Only checks that relate exclusively to the device tree itself
+ * (not the parameters passed to libfdt) are disabled by this
+ * assumption. This includes checking headers, tags and the like.
+ */
+ ASSUME_VALID_DTB = 1 << 0,
+
+ /*
+ * This builds on ASSUME_VALID_DTB and further assumes that libfdt
+ * functions are called with valid parameters, i.e. not trigger
+ * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any
+ * extensive checking of parameters and the device tree, making various
+ * assumptions about correctness.
+ *
+ * It doesn't make sense to enable this assumption unless
+ * ASSUME_VALID_DTB is also enabled.
+ */
+ ASSUME_VALID_INPUT = 1 << 1,
+
+ /*
+ * This disables checks for device-tree version and removes all code
+ * which handles older versions.
+ *
+ * Only enable this if you know you have a device tree with the latest
+ * version.
+ */
+ ASSUME_LATEST = 1 << 2,
+
+ /*
+ * This assumes that it is OK for a failed addition to the device tree,
+ * due to lack of space or some other problem, to skip any rollback
+ * steps (such as dropping the property name from the string table).
+ * This is safe to enable in most circumstances, even though it may
+ * leave the tree in a sub-optimal state.
+ */
+ ASSUME_NO_ROLLBACK = 1 << 3,
+
+ /*
+ * This assumes that the device tree components appear in a 'convenient'
+ * order, i.e. the memory reservation block first, then the structure
+ * block and finally the string block.
+ *
+ * This order is not specified by the device-tree specification,
+ * but is expected by libfdt. The device-tree compiler always created
+ * device trees with this order.
+ *
+ * This assumption disables a check in fdt_open_into() and removes the
+ * ability to fix the problem there. This is safe if you know that the
+ * device tree is correctly ordered. See fdt_blocks_misordered_().
+ */
+ ASSUME_LIBFDT_ORDER = 1 << 4,
+
+ /*
+ * This assumes that libfdt itself does not have any internal bugs. It
+ * drops certain checks that should never be needed unless libfdt has an
+ * undiscovered bug.
+ *
+ * This can generally be considered safe to enable.
+ */
+ ASSUME_LIBFDT_FLAWLESS = 1 << 5,
+};
+
+/**
+ * can_assume_() - check if a particular assumption is enabled
+ *
+ * @mask: Mask to check (ASSUME_...)
+ * @return true if that assumption is enabled, else false
+ */
+static inline bool can_assume_(int mask)
+{
+ return FDT_ASSUME_MASK & mask;
+}
+
+/** helper macros for checking assumptions */
+#define can_assume(_assume) can_assume_(ASSUME_ ## _assume)
+
+#endif /* LIBFDT_INTERNAL_H */