Import DTC v1.5.1

Device Tree Compiler is a toolchain for working with device tree files.
It also includes libfdt that can be linked to Hafnium for DT parsing.

Change-Id: Ib6745968baed97aa6d8ffbd7670f80701cb582c4
diff --git a/BSD-2-Clause b/BSD-2-Clause
new file mode 100644
index 0000000..da366e2
--- /dev/null
+++ b/BSD-2-Clause
@@ -0,0 +1,32 @@
+Valid-License-Identifier: BSD-2-Clause
+SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
+Usage-Guide:
+  To use the BSD 2-clause "Simplified" License put the following SPDX
+  tag/value pair into a comment according to the placement guidelines in
+  the licensing rules documentation:
+    SPDX-License-Identifier: BSD-2-Clause
+License-Text:
+
+Copyright (c) <year> <owner> . All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Documentation/dt-object-internal.txt b/Documentation/dt-object-internal.txt
new file mode 100644
index 0000000..51d68ab
--- /dev/null
+++ b/Documentation/dt-object-internal.txt
@@ -0,0 +1,310 @@
+Device Tree Dynamic Object format internals
+-------------------------------------------
+
+The Device Tree for most platforms is a static representation of
+the hardware capabilities. This is insufficient for platforms
+that need to dynamically insert Device Tree fragments into the
+live tree.
+
+This document explains the the Device Tree object format and
+modifications made to the Device Tree compiler, which make it possible.
+
+1. Simplified Problem Definition
+--------------------------------
+
+Assume we have a platform which boots using following simplified Device Tree.
+
+---- foo.dts -----------------------------------------------------------------
+	/* FOO platform */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated */
+			peripheral1 { ... };
+		};
+	};
+---- foo.dts -----------------------------------------------------------------
+
+We have a number of peripherals that after probing (using some undefined method)
+should result in different Device Tree configuration.
+
+We cannot boot with this static tree because due to the configuration of the
+foo platform there exist multiple conficting peripherals DT fragments.
+
+So for the bar peripheral we would have this:
+
+---- foo+bar.dts -------------------------------------------------------------
+	/* FOO platform + bar peripheral */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated */
+			peripheral1 { ... };
+
+			/* bar peripheral */
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and child nodes */
+			};
+		};
+	};
+---- foo+bar.dts -------------------------------------------------------------
+
+While for the baz peripheral we would have this:
+
+---- foo+baz.dts -------------------------------------------------------------
+	/* FOO platform + baz peripheral */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+			/* baz resources */
+			baz_res: res_baz { ... };
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated */
+			peripheral1 { ... };
+
+			/* baz peripheral */
+			baz {
+				compatible = "corp,baz";
+				/* reference to another point in the tree */
+				ref-to-res = <&baz_res>;
+				... /* various properties and child nodes */
+			};
+		};
+	};
+---- foo+baz.dts -------------------------------------------------------------
+
+We note that the baz case is more complicated, since the baz peripheral needs to
+reference another node in the DT tree.
+
+2. Device Tree Object Format Requirements
+-----------------------------------------
+
+Since the Device Tree is used for booting a number of very different hardware
+platforms it is imperative that we tread very carefully.
+
+2.a) No changes to the Device Tree binary format for the base tree. We cannot
+modify the tree format at all and all the information we require should be
+encoded using Device Tree itself. We can add nodes that can be safely ignored
+by both bootloaders and the kernel. The plugin dtbs are optionally tagged
+with a different magic number in the header but otherwise they're simple
+blobs.
+
+2.b) Changes to the DTS source format should be absolutely minimal, and should
+only be needed for the DT fragment definitions, and not the base boot DT.
+
+2.c) An explicit option should be used to instruct DTC to generate the required
+information needed for object resolution. Platforms that don't use the
+dynamic object format can safely ignore it.
+
+2.d) Finally, DT syntax changes should be kept to a minimum. It should be
+possible to express everything using the existing DT syntax.
+
+3. Implementation
+-----------------
+
+The basic unit of addressing in Device Tree is the phandle. Turns out it's
+relatively simple to extend the way phandles are generated and referenced
+so that it's possible to dynamically convert symbolic references (labels)
+to phandle values. This is a valid assumption as long as the author uses
+reference syntax and does not assign phandle values manually (which might
+be a problem with decompiled source files).
+
+We can roughly divide the operation into two steps.
+
+3.a) Compilation of the base board DTS file using the '-@' option
+generates a valid DT blob with an added __symbols__ node at the root node,
+containing a list of all nodes that are marked with a label.
+
+Using the foo.dts file above the following node will be generated;
+
+$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
+$ fdtdump foo.dtb
+...
+/ {
+	...
+	res {
+		...
+		phandle = <0x00000001>;
+		...
+	};
+	ocp {
+		...
+		phandle = <0x00000002>;
+		...
+	};
+	__symbols__ {
+		res="/res";
+		ocp="/ocp";
+	};
+};
+
+Notice that all the nodes that had a label have been recorded, and that
+phandles have been generated for them.
+
+This blob can be used to boot the board normally, the __symbols__ node will
+be safely ignored both by the bootloader and the kernel (the only loss will
+be a few bytes of memory and disk space).
+
+We generate a __symbols__ node to record nodes that had labels in the base
+tree (or subsequent loaded overlays) so that they can be matched up with
+references made to them in Device Tree objects.
+
+3.b) The Device Tree fragments must be compiled with the same option but they
+must also have a tag (/plugin/) that allows undefined references to nodes
+that are not present at compilation time to be recorded so that the runtime
+loader can fix them.
+
+So the bar peripheral's DTS format would be of the form:
+
+/dts-v1/;
+/plugin/;	/* allow undefined references and record them */
+/ {
+	....	/* various properties for loader use; i.e. part id etc. */
+	fragment@0 {
+		target = <&ocp>;
+		__overlay__ {
+			/* bar peripheral */
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and child nodes */
+			}
+		};
+	};
+};
+
+Note that there's a target property that specifies the location where the
+contents of the overlay node will be placed, and it references the node
+in the foo.dts file.
+
+$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
+$ fdtdump bar.dtbo
+...
+/ {
+	... /* properties */
+	fragment@0 {
+		target = <0xffffffff>;
+		__overlay__ {
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and child nodes */
+			}
+		};
+	};
+	__fixups__ {
+	    ocp = "/fragment@0:target:0";
+	};
+};
+
+No __symbols__ node has been generated (no label in bar.dts).
+Note that the target's ocp label is undefined, so the phandle
+value is filled with the illegal value '0xffffffff', while a __fixups__
+node has been generated, which marks the location in the tree where
+the label lookup should store the runtime phandle value of the ocp node.
+
+The format of the __fixups__ node entry is
+
+  <label> = "<local-full-path>:<property-name>:<offset>" 
+	    [, "<local-full-path>:<property-name>:<offset>"...];
+
+  <label> 		Is the label we're referring
+  <local-full-path>	Is the full path of the node the reference is
+  <property-name>	Is the name of the property containing the
+			reference
+  <offset>		The offset (in bytes) of where the property's
+			phandle value is located.
+
+Doing the same with the baz peripheral's DTS format is a little bit more
+involved, since baz contains references to local labels which require
+local fixups.
+
+/dts-v1/;
+/plugin/;	/* allow undefined label references and record them */
+/ {
+	....	/* various properties for loader use; i.e. part id etc. */
+	fragment@0 {
+		target = <&res>;
+		__overlay__ {
+			/* baz resources */
+			baz_res: res_baz { ... };
+		};
+	};
+	fragment@1 {
+		target = <&ocp>;
+		__overlay__ {
+			/* baz peripheral */
+			baz {
+				compatible = "corp,baz";
+				/* reference to another point in the tree */
+				ref-to-res = <&baz_res>;
+				... /* various properties and child nodes */
+			}
+		};
+	};
+};
+
+Note that &bar_res reference.
+
+$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
+$ fdtdump baz.dtbo
+...
+/ {
+	... /* properties */
+	fragment@0 {
+		target = <0xffffffff>;
+		__overlay__ {
+			res_baz {
+				....
+				phandle = <0x00000001>;
+			};
+		};
+	};
+	fragment@1 {
+		target = <0xffffffff>;
+		__overlay__ {
+			baz {
+				compatible = "corp,baz";
+				... /* various properties and child nodes */
+				ref-to-res = <0x00000001>;
+			}
+		};
+	};
+	__fixups__ {
+		res = "/fragment@0:target:0";
+		ocp = "/fragment@1:target:0";
+	};
+	__local_fixups__ {
+		fragment@1 {
+			__overlay__ {
+				baz {
+					ref-to-res = <0>;
+				};
+			};
+		};
+	};
+};
+
+This is similar to the bar case, but the reference of a local label by the
+baz node generates a __local_fixups__ entry that records the place that the
+local reference is being made. No matter how phandles are allocated from dtc
+the run time loader must apply an offset to each phandle in every dynamic
+DT object loaded. The __local_fixups__ node records the offset relative to the
+start of every local reference within that property so that the loader can apply
+the offset.
diff --git a/Documentation/dtc-paper.bib b/Documentation/dtc-paper.bib
new file mode 100644
index 0000000..d01e2ff
--- /dev/null
+++ b/Documentation/dtc-paper.bib
@@ -0,0 +1,43 @@
+@STRING{pub-IEEE = "IEEE Computer Society"}
+@STRING{pub-IEEE:adr = "345 E. 47th St, New York, NY 10017, USA"}
+
+@BOOK{IEEE1275,
+	key = "IEEE1275",
+	title = "{IEEE} {S}tandard for {B}oot ({I}nitialization {C}onfiguration) {F}irmware: {C}ore {R}equirements and {P}ractices",
+	publisher =    pub-IEEE,
+	address =      pub-IEEE:adr,
+	series = "IEEE Std 1275-1994",
+	year = 1994,
+}
+
+@BOOK{IEEE1275-pci,
+	key = "IEEE1275-pci",
+	title = "{PCI} {B}us {B}inding to: {IEEE} {S}td 1275-1994 {S}tandard for {B}oot ({I}nitialization {C}onfiguration) {F}irmware",
+	publisher =    pub-IEEE,
+	address =      pub-IEEE:adr,
+	note = "Revision 2.1",
+	year = 1998,
+}
+
+@MISC{noof1,
+	author = "Benjamin Herrenschmidt",
+	title = "Booting the {L}inux/ppc kernel without {O}pen {F}irmware",
+	month = may,
+	year = 2005,
+	note = "v0.1, \url{http://ozlabs.org/pipermail/linuxppc64-dev/2005-May/004073.html}",
+}
+
+@MISC{noof5,
+	author = "Benjamin Herrenschmidt",
+	title = "Booting the {L}inux/ppc kernel without {O}pen {F}irmware",
+	month = nov,
+	year = 2005,
+	note = "v0.5, \url{http://ozlabs.org/pipermail/linuxppc64-dev/2005-December/006994.html}",
+}
+
+@MISC{dtcgit,
+	author = "David Gibson et al.",
+	title = "\dtc{}",
+	howpublished = "git tree",
+	note = "\url{http://ozlabs.org/~dgibson/dtc/dtc.git}",
+}
diff --git a/Documentation/dtc-paper.tex b/Documentation/dtc-paper.tex
new file mode 100644
index 0000000..4494226
--- /dev/null
+++ b/Documentation/dtc-paper.tex
@@ -0,0 +1,597 @@
+\documentclass[a4paper,twocolumn]{article}
+
+\usepackage{abstract}
+\usepackage{xspace}
+\usepackage{amssymb}
+\usepackage{latexsym}
+\usepackage{tabularx}
+\usepackage[T1]{fontenc}
+\usepackage{calc}
+\usepackage{listings}
+\usepackage{color}
+\usepackage{url}
+
+\title{Device trees everywhere}
+
+\author{David Gibson \texttt{<{dwg}{@}{au1.ibm.com}>}\\
+  Benjamin Herrenschmidt \texttt{<{benh}{@}{kernel.crashing.org}>}\\
+  \emph{OzLabs, IBM Linux Technology Center}}
+
+\newcommand{\R}{\textsuperscript{\textregistered}\xspace}
+\newcommand{\tm}{\textsuperscript{\texttrademark}\xspace}
+\newcommand{\tge}{$\geqslant$}
+%\newcommand{\ditto}{\textquotedbl\xspace}
+
+\newcommand{\fixme}[1]{$\bigstar$\emph{\textbf{\large #1}}$\bigstar$\xspace}
+
+\newcommand{\ppc}{\mbox{PowerPC}\xspace}
+\newcommand{\of}{Open Firmware\xspace}
+\newcommand{\benh}{Ben Herrenschmidt\xspace}
+\newcommand{\kexec}{\texttt{kexec()}\xspace}
+\newcommand{\dtbeginnode}{\texttt{OF\_DT\_BEGIN\_NODE\xspace}}
+\newcommand{\dtendnode}{\texttt{OF\_DT\_END\_NODE\xspace}}
+\newcommand{\dtprop}{\texttt{OF\_DT\_PROP\xspace}}
+\newcommand{\dtend}{\texttt{OF\_DT\_END\xspace}}
+\newcommand{\dtc}{\texttt{dtc}\xspace}
+\newcommand{\phandle}{\texttt{linux,phandle}\xspace}
+\begin{document}
+
+\maketitle
+
+\begin{abstract}
+  We present a method for booting a \ppc{}\R Linux\R kernel on an
+  embedded machine.  To do this, we supply the kernel with a compact
+  flattened-tree representation of the system's hardware based on the
+  device tree supplied by Open Firmware on IBM\R servers and Apple\R
+  Power Macintosh\R machines.
+
+  The ``blob'' representing the device tree can be created using \dtc
+  --- the Device Tree Compiler --- that turns a simple text
+  representation of the tree into the compact representation used by
+  the kernel.  The compiler can produce either a binary ``blob'' or an
+  assembler file ready to be built into a firmware or bootwrapper
+  image.
+
+  This flattened-tree approach is now the only supported method of
+  booting a \texttt{ppc64} kernel without Open Firmware, and we plan
+  to make it the only supported method for all \texttt{powerpc}
+  kernels in the future.
+\end{abstract}
+
+\section{Introduction}
+
+\subsection{OF and the device tree}
+
+Historically, ``everyday'' \ppc machines have booted with the help of
+\of (OF), a firmware environment defined by IEEE1275 \cite{IEEE1275}.
+Among other boot-time services, OF maintains a device tree that
+describes all of the system's hardware devices and how they're
+connected.  During boot, before taking control of memory management,
+the Linux kernel uses OF calls to scan the device tree and transfer it
+to an internal representation that is used at run time to look up
+various device information.
+
+The device tree consists of nodes representing devices or
+buses\footnote{Well, mostly.  There are a few special exceptions.}.
+Each node contains \emph{properties}, name--value pairs that give
+information about the device.  The values are arbitrary byte strings,
+and for some properties, they contain tables or other structured
+information.
+
+\subsection{The bad old days}
+
+Embedded systems, by contrast, usually have a minimal firmware that
+might supply a few vital system parameters (size of RAM and the like),
+but nothing as detailed or complete as the OF device tree.  This has
+meant that the various 32-bit \ppc embedded ports have required a
+variety of hacks spread across the kernel to deal with the lack of
+device tree.  These vary from specialised boot wrappers to parse
+parameters (which are at least reasonably localised) to
+CONFIG-dependent hacks in drivers to override normal probe logic with
+hardcoded addresses for a particular board.  As well as being ugly of
+itself, such CONFIG-dependent hacks make it hard to build a single
+kernel image that supports multiple embedded machines.
+
+Until relatively recently, the only 64-bit \ppc machines without OF
+were legacy (pre-POWER5\R) iSeries\R machines.  iSeries machines often
+only have virtual IO devices, which makes it quite simple to work
+around the lack of a device tree.  Even so, the lack means the iSeries
+boot sequence must be quite different from the pSeries or Macintosh,
+which is not ideal.
+
+The device tree also presents a problem for implementing \kexec.  When
+the kernel boots, it takes over full control of the system from OF,
+even re-using OF's memory.  So, when \kexec comes to boot another
+kernel, OF is no longer around for the second kernel to query.
+
+\section{The Flattened Tree}
+
+In May 2005 \benh implemented a new approach to handling the device
+tree that addresses all these problems.  When booting on OF systems,
+the first thing the kernel runs is a small piece of code in
+\texttt{prom\_init.c}, which executes in the context of OF.  This code
+walks the device tree using OF calls, and transcribes it into a
+compact, flattened format.  The resulting device tree ``blob'' is then
+passed to the kernel proper, which eventually unflattens the tree into
+its runtime form.  This blob is the only data communicated between the
+\texttt{prom\_init.c} bootstrap and the rest of the kernel.
+
+When OF isn't available, either because the machine doesn't have it at
+all or because \kexec has been used, the kernel instead starts
+directly from the entry point taking a flattened device tree.  The
+device tree blob must be passed in from outside, rather than generated
+by part of the kernel from OF.  For \kexec, the userland
+\texttt{kexec} tools build the blob from the runtime device tree
+before invoking the new kernel.  For embedded systems the blob can
+come either from the embedded bootloader, or from a specialised
+version of the \texttt{zImage} wrapper for the system in question.
+
+\subsection{Properties of the flattened tree}
+
+The flattened tree format should be easy to handle, both for the
+kernel that parses it and the bootloader that generates it.  In
+particular, the following properties are desirable:
+
+\begin{itemize}
+\item \emph{relocatable}: the bootloader or kernel should be able to
+  move the blob around as a whole, without needing to parse or adjust
+  its internals.  In practice that means we must not use pointers
+  within the blob.
+\item \emph{insert and delete}: sometimes the bootloader might want to
+  make tweaks to the flattened tree, such as deleting or inserting a
+  node (or whole subtree).  It should be possible to do this without
+  having to effectively regenerate the whole flattened tree.  In
+  practice this means limiting the use of internal offsets in the blob
+  that need recalculation if a section is inserted or removed with
+  \texttt{memmove()}.
+\item \emph{compact}: embedded systems are frequently short of
+  resources, particularly RAM and flash memory space.  Thus, the tree
+  representation should be kept as small as conveniently possible.
+\end{itemize}
+
+\subsection{Format of the device tree blob}
+\label{sec:format}
+
+\begin{figure}[htb!]
+  \centering
+  \footnotesize
+  \begin{tabular}{r|c|l}
+    \multicolumn{1}{r}{\textbf{Offset}}& \multicolumn{1}{c}{\textbf{Contents}} \\\cline{2-2}
+    \texttt{0x00} & \texttt{0xd00dfeed} & magic number \\\cline{2-2}
+    \texttt{0x04} & \emph{totalsize} \\\cline{2-2}
+    \texttt{0x08} & \emph{off\_struct} & \\\cline{2-2}
+    \texttt{0x0C} & \emph{off\_strs} & \\\cline{2-2}
+    \texttt{0x10} & \emph{off\_rsvmap} & \\\cline{2-2}
+    \texttt{0x14} & \emph{version} \\\cline{2-2}
+    \texttt{0x18} & \emph{last\_comp\_ver} & \\\cline{2-2}
+    \texttt{0x1C} & \emph{boot\_cpu\_id} & \tge v2 only\\\cline{2-2}
+    \texttt{0x20} & \emph{size\_strs} & \tge v3 only\\\cline{2-2}
+    \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
+    \emph{off\_rsvmap} & \emph{address0} & memory reserve \\
+    + \texttt{0x04} & ...& table \\\cline{2-2}
+    + \texttt{0x08} & \emph{len0} & \\
+    + \texttt{0x0C} & ...& \\\cline{2-2}
+    \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2}
+    & \texttt{0x00000000}- & end marker\\
+    & \texttt{00000000} & \\\cline{2-2}
+    & \texttt{0x00000000}- & \\
+    & \texttt{00000000} & \\\cline{2-2}
+    \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
+    \emph{off\_strs} & \texttt{'n' 'a' 'm' 'e'} & strings block \\
+    + \texttt{0x04} & \texttt{~0~ 'm' 'o' 'd'} & \\
+    + \texttt{0x08} & \texttt{'e' 'l' ~0~ \makebox[\widthof{~~~}]{\textrm{...}}} & \\
+    \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2}
+    \multicolumn{1}{r}{+ \emph{size\_strs}} \\
+    \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
+    \emph{off\_struct} & \dtbeginnode & structure block \\\cline{2-2}
+    + \texttt{0x04} & \texttt{'/' ~0~ ~0~ ~0~}  & root node\\\cline{2-2}
+    + \texttt{0x08} & \dtprop & \\\cline{2-2}
+    + \texttt{0x0C} & \texttt{0x00000005} & ``\texttt{model}''\\\cline{2-2}
+    + \texttt{0x10} & \texttt{0x00000008} & \\\cline{2-2}
+    + \texttt{0x14} & \texttt{'M' 'y' 'B' 'o'} & \\
+    + \texttt{0x18} & \texttt{'a' 'r' 'd' ~0~} & \\\cline{2-2}
+    \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2}
+    & \texttt{\dtendnode} \\\cline{2-2}
+    & \texttt{\dtend} \\\cline{2-2}
+    \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
+    \multicolumn{1}{r}{\emph{totalsize}} \\
+  \end{tabular}
+  \caption{Device tree blob layout}
+  \label{fig:blob-layout}
+\end{figure}
+
+The format for the blob we devised, was first described on the
+\texttt{linuxppc64-dev} mailing list in \cite{noof1}.  The format has
+since evolved through various revisions, and the current version is
+included as part of the \dtc (see \S\ref{sec:dtc}) git tree,
+\cite{dtcgit}.
+
+Figure \ref{fig:blob-layout} shows the layout of the blob of data
+containing the device tree.  It has three sections of variable size:
+the \emph{memory reserve table}, the \emph{structure block} and the
+\emph{strings block}.  A small header gives the blob's size and
+version and the locations of the three sections, plus a handful of
+vital parameters used during early boot.
+
+The memory reserve map section gives a list of regions of memory that
+the kernel must not use\footnote{Usually such ranges contain some data
+structure initialised by the firmware that must be preserved by the
+kernel.}.  The list is represented as a simple array of (address,
+size) pairs of 64 bit values, terminated by a zero size entry.  The
+strings block is similarly simple, consisting of a number of
+null-terminated strings appended together, which are referenced from
+the structure block as described below.
+
+The structure block contains the device tree proper.  Each node is
+introduced with a 32-bit \dtbeginnode tag, followed by the node's name
+as a null-terminated string, padded to a 32-bit boundary.  Then
+follows all of the properties of the node, each introduced with a
+\dtprop tag, then all of the node's subnodes, each introduced with
+their own \dtbeginnode tag.  The node ends with an \dtendnode tag, and
+after the \dtendnode for the root node is an \dtend tag, indicating
+the end of the whole tree\footnote{This is redundant, but included for
+ease of parsing.}.  The structure block starts with the \dtbeginnode
+introducing the description of the root node (named \texttt{/}).
+
+Each property, after the \dtprop, has a 32-bit value giving an offset
+from the beginning of the strings block at which the property name is
+stored.  Because it's common for many nodes to have properties with
+the same name, this approach can substantially reduce the total size
+of the blob.  The name offset is followed by the length of the
+property value (as a 32-bit value) and then the data itself padded to
+a 32-bit boundary.
+
+\subsection{Contents of the tree}
+\label{sec:treecontents}
+
+Having seen how to represent the device tree structure as a flattened
+blob, what actually goes into the tree?  The short answer is ``the
+same as an OF tree''.  On OF systems, the flattened tree is
+transcribed directly from the OF device tree, so for simplicity we
+also use OF conventions for the tree on other systems.
+
+In many cases a flat tree can be simpler than a typical OF provided
+device tree.  The flattened tree need only provide those nodes and
+properties that the kernel actually requires; the flattened tree
+generally need not include devices that the kernel can probe itself.
+For example, an OF device tree would normally include nodes for each
+PCI device on the system.  A flattened tree need only include nodes
+for the PCI host bridges; the kernel will scan the buses thus
+described to find the subsidiary devices.  The device tree can include
+nodes for devices where the kernel needs extra information, though:
+for example, for ISA devices on a subsidiary PCI/ISA bridge, or for
+devices with unusual interrupt routing.
+
+Where they exist, we follow the IEEE1275 bindings that specify how to
+describe various buses in the device tree (for example,
+\cite{IEEE1275-pci} describe how to represent PCI devices).  The
+standard has not been updated for a long time, however, and lacks
+bindings for many modern buses and devices.  In particular, embedded
+specific devices such as the various System-on-Chip buses are not
+covered.  We intend to create new bindings for such buses, in keeping
+with the general conventions of IEEE1275 (a simple such binding for a
+System-on-Chip bus was included in \cite{noof5} a revision of
+\cite{noof1}).
+
+One complication arises for representing ``phandles'' in the flattened
+tree.  In OF, each node in the tree has an associated phandle, a
+32-bit integer that uniquely identifies the node\footnote{In practice
+usually implemented as a pointer or offset within OF memory.}.  This
+handle is used by the various OF calls to query and traverse the tree.
+Sometimes phandles are also used within the tree to refer to other
+nodes in the tree.  For example, devices that produce interrupts
+generally have an \texttt{interrupt-parent} property giving the
+phandle of the interrupt controller that handles interrupts from this
+device.  Parsing these and other interrupt related properties allows
+the kernel to build a complete representation of the system's
+interrupt tree, which can be quite different from the tree of bus
+connections.
+
+In the flattened tree, a node's phandle is represented by a special
+\phandle property.  When the kernel generates a flattened tree from
+OF, it adds a \phandle property to each node, containing the phandle
+retrieved from OF.  When the tree is generated without OF, however,
+only nodes that are actually referred to by phandle need to have this
+property.
+
+Another complication arises because nodes in an OF tree have two
+names.  First they have the ``unit name'', which is how the node is
+referred to in an OF path.  The unit name generally consists of a
+device type followed by an \texttt{@} followed by a \emph{unit
+address}.  For example \texttt{/memory@0} is the full path of a memory
+node at address 0, \texttt{/ht@0,f2000000/pci@1} is the path of a PCI
+bus node, which is under a HyperTransport\tm bus node.  The form of
+the unit address is bus dependent, but is generally derived from the
+node's \texttt{reg} property.  In addition, nodes have a property,
+\texttt{name}, whose value is usually equal to the first path of the
+unit name. For example, the nodes in the previous example would have
+\texttt{name} properties equal to \texttt{memory} and \texttt{pci},
+respectively.  To save space in the blob, the current version of the
+flattened tree format only requires the unit names to be present.
+When the kernel unflattens the tree, it automatically generates a
+\texttt{name} property from the node's path name.
+
+\section{The Device Tree Compiler}
+\label{sec:dtc}
+
+\begin{figure}[htb!]
+  \centering
+  \begin{lstlisting}[frame=single,basicstyle=\footnotesize\ttfamily,
+    tabsize=3,numbers=left,xleftmargin=2em]
+/memreserve/ 0x20000000-0x21FFFFFF;
+
+/ {
+	model = "MyBoard";
+	compatible = "MyBoardFamily";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		PowerPC,970@0 {
+			device_type = "cpu";
+			reg = <0>;
+			clock-frequency = <5f5e1000>;
+			timebase-frequency = <1FCA055>;
+			linux,boot-cpu;
+			i-cache-size = <10000>;
+			d-cache-size = <8000>;
+		};
+	};
+
+	memory@0 {
+		device_type = "memory";
+		memreg: reg = <00000000 00000000
+		               00000000 20000000>;
+	};
+
+	mpic@0x3fffdd08400 {
+		/* Interrupt controller */
+		/* ... */
+	};
+
+	pci@40000000000000 {
+		/* PCI host bridge */
+		/* ... */
+	};
+
+	chosen {
+		bootargs = "root=/dev/sda2";
+		linux,platform = <00000600>;
+		interrupt-controller =
+			< &/mpic@0x3fffdd08400 >;
+	};
+};
+\end{lstlisting}
+  \caption{Example \dtc source}
+  \label{fig:dts}
+\end{figure}
+
+As we've seen, the flattened device tree format provides a convenient
+way of communicating device tree information to the kernel.  It's
+simple for the kernel to parse, and simple for bootloaders to
+manipulate.  On OF systems, it's easy to generate the flattened tree
+by walking the OF maintained tree.  However, for embedded systems, the
+flattened tree must be generated from scratch.
+
+Embedded bootloaders are generally built for a particular board.  So,
+it's usually possible to build the device tree blob at compile time
+and include it in the bootloader image.  For minor revisions of the
+board, the bootloader can contain code to make the necessary tweaks to
+the tree before passing it to the booted kernel.
+
+The device trees for embedded boards are usually quite simple, and
+it's possible to hand construct the necessary blob by hand, but doing
+so is tedious.  The ``device tree compiler'', \dtc{}\footnote{\dtc can
+be obtained from \cite{dtcgit}.}, is designed to make creating device
+tree blobs easier by converting a text representation of the tree
+into the necessary blob.
+
+\subsection{Input and output formats}
+
+As well as the normal mode of compiling a device tree blob from text
+source, \dtc can convert a device tree between a number of
+representations.  It can take its input in one of three different
+formats:
+\begin{itemize}
+\item source, the normal case.  The device tree is described in a text
+  form, described in \S\ref{sec:dts}.
+\item blob (\texttt{dtb}), the flattened tree format described in
+  \S\ref{sec:format}.  This mode is useful for checking a pre-existing
+  device tree blob.
+\item filesystem (\texttt{fs}), input is a directory tree in the
+  layout of \texttt{/proc/device-tree} (roughly, a directory for each
+  node in the device tree, a file for each property).  This is useful
+  for building a blob for the device tree in use by the currently
+  running kernel.
+\end{itemize}
+
+In addition, \dtc can output the tree in one of three different
+formats:
+\begin{itemize}
+\item blob (\texttt{dtb}), as in \S\ref{sec:format}.  The most
+  straightforward use of \dtc is to compile from ``source'' to
+  ``blob'' format.
+\item source (\texttt{dts}), as in \S\ref{sec:dts}.  If used with blob
+  input, this allows \dtc to act as a ``decompiler''.
+\item assembler source (\texttt{asm}).  \dtc can produce an assembler
+  file, which will assemble into a \texttt{.o} file containing the
+  device tree blob, with symbols giving the beginning of the blob and
+  its various subsections.  This can then be linked directly into a
+  bootloader or firmware image.
+\end{itemize}
+
+For maximum applicability, \dtc can both read and write any of the
+existing revisions of the blob format.  When reading, \dtc takes the
+version from the blob header, and when writing it takes a command line
+option specifying the desired version.  It automatically makes any
+necessary adjustments to the tree that are necessary for the specified
+version.  For example, formats before 0x10 require each node to have
+an explicit \texttt{name} property.  When \dtc creates such a blob, it
+will automatically generate \texttt{name} properties from the unit
+names.
+
+\subsection{Source format}
+\label{sec:dts}
+
+The ``source'' format for \dtc is a text description of the device
+tree in a vaguely C-like form.  Figure \ref{fig:dts} shows an
+example.  The file starts with \texttt{/memreserve/} directives, which
+gives address ranges to add to the output blob's memory reserve table,
+then the device tree proper is described.
+
+Nodes of the tree are introduced with the node name, followed by a
+\texttt{\{} ... \texttt{\};} block containing the node's properties
+and subnodes.  Properties are given as just {\emph{name} \texttt{=}
+  \emph{value}\texttt{;}}.  The property values can be given in any
+of three forms:
+\begin{itemize}
+\item \emph{string} (for example, \texttt{"MyBoard"}).  The property
+  value is the given string, including terminating NULL.  C-style
+  escapes (\verb+\t+, \verb+\n+, \verb+\0+ and so forth) are allowed.
+\item \emph{cells} (for example, \texttt{<0 8000 f0000000>}).  The
+  property value is made up of a list of 32-bit ``cells'', each given
+  as a hex value.
+\item \emph{bytestring} (for example, \texttt{[1234abcdef]}).  The
+  property value is given as a hex bytestring.
+\end{itemize}
+
+Cell properties can also contain \emph{references}.  Instead of a hex
+number, the source can give an ampersand (\texttt{\&}) followed by the
+full path to some node in the tree.  For example, in Figure
+\ref{fig:dts}, the \texttt{/chosen} node has an
+\texttt{interrupt-controller} property referring to the interrupt
+controller described by the node \texttt{/mpic@0x3fffdd08400}.  In the
+output tree, the value of the referenced node's phandle is included in
+the property.  If that node doesn't have an explicit phandle property,
+\dtc will automatically create a unique phandle for it.  This approach
+makes it easy to create interrupt trees without having to explicitly
+assign and remember phandles for the various interrupt controller
+nodes.
+
+The \dtc source can also include ``labels'', which are placed on a
+particular node or property.  For example, Figure \ref{fig:dts} has a
+label ``\texttt{memreg}'' on the \texttt{reg} property of the node
+\texttt{/memory@0}.  When using assembler output, corresponding labels
+in the output are generated, which will assemble into symbols
+addressing the part of the blob with the node or property in question.
+This is useful for the common case where an embedded board has an
+essentially fixed device tree with a few variable properties, such as
+the size of memory.  The bootloader for such a board can have a device
+tree linked in, including a symbol referring to the right place in the
+blob to update the parameter with the correct value determined at
+runtime.
+
+\subsection{Tree checking}
+
+Between reading in the device tree and writing it out in the new
+format, \dtc performs a number of checks on the tree:
+\begin{itemize}
+\item \emph{syntactic structure}:  \dtc checks that node and property
+  names contain only allowed characters and meet length restrictions.
+  It checks that a node does not have multiple properties or subnodes
+  with the same name.
+\item \emph{semantic structure}: In some cases, \dtc checks that
+  properties whose contents are defined by convention have appropriate
+  values.  For example, it checks that \texttt{reg} properties have a
+  length that makes sense given the address forms specified by the
+  \texttt{\#address-cells} and \texttt{\#size-cells} properties.  It
+  checks that properties such as \texttt{interrupt-parent} contain a
+  valid phandle.
+\item \emph{Linux requirements}:  \dtc checks that the device tree
+  contains those nodes and properties that are required by the Linux
+  kernel to boot correctly.
+\end{itemize}
+
+These checks are useful to catch simple problems with the device tree,
+rather than having to debug the results on an embedded kernel.  With
+the blob input mode, it can also be used for diagnosing problems with
+an existing blob.
+
+\section{Future Work}
+
+\subsection{Board ports}
+
+The flattened device tree has always been the only supported way to
+boot a \texttt{ppc64} kernel on an embedded system.  With the merge of
+\texttt{ppc32} and \texttt{ppc64} code it has also become the only
+supported way to boot any merged \texttt{powerpc} kernel, 32-bit or
+64-bit.  In fact, the old \texttt{ppc} architecture exists mainly just
+to support the old ppc32 embedded ports that have not been migrated
+to the flattened device tree approach.  We plan to remove the
+\texttt{ppc} architecture eventually, which will mean porting all the
+various embedded boards to use the flattened device tree.
+
+\subsection{\dtc features}
+
+While it is already quite usable, there are a number of extra features
+that \dtc could include to make creating device trees more convenient:
+\begin{itemize}
+\item \emph{better tree checking}: Although \dtc already performs a
+  number of checks on the device tree, they are rather haphazard.  In
+  many cases \dtc will give up after detecting a minor error early and
+  won't pick up more interesting errors later on.  There is a
+  \texttt{-f} parameter that forces \dtc to generate an output tree
+  even if there are errors.  At present, this needs to be used more
+  often than one might hope, because \dtc is bad at deciding which
+  errors should really be fatal, and which rate mere warnings.
+\item \emph{binary include}: Occasionally, it is useful for the device
+  tree to incorporate as a property a block of binary data for some
+  board-specific purpose.  For example, many of Apple's device trees
+  incorporate bytecode drivers for certain platform devices.  \dtc's
+  source format ought to allow this by letting a property's value be
+  read directly from a binary file.
+\item \emph{macros}: it might be useful for \dtc to implement some
+  sort of macros so that a tree containing a number of similar devices
+  (for example, multiple identical ethernet controllers or PCI buses)
+  can be written more quickly.  At present, this can be accomplished
+  in part by running the source file through CPP before compiling with
+  \dtc.  It's not clear whether ``native'' support for macros would be
+  more useful.
+\end{itemize}
+
+\bibliographystyle{amsplain}
+\bibliography{dtc-paper}
+
+\section*{About the authors}
+
+David Gibson has been a member of the IBM Linux Technology Center,
+working from Canberra, Australia, since 2001.  Recently he has worked
+on Linux hugepage support and performance counter support for ppc64,
+as well as the device tree compiler.  In the past, he has worked on
+bringup for various ppc and ppc64 embedded systems, the orinoco
+wireless driver, ramfs, and a userspace checkpointing system
+(\texttt{esky}).
+
+Benjamin Herrenschmidt was a MacOS developer for about 10 years, but
+ultimately saw the light and installed Linux on his Apple PowerPC
+machine.  After writing a bootloader, BootX, for it in 1998, he
+started contributing to the PowerPC Linux port in various areas,
+mostly around the support for Apple machines. He became official
+PowerMac maintainer in 2001. In 2003, he joined the IBM Linux
+Technology Center in Canberra, Australia, where he ported the 64 bit
+PowerPC kernel to Apple G5 machines and the Maple embedded board,
+among others things.  He's a member of the ppc64 development ``team''
+and one of his current goals is to make the integration of embedded
+platforms smoother and more maintainable than in the 32-bit PowerPC
+kernel.
+
+\section*{Legal Statement}
+
+This work represents the view of the author and does not necessarily
+represent the view of IBM.
+
+IBM, \ppc, \ppc Architecture, POWER5, pSeries and iSeries are
+trademarks or registered trademarks of International Business Machines
+Corporation in the United States and/or other countries.
+
+Apple and Power Macintosh are a registered trademarks of Apple
+Computer Inc. in the United States, other countries, or both.
+
+Linux is a registered trademark of Linus Torvalds.
+
+Other company, product, and service names may be trademarks or service
+marks of others.
+
+\end{document}
diff --git a/Documentation/dts-format.txt b/Documentation/dts-format.txt
new file mode 100644
index 0000000..41741df
--- /dev/null
+++ b/Documentation/dts-format.txt
@@ -0,0 +1,122 @@
+Device Tree Source Format (version 1)
+=====================================
+
+The Device Tree Source (DTS) format is a textual representation of a
+device tree in a form that can be processed by dtc into a binary
+device tree in the form expected by the kernel.  The description below
+is not a formal syntax definition of DTS, but describes the basic
+constructs used to represent device trees.
+
+Node and property definitions
+-----------------------------
+
+Device tree nodes are defined with a node name and unit address with
+braces marking the start and end of the node definition.  They may be
+preceded by a label.
+
+	[label:] node-name[@unit-address] {
+		[properties definitions]
+		[child nodes]
+	}
+
+Nodes may contain property definitions and/or child node
+definitions. If both are present, properties must come before child
+nodes.
+
+Property definitions are name value pairs in the form:
+	[label:] property-name = value;
+except for properties with empty (zero length) value which have the
+form:
+	[label:] property-name;
+
+Property values may be defined as an array of 8, 16, 32, or 64-bit integer
+elements, as NUL-terminated strings, as bytestrings or a combination of these.
+
+* Arrays are represented by angle brackets surrounding a space separated list
+  of C-style integers or character literals.  Array elements default to 32-bits
+  in size.  An array of 32-bit elements is also known as a cell list or a list
+  of cells.  A cell being an unsigned 32-bit integer.
+
+	e.g. interrupts = <17 0xc>;
+
+* A 64-bit value can be represented with two 32-bit elements.
+
+	e.g. clock-frequency = <0x00000001 0x00000000>;
+
+* The storage size of an element can be changed using the /bits/ prefix.  The
+  /bits/ prefix allows for the creation of 8, 16, 32, and 64-bit elements.
+  The resulting array will not be padded to a multiple of the default 32-bit
+  element size.
+
+	e.g. interrupts = /bits/ 8 <17 0xc>;
+	e.g. clock-frequency = /bits/ 64 <0x0000000100000000>;
+
+* A NUL-terminated string value is represented using double quotes
+  (the property value is considered to include the terminating NUL
+  character).
+
+	e.g. compatible = "simple-bus";
+
+* A bytestring is enclosed in square brackets [] with each byte
+  represented by two hexadecimal digits.  Spaces between each byte are
+  optional.
+
+	e.g. local-mac-address = [00 00 12 34 56 78]; or equivalently
+	     local-mac-address = [000012345678];
+
+* Values may have several comma-separated components, which are
+  concatenated together.
+	e.g. compatible = "ns16550", "ns8250";
+	     example = <0xf00f0000 19>, "a strange property format";
+
+* In an array a reference to another node will be expanded to that node's
+  phandle.  References may by '&' followed by a node's label:
+	e.g. interrupt-parent = < &mpic >;
+  or they may be '&' followed by a node's full path in braces:
+	e.g. interrupt-parent = < &{/soc/interrupt-controller@40000} >;
+  References are only permitted in arrays that have an element size of
+  32-bits.
+
+* Outside an array, a reference to another node will be expanded to that
+  node's full path.
+	e.g. ethernet0 = &EMAC0;
+
+* Labels may also appear before or after any component of a property
+  value, or between elements of an array, or between bytes of a bytestring.
+	e.g. reg = reglabel: <0 sizelabel: 0x1000000>;
+	e.g. prop = [ab cd ef byte4: 00 ff fe];
+	e.g. str = start: "string value" end: ;
+
+
+File layout
+-----------
+
+Version 1 DTS files have the overall layout:
+	/dts-v1/;
+
+	[memory reservations]
+
+	/ {
+		[property definitions]
+		[child nodes]
+	};
+
+* The "/dts-v1/;" must be present to identify the file as a version 1
+  DTS (dts files without this tag will be treated by dtc as being in
+  the obsolete "version 0", which uses a different format for integers
+  amongst other small but incompatible changes).
+
+* Memory reservations define an entry for the device tree blob's
+  memory reservation table.  They have the form:
+	e.g. /memreserve/ <address> <length>;
+  Where <address> and <length> are 64-bit C-style integers.
+
+* The / { ... }; section defines the root node of the device tree.
+
+* C style (/* ... */) and C++ style (// ...) comments are supported.
+
+
+
+	-- David Gibson <david@gibson.dropbear.id.au>
+	-- Yoder Stuart <stuart.yoder@freescale.com>
+	-- Anton Staaf <robotboy@chromium.org>
diff --git a/Documentation/manual.txt b/Documentation/manual.txt
new file mode 100644
index 0000000..adf5ccb
--- /dev/null
+++ b/Documentation/manual.txt
@@ -0,0 +1,762 @@
+Device Tree Compiler Manual
+===========================
+
+I - "dtc", the device tree compiler
+    1) Obtaining Sources
+    1.1) Submitting Patches
+    2) Description
+    3) Command Line
+    4) Source File
+    4.1) Overview
+    4.2) Properties
+    4.3) Labels and References
+
+II - The DT block format
+    1) Header
+    2) Device tree generalities
+    3) Device tree "structure" block
+    4) Device tree "strings" block
+
+
+III - libfdt
+
+IV - Utility Tools
+    1) convert-dtsv0 -- Conversion to Version 1
+    1) fdtdump
+
+
+I - "dtc", the device tree compiler
+===================================
+
+1) Sources
+
+Source code for the Device Tree Compiler can be found at git.kernel.org.
+
+The upstream repository is here:
+
+    git://git.kernel.org/pub/scm/utils/dtc/dtc.git
+    https://git.kernel.org/pub/scm/utils/dtc/dtc.git
+
+The gitweb interface for the upstream repository is:
+
+    https://git.kernel.org/cgit/utils/dtc/dtc.git/
+
+1.1) Submitting Patches
+
+Patches should be sent to the maintainers:
+	David Gibson <david@gibson.dropbear.id.au>
+	Jon Loeliger <jdl@jdl.com>
+and CCed to <devicetree-compiler@vger.kernel.org>.
+
+2) Description
+
+The Device Tree Compiler, dtc, takes as input a device-tree in
+a given format and outputs a device-tree in another format.
+Typically, the input format is "dts", a human readable source
+format, and creates a "dtb", or binary format as output.
+
+The currently supported Input Formats are:
+
+    - "dtb": "blob" format.  A flattened device-tree block with
+        header in one binary blob.
+
+    - "dts": "source" format.  A text file containing a "source"
+        for a device-tree.
+
+    - "fs" format.  A representation equivalent to the output of
+        /proc/device-tree  where nodes are directories and
+	properties are files.
+
+The currently supported Output Formats are:
+
+     - "dtb": "blob" format
+
+     - "dts": "source" format
+
+     - "asm": assembly language file.  A file that can be sourced
+        by gas to generate a device-tree "blob".  That file can
+        then simply be added to your Makefile.  Additionally, the
+        assembly file exports some symbols that can be used.
+
+     - "yaml": DT encoded in YAML format. This representation is an
+       intermediate format used for validation tools.
+
+
+3) Command Line
+
+The syntax of the dtc command line is:
+
+    dtc [options] [<input_filename>]
+
+Options:
+
+    <input_filename>
+	The name of the input source file.  If no <input_filename>
+	or "-" is given, stdin is used.
+
+    -b <number>
+	Set the physical boot cpu.
+
+    -f
+	Force.  Try to produce output even if the input tree has errors.
+
+    -h
+	Emit a brief usage and help message.
+
+    -I <input_format>
+	The source input format, as listed above.
+
+    -o <output_filename>
+	The name of the generated output file.  Use "-" for stdout.
+
+    -O <output_format>
+	The generated output format, as listed above.
+
+    -d <dependency_filename>
+	Generate a dependency file during compilation.
+
+    -q
+	Quiet: -q suppress warnings, -qq errors, -qqq all
+
+    -R <number>
+	Make space for <number> reserve map entries
+	Relevant for dtb and asm output only.
+
+    -@
+	Generates a __symbols__ node at the root node of the resulting blob
+	for any node labels used, and for any local references using phandles
+	it also generates a __local_fixups__ node that tracks them.
+
+	When using the /plugin/ tag all unresolved label references to
+	be tracked in the __fixups__ node, making dynamic resolution possible.
+
+    -A
+	Generate automatically aliases for all node labels. This is similar to
+	the -@ option (the __symbols__ node contain identical information) but
+	the semantics are slightly different since no phandles are automatically
+	generated for labeled nodes.
+
+    -S <bytes>
+	Ensure the blob at least <bytes> long, adding additional
+	space if needed.
+
+    -v
+	Print DTC version and exit.
+
+    -V <output_version>
+	Generate output conforming to the given <output_version>.
+	By default the most recent version is generated.
+	Relevant for dtb and asm output only.
+
+
+The <output_version> defines what version of the "blob" format will be
+generated.  Supported versions are 1, 2, 3, 16 and 17.  The default is
+always the most recent version and is likely the highest number.
+
+Additionally, dtc performs various sanity checks on the tree.
+
+
+4) Device Tree Source file
+
+4.1) Overview
+
+Here is a very rough overview of the layout of a DTS source file:
+
+
+    sourcefile:   versioninfo plugindecl list_of_memreserve devicetree
+
+    memreserve:   label 'memreserve' ADDR ADDR ';'
+		| label 'memreserve' ADDR '-' ADDR ';'
+
+    devicetree:   '/' nodedef
+
+    versioninfo:  '/' 'dts-v1' '/' ';'
+
+    plugindecl:   '/' 'plugin' '/' ';'
+                | /* empty */
+
+    nodedef:      '{' list_of_property list_of_subnode '}' ';'
+
+    property:     label PROPNAME '=' propdata ';'
+
+    propdata:     STRING
+		| '<' list_of_cells '>'
+		| '[' list_of_bytes ']'
+
+    subnode:      label nodename nodedef
+
+That structure forms a hierarchical layout of nodes and properties
+rooted at an initial node as:
+
+    / {
+    }
+
+Both classic C style and C++ style comments are supported.
+
+Source files may be directly included using the syntax:
+
+    /include/ "filename"
+
+
+4.2) Properties
+
+Properties are named, possibly labeled, values.  Each value
+is one of:
+
+    - A null-teminated C-like string,
+    - A numeric value fitting in 32 bits,
+    - A list of 32-bit values
+    - A byte sequence
+
+Here are some example property definitions:
+
+    - A property containing a 0 terminated string
+
+	property1 = "string_value";
+
+    - A property containing a numerical 32-bit hexadecimal value
+
+	property2 = <1234abcd>;
+
+    - A property containing 3 numerical 32-bit hexadecimal values
+
+	property3 = <12345678 12345678 deadbeef>;
+
+    - A property whose content is an arbitrary array of bytes
+
+	property4 = [0a 0b 0c 0d de ea ad be ef];
+
+
+Node may contain sub-nodes to obtain a hierarchical structure.
+For example:
+
+    - A child node named "childnode" whose unit name is
+      "childnode at address".  It in turn has a string property
+      called "childprop".
+
+	childnode@address {
+	    childprop = "hello\n";
+	};
+
+
+By default, all numeric values are hexadecimal.  Alternate bases
+may be specified using a prefix "d#" for decimal, "b#" for binary,
+and "o#" for octal.
+
+Strings support common escape sequences from C: "\n", "\t", "\r",
+"\(octal value)", "\x(hex value)".
+
+
+4.3) Labels and References
+
+Labels may be applied to nodes or properties.  Labels appear
+before a node name, and are referenced using an ampersand: &label.
+Absolute node path names are also allowed in node references.
+
+In this example, a node is labeled "mpic" and then referenced:
+
+    mpic:  interrupt-controller@40000 {
+	...
+    };
+
+    ethernet-phy@3 {
+	interrupt-parent = <&mpic>;
+	...
+    };
+
+And used in properties, labels may appear before or after any value:
+
+    randomnode {
+	prop: string = data: "mystring\n" data_end: ;
+	...
+    };
+
+
+
+II - The DT block format
+========================
+
+This chapter defines the format of the flattened device-tree
+passed to the kernel. The actual content of the device tree
+are described in the kernel documentation in the file
+
+    linux-2.6/Documentation/powerpc/booting-without-of.txt
+
+You can find example of code manipulating that format within
+the kernel.  For example, the file:
+
+	including arch/powerpc/kernel/prom_init.c
+
+will generate a flattened device-tree from the Open Firmware
+representation.  Other utilities such as fs2dt, which is part of
+the kexec tools, will generate one from a filesystem representation.
+Some bootloaders such as U-Boot provide a bit more support by
+using the libfdt code.
+
+For booting the kernel, the device tree block has to be in main memory.
+It has to be accessible in both real mode and virtual mode with no
+mapping other than main memory.  If you are writing a simple flash
+bootloader, it should copy the block to RAM before passing it to
+the kernel.
+
+
+1) Header
+---------
+
+The kernel is entered with r3 pointing to an area of memory that is
+roughly described in include/asm-powerpc/prom.h by the structure
+boot_param_header:
+
+    struct boot_param_header {
+        u32     magic;                  /* magic word OF_DT_HEADER */
+        u32     totalsize;              /* total size of DT block */
+        u32     off_dt_struct;          /* offset to structure */
+        u32     off_dt_strings;         /* offset to strings */
+        u32     off_mem_rsvmap;         /* offset to memory reserve map */
+        u32     version;                /* format version */
+        u32     last_comp_version;      /* last compatible version */
+
+        /* version 2 fields below */
+        u32     boot_cpuid_phys;        /* Which physical CPU id we're
+                                           booting on */
+        /* version 3 fields below */
+        u32     size_dt_strings;        /* size of the strings block */
+
+        /* version 17 fields below */
+        u32	size_dt_struct;		/* size of the DT structure block */
+    };
+
+Along with the constants:
+
+    /* Definitions used by the flattened device tree */
+    #define OF_DT_HEADER            0xd00dfeed      /* 4: version,
+						       4: total size */
+    #define OF_DT_BEGIN_NODE        0x1             /* Start node: full name
+						       */
+    #define OF_DT_END_NODE          0x2             /* End node */
+    #define OF_DT_PROP              0x3             /* Property: name off,
+						       size, content */
+    #define OF_DT_END               0x9
+
+All values in this header are in big endian format, the various
+fields in this header are defined more precisely below.  All "offset"
+values are in bytes from the start of the header; that is from the
+value of r3.
+
+   - magic
+
+     This is a magic value that "marks" the beginning of the
+     device-tree block header. It contains the value 0xd00dfeed and is
+     defined by the constant OF_DT_HEADER
+
+   - totalsize
+
+     This is the total size of the DT block including the header. The
+     "DT" block should enclose all data structures defined in this
+     chapter (who are pointed to by offsets in this header). That is,
+     the device-tree structure, strings, and the memory reserve map.
+
+   - off_dt_struct
+
+     This is an offset from the beginning of the header to the start
+     of the "structure" part the device tree. (see 2) device tree)
+
+   - off_dt_strings
+
+     This is an offset from the beginning of the header to the start
+     of the "strings" part of the device-tree
+
+   - off_mem_rsvmap
+
+     This is an offset from the beginning of the header to the start
+     of the reserved memory map. This map is a list of pairs of 64-
+     bit integers. Each pair is a physical address and a size. The
+     list is terminated by an entry of size 0. This map provides the
+     kernel with a list of physical memory areas that are "reserved"
+     and thus not to be used for memory allocations, especially during
+     early initialization. The kernel needs to allocate memory during
+     boot for things like un-flattening the device-tree, allocating an
+     MMU hash table, etc... Those allocations must be done in such a
+     way to avoid overriding critical things like, on Open Firmware
+     capable machines, the RTAS instance, or on some pSeries, the TCE
+     tables used for the iommu. Typically, the reserve map should
+     contain _at least_ this DT block itself (header,total_size). If
+     you are passing an initrd to the kernel, you should reserve it as
+     well. You do not need to reserve the kernel image itself. The map
+     should be 64-bit aligned.
+
+   - version
+
+     This is the version of this structure. Version 1 stops
+     here. Version 2 adds an additional field boot_cpuid_phys.
+     Version 3 adds the size of the strings block, allowing the kernel
+     to reallocate it easily at boot and free up the unused flattened
+     structure after expansion. Version 16 introduces a new more
+     "compact" format for the tree itself that is however not backward
+     compatible. Version 17 adds an additional field, size_dt_struct,
+     allowing it to be reallocated or moved more easily (this is
+     particularly useful for bootloaders which need to make
+     adjustments to a device tree based on probed information). You
+     should always generate a structure of the highest version defined
+     at the time of your implementation. Currently that is version 17,
+     unless you explicitly aim at being backward compatible.
+
+   - last_comp_version
+
+     Last compatible version. This indicates down to what version of
+     the DT block you are backward compatible. For example, version 2
+     is backward compatible with version 1 (that is, a kernel build
+     for version 1 will be able to boot with a version 2 format). You
+     should put a 1 in this field if you generate a device tree of
+     version 1 to 3, or 16 if you generate a tree of version 16 or 17
+     using the new unit name format.
+
+   - boot_cpuid_phys
+
+     This field only exist on version 2 headers. It indicate which
+     physical CPU ID is calling the kernel entry point. This is used,
+     among others, by kexec. If you are on an SMP system, this value
+     should match the content of the "reg" property of the CPU node in
+     the device-tree corresponding to the CPU calling the kernel entry
+     point (see further chapters for more information on the required
+     device-tree contents)
+
+   - size_dt_strings
+
+     This field only exists on version 3 and later headers.  It
+     gives the size of the "strings" section of the device tree (which
+     starts at the offset given by off_dt_strings).
+
+   - size_dt_struct
+
+     This field only exists on version 17 and later headers.  It gives
+     the size of the "structure" section of the device tree (which
+     starts at the offset given by off_dt_struct).
+
+So the typical layout of a DT block (though the various parts don't
+need to be in that order) looks like this (addresses go from top to
+bottom):
+
+             ------------------------------
+       r3 -> |  struct boot_param_header  |
+             ------------------------------
+             |      (alignment gap) (*)   |
+             ------------------------------
+             |      memory reserve map    |
+             ------------------------------
+             |      (alignment gap)       |
+             ------------------------------
+             |                            |
+             |    device-tree structure   |
+             |                            |
+             ------------------------------
+             |      (alignment gap)       |
+             ------------------------------
+             |                            |
+             |     device-tree strings    |
+             |                            |
+      -----> ------------------------------
+      |
+      |
+      --- (r3 + totalsize)
+
+  (*) The alignment gaps are not necessarily present; their presence
+      and size are dependent on the various alignment requirements of
+      the individual data blocks.
+
+
+2) Device tree generalities
+---------------------------
+
+This device-tree itself is separated in two different blocks, a
+structure block and a strings block. Both need to be aligned to a 4
+byte boundary.
+
+First, let's quickly describe the device-tree concept before detailing
+the storage format. This chapter does _not_ describe the detail of the
+required types of nodes & properties for the kernel, this is done
+later in chapter III.
+
+The device-tree layout is strongly inherited from the definition of
+the Open Firmware IEEE 1275 device-tree. It's basically a tree of
+nodes, each node having two or more named properties. A property can
+have a value or not.
+
+It is a tree, so each node has one and only one parent except for the
+root node who has no parent.
+
+A node has 2 names. The actual node name is generally contained in a
+property of type "name" in the node property list whose value is a
+zero terminated string and is mandatory for version 1 to 3 of the
+format definition (as it is in Open Firmware). Version 16 makes it
+optional as it can generate it from the unit name defined below.
+
+There is also a "unit name" that is used to differentiate nodes with
+the same name at the same level, it is usually made of the node
+names, the "@" sign, and a "unit address", which definition is
+specific to the bus type the node sits on.
+
+The unit name doesn't exist as a property per-se but is included in
+the device-tree structure. It is typically used to represent "path" in
+the device-tree. More details about the actual format of these will be
+below.
+
+The kernel powerpc generic code does not make any formal use of the
+unit address (though some board support code may do) so the only real
+requirement here for the unit address is to ensure uniqueness of
+the node unit name at a given level of the tree. Nodes with no notion
+of address and no possible sibling of the same name (like /memory or
+/cpus) may omit the unit address in the context of this specification,
+or use the "@0" default unit address. The unit name is used to define
+a node "full path", which is the concatenation of all parent node
+unit names separated with "/".
+
+The root node doesn't have a defined name, and isn't required to have
+a name property either if you are using version 3 or earlier of the
+format. It also has no unit address (no @ symbol followed by a unit
+address). The root node unit name is thus an empty string. The full
+path to the root node is "/".
+
+Every node which actually represents an actual device (that is, a node
+which isn't only a virtual "container" for more nodes, like "/cpus"
+is) is also required to have a "device_type" property indicating the
+type of node .
+
+Finally, every node that can be referenced from a property in another
+node is required to have a "linux,phandle" property. Real open
+firmware implementations provide a unique "phandle" value for every
+node that the "prom_init()" trampoline code turns into
+"linux,phandle" properties. However, this is made optional if the
+flattened device tree is used directly. An example of a node
+referencing another node via "phandle" is when laying out the
+interrupt tree which will be described in a further version of this
+document.
+
+This "linux, phandle" property is a 32-bit value that uniquely
+identifies a node. You are free to use whatever values or system of
+values, internal pointers, or whatever to generate these, the only
+requirement is that every node for which you provide that property has
+a unique value for it.
+
+Here is an example of a simple device-tree. In this example, an "o"
+designates a node followed by the node unit name. Properties are
+presented with their name followed by their content. "content"
+represents an ASCII string (zero terminated) value, while <content>
+represents a 32-bit hexadecimal value. The various nodes in this
+example will be discussed in a later chapter. At this point, it is
+only meant to give you a idea of what a device-tree looks like. I have
+purposefully kept the "name" and "linux,phandle" properties which
+aren't necessary in order to give you a better idea of what the tree
+looks like in practice.
+
+  / o device-tree
+      |- name = "device-tree"
+      |- model = "MyBoardName"
+      |- compatible = "MyBoardFamilyName"
+      |- #address-cells = <2>
+      |- #size-cells = <2>
+      |- linux,phandle = <0>
+      |
+      o cpus
+      | | - name = "cpus"
+      | | - linux,phandle = <1>
+      | | - #address-cells = <1>
+      | | - #size-cells = <0>
+      | |
+      | o PowerPC,970@0
+      |   |- name = "PowerPC,970"
+      |   |- device_type = "cpu"
+      |   |- reg = <0>
+      |   |- clock-frequency = <5f5e1000>
+      |   |- 64-bit
+      |   |- linux,phandle = <2>
+      |
+      o memory@0
+      | |- name = "memory"
+      | |- device_type = "memory"
+      | |- reg = <00000000 00000000 00000000 20000000>
+      | |- linux,phandle = <3>
+      |
+      o chosen
+        |- name = "chosen"
+        |- bootargs = "root=/dev/sda2"
+        |- linux,phandle = <4>
+
+This tree is almost a minimal tree. It pretty much contains the
+minimal set of required nodes and properties to boot a linux kernel;
+that is, some basic model information at the root, the CPUs, and the
+physical memory layout.  It also includes misc information passed
+through /chosen, like in this example, the platform type (mandatory)
+and the kernel command line arguments (optional).
+
+The /cpus/PowerPC,970@0/64-bit property is an example of a
+property without a value. All other properties have a value. The
+significance of the #address-cells and #size-cells properties will be
+explained in chapter IV which defines precisely the required nodes and
+properties and their content.
+
+
+3) Device tree "structure" block
+
+The structure of the device tree is a linearized tree structure. The
+"OF_DT_BEGIN_NODE" token starts a new node, and the "OF_DT_END_NODE"
+ends that node definition. Child nodes are simply defined before
+"OF_DT_END_NODE" (that is nodes within the node). A 'token' is a 32
+bit value. The tree has to be "finished" with a OF_DT_END token
+
+Here's the basic structure of a single node:
+
+     * token OF_DT_BEGIN_NODE (that is 0x00000001)
+     * for version 1 to 3, this is the node full path as a zero
+       terminated string, starting with "/". For version 16 and later,
+       this is the node unit name only (or an empty string for the
+       root node)
+     * [align gap to next 4 bytes boundary]
+     * for each property:
+        * token OF_DT_PROP (that is 0x00000003)
+        * 32-bit value of property value size in bytes (or 0 if no
+          value)
+        * 32-bit value of offset in string block of property name
+        * property value data if any
+        * [align gap to next 4 bytes boundary]
+     * [child nodes if any]
+     * token OF_DT_END_NODE (that is 0x00000002)
+
+So the node content can be summarized as a start token, a full path,
+a list of properties, a list of child nodes, and an end token. Every
+child node is a full node structure itself as defined above.
+
+NOTE: The above definition requires that all property definitions for
+a particular node MUST precede any subnode definitions for that node.
+Although the structure would not be ambiguous if properties and
+subnodes were intermingled, the kernel parser requires that the
+properties come first (up until at least 2.6.22).  Any tools
+manipulating a flattened tree must take care to preserve this
+constraint.
+
+4) Device tree "strings" block
+
+In order to save space, property names, which are generally redundant,
+are stored separately in the "strings" block. This block is simply the
+whole bunch of zero terminated strings for all property names
+concatenated together. The device-tree property definitions in the
+structure block will contain offset values from the beginning of the
+strings block.
+
+
+III - libfdt
+============
+
+This library should be merged into dtc proper.
+This library should likely be worked into U-Boot and the kernel.
+
+
+IV - Utility Tools
+==================
+
+1) convert-dtsv0 -- Conversion to Version 1
+
+convert-dtsv0 is a small utility program which converts (DTS)
+Device Tree Source from the obsolete version 0 to version 1.
+
+Version 1 DTS files are marked by line "/dts-v1/;" at the top of the file.
+
+The syntax of the convert-dtsv0 command line is:
+
+    convert-dtsv0 [<input_filename ... >]
+
+Each file passed will be converted to the new /dts-v1/ version by creating
+a new file with a "v1" appended the filename.
+
+Comments, empty lines, etc. are preserved.
+
+
+2) fdtdump -- Flat Device Tree dumping utility
+
+The fdtdump program prints a readable version of a flat device tree file.
+
+The syntax of the fdtdump command line is:
+
+    fdtdump [options] <DTB-file-name>
+
+Where options are:
+    -d,--debug          Dump debug information while decoding the file
+    -s,--scan           Scan for an embedded fdt in given file
+
+3) fdtoverlay -- Flat Device Tree overlay applicator
+
+The fdtoverlay applies an arbitrary number of FDT overlays to a base FDT blob
+to a given output file.
+
+The syntax of the fdtoverlay command line is:
+
+    fdtoverlay -i <base-blob> -o <output-blob> <overlay-blob0> [<overlay-blob1> ...]
+
+Where options are:
+    -i, --input         Input base DT blob
+    -o, --output        Output DT blob
+    -v, --verbose       Verbose message output
+
+4 ) fdtget -- Read properties from device tree
+
+This command can be used to obtain individual values from the device tree in a
+nicely formatted way. You can specify multiple nodes to display (when using -p)
+or multiple node/property pairs (when not using -p). For the latter, each
+property is displayed on its own line, with a space between each cell within
+the property.
+
+The syntax of the fdtget command is:
+
+    fdtget <options> <dt file> [<node> <property>]...
+    fdtget -p <options> <dt file> [<node> ]...
+
+where options are:
+
+    <type>    s=string, i=int, u=unsigned, x=hex
+        Optional modifier prefix:
+            hh or b=byte, h=2 byte, l=4 byte (default)
+
+    Options: -[t:pld:hV]
+    -t, --type <arg>    Type of data
+    -p, --properties    List properties for each node
+    -l, --list          List subnodes for each node
+    -d, --default <arg> Default value to display when the property is missing
+    -h, --help          Print this help and exit
+    -V, --version       Print version and exit
+
+If -t is not provided, fdtget will try to figure out the type, trying to detect
+strings, string lists and the size of each value in the property. This is
+similar to how fdtdump works, and uses the same heuristics.
+
+
+5 ) fdtput - Write properties to a device tree
+
+The syntax of the fdtput command is:
+
+    fdtput <options> <dt file> <node> <property> [<value>...]
+    fdtput -c <options> <dt file> [<node>...]
+    fdtput -r <options> <dt file> [<node>...]
+    fdtput -d <options> <dt file> <node> [<property>...]
+
+Options are:
+
+    <type>    s=string, i=int, u=unsigned, x=hex
+        Optional modifier prefix:
+            hh or b=byte, h=2 byte, l=4 byte (default)
+
+    -c, --create     Create nodes if they don't already exist
+    -r, --remove     Delete nodes (and any subnodes) if they already exist
+    -d, --delete     Delete properties if they already exist
+    -p, --auto-path  Automatically create nodes as needed for the node path
+    -t, --type <arg> Type of data
+    -v, --verbose    Display each value decoded from command line
+    -h, --help       Print this help and exit
+    -V, --version    Print version and exit
+
+The option determines which usage is selected and therefore the operation that
+is performed. The first usage adds or updates properties; the rest are used to
+create/delete nodes and delete properties.
+
+For the first usage, the command line arguments are joined together into a
+single value which is written to the property. The -t option is required so
+that fdtput knows how to decode its arguments.
diff --git a/GPL b/GPL
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/GPL
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ed53c76
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,441 @@
+================================================================================
+README.license
+================================================================================
+
+Licensing and contribution policy of dtc and libfdt
+===================================================
+
+This dtc package contains two pieces of software: dtc itself, and
+libfdt which comprises the files in the libfdt/ subdirectory.  These
+two pieces of software, although closely related, are quite distinct.
+dtc does not incorporate or rely on libfdt for its operation, nor vice
+versa.  It is important that these two pieces of software have
+different license conditions.
+
+As SPDX license tags in each source file attest, dtc is licensed
+under the GNU GPL.  The full text of the GPL can be found in the file
+entitled 'GPL' which should be included in this package.  dtc code,
+therefore, may not be incorporated into works which do not have a GPL
+compatible license.
+
+libfdt, however, is GPL/BSD dual-licensed.  That is, it may be used
+either under the terms of the GPL, or under the terms of the 2-clause
+BSD license (aka the ISC license).  The full terms of that license can
+be found are in the file entitled 'BSD-2-Clause'. This is, in
+practice, equivalent to being BSD licensed, since the terms of the BSD
+license are strictly more permissive than the GPL.
+
+I made the decision to license libfdt in this way because I want to
+encourage widespread and correct usage of flattened device trees,
+including by proprietary or otherwise GPL-incompatible firmware or
+tools.  Allowing libfdt to be used under the terms of the BSD license
+makes that it easier for vendors or authors of such software to do so.
+
+This does mean that libfdt code could be "stolen" - say, included in a
+proprietary fimware and extended without contributing those extensions
+back to the libfdt mainline.  While I hope that doesn't happen, I
+believe the goal of allowing libfdt to be widely used is more
+important than avoiding that.  libfdt is quite small, and hardly
+rocket science; so the incentive for such impolite behaviour is small,
+and the inconvenience caused thereby is not dire.
+
+Licenses such as the LGPL which would allow code to be used in non-GPL
+software, but also require contributions to be returned were
+considered.  However, libfdt is designed to be used in firmwares and
+other environments with unusual technical constraints.  It's difficult
+to anticipate all possible changes which might be needed to meld
+libfdt into such environments and so difficult to suitably word a
+license that puts the boundary between what is and isn't permitted in
+the intended place.  Again, I judged encouraging widespread use of
+libfdt by keeping the license terms simple and familiar to be the more
+important goal.
+
+**IMPORTANT** It's intended that all of libfdt as released remain
+permissively licensed this way.  Therefore only contributions which
+are released under these terms can be merged into the libfdt mainline.
+
+
+David Gibson <david@gibson.dropbear.id.au>
+(principal original author of dtc and libfdt)
+2 November 2007
+
+================================================================================
+BSD-2-Clause
+================================================================================
+
+Valid-License-Identifier: BSD-2-Clause
+SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
+Usage-Guide:
+  To use the BSD 2-clause "Simplified" License put the following SPDX
+  tag/value pair into a comment according to the placement guidelines in
+  the licensing rules documentation:
+    SPDX-License-Identifier: BSD-2-Clause
+License-Text:
+
+Copyright (c) <year> <owner> . All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+================================================================================
+GPL
+================================================================================
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..efcbcb3
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "Device Tree Compiler"
+description: "Toolchain for working with device tree files"
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/dgibson/dtc"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://github.com/dgibson/dtc/archive/v1.5.1.tar.gz"
+  }
+  version: "1.5.1"
+  last_upgrade_date { year: 2020 month: 1 day: 17 }
+
+  # The `dtc` tool is GPL only, but `libfdt` is dual GPL/BSD.
+  # We statically link only code from `libfdt`.
+  license_type: RESTRICTED
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e955242
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,373 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Device Tree Compiler
+#
+
+#
+# Version information will be constructed in this order:
+# EXTRAVERSION might be "-rc", for example.
+# LOCAL_VERSION is likely from command line.
+# CONFIG_LOCALVERSION from some future config system.
+#
+VERSION = 1
+PATCHLEVEL = 5
+SUBLEVEL = 0
+EXTRAVERSION =
+LOCAL_VERSION =
+CONFIG_LOCALVERSION =
+
+CPPFLAGS = -I libfdt -I .
+WARNINGS = -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
+	-Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wshadow
+CFLAGS = -g -Os $(SHAREDLIB_CFLAGS) -Werror $(WARNINGS) $(EXTRA_CFLAGS)
+
+BISON = bison
+LEX = flex
+SWIG = swig
+PKG_CONFIG ?= pkg-config
+PYTHON ?= python3
+
+INSTALL = /usr/bin/install
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_LIB = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_SCRIPT = $(INSTALL)
+DESTDIR =
+PREFIX = $(HOME)
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+INCLUDEDIR = $(PREFIX)/include
+
+HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
+	    sed -e 's/\(cygwin\|msys\).*/\1/')
+
+NO_VALGRIND := $(shell $(PKG_CONFIG) --exists valgrind; echo $$?)
+ifeq ($(NO_VALGRIND),1)
+	CFLAGS += -DNO_VALGRIND
+else
+	CFLAGS += $(shell $(PKG_CONFIG) --cflags valgrind)
+endif
+
+NO_YAML := $(shell $(PKG_CONFIG) --exists yaml-0.1; echo $$?)
+ifeq ($(NO_YAML),1)
+	CFLAGS += -DNO_YAML
+else
+	LDLIBS_dtc += $(shell $(PKG_CONFIG) --libs yaml-0.1)
+endif
+
+ifeq ($(HOSTOS),darwin)
+SHAREDLIB_EXT     = dylib
+SHAREDLIB_CFLAGS  = -fPIC
+SHAREDLIB_LDFLAGS = -fPIC -dynamiclib -Wl,-install_name -Wl,
+else ifeq ($(HOSTOS),$(filter $(HOSTOS),msys cygwin))
+SHAREDLIB_EXT     = so
+SHAREDLIB_CFLAGS  =
+SHAREDLIB_LDFLAGS = -shared -Wl,--version-script=$(LIBFDT_version) -Wl,-soname,
+else
+SHAREDLIB_EXT     = so
+SHAREDLIB_CFLAGS  = -fPIC
+SHAREDLIB_LDFLAGS = -fPIC -shared -Wl,--version-script=$(LIBFDT_version) -Wl,-soname,
+endif
+
+#
+# Overall rules
+#
+ifdef V
+VECHO = :
+else
+VECHO = echo "	"
+ARFLAGS = rc
+.SILENT:
+endif
+
+NODEPTARGETS = clean
+ifeq ($(MAKECMDGOALS),)
+DEPTARGETS = all
+else
+DEPTARGETS = $(filter-out $(NODEPTARGETS),$(MAKECMDGOALS))
+endif
+
+#
+# Rules for versioning
+#
+
+DTC_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+VERSION_FILE = version_gen.h
+
+CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+	  else if [ -x /bin/bash ]; then echo /bin/bash; \
+	  else echo sh; fi ; fi)
+
+nullstring :=
+space	:= $(nullstring) # end of line
+
+localver_config = $(subst $(space),, $(string) \
+			      $(patsubst "%",%,$(CONFIG_LOCALVERSION)))
+
+localver_cmd = $(subst $(space),, $(string) \
+			      $(patsubst "%",%,$(LOCALVERSION)))
+
+localver_scm = $(shell $(CONFIG_SHELL) ./scripts/setlocalversion)
+localver_full  = $(localver_config)$(localver_cmd)$(localver_scm)
+
+dtc_version = $(DTC_VERSION)$(localver_full)
+
+# Contents of the generated version file.
+define filechk_version
+	(echo "#define DTC_VERSION \"DTC $(dtc_version)\""; )
+endef
+
+define filechk
+	set -e;					\
+	echo '	CHK $@';			\
+	mkdir -p $(dir $@);			\
+	$(filechk_$(1)) < $< > $@.tmp;		\
+	if [ -r $@ ] && cmp -s $@ $@.tmp; then	\
+		rm -f $@.tmp;			\
+	else					\
+		echo '	UPD $@';		\
+		mv -f $@.tmp $@;		\
+	fi;
+endef
+
+
+include Makefile.convert-dtsv0
+include Makefile.dtc
+include Makefile.utils
+
+BIN += convert-dtsv0
+BIN += dtc
+BIN += fdtdump
+BIN += fdtget
+BIN += fdtput
+BIN += fdtoverlay
+
+SCRIPTS = dtdiff
+
+all: $(BIN) libfdt
+
+# We need both Python and swig to build/install pylibfdt.
+# This builds the given make ${target} if those deps are found.
+check_python_deps = \
+	if $(PKG_CONFIG) --cflags $(PYTHON) >/dev/null 2>&1; then \
+		if which swig >/dev/null 2>&1; then \
+			can_build=yes; \
+		fi; \
+	fi; \
+	if [ "$${can_build}" = "yes" ]; then \
+		$(MAKE) $${target}; \
+	else \
+		echo "\#\# Skipping pylibfdt (install python dev and swig to build)"; \
+	fi ;
+
+.PHONY: maybe_pylibfdt
+maybe_pylibfdt: FORCE
+	target=pylibfdt; $(check_python_deps)
+
+ifeq ($(NO_PYTHON),)
+all: maybe_pylibfdt
+endif
+
+
+ifneq ($(DEPTARGETS),)
+-include $(DTC_OBJS:%.o=%.d)
+-include $(CONVERT_OBJS:%.o=%.d)
+-include $(FDTDUMP_OBJS:%.o=%.d)
+-include $(FDTGET_OBJS:%.o=%.d)
+-include $(FDTPUT_OBJS:%.o=%.d)
+-include $(FDTOVERLAY_OBJS:%.o=%.d)
+endif
+
+
+
+#
+# Rules for libfdt
+#
+LIBFDT_dir = libfdt
+LIBFDT_archive = $(LIBFDT_dir)/libfdt.a
+LIBFDT_lib = $(LIBFDT_dir)/$(LIBFDT_LIB)
+LIBFDT_include = $(addprefix $(LIBFDT_dir)/,$(LIBFDT_INCLUDES))
+LIBFDT_version = $(addprefix $(LIBFDT_dir)/,$(LIBFDT_VERSION))
+
+include $(LIBFDT_dir)/Makefile.libfdt
+
+.PHONY: libfdt
+libfdt: $(LIBFDT_archive) $(LIBFDT_lib)
+
+$(LIBFDT_archive): $(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS))
+
+$(LIBFDT_lib): $(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS)) $(LIBFDT_version)
+	@$(VECHO) LD $@
+	$(CC) $(LDFLAGS) $(SHAREDLIB_LDFLAGS)$(LIBFDT_soname) -o $(LIBFDT_lib) \
+		$(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS))
+	ln -sf $(LIBFDT_LIB) $(LIBFDT_dir)/$(LIBFDT_soname)
+
+ifneq ($(DEPTARGETS),)
+-include $(LIBFDT_OBJS:%.o=$(LIBFDT_dir)/%.d)
+endif
+
+# This stops make from generating the lex and bison output during
+# auto-dependency computation, but throwing them away as an
+# intermediate target and building them again "for real"
+.SECONDARY: $(DTC_GEN_SRCS) $(CONVERT_GEN_SRCS)
+
+install-bin: all $(SCRIPTS)
+	@$(VECHO) INSTALL-BIN
+	$(INSTALL) -d $(DESTDIR)$(BINDIR)
+	$(INSTALL_PROGRAM) $(BIN) $(DESTDIR)$(BINDIR)
+	$(INSTALL_SCRIPT) $(SCRIPTS) $(DESTDIR)$(BINDIR)
+
+install-lib: all
+	@$(VECHO) INSTALL-LIB
+	$(INSTALL) -d $(DESTDIR)$(LIBDIR)
+	$(INSTALL_LIB) $(LIBFDT_lib) $(DESTDIR)$(LIBDIR)
+	ln -sf $(notdir $(LIBFDT_lib)) $(DESTDIR)$(LIBDIR)/$(LIBFDT_soname)
+	ln -sf $(LIBFDT_soname) $(DESTDIR)$(LIBDIR)/libfdt.$(SHAREDLIB_EXT)
+	$(INSTALL_DATA) $(LIBFDT_archive) $(DESTDIR)$(LIBDIR)
+
+install-includes:
+	@$(VECHO) INSTALL-INC
+	$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)
+	$(INSTALL_DATA) $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR)
+
+install: install-bin install-lib install-includes
+
+.PHONY: maybe_install_pylibfdt
+maybe_install_pylibfdt: FORCE
+	target=install_pylibfdt; $(check_python_deps)
+
+ifeq ($(NO_PYTHON),)
+install: maybe_install_pylibfdt
+endif
+
+$(VERSION_FILE): Makefile FORCE
+	$(call filechk,version)
+
+
+dtc: $(DTC_OBJS)
+
+convert-dtsv0: $(CONVERT_OBJS)
+	@$(VECHO) LD $@
+	$(LINK.c) -o $@ $^
+
+fdtdump:	$(FDTDUMP_OBJS)
+
+fdtget:	$(FDTGET_OBJS) $(LIBFDT_lib)
+
+fdtput:	$(FDTPUT_OBJS) $(LIBFDT_lib)
+
+fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_lib)
+
+dist:
+	git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \
+		> ../dtc-$(dtc_version).tar
+	cat ../dtc-$(dtc_version).tar | \
+		gzip -9 > ../dtc-$(dtc_version).tar.gz
+
+
+#
+# Rules for pylibfdt
+#
+PYLIBFDT_dir = pylibfdt
+
+include $(PYLIBFDT_dir)/Makefile.pylibfdt
+
+.PHONY: pylibfdt
+pylibfdt: $(PYLIBFDT_dir)/_libfdt.so
+
+#
+# Release signing and uploading
+# This is for maintainer convenience, don't try this at home.
+#
+ifeq ($(MAINTAINER),y)
+GPG = gpg2
+KUP = kup
+KUPDIR = /pub/software/utils/dtc
+
+kup: dist
+	$(GPG) --detach-sign --armor -o ../dtc-$(dtc_version).tar.sign \
+		../dtc-$(dtc_version).tar
+	$(KUP) put ../dtc-$(dtc_version).tar.gz ../dtc-$(dtc_version).tar.sign \
+		$(KUPDIR)/dtc-$(dtc_version).tar.gz
+endif
+
+tags: FORCE
+	rm -f tags
+	find . \( -name tests -type d -prune \) -o \
+	       \( ! -name '*.tab.[ch]' ! -name '*.lex.c' \
+	       -name '*.[chly]' -type f -print \) | xargs ctags -a
+
+#
+# Testsuite rules
+#
+TESTS_PREFIX=tests/
+
+TESTS_BIN += dtc
+TESTS_BIN += convert-dtsv0
+TESTS_BIN += fdtput
+TESTS_BIN += fdtget
+TESTS_BIN += fdtdump
+TESTS_BIN += fdtoverlay
+ifeq ($(NO_PYTHON),)
+TESTS_PYLIBFDT += maybe_pylibfdt
+endif
+
+include tests/Makefile.tests
+
+#
+# Clean rules
+#
+STD_CLEANFILES = *~ *.o *.$(SHAREDLIB_EXT) *.d *.a *.i *.s core a.out vgcore.* \
+	*.tab.[ch] *.lex.c *.output
+
+clean: libfdt_clean pylibfdt_clean tests_clean
+	@$(VECHO) CLEAN
+	rm -f $(STD_CLEANFILES)
+	rm -f $(VERSION_FILE)
+	rm -f $(BIN)
+	rm -f dtc-*.tar dtc-*.tar.sign dtc-*.tar.asc
+
+#
+# Generic compile rules
+#
+%: %.o
+	@$(VECHO) LD $@
+	$(LINK.c) -o $@ $^ $(LDLIBS_$*)
+
+%.o: %.c
+	@$(VECHO) CC $@
+	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+
+%.o: %.S
+	@$(VECHO) AS $@
+	$(CC) $(CPPFLAGS) $(AFLAGS) -D__ASSEMBLY__ -o $@ -c $<
+
+%.d: %.c
+	@$(VECHO) DEP $<
+	$(CC) $(CPPFLAGS) -MM -MG -MT "$*.o $@" $< > $@
+
+%.d: %.S
+	@$(VECHO) DEP $<
+	$(CC) $(CPPFLAGS) -MM -MG -MT "$*.o $@" $< > $@
+
+%.i:	%.c
+	@$(VECHO) CPP $@
+	$(CC) $(CPPFLAGS) -E $< > $@
+
+%.s:	%.c
+	@$(VECHO) CC -S $@
+	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -S $<
+
+%.a:
+	@$(VECHO) AR $@
+	$(AR) $(ARFLAGS) $@ $^
+
+%.lex.c: %.l
+	@$(VECHO) LEX $@
+	$(LEX) -o$@ $<
+
+%.tab.c %.tab.h %.output: %.y
+	@$(VECHO) BISON $@
+	$(BISON) -d $<
+
+FORCE:
diff --git a/Makefile.convert-dtsv0 b/Makefile.convert-dtsv0
new file mode 100644
index 0000000..c12ed40
--- /dev/null
+++ b/Makefile.convert-dtsv0
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This is not a complete Makefile of itself.
+# Instead, it is designed to be easily embeddable
+# into other systems of Makefiles.
+#
+
+CONVERT_SRCS = \
+	srcpos.c \
+	util.c
+
+CONVERT_GEN_SRCS = convert-dtsv0-lexer.lex.c
+
+CONVERT_OBJS = $(CONVERT_SRCS:%.c=%.o) $(CONVERT_GEN_SRCS:%.c=%.o)
diff --git a/Makefile.dtc b/Makefile.dtc
new file mode 100644
index 0000000..9c467b0
--- /dev/null
+++ b/Makefile.dtc
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Makefile.dtc
+#
+# This is not a complete Makefile of itself.  Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+DTC_SRCS = \
+	checks.c \
+	data.c \
+	dtc.c \
+	flattree.c \
+	fstree.c \
+	livetree.c \
+	srcpos.c \
+	treesource.c \
+	util.c
+
+ifneq ($(NO_YAML),1)
+DTC_SRCS += yamltree.c
+endif
+
+DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
+DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
diff --git a/Makefile.utils b/Makefile.utils
new file mode 100644
index 0000000..9436b34
--- /dev/null
+++ b/Makefile.utils
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This is not a complete Makefile of itself.  Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+
+FDTDUMP_SRCS = \
+	fdtdump.c \
+	util.c
+
+FDTDUMP_OBJS = $(FDTDUMP_SRCS:%.c=%.o)
+
+
+FDTGET_SRCS = \
+	fdtget.c \
+	util.c
+
+FDTGET_OBJS = $(FDTGET_SRCS:%.c=%.o)
+
+
+FDTPUT_SRCS = \
+	fdtput.c \
+	util.c
+
+FDTPUT_OBJS = $(FDTPUT_SRCS:%.c=%.o)
+
+FDTOVERLAY_SRCS = \
+	fdtoverlay.c \
+	util.c
+
+FDTOVERLAY_OBJS = $(FDTOVERLAY_SRCS:%.c=%.o)
diff --git a/README b/README
new file mode 100644
index 0000000..9465ee5
--- /dev/null
+++ b/README
@@ -0,0 +1,91 @@
+The source tree contains the Device Tree Compiler (dtc) toolchain for
+working with device tree source and binary files and also libfdt, a
+utility library for reading and manipulating the binary format.
+
+DTC and LIBFDT are maintained by:
+
+David Gibson <david@gibson.dropbear.id.au>
+Jon Loeliger <jdl@jdl.com>
+
+
+Python library
+--------------
+
+A Python library is also available. To build this you will need to install
+swig and Python development files. On Debian distributions:
+
+   sudo apt-get install swig python3-dev
+
+The library provides an Fdt class which you can use like this:
+
+$ PYTHONPATH=../pylibfdt python3
+>>> import libfdt
+>>> fdt = libfdt.Fdt(open('test_tree1.dtb', mode='rb').read())
+>>> node = fdt.path_offset('/subnode@1')
+>>> print(node)
+124
+>>> prop_offset = fdt.first_property_offset(node)
+>>> prop = fdt.get_property_by_offset(prop_offset)
+>>> print('%s=%s' % (prop.name, prop.as_str()))
+compatible=subnode1
+>>> node2 = fdt.path_offset('/')
+>>> print(fdt.getprop(node2, 'compatible').as_str())
+test_tree1
+
+You will find tests in tests/pylibfdt_tests.py showing how to use each
+method. Help is available using the Python help command, e.g.:
+
+    $ cd pylibfdt
+    $ python3 -c "import libfdt; help(libfdt)"
+
+If you add new features, please check code coverage:
+
+    $ sudo apt-get install python3-coverage
+    $ cd tests
+    # It's just 'coverage' on most other distributions
+    $ python3-coverage run pylibfdt_tests.py
+    $ python3-coverage html
+    # Open 'htmlcov/index.html' in your browser
+
+
+To install the library via the normal setup.py method, use:
+
+    ./pylibfdt/setup.py install [--prefix=/path/to/install_dir]
+
+If --prefix is not provided, the default prefix is used, typically '/usr'
+or '/usr/local'. See Python's distutils documentation for details. You can
+also install via the Makefile if you like, but the above is more common.
+
+To install both libfdt and pylibfdt you can use:
+
+    make install [SETUP_PREFIX=/path/to/install_dir] \
+            [PREFIX=/path/to/install_dir]
+
+To disable building the python library, even if swig and Python are available,
+use:
+
+    make NO_PYTHON=1
+
+
+More work remains to support all of libfdt, including access to numeric
+values.
+
+
+Tests
+-----
+
+Test files are kept in the tests/ directory. Use 'make check' to build and run
+all tests.
+
+If you want to adjust a test file, be aware that tree_tree1.dts is compiled
+and checked against a binary tree from assembler macros in trees.S. So
+if you change that file you must change tree.S also.
+
+
+Mailing list
+------------
+The following list is for discussion about dtc and libfdt implementation
+mailto:devicetree-compiler@vger.kernel.org
+
+Core device tree bindings are discussed on the devicetree-spec list:
+mailto:devicetree-spec@vger.kernel.org
diff --git a/README.license b/README.license
new file mode 100644
index 0000000..102b004
--- /dev/null
+++ b/README.license
@@ -0,0 +1,56 @@
+Licensing and contribution policy of dtc and libfdt
+===================================================
+
+This dtc package contains two pieces of software: dtc itself, and
+libfdt which comprises the files in the libfdt/ subdirectory.  These
+two pieces of software, although closely related, are quite distinct.
+dtc does not incorporate or rely on libfdt for its operation, nor vice
+versa.  It is important that these two pieces of software have
+different license conditions.
+
+As SPDX license tags in each source file attest, dtc is licensed
+under the GNU GPL.  The full text of the GPL can be found in the file
+entitled 'GPL' which should be included in this package.  dtc code,
+therefore, may not be incorporated into works which do not have a GPL
+compatible license.
+
+libfdt, however, is GPL/BSD dual-licensed.  That is, it may be used
+either under the terms of the GPL, or under the terms of the 2-clause
+BSD license (aka the ISC license).  The full terms of that license can
+be found are in the file entitled 'BSD-2-Clause'. This is, in
+practice, equivalent to being BSD licensed, since the terms of the BSD
+license are strictly more permissive than the GPL.
+
+I made the decision to license libfdt in this way because I want to
+encourage widespread and correct usage of flattened device trees,
+including by proprietary or otherwise GPL-incompatible firmware or
+tools.  Allowing libfdt to be used under the terms of the BSD license
+makes that it easier for vendors or authors of such software to do so.
+
+This does mean that libfdt code could be "stolen" - say, included in a
+proprietary fimware and extended without contributing those extensions
+back to the libfdt mainline.  While I hope that doesn't happen, I
+believe the goal of allowing libfdt to be widely used is more
+important than avoiding that.  libfdt is quite small, and hardly
+rocket science; so the incentive for such impolite behaviour is small,
+and the inconvenience caused thereby is not dire.
+
+Licenses such as the LGPL which would allow code to be used in non-GPL
+software, but also require contributions to be returned were
+considered.  However, libfdt is designed to be used in firmwares and
+other environments with unusual technical constraints.  It's difficult
+to anticipate all possible changes which might be needed to meld
+libfdt into such environments and so difficult to suitably word a
+license that puts the boundary between what is and isn't permitted in
+the intended place.  Again, I judged encouraging widespread use of
+libfdt by keeping the license terms simple and familiar to be the more
+important goal.
+
+**IMPORTANT** It's intended that all of libfdt as released remain
+permissively licensed this way.  Therefore only contributions which
+are released under these terms can be merged into the libfdt mainline.
+
+
+David Gibson <david@gibson.dropbear.id.au>
+(principal original author of dtc and libfdt)
+2 November 2007
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..a3e7182
--- /dev/null
+++ b/TODO
@@ -0,0 +1,8 @@
+- Bugfixes:
+	* Proper handling of boot cpu information
+- Generate mem reserve map
+	* linux,reserve-map property
+	* generating reserve entry for device tree itself
+	* generating reserve entries from tce, rtas etc. properties
+- Expression support
+- Macro system
diff --git a/checks.c b/checks.c
new file mode 100644
index 0000000..756f0fa
--- /dev/null
+++ b/checks.c
@@ -0,0 +1,1919 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2007.
+ */
+
+#include "dtc.h"
+#include "srcpos.h"
+
+#ifdef TRACE_CHECKS
+#define TRACE(c, ...) \
+	do { \
+		fprintf(stderr, "=== %s: ", (c)->name); \
+		fprintf(stderr, __VA_ARGS__); \
+		fprintf(stderr, "\n"); \
+	} while (0)
+#else
+#define TRACE(c, fmt, ...)	do { } while (0)
+#endif
+
+enum checkstatus {
+	UNCHECKED = 0,
+	PREREQ,
+	PASSED,
+	FAILED,
+};
+
+struct check;
+
+typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node);
+
+struct check {
+	const char *name;
+	check_fn fn;
+	void *data;
+	bool warn, error;
+	enum checkstatus status;
+	bool inprogress;
+	int num_prereqs;
+	struct check **prereq;
+};
+
+#define CHECK_ENTRY(nm_, fn_, d_, w_, e_, ...)	       \
+	static struct check *nm_##_prereqs[] = { __VA_ARGS__ }; \
+	static struct check nm_ = { \
+		.name = #nm_, \
+		.fn = (fn_), \
+		.data = (d_), \
+		.warn = (w_), \
+		.error = (e_), \
+		.status = UNCHECKED, \
+		.num_prereqs = ARRAY_SIZE(nm_##_prereqs), \
+		.prereq = nm_##_prereqs, \
+	};
+#define WARNING(nm_, fn_, d_, ...) \
+	CHECK_ENTRY(nm_, fn_, d_, true, false, __VA_ARGS__)
+#define ERROR(nm_, fn_, d_, ...) \
+	CHECK_ENTRY(nm_, fn_, d_, false, true, __VA_ARGS__)
+#define CHECK(nm_, fn_, d_, ...) \
+	CHECK_ENTRY(nm_, fn_, d_, false, false, __VA_ARGS__)
+
+static inline void  PRINTF(5, 6) check_msg(struct check *c, struct dt_info *dti,
+					   struct node *node,
+					   struct property *prop,
+					   const char *fmt, ...)
+{
+	va_list ap;
+	char *str = NULL;
+	struct srcpos *pos = NULL;
+	char *file_str;
+
+	if (!(c->warn && (quiet < 1)) && !(c->error && (quiet < 2)))
+		return;
+
+	if (prop && prop->srcpos)
+		pos = prop->srcpos;
+	else if (node && node->srcpos)
+		pos = node->srcpos;
+
+	if (pos) {
+		file_str = srcpos_string(pos);
+		xasprintf(&str, "%s", file_str);
+		free(file_str);
+	} else if (streq(dti->outname, "-")) {
+		xasprintf(&str, "<stdout>");
+	} else {
+		xasprintf(&str, "%s", dti->outname);
+	}
+
+	xasprintf_append(&str, ": %s (%s): ",
+			(c->error) ? "ERROR" : "Warning", c->name);
+
+	if (node) {
+		if (prop)
+			xasprintf_append(&str, "%s:%s: ", node->fullpath, prop->name);
+		else
+			xasprintf_append(&str, "%s: ", node->fullpath);
+	}
+
+	va_start(ap, fmt);
+	xavsprintf_append(&str, fmt, ap);
+	va_end(ap);
+
+	xasprintf_append(&str, "\n");
+
+	if (!prop && pos) {
+		pos = node->srcpos;
+		while (pos->next) {
+			pos = pos->next;
+
+			file_str = srcpos_string(pos);
+			xasprintf_append(&str, "  also defined at %s\n", file_str);
+			free(file_str);
+		}
+	}
+
+	fputs(str, stderr);
+}
+
+#define FAIL(c, dti, node, ...)						\
+	do {								\
+		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__);	\
+		(c)->status = FAILED;					\
+		check_msg((c), dti, node, NULL, __VA_ARGS__);		\
+	} while (0)
+
+#define FAIL_PROP(c, dti, node, prop, ...)				\
+	do {								\
+		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__);	\
+		(c)->status = FAILED;					\
+		check_msg((c), dti, node, prop, __VA_ARGS__);		\
+	} while (0)
+
+
+static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct node *child;
+
+	TRACE(c, "%s", node->fullpath);
+	if (c->fn)
+		c->fn(c, dti, node);
+
+	for_each_child(node, child)
+		check_nodes_props(c, dti, child);
+}
+
+static bool run_check(struct check *c, struct dt_info *dti)
+{
+	struct node *dt = dti->dt;
+	bool error = false;
+	int i;
+
+	assert(!c->inprogress);
+
+	if (c->status != UNCHECKED)
+		goto out;
+
+	c->inprogress = true;
+
+	for (i = 0; i < c->num_prereqs; i++) {
+		struct check *prq = c->prereq[i];
+		error = error || run_check(prq, dti);
+		if (prq->status != PASSED) {
+			c->status = PREREQ;
+			check_msg(c, dti, NULL, NULL, "Failed prerequisite '%s'",
+				  c->prereq[i]->name);
+		}
+	}
+
+	if (c->status != UNCHECKED)
+		goto out;
+
+	check_nodes_props(c, dti, dt);
+
+	if (c->status == UNCHECKED)
+		c->status = PASSED;
+
+	TRACE(c, "\tCompleted, status %d", c->status);
+
+out:
+	c->inprogress = false;
+	if ((c->status != PASSED) && (c->error))
+		error = true;
+	return error;
+}
+
+/*
+ * Utility check functions
+ */
+
+/* A check which always fails, for testing purposes only */
+static inline void check_always_fail(struct check *c, struct dt_info *dti,
+				     struct node *node)
+{
+	FAIL(c, dti, node, "always_fail check");
+}
+CHECK(always_fail, check_always_fail, NULL);
+
+static void check_is_string(struct check *c, struct dt_info *dti,
+			    struct node *node)
+{
+	struct property *prop;
+	char *propname = c->data;
+
+	prop = get_property(node, propname);
+	if (!prop)
+		return; /* Not present, assumed ok */
+
+	if (!data_is_one_string(prop->val))
+		FAIL_PROP(c, dti, node, prop, "property is not a string");
+}
+#define WARNING_IF_NOT_STRING(nm, propname) \
+	WARNING(nm, check_is_string, (propname))
+#define ERROR_IF_NOT_STRING(nm, propname) \
+	ERROR(nm, check_is_string, (propname))
+
+static void check_is_string_list(struct check *c, struct dt_info *dti,
+				 struct node *node)
+{
+	int rem, l;
+	struct property *prop;
+	char *propname = c->data;
+	char *str;
+
+	prop = get_property(node, propname);
+	if (!prop)
+		return; /* Not present, assumed ok */
+
+	str = prop->val.val;
+	rem = prop->val.len;
+	while (rem > 0) {
+		l = strnlen(str, rem);
+		if (l == rem) {
+			FAIL_PROP(c, dti, node, prop, "property is not a string list");
+			break;
+		}
+		rem -= l + 1;
+		str += l + 1;
+	}
+}
+#define WARNING_IF_NOT_STRING_LIST(nm, propname) \
+	WARNING(nm, check_is_string_list, (propname))
+#define ERROR_IF_NOT_STRING_LIST(nm, propname) \
+	ERROR(nm, check_is_string_list, (propname))
+
+static void check_is_cell(struct check *c, struct dt_info *dti,
+			  struct node *node)
+{
+	struct property *prop;
+	char *propname = c->data;
+
+	prop = get_property(node, propname);
+	if (!prop)
+		return; /* Not present, assumed ok */
+
+	if (prop->val.len != sizeof(cell_t))
+		FAIL_PROP(c, dti, node, prop, "property is not a single cell");
+}
+#define WARNING_IF_NOT_CELL(nm, propname) \
+	WARNING(nm, check_is_cell, (propname))
+#define ERROR_IF_NOT_CELL(nm, propname) \
+	ERROR(nm, check_is_cell, (propname))
+
+/*
+ * Structural check functions
+ */
+
+static void check_duplicate_node_names(struct check *c, struct dt_info *dti,
+				       struct node *node)
+{
+	struct node *child, *child2;
+
+	for_each_child(node, child)
+		for (child2 = child->next_sibling;
+		     child2;
+		     child2 = child2->next_sibling)
+			if (streq(child->name, child2->name))
+				FAIL(c, dti, child2, "Duplicate node name");
+}
+ERROR(duplicate_node_names, check_duplicate_node_names, NULL);
+
+static void check_duplicate_property_names(struct check *c, struct dt_info *dti,
+					   struct node *node)
+{
+	struct property *prop, *prop2;
+
+	for_each_property(node, prop) {
+		for (prop2 = prop->next; prop2; prop2 = prop2->next) {
+			if (prop2->deleted)
+				continue;
+			if (streq(prop->name, prop2->name))
+				FAIL_PROP(c, dti, node, prop, "Duplicate property name");
+		}
+	}
+}
+ERROR(duplicate_property_names, check_duplicate_property_names, NULL);
+
+#define LOWERCASE	"abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define DIGITS		"0123456789"
+#define PROPNODECHARS	LOWERCASE UPPERCASE DIGITS ",._+*#?-"
+#define PROPNODECHARSSTRICT	LOWERCASE UPPERCASE DIGITS ",-"
+
+static void check_node_name_chars(struct check *c, struct dt_info *dti,
+				  struct node *node)
+{
+	int n = strspn(node->name, c->data);
+
+	if (n < strlen(node->name))
+		FAIL(c, dti, node, "Bad character '%c' in node name",
+		     node->name[n]);
+}
+ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@");
+
+static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,
+					 struct node *node)
+{
+	int n = strspn(node->name, c->data);
+
+	if (n < node->basenamelen)
+		FAIL(c, dti, node, "Character '%c' not recommended in node name",
+		     node->name[n]);
+}
+CHECK(node_name_chars_strict, check_node_name_chars_strict, PROPNODECHARSSTRICT);
+
+static void check_node_name_format(struct check *c, struct dt_info *dti,
+				   struct node *node)
+{
+	if (strchr(get_unitname(node), '@'))
+		FAIL(c, dti, node, "multiple '@' characters in node name");
+}
+ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
+
+static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,
+				      struct node *node)
+{
+	const char *unitname = get_unitname(node);
+	struct property *prop = get_property(node, "reg");
+
+	if (get_subnode(node, "__overlay__")) {
+		/* HACK: Overlay fragments are a special case */
+		return;
+	}
+
+	if (!prop) {
+		prop = get_property(node, "ranges");
+		if (prop && !prop->val.len)
+			prop = NULL;
+	}
+
+	if (prop) {
+		if (!unitname[0])
+			FAIL(c, dti, node, "node has a reg or ranges property, but no unit name");
+	} else {
+		if (unitname[0])
+			FAIL(c, dti, node, "node has a unit name, but no reg property");
+	}
+}
+WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL);
+
+static void check_property_name_chars(struct check *c, struct dt_info *dti,
+				      struct node *node)
+{
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		int n = strspn(prop->name, c->data);
+
+		if (n < strlen(prop->name))
+			FAIL_PROP(c, dti, node, prop, "Bad character '%c' in property name",
+				  prop->name[n]);
+	}
+}
+ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS);
+
+static void check_property_name_chars_strict(struct check *c,
+					     struct dt_info *dti,
+					     struct node *node)
+{
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		const char *name = prop->name;
+		int n = strspn(name, c->data);
+
+		if (n == strlen(prop->name))
+			continue;
+
+		/* Certain names are whitelisted */
+		if (streq(name, "device_type"))
+			continue;
+
+		/*
+		 * # is only allowed at the beginning of property names not counting
+		 * the vendor prefix.
+		 */
+		if (name[n] == '#' && ((n == 0) || (name[n-1] == ','))) {
+			name += n + 1;
+			n = strspn(name, c->data);
+		}
+		if (n < strlen(name))
+			FAIL_PROP(c, dti, node, prop, "Character '%c' not recommended in property name",
+				  name[n]);
+	}
+}
+CHECK(property_name_chars_strict, check_property_name_chars_strict, PROPNODECHARSSTRICT);
+
+#define DESCLABEL_FMT	"%s%s%s%s%s"
+#define DESCLABEL_ARGS(node,prop,mark)		\
+	((mark) ? "value of " : ""),		\
+	((prop) ? "'" : ""), \
+	((prop) ? (prop)->name : ""), \
+	((prop) ? "' in " : ""), (node)->fullpath
+
+static void check_duplicate_label(struct check *c, struct dt_info *dti,
+				  const char *label, struct node *node,
+				  struct property *prop, struct marker *mark)
+{
+	struct node *dt = dti->dt;
+	struct node *othernode = NULL;
+	struct property *otherprop = NULL;
+	struct marker *othermark = NULL;
+
+	othernode = get_node_by_label(dt, label);
+
+	if (!othernode)
+		otherprop = get_property_by_label(dt, label, &othernode);
+	if (!othernode)
+		othermark = get_marker_label(dt, label, &othernode,
+					       &otherprop);
+
+	if (!othernode)
+		return;
+
+	if ((othernode != node) || (otherprop != prop) || (othermark != mark))
+		FAIL(c, dti, node, "Duplicate label '%s' on " DESCLABEL_FMT
+		     " and " DESCLABEL_FMT,
+		     label, DESCLABEL_ARGS(node, prop, mark),
+		     DESCLABEL_ARGS(othernode, otherprop, othermark));
+}
+
+static void check_duplicate_label_node(struct check *c, struct dt_info *dti,
+				       struct node *node)
+{
+	struct label *l;
+	struct property *prop;
+
+	for_each_label(node->labels, l)
+		check_duplicate_label(c, dti, l->label, node, NULL, NULL);
+
+	for_each_property(node, prop) {
+		struct marker *m = prop->val.markers;
+
+		for_each_label(prop->labels, l)
+			check_duplicate_label(c, dti, l->label, node, prop, NULL);
+
+		for_each_marker_of_type(m, LABEL)
+			check_duplicate_label(c, dti, m->ref, node, prop, m);
+	}
+}
+ERROR(duplicate_label, check_duplicate_label_node, NULL);
+
+static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
+				 struct node *node, const char *propname)
+{
+	struct node *root = dti->dt;
+	struct property *prop;
+	struct marker *m;
+	cell_t phandle;
+
+	prop = get_property(node, propname);
+	if (!prop)
+		return 0;
+
+	if (prop->val.len != sizeof(cell_t)) {
+		FAIL_PROP(c, dti, node, prop, "bad length (%d) %s property",
+			  prop->val.len, prop->name);
+		return 0;
+	}
+
+	m = prop->val.markers;
+	for_each_marker_of_type(m, REF_PHANDLE) {
+		assert(m->offset == 0);
+		if (node != get_node_by_ref(root, m->ref))
+			/* "Set this node's phandle equal to some
+			 * other node's phandle".  That's nonsensical
+			 * by construction. */ {
+			FAIL(c, dti, node, "%s is a reference to another node",
+			     prop->name);
+		}
+		/* But setting this node's phandle equal to its own
+		 * phandle is allowed - that means allocate a unique
+		 * phandle for this node, even if it's not otherwise
+		 * referenced.  The value will be filled in later, so
+		 * we treat it as having no phandle data for now. */
+		return 0;
+	}
+
+	phandle = propval_cell(prop);
+
+	if ((phandle == 0) || (phandle == -1)) {
+		FAIL_PROP(c, dti, node, prop, "bad value (0x%x) in %s property",
+		     phandle, prop->name);
+		return 0;
+	}
+
+	return phandle;
+}
+
+static void check_explicit_phandles(struct check *c, struct dt_info *dti,
+				    struct node *node)
+{
+	struct node *root = dti->dt;
+	struct node *other;
+	cell_t phandle, linux_phandle;
+
+	/* Nothing should have assigned phandles yet */
+	assert(!node->phandle);
+
+	phandle = check_phandle_prop(c, dti, node, "phandle");
+
+	linux_phandle = check_phandle_prop(c, dti, node, "linux,phandle");
+
+	if (!phandle && !linux_phandle)
+		/* No valid phandles; nothing further to check */
+		return;
+
+	if (linux_phandle && phandle && (phandle != linux_phandle))
+		FAIL(c, dti, node, "mismatching 'phandle' and 'linux,phandle'"
+		     " properties");
+
+	if (linux_phandle && !phandle)
+		phandle = linux_phandle;
+
+	other = get_node_by_phandle(root, phandle);
+	if (other && (other != node)) {
+		FAIL(c, dti, node, "duplicated phandle 0x%x (seen before at %s)",
+		     phandle, other->fullpath);
+		return;
+	}
+
+	node->phandle = phandle;
+}
+ERROR(explicit_phandles, check_explicit_phandles, NULL);
+
+static void check_name_properties(struct check *c, struct dt_info *dti,
+				  struct node *node)
+{
+	struct property **pp, *prop = NULL;
+
+	for (pp = &node->proplist; *pp; pp = &((*pp)->next))
+		if (streq((*pp)->name, "name")) {
+			prop = *pp;
+			break;
+		}
+
+	if (!prop)
+		return; /* No name property, that's fine */
+
+	if ((prop->val.len != node->basenamelen+1)
+	    || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) {
+		FAIL(c, dti, node, "\"name\" property is incorrect (\"%s\" instead"
+		     " of base node name)", prop->val.val);
+	} else {
+		/* The name property is correct, and therefore redundant.
+		 * Delete it */
+		*pp = prop->next;
+		free(prop->name);
+		data_free(prop->val);
+		free(prop);
+	}
+}
+ERROR_IF_NOT_STRING(name_is_string, "name");
+ERROR(name_properties, check_name_properties, NULL, &name_is_string);
+
+/*
+ * Reference fixup functions
+ */
+
+static void fixup_phandle_references(struct check *c, struct dt_info *dti,
+				     struct node *node)
+{
+	struct node *dt = dti->dt;
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		struct marker *m = prop->val.markers;
+		struct node *refnode;
+		cell_t phandle;
+
+		for_each_marker_of_type(m, REF_PHANDLE) {
+			assert(m->offset + sizeof(cell_t) <= prop->val.len);
+
+			refnode = get_node_by_ref(dt, m->ref);
+			if (! refnode) {
+				if (!(dti->dtsflags & DTSF_PLUGIN))
+					FAIL(c, dti, node, "Reference to non-existent node or "
+							"label \"%s\"\n", m->ref);
+				else /* mark the entry as unresolved */
+					*((fdt32_t *)(prop->val.val + m->offset)) =
+						cpu_to_fdt32(0xffffffff);
+				continue;
+			}
+
+			phandle = get_node_phandle(dt, refnode);
+			*((fdt32_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
+
+			reference_node(refnode);
+		}
+	}
+}
+ERROR(phandle_references, fixup_phandle_references, NULL,
+      &duplicate_node_names, &explicit_phandles);
+
+static void fixup_path_references(struct check *c, struct dt_info *dti,
+				  struct node *node)
+{
+	struct node *dt = dti->dt;
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		struct marker *m = prop->val.markers;
+		struct node *refnode;
+		char *path;
+
+		for_each_marker_of_type(m, REF_PATH) {
+			assert(m->offset <= prop->val.len);
+
+			refnode = get_node_by_ref(dt, m->ref);
+			if (!refnode) {
+				FAIL(c, dti, node, "Reference to non-existent node or label \"%s\"\n",
+				     m->ref);
+				continue;
+			}
+
+			path = refnode->fullpath;
+			prop->val = data_insert_at_marker(prop->val, m, path,
+							  strlen(path) + 1);
+
+			reference_node(refnode);
+		}
+	}
+}
+ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names);
+
+static void fixup_omit_unused_nodes(struct check *c, struct dt_info *dti,
+				    struct node *node)
+{
+	if (generate_symbols && node->labels)
+		return;
+	if (node->omit_if_unused && !node->is_referenced)
+		delete_node(node);
+}
+ERROR(omit_unused_nodes, fixup_omit_unused_nodes, NULL, &phandle_references, &path_references);
+
+/*
+ * Semantic checks
+ */
+WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells");
+WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells");
+WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells");
+
+WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
+WARNING_IF_NOT_STRING(model_is_string, "model");
+WARNING_IF_NOT_STRING(status_is_string, "status");
+WARNING_IF_NOT_STRING(label_is_string, "label");
+
+WARNING_IF_NOT_STRING_LIST(compatible_is_string_list, "compatible");
+
+static void check_names_is_string_list(struct check *c, struct dt_info *dti,
+				       struct node *node)
+{
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		const char *s = strrchr(prop->name, '-');
+		if (!s || !streq(s, "-names"))
+			continue;
+
+		c->data = prop->name;
+		check_is_string_list(c, dti, node);
+	}
+}
+WARNING(names_is_string_list, check_names_is_string_list, NULL);
+
+static void check_alias_paths(struct check *c, struct dt_info *dti,
+				    struct node *node)
+{
+	struct property *prop;
+
+	if (!streq(node->name, "aliases"))
+		return;
+
+	for_each_property(node, prop) {
+		if (streq(prop->name, "phandle")
+		    || streq(prop->name, "linux,phandle")) {
+			continue;
+		}
+
+		if (!prop->val.val || !get_node_by_path(dti->dt, prop->val.val)) {
+			FAIL_PROP(c, dti, node, prop, "aliases property is not a valid node (%s)",
+				  prop->val.val);
+			continue;
+		}
+		if (strspn(prop->name, LOWERCASE DIGITS "-") != strlen(prop->name))
+			FAIL(c, dti, node, "aliases property name must include only lowercase and '-'");
+	}
+}
+WARNING(alias_paths, check_alias_paths, NULL);
+
+static void fixup_addr_size_cells(struct check *c, struct dt_info *dti,
+				  struct node *node)
+{
+	struct property *prop;
+
+	node->addr_cells = -1;
+	node->size_cells = -1;
+
+	prop = get_property(node, "#address-cells");
+	if (prop)
+		node->addr_cells = propval_cell(prop);
+
+	prop = get_property(node, "#size-cells");
+	if (prop)
+		node->size_cells = propval_cell(prop);
+}
+WARNING(addr_size_cells, fixup_addr_size_cells, NULL,
+	&address_cells_is_cell, &size_cells_is_cell);
+
+#define node_addr_cells(n) \
+	(((n)->addr_cells == -1) ? 2 : (n)->addr_cells)
+#define node_size_cells(n) \
+	(((n)->size_cells == -1) ? 1 : (n)->size_cells)
+
+static void check_reg_format(struct check *c, struct dt_info *dti,
+			     struct node *node)
+{
+	struct property *prop;
+	int addr_cells, size_cells, entrylen;
+
+	prop = get_property(node, "reg");
+	if (!prop)
+		return; /* No "reg", that's fine */
+
+	if (!node->parent) {
+		FAIL(c, dti, node, "Root node has a \"reg\" property");
+		return;
+	}
+
+	if (prop->val.len == 0)
+		FAIL_PROP(c, dti, node, prop, "property is empty");
+
+	addr_cells = node_addr_cells(node->parent);
+	size_cells = node_size_cells(node->parent);
+	entrylen = (addr_cells + size_cells) * sizeof(cell_t);
+
+	if (!entrylen || (prop->val.len % entrylen) != 0)
+		FAIL_PROP(c, dti, node, prop, "property has invalid length (%d bytes) "
+			  "(#address-cells == %d, #size-cells == %d)",
+			  prop->val.len, addr_cells, size_cells);
+}
+WARNING(reg_format, check_reg_format, NULL, &addr_size_cells);
+
+static void check_ranges_format(struct check *c, struct dt_info *dti,
+				struct node *node)
+{
+	struct property *prop;
+	int c_addr_cells, p_addr_cells, c_size_cells, p_size_cells, entrylen;
+
+	prop = get_property(node, "ranges");
+	if (!prop)
+		return;
+
+	if (!node->parent) {
+		FAIL_PROP(c, dti, node, prop, "Root node has a \"ranges\" property");
+		return;
+	}
+
+	p_addr_cells = node_addr_cells(node->parent);
+	p_size_cells = node_size_cells(node->parent);
+	c_addr_cells = node_addr_cells(node);
+	c_size_cells = node_size_cells(node);
+	entrylen = (p_addr_cells + c_addr_cells + c_size_cells) * sizeof(cell_t);
+
+	if (prop->val.len == 0) {
+		if (p_addr_cells != c_addr_cells)
+			FAIL_PROP(c, dti, node, prop, "empty \"ranges\" property but its "
+				  "#address-cells (%d) differs from %s (%d)",
+				  c_addr_cells, node->parent->fullpath,
+				  p_addr_cells);
+		if (p_size_cells != c_size_cells)
+			FAIL_PROP(c, dti, node, prop, "empty \"ranges\" property but its "
+				  "#size-cells (%d) differs from %s (%d)",
+				  c_size_cells, node->parent->fullpath,
+				  p_size_cells);
+	} else if ((prop->val.len % entrylen) != 0) {
+		FAIL_PROP(c, dti, node, prop, "\"ranges\" property has invalid length (%d bytes) "
+			  "(parent #address-cells == %d, child #address-cells == %d, "
+			  "#size-cells == %d)", prop->val.len,
+			  p_addr_cells, c_addr_cells, c_size_cells);
+	}
+}
+WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells);
+
+static const struct bus_type pci_bus = {
+	.name = "PCI",
+};
+
+static void check_pci_bridge(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	cell_t *cells;
+
+	prop = get_property(node, "device_type");
+	if (!prop || !streq(prop->val.val, "pci"))
+		return;
+
+	node->bus = &pci_bus;
+
+	if (!strprefixeq(node->name, node->basenamelen, "pci") &&
+	    !strprefixeq(node->name, node->basenamelen, "pcie"))
+		FAIL(c, dti, node, "node name is not \"pci\" or \"pcie\"");
+
+	prop = get_property(node, "ranges");
+	if (!prop)
+		FAIL(c, dti, node, "missing ranges for PCI bridge (or not a bridge)");
+
+	if (node_addr_cells(node) != 3)
+		FAIL(c, dti, node, "incorrect #address-cells for PCI bridge");
+	if (node_size_cells(node) != 2)
+		FAIL(c, dti, node, "incorrect #size-cells for PCI bridge");
+
+	prop = get_property(node, "bus-range");
+	if (!prop)
+		return;
+
+	if (prop->val.len != (sizeof(cell_t) * 2)) {
+		FAIL_PROP(c, dti, node, prop, "value must be 2 cells");
+		return;
+	}
+	cells = (cell_t *)prop->val.val;
+	if (fdt32_to_cpu(cells[0]) > fdt32_to_cpu(cells[1]))
+		FAIL_PROP(c, dti, node, prop, "1st cell must be less than or equal to 2nd cell");
+	if (fdt32_to_cpu(cells[1]) > 0xff)
+		FAIL_PROP(c, dti, node, prop, "maximum bus number must be less than 256");
+}
+WARNING(pci_bridge, check_pci_bridge, NULL,
+	&device_type_is_string, &addr_size_cells);
+
+static void check_pci_device_bus_num(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	unsigned int bus_num, min_bus, max_bus;
+	cell_t *cells;
+
+	if (!node->parent || (node->parent->bus != &pci_bus))
+		return;
+
+	prop = get_property(node, "reg");
+	if (!prop)
+		return;
+
+	cells = (cell_t *)prop->val.val;
+	bus_num = (fdt32_to_cpu(cells[0]) & 0x00ff0000) >> 16;
+
+	prop = get_property(node->parent, "bus-range");
+	if (!prop) {
+		min_bus = max_bus = 0;
+	} else {
+		cells = (cell_t *)prop->val.val;
+		min_bus = fdt32_to_cpu(cells[0]);
+		max_bus = fdt32_to_cpu(cells[0]);
+	}
+	if ((bus_num < min_bus) || (bus_num > max_bus))
+		FAIL_PROP(c, dti, node, prop, "PCI bus number %d out of range, expected (%d - %d)",
+			  bus_num, min_bus, max_bus);
+}
+WARNING(pci_device_bus_num, check_pci_device_bus_num, NULL, &reg_format, &pci_bridge);
+
+static void check_pci_device_reg(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	const char *unitname = get_unitname(node);
+	char unit_addr[5];
+	unsigned int dev, func, reg;
+	cell_t *cells;
+
+	if (!node->parent || (node->parent->bus != &pci_bus))
+		return;
+
+	prop = get_property(node, "reg");
+	if (!prop) {
+		FAIL(c, dti, node, "missing PCI reg property");
+		return;
+	}
+
+	cells = (cell_t *)prop->val.val;
+	if (cells[1] || cells[2])
+		FAIL_PROP(c, dti, node, prop, "PCI reg config space address cells 2 and 3 must be 0");
+
+	reg = fdt32_to_cpu(cells[0]);
+	dev = (reg & 0xf800) >> 11;
+	func = (reg & 0x700) >> 8;
+
+	if (reg & 0xff000000)
+		FAIL_PROP(c, dti, node, prop, "PCI reg address is not configuration space");
+	if (reg & 0x000000ff)
+		FAIL_PROP(c, dti, node, prop, "PCI reg config space address register number must be 0");
+
+	if (func == 0) {
+		snprintf(unit_addr, sizeof(unit_addr), "%x", dev);
+		if (streq(unitname, unit_addr))
+			return;
+	}
+
+	snprintf(unit_addr, sizeof(unit_addr), "%x,%x", dev, func);
+	if (streq(unitname, unit_addr))
+		return;
+
+	FAIL(c, dti, node, "PCI unit address format error, expected \"%s\"",
+	     unit_addr);
+}
+WARNING(pci_device_reg, check_pci_device_reg, NULL, &reg_format, &pci_bridge);
+
+static const struct bus_type simple_bus = {
+	.name = "simple-bus",
+};
+
+static bool node_is_compatible(struct node *node, const char *compat)
+{
+	struct property *prop;
+	const char *str, *end;
+
+	prop = get_property(node, "compatible");
+	if (!prop)
+		return false;
+
+	for (str = prop->val.val, end = str + prop->val.len; str < end;
+	     str += strnlen(str, end - str) + 1) {
+		if (streq(str, compat))
+			return true;
+	}
+	return false;
+}
+
+static void check_simple_bus_bridge(struct check *c, struct dt_info *dti, struct node *node)
+{
+	if (node_is_compatible(node, "simple-bus"))
+		node->bus = &simple_bus;
+}
+WARNING(simple_bus_bridge, check_simple_bus_bridge, NULL,
+	&addr_size_cells, &compatible_is_string_list);
+
+static void check_simple_bus_reg(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	const char *unitname = get_unitname(node);
+	char unit_addr[17];
+	unsigned int size;
+	uint64_t reg = 0;
+	cell_t *cells = NULL;
+
+	if (!node->parent || (node->parent->bus != &simple_bus))
+		return;
+
+	prop = get_property(node, "reg");
+	if (prop)
+		cells = (cell_t *)prop->val.val;
+	else {
+		prop = get_property(node, "ranges");
+		if (prop && prop->val.len)
+			/* skip of child address */
+			cells = ((cell_t *)prop->val.val) + node_addr_cells(node);
+	}
+
+	if (!cells) {
+		if (node->parent->parent && !(node->bus == &simple_bus))
+			FAIL(c, dti, node, "missing or empty reg/ranges property");
+		return;
+	}
+
+	size = node_addr_cells(node->parent);
+	while (size--)
+		reg = (reg << 32) | fdt32_to_cpu(*(cells++));
+
+	snprintf(unit_addr, sizeof(unit_addr), "%"PRIx64, reg);
+	if (!streq(unitname, unit_addr))
+		FAIL(c, dti, node, "simple-bus unit address format error, expected \"%s\"",
+		     unit_addr);
+}
+WARNING(simple_bus_reg, check_simple_bus_reg, NULL, &reg_format, &simple_bus_bridge);
+
+static const struct bus_type i2c_bus = {
+	.name = "i2c-bus",
+};
+
+static void check_i2c_bus_bridge(struct check *c, struct dt_info *dti, struct node *node)
+{
+	if (strprefixeq(node->name, node->basenamelen, "i2c-bus") ||
+	    strprefixeq(node->name, node->basenamelen, "i2c-arb")) {
+		node->bus = &i2c_bus;
+	} else if (strprefixeq(node->name, node->basenamelen, "i2c")) {
+		struct node *child;
+		for_each_child(node, child) {
+			if (strprefixeq(child->name, node->basenamelen, "i2c-bus"))
+				return;
+		}
+		node->bus = &i2c_bus;
+	} else
+		return;
+
+	if (!node->children)
+		return;
+
+	if (node_addr_cells(node) != 1)
+		FAIL(c, dti, node, "incorrect #address-cells for I2C bus");
+	if (node_size_cells(node) != 0)
+		FAIL(c, dti, node, "incorrect #size-cells for I2C bus");
+
+}
+WARNING(i2c_bus_bridge, check_i2c_bus_bridge, NULL, &addr_size_cells);
+
+static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	const char *unitname = get_unitname(node);
+	char unit_addr[17];
+	uint32_t reg = 0;
+	int len;
+	cell_t *cells = NULL;
+
+	if (!node->parent || (node->parent->bus != &i2c_bus))
+		return;
+
+	prop = get_property(node, "reg");
+	if (prop)
+		cells = (cell_t *)prop->val.val;
+
+	if (!cells) {
+		FAIL(c, dti, node, "missing or empty reg property");
+		return;
+	}
+
+	reg = fdt32_to_cpu(*cells);
+	snprintf(unit_addr, sizeof(unit_addr), "%x", reg);
+	if (!streq(unitname, unit_addr))
+		FAIL(c, dti, node, "I2C bus unit address format error, expected \"%s\"",
+		     unit_addr);
+
+	for (len = prop->val.len; len > 0; len -= 4) {
+		reg = fdt32_to_cpu(*(cells++));
+		if (reg > 0x3ff)
+			FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"",
+				  reg);
+
+	}
+}
+WARNING(i2c_bus_reg, check_i2c_bus_reg, NULL, &reg_format, &i2c_bus_bridge);
+
+static const struct bus_type spi_bus = {
+	.name = "spi-bus",
+};
+
+static void check_spi_bus_bridge(struct check *c, struct dt_info *dti, struct node *node)
+{
+	int spi_addr_cells = 1;
+
+	if (strprefixeq(node->name, node->basenamelen, "spi")) {
+		node->bus = &spi_bus;
+	} else {
+		/* Try to detect SPI buses which don't have proper node name */
+		struct node *child;
+
+		if (node_addr_cells(node) != 1 || node_size_cells(node) != 0)
+			return;
+
+		for_each_child(node, child) {
+			struct property *prop;
+			for_each_property(child, prop) {
+				if (strprefixeq(prop->name, 4, "spi-")) {
+					node->bus = &spi_bus;
+					break;
+				}
+			}
+			if (node->bus == &spi_bus)
+				break;
+		}
+
+		if (node->bus == &spi_bus && get_property(node, "reg"))
+			FAIL(c, dti, node, "node name for SPI buses should be 'spi'");
+	}
+	if (node->bus != &spi_bus || !node->children)
+		return;
+
+	if (get_property(node, "spi-slave"))
+		spi_addr_cells = 0;
+	if (node_addr_cells(node) != spi_addr_cells)
+		FAIL(c, dti, node, "incorrect #address-cells for SPI bus");
+	if (node_size_cells(node) != 0)
+		FAIL(c, dti, node, "incorrect #size-cells for SPI bus");
+
+}
+WARNING(spi_bus_bridge, check_spi_bus_bridge, NULL, &addr_size_cells);
+
+static void check_spi_bus_reg(struct check *c, struct dt_info *dti, struct node *node)
+{
+	struct property *prop;
+	const char *unitname = get_unitname(node);
+	char unit_addr[9];
+	uint32_t reg = 0;
+	cell_t *cells = NULL;
+
+	if (!node->parent || (node->parent->bus != &spi_bus))
+		return;
+
+	if (get_property(node->parent, "spi-slave"))
+		return;
+
+	prop = get_property(node, "reg");
+	if (prop)
+		cells = (cell_t *)prop->val.val;
+
+	if (!cells) {
+		FAIL(c, dti, node, "missing or empty reg property");
+		return;
+	}
+
+	reg = fdt32_to_cpu(*cells);
+	snprintf(unit_addr, sizeof(unit_addr), "%x", reg);
+	if (!streq(unitname, unit_addr))
+		FAIL(c, dti, node, "SPI bus unit address format error, expected \"%s\"",
+		     unit_addr);
+}
+WARNING(spi_bus_reg, check_spi_bus_reg, NULL, &reg_format, &spi_bus_bridge);
+
+static void check_unit_address_format(struct check *c, struct dt_info *dti,
+				      struct node *node)
+{
+	const char *unitname = get_unitname(node);
+
+	if (node->parent && node->parent->bus)
+		return;
+
+	if (!unitname[0])
+		return;
+
+	if (!strncmp(unitname, "0x", 2)) {
+		FAIL(c, dti, node, "unit name should not have leading \"0x\"");
+		/* skip over 0x for next test */
+		unitname += 2;
+	}
+	if (unitname[0] == '0' && isxdigit(unitname[1]))
+		FAIL(c, dti, node, "unit name should not have leading 0s");
+}
+WARNING(unit_address_format, check_unit_address_format, NULL,
+	&node_name_format, &pci_bridge, &simple_bus_bridge);
+
+/*
+ * Style checks
+ */
+static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti,
+					  struct node *node)
+{
+	struct property *reg, *ranges;
+
+	if (!node->parent)
+		return; /* Ignore root node */
+
+	reg = get_property(node, "reg");
+	ranges = get_property(node, "ranges");
+
+	if (!reg && !ranges)
+		return;
+
+	if (node->parent->addr_cells == -1)
+		FAIL(c, dti, node, "Relying on default #address-cells value");
+
+	if (node->parent->size_cells == -1)
+		FAIL(c, dti, node, "Relying on default #size-cells value");
+}
+WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL,
+	&addr_size_cells);
+
+static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *dti,
+					      struct node *node)
+{
+	struct property *prop;
+	struct node *child;
+	bool has_reg = false;
+
+	if (!node->parent || node->addr_cells < 0 || node->size_cells < 0)
+		return;
+
+	if (get_property(node, "ranges") || !node->children)
+		return;
+
+	for_each_child(node, child) {
+		prop = get_property(child, "reg");
+		if (prop)
+			has_reg = true;
+	}
+
+	if (!has_reg)
+		FAIL(c, dti, node, "unnecessary #address-cells/#size-cells without \"ranges\" or child \"reg\" property");
+}
+WARNING(avoid_unnecessary_addr_size, check_avoid_unnecessary_addr_size, NULL, &avoid_default_addr_size);
+
+static bool node_is_disabled(struct node *node)
+{
+	struct property *prop;
+
+	prop = get_property(node, "status");
+	if (prop) {
+		char *str = prop->val.val;
+		if (streq("disabled", str))
+			return true;
+	}
+
+	return false;
+}
+
+static void check_unique_unit_address_common(struct check *c,
+						struct dt_info *dti,
+						struct node *node,
+						bool disable_check)
+{
+	struct node *childa;
+
+	if (node->addr_cells < 0 || node->size_cells < 0)
+		return;
+
+	if (!node->children)
+		return;
+
+	for_each_child(node, childa) {
+		struct node *childb;
+		const char *addr_a = get_unitname(childa);
+
+		if (!strlen(addr_a))
+			continue;
+
+		if (disable_check && node_is_disabled(childa))
+			continue;
+
+		for_each_child(node, childb) {
+			const char *addr_b = get_unitname(childb);
+			if (childa == childb)
+				break;
+
+			if (disable_check && node_is_disabled(childb))
+				continue;
+
+			if (streq(addr_a, addr_b))
+				FAIL(c, dti, childb, "duplicate unit-address (also used in node %s)", childa->fullpath);
+		}
+	}
+}
+
+static void check_unique_unit_address(struct check *c, struct dt_info *dti,
+					      struct node *node)
+{
+	check_unique_unit_address_common(c, dti, node, false);
+}
+WARNING(unique_unit_address, check_unique_unit_address, NULL, &avoid_default_addr_size);
+
+static void check_unique_unit_address_if_enabled(struct check *c, struct dt_info *dti,
+					      struct node *node)
+{
+	check_unique_unit_address_common(c, dti, node, true);
+}
+CHECK_ENTRY(unique_unit_address_if_enabled, check_unique_unit_address_if_enabled,
+	    NULL, false, false, &avoid_default_addr_size);
+
+static void check_obsolete_chosen_interrupt_controller(struct check *c,
+						       struct dt_info *dti,
+						       struct node *node)
+{
+	struct node *dt = dti->dt;
+	struct node *chosen;
+	struct property *prop;
+
+	if (node != dt)
+		return;
+
+
+	chosen = get_node_by_path(dt, "/chosen");
+	if (!chosen)
+		return;
+
+	prop = get_property(chosen, "interrupt-controller");
+	if (prop)
+		FAIL_PROP(c, dti, node, prop,
+			  "/chosen has obsolete \"interrupt-controller\" property");
+}
+WARNING(obsolete_chosen_interrupt_controller,
+	check_obsolete_chosen_interrupt_controller, NULL);
+
+static void check_chosen_node_is_root(struct check *c, struct dt_info *dti,
+				      struct node *node)
+{
+	if (!streq(node->name, "chosen"))
+		return;
+
+	if (node->parent != dti->dt)
+		FAIL(c, dti, node, "chosen node must be at root node");
+}
+WARNING(chosen_node_is_root, check_chosen_node_is_root, NULL);
+
+static void check_chosen_node_bootargs(struct check *c, struct dt_info *dti,
+				       struct node *node)
+{
+	struct property *prop;
+
+	if (!streq(node->name, "chosen"))
+		return;
+
+	prop = get_property(node, "bootargs");
+	if (!prop)
+		return;
+
+	c->data = prop->name;
+	check_is_string(c, dti, node);
+}
+WARNING(chosen_node_bootargs, check_chosen_node_bootargs, NULL);
+
+static void check_chosen_node_stdout_path(struct check *c, struct dt_info *dti,
+					  struct node *node)
+{
+	struct property *prop;
+
+	if (!streq(node->name, "chosen"))
+		return;
+
+	prop = get_property(node, "stdout-path");
+	if (!prop) {
+		prop = get_property(node, "linux,stdout-path");
+		if (!prop)
+			return;
+		FAIL_PROP(c, dti, node, prop, "Use 'stdout-path' instead");
+	}
+
+	c->data = prop->name;
+	check_is_string(c, dti, node);
+}
+WARNING(chosen_node_stdout_path, check_chosen_node_stdout_path, NULL);
+
+struct provider {
+	const char *prop_name;
+	const char *cell_name;
+	bool optional;
+};
+
+static void check_property_phandle_args(struct check *c,
+					  struct dt_info *dti,
+				          struct node *node,
+				          struct property *prop,
+				          const struct provider *provider)
+{
+	struct node *root = dti->dt;
+	int cell, cellsize = 0;
+
+	if (prop->val.len % sizeof(cell_t)) {
+		FAIL_PROP(c, dti, node, prop,
+			  "property size (%d) is invalid, expected multiple of %zu",
+			  prop->val.len, sizeof(cell_t));
+		return;
+	}
+
+	for (cell = 0; cell < prop->val.len / sizeof(cell_t); cell += cellsize + 1) {
+		struct node *provider_node;
+		struct property *cellprop;
+		int phandle;
+
+		phandle = propval_cell_n(prop, cell);
+		/*
+		 * Some bindings use a cell value 0 or -1 to skip over optional
+		 * entries when each index position has a specific definition.
+		 */
+		if (phandle == 0 || phandle == -1) {
+			/* Give up if this is an overlay with external references */
+			if (dti->dtsflags & DTSF_PLUGIN)
+				break;
+
+			cellsize = 0;
+			continue;
+		}
+
+		/* If we have markers, verify the current cell is a phandle */
+		if (prop->val.markers) {
+			struct marker *m = prop->val.markers;
+			for_each_marker_of_type(m, REF_PHANDLE) {
+				if (m->offset == (cell * sizeof(cell_t)))
+					break;
+			}
+			if (!m)
+				FAIL_PROP(c, dti, node, prop,
+					  "cell %d is not a phandle reference",
+					  cell);
+		}
+
+		provider_node = get_node_by_phandle(root, phandle);
+		if (!provider_node) {
+			FAIL_PROP(c, dti, node, prop,
+				  "Could not get phandle node for (cell %d)",
+				  cell);
+			break;
+		}
+
+		cellprop = get_property(provider_node, provider->cell_name);
+		if (cellprop) {
+			cellsize = propval_cell(cellprop);
+		} else if (provider->optional) {
+			cellsize = 0;
+		} else {
+			FAIL(c, dti, node, "Missing property '%s' in node %s or bad phandle (referred from %s[%d])",
+			     provider->cell_name,
+			     provider_node->fullpath,
+			     prop->name, cell);
+			break;
+		}
+
+		if (prop->val.len < ((cell + cellsize + 1) * sizeof(cell_t))) {
+			FAIL_PROP(c, dti, node, prop,
+				  "property size (%d) too small for cell size %d",
+				  prop->val.len, cellsize);
+		}
+	}
+}
+
+static void check_provider_cells_property(struct check *c,
+					  struct dt_info *dti,
+				          struct node *node)
+{
+	struct provider *provider = c->data;
+	struct property *prop;
+
+	prop = get_property(node, provider->prop_name);
+	if (!prop)
+		return;
+
+	check_property_phandle_args(c, dti, node, prop, provider);
+}
+#define WARNING_PROPERTY_PHANDLE_CELLS(nm, propname, cells_name, ...) \
+	static struct provider nm##_provider = { (propname), (cells_name), __VA_ARGS__ }; \
+	WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &phandle_references);
+
+WARNING_PROPERTY_PHANDLE_CELLS(clocks, "clocks", "#clock-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(cooling_device, "cooling-device", "#cooling-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(dmas, "dmas", "#dma-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(hwlocks, "hwlocks", "#hwlock-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(interrupts_extended, "interrupts-extended", "#interrupt-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(io_channels, "io-channels", "#io-channel-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(iommus, "iommus", "#iommu-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(mboxes, "mboxes", "#mbox-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(msi_parent, "msi-parent", "#msi-cells", true);
+WARNING_PROPERTY_PHANDLE_CELLS(mux_controls, "mux-controls", "#mux-control-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(phys, "phys", "#phy-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(power_domains, "power-domains", "#power-domain-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(pwms, "pwms", "#pwm-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(resets, "resets", "#reset-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(sound_dai, "sound-dai", "#sound-dai-cells");
+WARNING_PROPERTY_PHANDLE_CELLS(thermal_sensors, "thermal-sensors", "#thermal-sensor-cells");
+
+static bool prop_is_gpio(struct property *prop)
+{
+	char *str;
+
+	/*
+	 * *-gpios and *-gpio can appear in property names,
+	 * so skip over any false matches (only one known ATM)
+	 */
+	if (strstr(prop->name, "nr-gpio"))
+		return false;
+
+	str = strrchr(prop->name, '-');
+	if (str)
+		str++;
+	else
+		str = prop->name;
+	if (!(streq(str, "gpios") || streq(str, "gpio")))
+		return false;
+
+	return true;
+}
+
+static void check_gpios_property(struct check *c,
+					  struct dt_info *dti,
+				          struct node *node)
+{
+	struct property *prop;
+
+	/* Skip GPIO hog nodes which have 'gpios' property */
+	if (get_property(node, "gpio-hog"))
+		return;
+
+	for_each_property(node, prop) {
+		struct provider provider;
+
+		if (!prop_is_gpio(prop))
+			continue;
+
+		provider.prop_name = prop->name;
+		provider.cell_name = "#gpio-cells";
+		provider.optional = false;
+		check_property_phandle_args(c, dti, node, prop, &provider);
+	}
+
+}
+WARNING(gpios_property, check_gpios_property, NULL, &phandle_references);
+
+static void check_deprecated_gpio_property(struct check *c,
+					   struct dt_info *dti,
+				           struct node *node)
+{
+	struct property *prop;
+
+	for_each_property(node, prop) {
+		char *str;
+
+		if (!prop_is_gpio(prop))
+			continue;
+
+		str = strstr(prop->name, "gpio");
+		if (!streq(str, "gpio"))
+			continue;
+
+		FAIL_PROP(c, dti, node, prop,
+			  "'[*-]gpio' is deprecated, use '[*-]gpios' instead");
+	}
+
+}
+CHECK(deprecated_gpio_property, check_deprecated_gpio_property, NULL);
+
+static bool node_is_interrupt_provider(struct node *node)
+{
+	struct property *prop;
+
+	prop = get_property(node, "interrupt-controller");
+	if (prop)
+		return true;
+
+	prop = get_property(node, "interrupt-map");
+	if (prop)
+		return true;
+
+	return false;
+}
+static void check_interrupts_property(struct check *c,
+				      struct dt_info *dti,
+				      struct node *node)
+{
+	struct node *root = dti->dt;
+	struct node *irq_node = NULL, *parent = node;
+	struct property *irq_prop, *prop = NULL;
+	int irq_cells, phandle;
+
+	irq_prop = get_property(node, "interrupts");
+	if (!irq_prop)
+		return;
+
+	if (irq_prop->val.len % sizeof(cell_t))
+		FAIL_PROP(c, dti, node, irq_prop, "size (%d) is invalid, expected multiple of %zu",
+		     irq_prop->val.len, sizeof(cell_t));
+
+	while (parent && !prop) {
+		if (parent != node && node_is_interrupt_provider(parent)) {
+			irq_node = parent;
+			break;
+		}
+
+		prop = get_property(parent, "interrupt-parent");
+		if (prop) {
+			phandle = propval_cell(prop);
+			if ((phandle == 0) || (phandle == -1)) {
+				/* Give up if this is an overlay with
+				 * external references */
+				if (dti->dtsflags & DTSF_PLUGIN)
+					return;
+				FAIL_PROP(c, dti, parent, prop, "Invalid phandle");
+				continue;
+			}
+
+			irq_node = get_node_by_phandle(root, phandle);
+			if (!irq_node) {
+				FAIL_PROP(c, dti, parent, prop, "Bad phandle");
+				return;
+			}
+			if (!node_is_interrupt_provider(irq_node))
+				FAIL(c, dti, irq_node,
+				     "Missing interrupt-controller or interrupt-map property");
+
+			break;
+		}
+
+		parent = parent->parent;
+	}
+
+	if (!irq_node) {
+		FAIL(c, dti, node, "Missing interrupt-parent");
+		return;
+	}
+
+	prop = get_property(irq_node, "#interrupt-cells");
+	if (!prop) {
+		FAIL(c, dti, irq_node, "Missing #interrupt-cells in interrupt-parent");
+		return;
+	}
+
+	irq_cells = propval_cell(prop);
+	if (irq_prop->val.len % (irq_cells * sizeof(cell_t))) {
+		FAIL_PROP(c, dti, node, prop,
+			  "size is (%d), expected multiple of %d",
+			  irq_prop->val.len, (int)(irq_cells * sizeof(cell_t)));
+	}
+}
+WARNING(interrupts_property, check_interrupts_property, &phandle_references);
+
+static const struct bus_type graph_port_bus = {
+	.name = "graph-port",
+};
+
+static const struct bus_type graph_ports_bus = {
+	.name = "graph-ports",
+};
+
+static void check_graph_nodes(struct check *c, struct dt_info *dti,
+			      struct node *node)
+{
+	struct node *child;
+
+	for_each_child(node, child) {
+		if (!(strprefixeq(child->name, child->basenamelen, "endpoint") ||
+		      get_property(child, "remote-endpoint")))
+			continue;
+
+		node->bus = &graph_port_bus;
+
+		/* The parent of 'port' nodes can be either 'ports' or a device */
+		if (!node->parent->bus &&
+		    (streq(node->parent->name, "ports") || get_property(node, "reg")))
+			node->parent->bus = &graph_ports_bus;
+
+		break;
+	}
+
+}
+WARNING(graph_nodes, check_graph_nodes, NULL);
+
+static void check_graph_child_address(struct check *c, struct dt_info *dti,
+				      struct node *node)
+{
+	int cnt = 0;
+	struct node *child;
+
+	if (node->bus != &graph_ports_bus && node->bus != &graph_port_bus)
+		return;
+
+	for_each_child(node, child) {
+		struct property *prop = get_property(child, "reg");
+
+		/* No error if we have any non-zero unit address */
+		if (prop && propval_cell(prop) != 0)
+			return;
+
+		cnt++;
+	}
+
+	if (cnt == 1 && node->addr_cells != -1)
+		FAIL(c, dti, node, "graph node has single child node '%s', #address-cells/#size-cells are not necessary",
+		     node->children->name);
+}
+WARNING(graph_child_address, check_graph_child_address, NULL, &graph_nodes);
+
+static void check_graph_reg(struct check *c, struct dt_info *dti,
+			    struct node *node)
+{
+	char unit_addr[9];
+	const char *unitname = get_unitname(node);
+	struct property *prop;
+
+	prop = get_property(node, "reg");
+	if (!prop || !unitname)
+		return;
+
+	if (!(prop->val.val && prop->val.len == sizeof(cell_t))) {
+		FAIL(c, dti, node, "graph node malformed 'reg' property");
+		return;
+	}
+
+	snprintf(unit_addr, sizeof(unit_addr), "%x", propval_cell(prop));
+	if (!streq(unitname, unit_addr))
+		FAIL(c, dti, node, "graph node unit address error, expected \"%s\"",
+		     unit_addr);
+
+	if (node->parent->addr_cells != 1)
+		FAIL_PROP(c, dti, node, get_property(node, "#address-cells"),
+			  "graph node '#address-cells' is %d, must be 1",
+			  node->parent->addr_cells);
+	if (node->parent->size_cells != 0)
+		FAIL_PROP(c, dti, node, get_property(node, "#size-cells"),
+			  "graph node '#size-cells' is %d, must be 0",
+			  node->parent->size_cells);
+}
+
+static void check_graph_port(struct check *c, struct dt_info *dti,
+			     struct node *node)
+{
+	if (node->bus != &graph_port_bus)
+		return;
+
+	if (!strprefixeq(node->name, node->basenamelen, "port"))
+		FAIL(c, dti, node, "graph port node name should be 'port'");
+
+	check_graph_reg(c, dti, node);
+}
+WARNING(graph_port, check_graph_port, NULL, &graph_nodes);
+
+static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti,
+					struct node *endpoint)
+{
+	int phandle;
+	struct node *node;
+	struct property *prop;
+
+	prop = get_property(endpoint, "remote-endpoint");
+	if (!prop)
+		return NULL;
+
+	phandle = propval_cell(prop);
+	/* Give up if this is an overlay with external references */
+	if (phandle == 0 || phandle == -1)
+		return NULL;
+
+	node = get_node_by_phandle(dti->dt, phandle);
+	if (!node)
+		FAIL_PROP(c, dti, endpoint, prop, "graph phandle is not valid");
+
+	return node;
+}
+
+static void check_graph_endpoint(struct check *c, struct dt_info *dti,
+				 struct node *node)
+{
+	struct node *remote_node;
+
+	if (!node->parent || node->parent->bus != &graph_port_bus)
+		return;
+
+	if (!strprefixeq(node->name, node->basenamelen, "endpoint"))
+		FAIL(c, dti, node, "graph endpoint node name should be 'endpoint'");
+
+	check_graph_reg(c, dti, node);
+
+	remote_node = get_remote_endpoint(c, dti, node);
+	if (!remote_node)
+		return;
+
+	if (get_remote_endpoint(c, dti, remote_node) != node)
+		FAIL(c, dti, node, "graph connection to node '%s' is not bidirectional",
+		     remote_node->fullpath);
+}
+WARNING(graph_endpoint, check_graph_endpoint, NULL, &graph_nodes);
+
+static struct check *check_table[] = {
+	&duplicate_node_names, &duplicate_property_names,
+	&node_name_chars, &node_name_format, &property_name_chars,
+	&name_is_string, &name_properties,
+
+	&duplicate_label,
+
+	&explicit_phandles,
+	&phandle_references, &path_references,
+	&omit_unused_nodes,
+
+	&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
+	&device_type_is_string, &model_is_string, &status_is_string,
+	&label_is_string,
+
+	&compatible_is_string_list, &names_is_string_list,
+
+	&property_name_chars_strict,
+	&node_name_chars_strict,
+
+	&addr_size_cells, &reg_format, &ranges_format,
+
+	&unit_address_vs_reg,
+	&unit_address_format,
+
+	&pci_bridge,
+	&pci_device_reg,
+	&pci_device_bus_num,
+
+	&simple_bus_bridge,
+	&simple_bus_reg,
+
+	&i2c_bus_bridge,
+	&i2c_bus_reg,
+
+	&spi_bus_bridge,
+	&spi_bus_reg,
+
+	&avoid_default_addr_size,
+	&avoid_unnecessary_addr_size,
+	&unique_unit_address,
+	&unique_unit_address_if_enabled,
+	&obsolete_chosen_interrupt_controller,
+	&chosen_node_is_root, &chosen_node_bootargs, &chosen_node_stdout_path,
+
+	&clocks_property,
+	&cooling_device_property,
+	&dmas_property,
+	&hwlocks_property,
+	&interrupts_extended_property,
+	&io_channels_property,
+	&iommus_property,
+	&mboxes_property,
+	&msi_parent_property,
+	&mux_controls_property,
+	&phys_property,
+	&power_domains_property,
+	&pwms_property,
+	&resets_property,
+	&sound_dai_property,
+	&thermal_sensors_property,
+
+	&deprecated_gpio_property,
+	&gpios_property,
+	&interrupts_property,
+
+	&alias_paths,
+
+	&graph_nodes, &graph_child_address, &graph_port, &graph_endpoint,
+
+	&always_fail,
+};
+
+static void enable_warning_error(struct check *c, bool warn, bool error)
+{
+	int i;
+
+	/* Raising level, also raise it for prereqs */
+	if ((warn && !c->warn) || (error && !c->error))
+		for (i = 0; i < c->num_prereqs; i++)
+			enable_warning_error(c->prereq[i], warn, error);
+
+	c->warn = c->warn || warn;
+	c->error = c->error || error;
+}
+
+static void disable_warning_error(struct check *c, bool warn, bool error)
+{
+	int i;
+
+	/* Lowering level, also lower it for things this is the prereq
+	 * for */
+	if ((warn && c->warn) || (error && c->error)) {
+		for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+			struct check *cc = check_table[i];
+			int j;
+
+			for (j = 0; j < cc->num_prereqs; j++)
+				if (cc->prereq[j] == c)
+					disable_warning_error(cc, warn, error);
+		}
+	}
+
+	c->warn = c->warn && !warn;
+	c->error = c->error && !error;
+}
+
+void parse_checks_option(bool warn, bool error, const char *arg)
+{
+	int i;
+	const char *name = arg;
+	bool enable = true;
+
+	if ((strncmp(arg, "no-", 3) == 0)
+	    || (strncmp(arg, "no_", 3) == 0)) {
+		name = arg + 3;
+		enable = false;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+		struct check *c = check_table[i];
+
+		if (streq(c->name, name)) {
+			if (enable)
+				enable_warning_error(c, warn, error);
+			else
+				disable_warning_error(c, warn, error);
+			return;
+		}
+	}
+
+	die("Unrecognized check name \"%s\"\n", name);
+}
+
+void process_checks(bool force, struct dt_info *dti)
+{
+	int i;
+	int error = 0;
+
+	for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+		struct check *c = check_table[i];
+
+		if (c->warn || c->error)
+			error = error || run_check(c, dti);
+	}
+
+	if (error) {
+		if (!force) {
+			fprintf(stderr, "ERROR: Input tree has errors, aborting "
+				"(use -f to force output)\n");
+			exit(2);
+		} else if (quiet < 3) {
+			fprintf(stderr, "Warning: Input tree has errors, "
+				"output forced\n");
+		}
+	}
+}
diff --git a/convert-dtsv0-lexer.l b/convert-dtsv0-lexer.l
new file mode 100644
index 0000000..f52e8a1
--- /dev/null
+++ b/convert-dtsv0-lexer.l
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005, 2008.
+ */
+
+%option noyywrap nounput noinput never-interactive
+
+%x BYTESTRING
+%x PROPNODENAME
+
+PROPNODECHAR	[a-zA-Z0-9,._+*#?@-]
+PATHCHAR	({PROPNODECHAR}|[/])
+LABEL		[a-zA-Z_][a-zA-Z0-9_]*
+STRING		\"([^\\"]|\\.)*\"
+WS		[[:space:]]
+COMMENT		"/*"([^*]|\*+[^*/])*\*+"/"
+LINECOMMENT	"//".*\n
+GAP		({WS}|{COMMENT}|{LINECOMMENT})*
+
+%{
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <errno.h>
+#include <assert.h>
+#include <fnmatch.h>
+
+#include "srcpos.h"
+#include "util.h"
+
+static int v1_tagged; /* = 0 */
+static int cbase = 16;
+static int saw_hyphen; /* = 0 */
+static unsigned long long last_val;
+static char *last_name; /* = NULL */
+
+static const struct {
+	const char *pattern;
+	int obase, width;
+} guess_table[] = {
+	{ "*-frequency", 10, 0 },
+	{ "num-*", 10, 0 },
+	{ "#*-cells", 10, 0 },
+	{ "*cache-line-size", 10, 0 },
+	{ "*cache-block-size", 10, 0 },
+	{ "*cache-size", 10, 0 },
+	{ "*cache-sets", 10, 0 },
+	{ "cell-index", 10, 0 },
+	{ "bank-width", 10, 0 },
+	{ "*-fifo-size", 10, 0 },
+	{ "*-frame-size", 10, 0 },
+	{ "*-channel", 10, 0 },
+	{ "current-speed", 10, 0 },
+	{ "phy-map", 16, 8 },
+	{ "dcr-reg", 16, 3 },
+	{ "reg", 16, 8 },
+	{ "ranges", 16, 8},
+};
+%}
+
+%%
+<*>"/include/"{GAP}{STRING}	ECHO;
+
+<*>\"([^\\"]|\\.)*\"	ECHO;
+
+<*>"/dts-v1/"	{
+			die("Input dts file is already version 1\n");
+		}
+
+<*>"/memreserve/"	{
+			if (!v1_tagged) {
+				fprintf(yyout, "/dts-v1/;\n\n");
+				v1_tagged = 1;
+			}
+
+			ECHO;
+			BEGIN(INITIAL);
+		}
+
+<*>{LABEL}:		ECHO;
+
+<INITIAL>[bodh]# {
+			if (*yytext == 'b')
+				cbase = 2;
+			else if (*yytext == 'o')
+				cbase = 8;
+			else if (*yytext == 'd')
+				cbase = 10;
+			else
+				cbase = 16;
+		}
+
+<INITIAL>[0-9a-fA-F]+	{
+			unsigned long long val;
+			int obase = 16, width = 0;
+			int i;
+
+			val = strtoull(yytext, NULL, cbase);
+
+			if (saw_hyphen)
+				val = val - last_val + 1;
+
+			if (last_name) {
+				for (i = 0; i < ARRAY_SIZE(guess_table); i++)
+					if (fnmatch(guess_table[i].pattern,
+					    last_name, 0) == 0) {
+						obase = guess_table[i].obase;
+						width = guess_table[i].width;
+					}
+			} else {
+				obase = 16;
+				width = 16;
+			}
+
+			if (cbase != 16)
+				obase = cbase;
+
+			switch (obase) {
+			case 2:
+			case 16:
+				fprintf(yyout, "0x%0*llx", width, val);
+				break;
+			case 8:
+				fprintf(yyout, "0%0*llo", width, val);
+				break;
+			case 10:
+				fprintf(yyout, "%*llu", width, val);
+				break;
+			}
+
+			cbase = 16;
+			last_val = val;
+			saw_hyphen = 0;
+		}
+
+\&{LABEL}		ECHO;
+
+"&{/"{PATHCHAR}+\}	ECHO;
+
+<INITIAL>"&/"{PATHCHAR}+ fprintf(yyout, "&{/%s}", yytext + 2);
+
+<BYTESTRING>[0-9a-fA-F]{2} ECHO;
+
+<BYTESTRING>"]"	{
+			ECHO;
+			BEGIN(INITIAL);
+		}
+
+<PROPNODENAME>{PROPNODECHAR}+ {
+			ECHO;
+			last_name = xstrdup(yytext);
+			BEGIN(INITIAL);
+		}
+
+<*>{GAP}	ECHO;
+
+<*>-		{	/* Hack to convert old style memreserves */
+			saw_hyphen = 1;
+			fprintf(yyout, " ");
+		}
+
+<*>.		{
+			if (!v1_tagged) {
+				fprintf(yyout, "/dts-v1/;\n\n");
+				v1_tagged = 1;
+			}
+
+			ECHO;
+			if (yytext[0] == '[') {
+				BEGIN(BYTESTRING);
+			}
+			if ((yytext[0] == '{')
+			    || (yytext[0] == ';')) {
+				BEGIN(PROPNODENAME);
+			}
+		}
+
+%%
+/* Usage related data. */
+static const char usage_synopsis[] = "convert-dtsv0 [options] <v0 dts file>...";
+static const char usage_short_opts[] = "" USAGE_COMMON_SHORT_OPTS;
+static struct option const usage_long_opts[] = {
+	USAGE_COMMON_LONG_OPTS
+};
+static const char * const usage_opts_help[] = {
+	USAGE_COMMON_OPTS_HELP
+};
+
+static void convert_file(const char *fname)
+{
+	const char suffix[] = "v1";
+	int len = strlen(fname);
+	char *newname;
+
+	newname = xmalloc(len + sizeof(suffix));
+	memcpy(newname, fname, len);
+	memcpy(newname + len, suffix, sizeof(suffix));
+
+	yyin = fopen(fname, "r");
+	if (!yyin)
+		die("Couldn't open input file %s: %s\n",
+		    fname, strerror(errno));
+
+	yyout = fopen(newname, "w");
+	if (!yyout)
+		die("Couldn't open output file %s: %s\n",
+		    newname, strerror(errno));
+
+	while(yylex())
+		;
+
+	free(newname);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	int i;
+
+	while ((opt = util_getopt_long()) != EOF) {
+		switch (opt) {
+		case_USAGE_COMMON_FLAGS
+		}
+	}
+	if (argc < 2)
+		usage("missing filename");
+
+	for (i = 1; i < argc; i++) {
+		fprintf(stderr, "Converting %s from dts v0 to dts v1\n", argv[i]);
+		convert_file(argv[i]);
+	}
+
+	exit(0);
+}
diff --git a/data.c b/data.c
new file mode 100644
index 0000000..0a43b6d
--- /dev/null
+++ b/data.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include "dtc.h"
+
+void data_free(struct data d)
+{
+	struct marker *m, *nm;
+
+	m = d.markers;
+	while (m) {
+		nm = m->next;
+		free(m->ref);
+		free(m);
+		m = nm;
+	}
+
+	if (d.val)
+		free(d.val);
+}
+
+struct data data_grow_for(struct data d, int xlen)
+{
+	struct data nd;
+	int newsize;
+
+	if (xlen == 0)
+		return d;
+
+	nd = d;
+
+	newsize = xlen;
+
+	while ((d.len + xlen) > newsize)
+		newsize *= 2;
+
+	nd.val = xrealloc(d.val, newsize);
+
+	return nd;
+}
+
+struct data data_copy_mem(const char *mem, int len)
+{
+	struct data d;
+
+	d = data_grow_for(empty_data, len);
+
+	d.len = len;
+	memcpy(d.val, mem, len);
+
+	return d;
+}
+
+struct data data_copy_escape_string(const char *s, int len)
+{
+	int i = 0;
+	struct data d;
+	char *q;
+
+	d = data_add_marker(empty_data, TYPE_STRING, NULL);
+	d = data_grow_for(d, len + 1);
+
+	q = d.val;
+	while (i < len) {
+		char c = s[i++];
+
+		if (c == '\\')
+			c = get_escape_char(s, &i);
+
+		q[d.len++] = c;
+	}
+
+	q[d.len++] = '\0';
+	return d;
+}
+
+struct data data_copy_file(FILE *f, size_t maxlen)
+{
+	struct data d = empty_data;
+
+	d = data_add_marker(d, TYPE_NONE, NULL);
+	while (!feof(f) && (d.len < maxlen)) {
+		size_t chunksize, ret;
+
+		if (maxlen == -1)
+			chunksize = 4096;
+		else
+			chunksize = maxlen - d.len;
+
+		d = data_grow_for(d, chunksize);
+		ret = fread(d.val + d.len, 1, chunksize, f);
+
+		if (ferror(f))
+			die("Error reading file into data: %s", strerror(errno));
+
+		if (d.len + ret < d.len)
+			die("Overflow reading file into data\n");
+
+		d.len += ret;
+	}
+
+	return d;
+}
+
+struct data data_append_data(struct data d, const void *p, int len)
+{
+	d = data_grow_for(d, len);
+	memcpy(d.val + d.len, p, len);
+	d.len += len;
+	return d;
+}
+
+struct data data_insert_at_marker(struct data d, struct marker *m,
+				  const void *p, int len)
+{
+	d = data_grow_for(d, len);
+	memmove(d.val + m->offset + len, d.val + m->offset, d.len - m->offset);
+	memcpy(d.val + m->offset, p, len);
+	d.len += len;
+
+	/* Adjust all markers after the one we're inserting at */
+	m = m->next;
+	for_each_marker(m)
+		m->offset += len;
+	return d;
+}
+
+static struct data data_append_markers(struct data d, struct marker *m)
+{
+	struct marker **mp = &d.markers;
+
+	/* Find the end of the markerlist */
+	while (*mp)
+		mp = &((*mp)->next);
+	*mp = m;
+	return d;
+}
+
+struct data data_merge(struct data d1, struct data d2)
+{
+	struct data d;
+	struct marker *m2 = d2.markers;
+
+	d = data_append_markers(data_append_data(d1, d2.val, d2.len), m2);
+
+	/* Adjust for the length of d1 */
+	for_each_marker(m2)
+		m2->offset += d1.len;
+
+	d2.markers = NULL; /* So data_free() doesn't clobber them */
+	data_free(d2);
+
+	return d;
+}
+
+struct data data_append_integer(struct data d, uint64_t value, int bits)
+{
+	uint8_t value_8;
+	fdt16_t value_16;
+	fdt32_t value_32;
+	fdt64_t value_64;
+
+	switch (bits) {
+	case 8:
+		value_8 = value;
+		return data_append_data(d, &value_8, 1);
+
+	case 16:
+		value_16 = cpu_to_fdt16(value);
+		return data_append_data(d, &value_16, 2);
+
+	case 32:
+		value_32 = cpu_to_fdt32(value);
+		return data_append_data(d, &value_32, 4);
+
+	case 64:
+		value_64 = cpu_to_fdt64(value);
+		return data_append_data(d, &value_64, 8);
+
+	default:
+		die("Invalid literal size (%d)\n", bits);
+	}
+}
+
+struct data data_append_re(struct data d, uint64_t address, uint64_t size)
+{
+	struct fdt_reserve_entry re;
+
+	re.address = cpu_to_fdt64(address);
+	re.size = cpu_to_fdt64(size);
+
+	return data_append_data(d, &re, sizeof(re));
+}
+
+struct data data_append_cell(struct data d, cell_t word)
+{
+	return data_append_integer(d, word, sizeof(word) * 8);
+}
+
+struct data data_append_addr(struct data d, uint64_t addr)
+{
+	return data_append_integer(d, addr, sizeof(addr) * 8);
+}
+
+struct data data_append_byte(struct data d, uint8_t byte)
+{
+	return data_append_data(d, &byte, 1);
+}
+
+struct data data_append_zeroes(struct data d, int len)
+{
+	d = data_grow_for(d, len);
+
+	memset(d.val + d.len, 0, len);
+	d.len += len;
+	return d;
+}
+
+struct data data_append_align(struct data d, int align)
+{
+	int newlen = ALIGN(d.len, align);
+	return data_append_zeroes(d, newlen - d.len);
+}
+
+struct data data_add_marker(struct data d, enum markertype type, char *ref)
+{
+	struct marker *m;
+
+	m = xmalloc(sizeof(*m));
+	m->offset = d.len;
+	m->type = type;
+	m->ref = ref;
+	m->next = NULL;
+
+	return data_append_markers(d, m);
+}
+
+bool data_is_one_string(struct data d)
+{
+	int i;
+	int len = d.len;
+
+	if (len == 0)
+		return false;
+
+	for (i = 0; i < len-1; i++)
+		if (d.val[i] == '\0')
+			return false;
+
+	if (d.val[len-1] != '\0')
+		return false;
+
+	return true;
+}
diff --git a/dtc-lexer.l b/dtc-lexer.l
new file mode 100644
index 0000000..5c6c3fd
--- /dev/null
+++ b/dtc-lexer.l
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+%option noyywrap nounput noinput never-interactive
+
+%x BYTESTRING
+%x PROPNODENAME
+%s V1
+
+PROPNODECHAR	[a-zA-Z0-9,._+*#?@-]
+PATHCHAR	({PROPNODECHAR}|[/])
+LABEL		[a-zA-Z_][a-zA-Z0-9_]*
+STRING		\"([^\\"]|\\.)*\"
+CHAR_LITERAL	'([^']|\\')*'
+WS		[[:space:]]
+COMMENT		"/*"([^*]|\*+[^*/])*\*+"/"
+LINECOMMENT	"//".*\n
+
+%{
+#include "dtc.h"
+#include "srcpos.h"
+#include "dtc-parser.tab.h"
+
+YYLTYPE yylloc;
+extern bool treesource_error;
+
+/* CAUTION: this will stop working if we ever use yyless() or yyunput() */
+#define	YY_USER_ACTION \
+	{ \
+		srcpos_update(&yylloc, yytext, yyleng); \
+	}
+
+/*#define LEXDEBUG	1*/
+
+#ifdef LEXDEBUG
+#define DPRINT(fmt, ...)	fprintf(stderr, fmt, ##__VA_ARGS__)
+#else
+#define DPRINT(fmt, ...)	do { } while (0)
+#endif
+
+static int dts_version = 1;
+
+#define BEGIN_DEFAULT()		DPRINT("<V1>\n"); \
+				BEGIN(V1); \
+
+static void push_input_file(const char *filename);
+static bool pop_input_file(void);
+static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
+
+%}
+
+%%
+<*>"/include/"{WS}*{STRING} {
+			char *name = strchr(yytext, '\"') + 1;
+			yytext[yyleng-1] = '\0';
+			push_input_file(name);
+		}
+
+<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? {
+			char *line, *fnstart, *fnend;
+			struct data fn;
+			/* skip text before line # */
+			line = yytext;
+			while (!isdigit((unsigned char)*line))
+				line++;
+
+			/* regexp ensures that first and list "
+			 * in the whole yytext are those at
+			 * beginning and end of the filename string */
+			fnstart = memchr(yytext, '"', yyleng);
+			for (fnend = yytext + yyleng - 1;
+			     *fnend != '"'; fnend--)
+				;
+			assert(fnstart && fnend && (fnend > fnstart));
+
+			fn = data_copy_escape_string(fnstart + 1,
+						     fnend - fnstart - 1);
+
+			/* Don't allow nuls in filenames */
+			if (memchr(fn.val, '\0', fn.len - 1))
+				lexical_error("nul in line number directive");
+
+			/* -1 since #line is the number of the next line */
+			srcpos_set_line(xstrdup(fn.val), atoi(line) - 1);
+			data_free(fn);
+		}
+
+<*><<EOF>>		{
+			if (!pop_input_file()) {
+				yyterminate();
+			}
+		}
+
+<*>{STRING}	{
+			DPRINT("String: %s\n", yytext);
+			yylval.data = data_copy_escape_string(yytext+1,
+					yyleng-2);
+			return DT_STRING;
+		}
+
+<*>"/dts-v1/"	{
+			DPRINT("Keyword: /dts-v1/\n");
+			dts_version = 1;
+			BEGIN_DEFAULT();
+			return DT_V1;
+		}
+
+<*>"/plugin/"	{
+			DPRINT("Keyword: /plugin/\n");
+			return DT_PLUGIN;
+		}
+
+<*>"/memreserve/"	{
+			DPRINT("Keyword: /memreserve/\n");
+			BEGIN_DEFAULT();
+			return DT_MEMRESERVE;
+		}
+
+<*>"/bits/"	{
+			DPRINT("Keyword: /bits/\n");
+			BEGIN_DEFAULT();
+			return DT_BITS;
+		}
+
+<*>"/delete-property/"	{
+			DPRINT("Keyword: /delete-property/\n");
+			DPRINT("<PROPNODENAME>\n");
+			BEGIN(PROPNODENAME);
+			return DT_DEL_PROP;
+		}
+
+<*>"/delete-node/"	{
+			DPRINT("Keyword: /delete-node/\n");
+			DPRINT("<PROPNODENAME>\n");
+			BEGIN(PROPNODENAME);
+			return DT_DEL_NODE;
+		}
+
+<*>"/omit-if-no-ref/"	{
+			DPRINT("Keyword: /omit-if-no-ref/\n");
+			DPRINT("<PROPNODENAME>\n");
+			BEGIN(PROPNODENAME);
+			return DT_OMIT_NO_REF;
+		}
+
+<*>{LABEL}:	{
+			DPRINT("Label: %s\n", yytext);
+			yylval.labelref = xstrdup(yytext);
+			yylval.labelref[yyleng-1] = '\0';
+			return DT_LABEL;
+		}
+
+<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
+			char *e;
+			DPRINT("Integer Literal: '%s'\n", yytext);
+
+			errno = 0;
+			yylval.integer = strtoull(yytext, &e, 0);
+
+			if (*e && e[strspn(e, "UL")]) {
+				lexical_error("Bad integer literal '%s'",
+					      yytext);
+			}
+
+			if (errno == ERANGE)
+				lexical_error("Integer literal '%s' out of range",
+					      yytext);
+			else
+				/* ERANGE is the only strtoull error triggerable
+				 *  by strings matching the pattern */
+				assert(errno == 0);
+			return DT_LITERAL;
+		}
+
+<*>{CHAR_LITERAL}	{
+			struct data d;
+			DPRINT("Character literal: %s\n", yytext);
+
+			d = data_copy_escape_string(yytext+1, yyleng-2);
+			if (d.len == 1) {
+				lexical_error("Empty character literal");
+				yylval.integer = 0;
+			} else {
+				yylval.integer = (unsigned char)d.val[0];
+
+				if (d.len > 2)
+					lexical_error("Character literal has %d"
+						      " characters instead of 1",
+						      d.len - 1);
+			}
+
+			data_free(d);
+			return DT_CHAR_LITERAL;
+		}
+
+<*>\&{LABEL}	{	/* label reference */
+			DPRINT("Ref: %s\n", yytext+1);
+			yylval.labelref = xstrdup(yytext+1);
+			return DT_LABEL_REF;
+		}
+
+<*>"&{/"{PATHCHAR}*\}	{	/* new-style path reference */
+			yytext[yyleng-1] = '\0';
+			DPRINT("Ref: %s\n", yytext+2);
+			yylval.labelref = xstrdup(yytext+2);
+			return DT_PATH_REF;
+		}
+
+<BYTESTRING>[0-9a-fA-F]{2} {
+			yylval.byte = strtol(yytext, NULL, 16);
+			DPRINT("Byte: %02x\n", (int)yylval.byte);
+			return DT_BYTE;
+		}
+
+<BYTESTRING>"]"	{
+			DPRINT("/BYTESTRING\n");
+			BEGIN_DEFAULT();
+			return ']';
+		}
+
+<PROPNODENAME>\\?{PROPNODECHAR}+ {
+			DPRINT("PropNodeName: %s\n", yytext);
+			yylval.propnodename = xstrdup((yytext[0] == '\\') ?
+							yytext + 1 : yytext);
+			BEGIN_DEFAULT();
+			return DT_PROPNODENAME;
+		}
+
+"/incbin/"	{
+			DPRINT("Binary Include\n");
+			return DT_INCBIN;
+		}
+
+<*>{WS}+	/* eat whitespace */
+<*>{COMMENT}+	/* eat C-style comments */
+<*>{LINECOMMENT}+ /* eat C++-style comments */
+
+<*>"<<"		{ return DT_LSHIFT; };
+<*>">>"		{ return DT_RSHIFT; };
+<*>"<="		{ return DT_LE; };
+<*>">="		{ return DT_GE; };
+<*>"=="		{ return DT_EQ; };
+<*>"!="		{ return DT_NE; };
+<*>"&&"		{ return DT_AND; };
+<*>"||"		{ return DT_OR; };
+
+<*>.		{
+			DPRINT("Char: %c (\\x%02x)\n", yytext[0],
+				(unsigned)yytext[0]);
+			if (yytext[0] == '[') {
+				DPRINT("<BYTESTRING>\n");
+				BEGIN(BYTESTRING);
+			}
+			if ((yytext[0] == '{')
+			    || (yytext[0] == ';')) {
+				DPRINT("<PROPNODENAME>\n");
+				BEGIN(PROPNODENAME);
+			}
+			return yytext[0];
+		}
+
+%%
+
+static void push_input_file(const char *filename)
+{
+	assert(filename);
+
+	srcfile_push(filename);
+
+	yyin = current_srcfile->f;
+
+	yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
+}
+
+
+static bool pop_input_file(void)
+{
+	if (srcfile_pop() == 0)
+		return false;
+
+	yypop_buffer_state();
+	yyin = current_srcfile->f;
+
+	return true;
+}
+
+static void lexical_error(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	srcpos_verror(&yylloc, "Lexical error", fmt, ap);
+	va_end(ap);
+
+	treesource_error = true;
+}
diff --git a/dtc-parser.y b/dtc-parser.y
new file mode 100644
index 0000000..2ed4dc1
--- /dev/null
+++ b/dtc-parser.y
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+%{
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "dtc.h"
+#include "srcpos.h"
+
+extern int yylex(void);
+extern void yyerror(char const *s);
+#define ERROR(loc, ...) \
+	do { \
+		srcpos_error((loc), "Error", __VA_ARGS__); \
+		treesource_error = true; \
+	} while (0)
+
+extern struct dt_info *parser_output;
+extern bool treesource_error;
+%}
+
+%union {
+	char *propnodename;
+	char *labelref;
+	uint8_t byte;
+	struct data data;
+
+	struct {
+		struct data	data;
+		int		bits;
+	} array;
+
+	struct property *prop;
+	struct property *proplist;
+	struct node *node;
+	struct node *nodelist;
+	struct reserve_info *re;
+	uint64_t integer;
+	unsigned int flags;
+}
+
+%token DT_V1
+%token DT_PLUGIN
+%token DT_MEMRESERVE
+%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
+%token DT_BITS
+%token DT_DEL_PROP
+%token DT_DEL_NODE
+%token DT_OMIT_NO_REF
+%token <propnodename> DT_PROPNODENAME
+%token <integer> DT_LITERAL
+%token <integer> DT_CHAR_LITERAL
+%token <byte> DT_BYTE
+%token <data> DT_STRING
+%token <labelref> DT_LABEL
+%token <labelref> DT_LABEL_REF
+%token <labelref> DT_PATH_REF
+%token DT_INCBIN
+
+%type <data> propdata
+%type <data> propdataprefix
+%type <flags> header
+%type <flags> headers
+%type <re> memreserve
+%type <re> memreserves
+%type <array> arrayprefix
+%type <data> bytestring
+%type <prop> propdef
+%type <proplist> proplist
+%type <labelref> dt_ref
+
+%type <node> devicetree
+%type <node> nodedef
+%type <node> subnode
+%type <nodelist> subnodes
+
+%type <integer> integer_prim
+%type <integer> integer_unary
+%type <integer> integer_mul
+%type <integer> integer_add
+%type <integer> integer_shift
+%type <integer> integer_rela
+%type <integer> integer_eq
+%type <integer> integer_bitand
+%type <integer> integer_bitxor
+%type <integer> integer_bitor
+%type <integer> integer_and
+%type <integer> integer_or
+%type <integer> integer_trinary
+%type <integer> integer_expr
+
+%%
+
+sourcefile:
+	  headers memreserves devicetree
+		{
+			parser_output = build_dt_info($1, $2, $3,
+			                              guess_boot_cpuid($3));
+		}
+	;
+
+header:
+	  DT_V1 ';'
+		{
+			$$ = DTSF_V1;
+		}
+	| DT_V1 ';' DT_PLUGIN ';'
+		{
+			$$ = DTSF_V1 | DTSF_PLUGIN;
+		}
+	;
+
+headers:
+	  header
+	| header headers
+		{
+			if ($2 != $1)
+				ERROR(&@2, "Header flags don't match earlier ones");
+			$$ = $1;
+		}
+	;
+
+memreserves:
+	  /* empty */
+		{
+			$$ = NULL;
+		}
+	| memreserve memreserves
+		{
+			$$ = chain_reserve_entry($1, $2);
+		}
+	;
+
+memreserve:
+	  DT_MEMRESERVE integer_prim integer_prim ';'
+		{
+			$$ = build_reserve_entry($2, $3);
+		}
+	| DT_LABEL memreserve
+		{
+			add_label(&$2->labels, $1);
+			$$ = $2;
+		}
+	;
+
+dt_ref: DT_LABEL_REF | DT_PATH_REF;
+
+devicetree:
+	  '/' nodedef
+		{
+			$$ = name_node($2, "");
+		}
+	| devicetree '/' nodedef
+		{
+			$$ = merge_nodes($1, $3);
+		}
+	| dt_ref nodedef
+		{
+			/*
+			 * We rely on the rule being always:
+			 *   versioninfo plugindecl memreserves devicetree
+			 * so $-1 is what we want (plugindecl)
+			 */
+			if (!($<flags>-1 & DTSF_PLUGIN))
+				ERROR(&@2, "Label or path %s not found", $1);
+			$$ = add_orphan_node(
+					name_node(build_node(NULL, NULL, NULL),
+						  ""),
+					$2, $1);
+		}
+	| devicetree DT_LABEL dt_ref nodedef
+		{
+			struct node *target = get_node_by_ref($1, $3);
+
+			if (target) {
+				add_label(&target->labels, $2);
+				merge_nodes(target, $4);
+			} else
+				ERROR(&@3, "Label or path %s not found", $3);
+			$$ = $1;
+		}
+	| devicetree DT_PATH_REF nodedef
+		{
+			/*
+			 * We rely on the rule being always:
+			 *   versioninfo plugindecl memreserves devicetree
+			 * so $-1 is what we want (plugindecl)
+			 */
+			if ($<flags>-1 & DTSF_PLUGIN) {
+				add_orphan_node($1, $3, $2);
+			} else {
+				struct node *target = get_node_by_ref($1, $2);
+
+				if (target)
+					merge_nodes(target, $3);
+				else
+					ERROR(&@2, "Label or path %s not found", $2);
+			}
+			$$ = $1;
+		}
+	| devicetree DT_LABEL_REF nodedef
+		{
+			struct node *target = get_node_by_ref($1, $2);
+
+			if (target) {
+				merge_nodes(target, $3);
+			} else {
+				/*
+				 * We rely on the rule being always:
+				 *   versioninfo plugindecl memreserves devicetree
+				 * so $-1 is what we want (plugindecl)
+				 */
+				if ($<flags>-1 & DTSF_PLUGIN)
+					add_orphan_node($1, $3, $2);
+				else
+					ERROR(&@2, "Label or path %s not found", $2);
+			}
+			$$ = $1;
+		}
+	| devicetree DT_DEL_NODE dt_ref ';'
+		{
+			struct node *target = get_node_by_ref($1, $3);
+
+			if (target)
+				delete_node(target);
+			else
+				ERROR(&@3, "Label or path %s not found", $3);
+
+
+			$$ = $1;
+		}
+	| devicetree DT_OMIT_NO_REF dt_ref ';'
+		{
+			struct node *target = get_node_by_ref($1, $3);
+
+			if (target)
+				omit_node_if_unused(target);
+			else
+				ERROR(&@3, "Label or path %s not found", $3);
+
+
+			$$ = $1;
+		}
+	;
+
+nodedef:
+	  '{' proplist subnodes '}' ';'
+		{
+			$$ = build_node($2, $3, &@$);
+		}
+	;
+
+proplist:
+	  /* empty */
+		{
+			$$ = NULL;
+		}
+	| proplist propdef
+		{
+			$$ = chain_property($2, $1);
+		}
+	;
+
+propdef:
+	  DT_PROPNODENAME '=' propdata ';'
+		{
+			$$ = build_property($1, $3, &@$);
+		}
+	| DT_PROPNODENAME ';'
+		{
+			$$ = build_property($1, empty_data, &@$);
+		}
+	| DT_DEL_PROP DT_PROPNODENAME ';'
+		{
+			$$ = build_property_delete($2);
+		}
+	| DT_LABEL propdef
+		{
+			add_label(&$2->labels, $1);
+			$$ = $2;
+		}
+	;
+
+propdata:
+	  propdataprefix DT_STRING
+		{
+			$$ = data_merge($1, $2);
+		}
+	| propdataprefix arrayprefix '>'
+		{
+			$$ = data_merge($1, $2.data);
+		}
+	| propdataprefix '[' bytestring ']'
+		{
+			$$ = data_merge($1, $3);
+		}
+	| propdataprefix dt_ref
+		{
+			$1 = data_add_marker($1, TYPE_STRING, $2);
+			$$ = data_add_marker($1, REF_PATH, $2);
+		}
+	| propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')'
+		{
+			FILE *f = srcfile_relative_open($4.val, NULL);
+			struct data d;
+
+			if ($6 != 0)
+				if (fseek(f, $6, SEEK_SET) != 0)
+					die("Couldn't seek to offset %llu in \"%s\": %s",
+					    (unsigned long long)$6, $4.val,
+					    strerror(errno));
+
+			d = data_copy_file(f, $8);
+
+			$$ = data_merge($1, d);
+			fclose(f);
+		}
+	| propdataprefix DT_INCBIN '(' DT_STRING ')'
+		{
+			FILE *f = srcfile_relative_open($4.val, NULL);
+			struct data d = empty_data;
+
+			d = data_copy_file(f, -1);
+
+			$$ = data_merge($1, d);
+			fclose(f);
+		}
+	| propdata DT_LABEL
+		{
+			$$ = data_add_marker($1, LABEL, $2);
+		}
+	;
+
+propdataprefix:
+	  /* empty */
+		{
+			$$ = empty_data;
+		}
+	| propdata ','
+		{
+			$$ = $1;
+		}
+	| propdataprefix DT_LABEL
+		{
+			$$ = data_add_marker($1, LABEL, $2);
+		}
+	;
+
+arrayprefix:
+	DT_BITS DT_LITERAL '<'
+		{
+			unsigned long long bits;
+			enum markertype type = TYPE_UINT32;
+
+			bits = $2;
+
+			switch (bits) {
+			case 8: type = TYPE_UINT8; break;
+			case 16: type = TYPE_UINT16; break;
+			case 32: type = TYPE_UINT32; break;
+			case 64: type = TYPE_UINT64; break;
+			default:
+				ERROR(&@2, "Array elements must be"
+				      " 8, 16, 32 or 64-bits");
+				bits = 32;
+			}
+
+			$$.data = data_add_marker(empty_data, type, NULL);
+			$$.bits = bits;
+		}
+	| '<'
+		{
+			$$.data = data_add_marker(empty_data, TYPE_UINT32, NULL);
+			$$.bits = 32;
+		}
+	| arrayprefix integer_prim
+		{
+			if ($1.bits < 64) {
+				uint64_t mask = (1ULL << $1.bits) - 1;
+				/*
+				 * Bits above mask must either be all zero
+				 * (positive within range of mask) or all one
+				 * (negative and sign-extended). The second
+				 * condition is true if when we set all bits
+				 * within the mask to one (i.e. | in the
+				 * mask), all bits are one.
+				 */
+				if (($2 > mask) && (($2 | mask) != -1ULL))
+					ERROR(&@2, "Value out of range for"
+					      " %d-bit array element", $1.bits);
+			}
+
+			$$.data = data_append_integer($1.data, $2, $1.bits);
+		}
+	| arrayprefix dt_ref
+		{
+			uint64_t val = ~0ULL >> (64 - $1.bits);
+
+			if ($1.bits == 32)
+				$1.data = data_add_marker($1.data,
+							  REF_PHANDLE,
+							  $2);
+			else
+				ERROR(&@2, "References are only allowed in "
+					    "arrays with 32-bit elements.");
+
+			$$.data = data_append_integer($1.data, val, $1.bits);
+		}
+	| arrayprefix DT_LABEL
+		{
+			$$.data = data_add_marker($1.data, LABEL, $2);
+		}
+	;
+
+integer_prim:
+	  DT_LITERAL
+	| DT_CHAR_LITERAL
+	| '(' integer_expr ')'
+		{
+			$$ = $2;
+		}
+	;
+
+integer_expr:
+	integer_trinary
+	;
+
+integer_trinary:
+	  integer_or
+	| integer_or '?' integer_expr ':' integer_trinary { $$ = $1 ? $3 : $5; }
+	;
+
+integer_or:
+	  integer_and
+	| integer_or DT_OR integer_and { $$ = $1 || $3; }
+	;
+
+integer_and:
+	  integer_bitor
+	| integer_and DT_AND integer_bitor { $$ = $1 && $3; }
+	;
+
+integer_bitor:
+	  integer_bitxor
+	| integer_bitor '|' integer_bitxor { $$ = $1 | $3; }
+	;
+
+integer_bitxor:
+	  integer_bitand
+	| integer_bitxor '^' integer_bitand { $$ = $1 ^ $3; }
+	;
+
+integer_bitand:
+	  integer_eq
+	| integer_bitand '&' integer_eq { $$ = $1 & $3; }
+	;
+
+integer_eq:
+	  integer_rela
+	| integer_eq DT_EQ integer_rela { $$ = $1 == $3; }
+	| integer_eq DT_NE integer_rela { $$ = $1 != $3; }
+	;
+
+integer_rela:
+	  integer_shift
+	| integer_rela '<' integer_shift { $$ = $1 < $3; }
+	| integer_rela '>' integer_shift { $$ = $1 > $3; }
+	| integer_rela DT_LE integer_shift { $$ = $1 <= $3; }
+	| integer_rela DT_GE integer_shift { $$ = $1 >= $3; }
+	;
+
+integer_shift:
+	  integer_shift DT_LSHIFT integer_add { $$ = $1 << $3; }
+	| integer_shift DT_RSHIFT integer_add { $$ = $1 >> $3; }
+	| integer_add
+	;
+
+integer_add:
+	  integer_add '+' integer_mul { $$ = $1 + $3; }
+	| integer_add '-' integer_mul { $$ = $1 - $3; }
+	| integer_mul
+	;
+
+integer_mul:
+	  integer_mul '*' integer_unary { $$ = $1 * $3; }
+	| integer_mul '/' integer_unary
+		{
+			if ($3 != 0) {
+				$$ = $1 / $3;
+			} else {
+				ERROR(&@$, "Division by zero");
+				$$ = 0;
+			}
+		}
+	| integer_mul '%' integer_unary
+		{
+			if ($3 != 0) {
+				$$ = $1 % $3;
+			} else {
+				ERROR(&@$, "Division by zero");
+				$$ = 0;
+			}
+		}
+	| integer_unary
+	;
+
+integer_unary:
+	  integer_prim
+	| '-' integer_unary { $$ = -$2; }
+	| '~' integer_unary { $$ = ~$2; }
+	| '!' integer_unary { $$ = !$2; }
+	;
+
+bytestring:
+	  /* empty */
+		{
+			$$ = data_add_marker(empty_data, TYPE_UINT8, NULL);
+		}
+	| bytestring DT_BYTE
+		{
+			$$ = data_append_byte($1, $2);
+		}
+	| bytestring DT_LABEL
+		{
+			$$ = data_add_marker($1, LABEL, $2);
+		}
+	;
+
+subnodes:
+	  /* empty */
+		{
+			$$ = NULL;
+		}
+	| subnode subnodes
+		{
+			$$ = chain_node($1, $2);
+		}
+	| subnode propdef
+		{
+			ERROR(&@2, "Properties must precede subnodes");
+			YYERROR;
+		}
+	;
+
+subnode:
+	  DT_PROPNODENAME nodedef
+		{
+			$$ = name_node($2, $1);
+		}
+	| DT_DEL_NODE DT_PROPNODENAME ';'
+		{
+			$$ = name_node(build_node_delete(&@$), $2);
+		}
+	| DT_OMIT_NO_REF subnode
+		{
+			$$ = omit_node_if_unused($2);
+		}
+	| DT_LABEL subnode
+		{
+			add_label(&$2->labels, $1);
+			$$ = $2;
+		}
+	;
+
+%%
+
+void yyerror(char const *s)
+{
+	ERROR(&yylloc, "%s", s);
+}
diff --git a/dtc.c b/dtc.c
new file mode 100644
index 0000000..bdb3f59
--- /dev/null
+++ b/dtc.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include <sys/stat.h>
+
+#include "dtc.h"
+#include "srcpos.h"
+
+/*
+ * Command line options
+ */
+int quiet;		/* Level of quietness */
+int reservenum;		/* Number of memory reservation slots */
+int minsize;		/* Minimum blob size */
+int padsize;		/* Additional padding to blob */
+int alignsize;		/* Additional padding to blob accroding to the alignsize */
+int phandle_format = PHANDLE_EPAPR;	/* Use linux,phandle or phandle properties */
+int generate_symbols;	/* enable symbols & fixup support */
+int generate_fixups;		/* suppress generation of fixups on symbol support */
+int auto_label_aliases;		/* auto generate labels -> aliases */
+int annotate;		/* Level of annotation: 1 for input source location
+			   >1 for full input source location. */
+
+static int is_power_of_2(int x)
+{
+	return (x > 0) && ((x & (x - 1)) == 0);
+}
+
+static void fill_fullpaths(struct node *tree, const char *prefix)
+{
+	struct node *child;
+	const char *unit;
+
+	tree->fullpath = join_path(prefix, tree->name);
+
+	unit = strchr(tree->name, '@');
+	if (unit)
+		tree->basenamelen = unit - tree->name;
+	else
+		tree->basenamelen = strlen(tree->name);
+
+	for_each_child(tree, child)
+		fill_fullpaths(child, tree->fullpath);
+}
+
+/* Usage related data. */
+static const char usage_synopsis[] = "dtc [options] <input file>";
+static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@AThv";
+static struct option const usage_long_opts[] = {
+	{"quiet",            no_argument, NULL, 'q'},
+	{"in-format",         a_argument, NULL, 'I'},
+	{"out",               a_argument, NULL, 'o'},
+	{"out-format",        a_argument, NULL, 'O'},
+	{"out-version",       a_argument, NULL, 'V'},
+	{"out-dependency",    a_argument, NULL, 'd'},
+	{"reserve",           a_argument, NULL, 'R'},
+	{"space",             a_argument, NULL, 'S'},
+	{"pad",               a_argument, NULL, 'p'},
+	{"align",             a_argument, NULL, 'a'},
+	{"boot-cpu",          a_argument, NULL, 'b'},
+	{"force",            no_argument, NULL, 'f'},
+	{"include",           a_argument, NULL, 'i'},
+	{"sort",             no_argument, NULL, 's'},
+	{"phandle",           a_argument, NULL, 'H'},
+	{"warning",           a_argument, NULL, 'W'},
+	{"error",             a_argument, NULL, 'E'},
+	{"symbols",	     no_argument, NULL, '@'},
+	{"auto-alias",       no_argument, NULL, 'A'},
+	{"annotate",         no_argument, NULL, 'T'},
+	{"help",             no_argument, NULL, 'h'},
+	{"version",          no_argument, NULL, 'v'},
+	{NULL,               no_argument, NULL, 0x0},
+};
+static const char * const usage_opts_help[] = {
+	"\n\tQuiet: -q suppress warnings, -qq errors, -qqq all",
+	"\n\tInput formats are:\n"
+	 "\t\tdts - device tree source text\n"
+	 "\t\tdtb - device tree blob\n"
+	 "\t\tfs  - /proc/device-tree style directory",
+	"\n\tOutput file",
+	"\n\tOutput formats are:\n"
+	 "\t\tdts - device tree source text\n"
+	 "\t\tdtb - device tree blob\n"
+#ifndef NO_YAML
+	 "\t\tyaml - device tree encoded as YAML\n"
+#endif
+	 "\t\tasm - assembler source",
+	"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
+	"\n\tOutput dependency file",
+	"\n\tMake space for <number> reserve map entries (for dtb and asm output)",
+	"\n\tMake the blob at least <bytes> long (extra space)",
+	"\n\tAdd padding to the blob of <bytes> long (extra space)",
+	"\n\tMake the blob align to the <bytes> (extra space)",
+	"\n\tSet the physical boot cpu",
+	"\n\tTry to produce output even if the input tree has errors",
+	"\n\tAdd a path to search for include files",
+	"\n\tSort nodes and properties before outputting (useful for comparing trees)",
+	"\n\tValid phandle formats are:\n"
+	 "\t\tlegacy - \"linux,phandle\" properties only\n"
+	 "\t\tepapr  - \"phandle\" properties only\n"
+	 "\t\tboth   - Both \"linux,phandle\" and \"phandle\" properties",
+	"\n\tEnable/disable warnings (prefix with \"no-\")",
+	"\n\tEnable/disable errors (prefix with \"no-\")",
+	"\n\tEnable generation of symbols",
+	"\n\tEnable auto-alias of labels",
+	"\n\tAnnotate output .dts with input source file and line (-T -T for more details)",
+	"\n\tPrint this help and exit",
+	"\n\tPrint version and exit",
+	NULL,
+};
+
+static const char *guess_type_by_name(const char *fname, const char *fallback)
+{
+	const char *s;
+
+	s = strrchr(fname, '.');
+	if (s == NULL)
+		return fallback;
+	if (!strcasecmp(s, ".dts"))
+		return "dts";
+	if (!strcasecmp(s, ".yaml"))
+		return "yaml";
+	if (!strcasecmp(s, ".dtb"))
+		return "dtb";
+	return fallback;
+}
+
+static const char *guess_input_format(const char *fname, const char *fallback)
+{
+	struct stat statbuf;
+	fdt32_t magic;
+	FILE *f;
+
+	if (stat(fname, &statbuf) != 0)
+		return fallback;
+
+	if (S_ISDIR(statbuf.st_mode))
+		return "fs";
+
+	if (!S_ISREG(statbuf.st_mode))
+		return fallback;
+
+	f = fopen(fname, "r");
+	if (f == NULL)
+		return fallback;
+	if (fread(&magic, 4, 1, f) != 1) {
+		fclose(f);
+		return fallback;
+	}
+	fclose(f);
+
+	if (fdt32_to_cpu(magic) == FDT_MAGIC)
+		return "dtb";
+
+	return guess_type_by_name(fname, fallback);
+}
+
+int main(int argc, char *argv[])
+{
+	struct dt_info *dti;
+	const char *inform = NULL;
+	const char *outform = NULL;
+	const char *outname = "-";
+	const char *depname = NULL;
+	bool force = false, sort = false;
+	const char *arg;
+	int opt;
+	FILE *outf = NULL;
+	int outversion = DEFAULT_FDT_VERSION;
+	long long cmdline_boot_cpuid = -1;
+
+	quiet      = 0;
+	reservenum = 0;
+	minsize    = 0;
+	padsize    = 0;
+	alignsize  = 0;
+
+	while ((opt = util_getopt_long()) != EOF) {
+		switch (opt) {
+		case 'I':
+			inform = optarg;
+			break;
+		case 'O':
+			outform = optarg;
+			break;
+		case 'o':
+			outname = optarg;
+			break;
+		case 'V':
+			outversion = strtol(optarg, NULL, 0);
+			break;
+		case 'd':
+			depname = optarg;
+			break;
+		case 'R':
+			reservenum = strtol(optarg, NULL, 0);
+			break;
+		case 'S':
+			minsize = strtol(optarg, NULL, 0);
+			break;
+		case 'p':
+			padsize = strtol(optarg, NULL, 0);
+			break;
+		case 'a':
+			alignsize = strtol(optarg, NULL, 0);
+			if (!is_power_of_2(alignsize))
+				die("Invalid argument \"%d\" to -a option\n",
+				    alignsize);
+			break;
+		case 'f':
+			force = true;
+			break;
+		case 'q':
+			quiet++;
+			break;
+		case 'b':
+			cmdline_boot_cpuid = strtoll(optarg, NULL, 0);
+			break;
+		case 'i':
+			srcfile_add_search_path(optarg);
+			break;
+		case 'v':
+			util_version();
+		case 'H':
+			if (streq(optarg, "legacy"))
+				phandle_format = PHANDLE_LEGACY;
+			else if (streq(optarg, "epapr"))
+				phandle_format = PHANDLE_EPAPR;
+			else if (streq(optarg, "both"))
+				phandle_format = PHANDLE_BOTH;
+			else
+				die("Invalid argument \"%s\" to -H option\n",
+				    optarg);
+			break;
+
+		case 's':
+			sort = true;
+			break;
+
+		case 'W':
+			parse_checks_option(true, false, optarg);
+			break;
+
+		case 'E':
+			parse_checks_option(false, true, optarg);
+			break;
+
+		case '@':
+			generate_symbols = 1;
+			break;
+		case 'A':
+			auto_label_aliases = 1;
+			break;
+		case 'T':
+			annotate++;
+			break;
+
+		case 'h':
+			usage(NULL);
+		default:
+			usage("unknown option");
+		}
+	}
+
+	if (argc > (optind+1))
+		usage("missing files");
+	else if (argc < (optind+1))
+		arg = "-";
+	else
+		arg = argv[optind];
+
+	/* minsize and padsize are mutually exclusive */
+	if (minsize && padsize)
+		die("Can't set both -p and -S\n");
+
+	if (depname) {
+		depfile = fopen(depname, "w");
+		if (!depfile)
+			die("Couldn't open dependency file %s: %s\n", depname,
+			    strerror(errno));
+		fprintf(depfile, "%s:", outname);
+	}
+
+	if (inform == NULL)
+		inform = guess_input_format(arg, "dts");
+	if (outform == NULL) {
+		outform = guess_type_by_name(outname, NULL);
+		if (outform == NULL) {
+			if (streq(inform, "dts"))
+				outform = "dtb";
+			else
+				outform = "dts";
+		}
+	}
+	if (annotate && (!streq(inform, "dts") || !streq(outform, "dts")))
+		die("--annotate requires -I dts -O dts\n");
+	if (streq(inform, "dts"))
+		dti = dt_from_source(arg);
+	else if (streq(inform, "fs"))
+		dti = dt_from_fs(arg);
+	else if(streq(inform, "dtb"))
+		dti = dt_from_blob(arg);
+	else
+		die("Unknown input format \"%s\"\n", inform);
+
+	dti->outname = outname;
+
+	if (depfile) {
+		fputc('\n', depfile);
+		fclose(depfile);
+	}
+
+	if (cmdline_boot_cpuid != -1)
+		dti->boot_cpuid_phys = cmdline_boot_cpuid;
+
+	fill_fullpaths(dti->dt, "");
+
+	/* on a plugin, generate by default */
+	if (dti->dtsflags & DTSF_PLUGIN) {
+		generate_fixups = 1;
+	}
+
+	process_checks(force, dti);
+
+	if (auto_label_aliases)
+		generate_label_tree(dti, "aliases", false);
+
+	if (generate_symbols)
+		generate_label_tree(dti, "__symbols__", true);
+
+	if (generate_fixups) {
+		generate_fixups_tree(dti, "__fixups__");
+		generate_local_fixups_tree(dti, "__local_fixups__");
+	}
+
+	if (sort)
+		sort_tree(dti);
+
+	if (streq(outname, "-")) {
+		outf = stdout;
+	} else {
+		outf = fopen(outname, "wb");
+		if (! outf)
+			die("Couldn't open output file %s: %s\n",
+			    outname, strerror(errno));
+	}
+
+	if (streq(outform, "dts")) {
+		dt_to_source(outf, dti);
+#ifndef NO_YAML
+	} else if (streq(outform, "yaml")) {
+		if (!streq(inform, "dts"))
+			die("YAML output format requires dts input format\n");
+		dt_to_yaml(outf, dti);
+#endif
+	} else if (streq(outform, "dtb")) {
+		dt_to_blob(outf, dti, outversion);
+	} else if (streq(outform, "asm")) {
+		dt_to_asm(outf, dti, outversion);
+	} else if (streq(outform, "null")) {
+		/* do nothing */
+	} else {
+		die("Unknown output format \"%s\"\n", outform);
+	}
+
+	exit(0);
+}
diff --git a/dtc.h b/dtc.h
new file mode 100644
index 0000000..6e74ece
--- /dev/null
+++ b/dtc.h
@@ -0,0 +1,301 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef DTC_H
+#define DTC_H
+
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <libfdt_env.h>
+#include <fdt.h>
+
+#include "util.h"
+
+#ifdef DEBUG
+#define debug(...)	printf(__VA_ARGS__)
+#else
+#define debug(...)
+#endif
+
+#define DEFAULT_FDT_VERSION	17
+
+/*
+ * Command line options
+ */
+extern int quiet;		/* Level of quietness */
+extern int reservenum;		/* Number of memory reservation slots */
+extern int minsize;		/* Minimum blob size */
+extern int padsize;		/* Additional padding to blob */
+extern int alignsize;		/* Additional padding to blob accroding to the alignsize */
+extern int phandle_format;	/* Use linux,phandle or phandle properties */
+extern int generate_symbols;	/* generate symbols for nodes with labels */
+extern int generate_fixups;	/* generate fixups */
+extern int auto_label_aliases;	/* auto generate labels -> aliases */
+extern int annotate;		/* annotate .dts with input source location */
+
+#define PHANDLE_LEGACY	0x1
+#define PHANDLE_EPAPR	0x2
+#define PHANDLE_BOTH	0x3
+
+typedef uint32_t cell_t;
+
+
+#define streq(a, b)	(strcmp((a), (b)) == 0)
+#define strstarts(s, prefix)	(strncmp((s), (prefix), strlen(prefix)) == 0)
+#define strprefixeq(a, n, b)	(strlen(b) == (n) && (memcmp(a, b, n) == 0))
+
+#define ALIGN(x, a)	(((x) + (a) - 1) & ~((a) - 1))
+
+/* Data blobs */
+enum markertype {
+	TYPE_NONE,
+	REF_PHANDLE,
+	REF_PATH,
+	LABEL,
+	TYPE_UINT8,
+	TYPE_UINT16,
+	TYPE_UINT32,
+	TYPE_UINT64,
+	TYPE_STRING,
+};
+extern const char *markername(enum markertype markertype);
+
+struct  marker {
+	enum markertype type;
+	int offset;
+	char *ref;
+	struct marker *next;
+};
+
+struct data {
+	int len;
+	char *val;
+	struct marker *markers;
+};
+
+
+#define empty_data ((struct data){ 0 /* all .members = 0 or NULL */ })
+
+#define for_each_marker(m) \
+	for (; (m); (m) = (m)->next)
+#define for_each_marker_of_type(m, t) \
+	for_each_marker(m) \
+		if ((m)->type == (t))
+
+size_t type_marker_length(struct marker *m);
+
+void data_free(struct data d);
+
+struct data data_grow_for(struct data d, int xlen);
+
+struct data data_copy_mem(const char *mem, int len);
+struct data data_copy_escape_string(const char *s, int len);
+struct data data_copy_file(FILE *f, size_t len);
+
+struct data data_append_data(struct data d, const void *p, int len);
+struct data data_insert_at_marker(struct data d, struct marker *m,
+				  const void *p, int len);
+struct data data_merge(struct data d1, struct data d2);
+struct data data_append_cell(struct data d, cell_t word);
+struct data data_append_integer(struct data d, uint64_t word, int bits);
+struct data data_append_re(struct data d, uint64_t address, uint64_t size);
+struct data data_append_addr(struct data d, uint64_t addr);
+struct data data_append_byte(struct data d, uint8_t byte);
+struct data data_append_zeroes(struct data d, int len);
+struct data data_append_align(struct data d, int align);
+
+struct data data_add_marker(struct data d, enum markertype type, char *ref);
+
+bool data_is_one_string(struct data d);
+
+/* DT constraints */
+
+#define MAX_PROPNAME_LEN	31
+#define MAX_NODENAME_LEN	31
+
+/* Live trees */
+struct label {
+	bool deleted;
+	char *label;
+	struct label *next;
+};
+
+struct bus_type {
+	const char *name;
+};
+
+struct property {
+	bool deleted;
+	char *name;
+	struct data val;
+
+	struct property *next;
+
+	struct label *labels;
+	struct srcpos *srcpos;
+};
+
+struct node {
+	bool deleted;
+	char *name;
+	struct property *proplist;
+	struct node *children;
+
+	struct node *parent;
+	struct node *next_sibling;
+
+	char *fullpath;
+	int basenamelen;
+
+	cell_t phandle;
+	int addr_cells, size_cells;
+
+	struct label *labels;
+	const struct bus_type *bus;
+	struct srcpos *srcpos;
+
+	bool omit_if_unused, is_referenced;
+};
+
+#define for_each_label_withdel(l0, l) \
+	for ((l) = (l0); (l); (l) = (l)->next)
+
+#define for_each_label(l0, l) \
+	for_each_label_withdel(l0, l) \
+		if (!(l)->deleted)
+
+#define for_each_property_withdel(n, p) \
+	for ((p) = (n)->proplist; (p); (p) = (p)->next)
+
+#define for_each_property(n, p) \
+	for_each_property_withdel(n, p) \
+		if (!(p)->deleted)
+
+#define for_each_child_withdel(n, c) \
+	for ((c) = (n)->children; (c); (c) = (c)->next_sibling)
+
+#define for_each_child(n, c) \
+	for_each_child_withdel(n, c) \
+		if (!(c)->deleted)
+
+void add_label(struct label **labels, char *label);
+void delete_labels(struct label **labels);
+
+struct property *build_property(char *name, struct data val,
+				struct srcpos *srcpos);
+struct property *build_property_delete(char *name);
+struct property *chain_property(struct property *first, struct property *list);
+struct property *reverse_properties(struct property *first);
+
+struct node *build_node(struct property *proplist, struct node *children,
+			struct srcpos *srcpos);
+struct node *build_node_delete(struct srcpos *srcpos);
+struct node *name_node(struct node *node, char *name);
+struct node *omit_node_if_unused(struct node *node);
+struct node *reference_node(struct node *node);
+struct node *chain_node(struct node *first, struct node *list);
+struct node *merge_nodes(struct node *old_node, struct node *new_node);
+struct node *add_orphan_node(struct node *old_node, struct node *new_node, char *ref);
+
+void add_property(struct node *node, struct property *prop);
+void delete_property_by_name(struct node *node, char *name);
+void delete_property(struct property *prop);
+void add_child(struct node *parent, struct node *child);
+void delete_node_by_name(struct node *parent, char *name);
+void delete_node(struct node *node);
+void append_to_property(struct node *node,
+			char *name, const void *data, int len,
+			enum markertype type);
+
+const char *get_unitname(struct node *node);
+struct property *get_property(struct node *node, const char *propname);
+cell_t propval_cell(struct property *prop);
+cell_t propval_cell_n(struct property *prop, int n);
+struct property *get_property_by_label(struct node *tree, const char *label,
+				       struct node **node);
+struct marker *get_marker_label(struct node *tree, const char *label,
+				struct node **node, struct property **prop);
+struct node *get_subnode(struct node *node, const char *nodename);
+struct node *get_node_by_path(struct node *tree, const char *path);
+struct node *get_node_by_label(struct node *tree, const char *label);
+struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
+struct node *get_node_by_ref(struct node *tree, const char *ref);
+cell_t get_node_phandle(struct node *root, struct node *node);
+
+uint32_t guess_boot_cpuid(struct node *tree);
+
+/* Boot info (tree plus memreserve information */
+
+struct reserve_info {
+	uint64_t address, size;
+
+	struct reserve_info *next;
+
+	struct label *labels;
+};
+
+struct reserve_info *build_reserve_entry(uint64_t start, uint64_t len);
+struct reserve_info *chain_reserve_entry(struct reserve_info *first,
+					 struct reserve_info *list);
+struct reserve_info *add_reserve_entry(struct reserve_info *list,
+				       struct reserve_info *new);
+
+
+struct dt_info {
+	unsigned int dtsflags;
+	struct reserve_info *reservelist;
+	uint32_t boot_cpuid_phys;
+	struct node *dt;		/* the device tree */
+	const char *outname;		/* filename being written to, "-" for stdout */
+};
+
+/* DTS version flags definitions */
+#define DTSF_V1		0x0001	/* /dts-v1/ */
+#define DTSF_PLUGIN	0x0002	/* /plugin/ */
+
+struct dt_info *build_dt_info(unsigned int dtsflags,
+			      struct reserve_info *reservelist,
+			      struct node *tree, uint32_t boot_cpuid_phys);
+void sort_tree(struct dt_info *dti);
+void generate_label_tree(struct dt_info *dti, char *name, bool allocph);
+void generate_fixups_tree(struct dt_info *dti, char *name);
+void generate_local_fixups_tree(struct dt_info *dti, char *name);
+
+/* Checks */
+
+void parse_checks_option(bool warn, bool error, const char *arg);
+void process_checks(bool force, struct dt_info *dti);
+
+/* Flattened trees */
+
+void dt_to_blob(FILE *f, struct dt_info *dti, int version);
+void dt_to_asm(FILE *f, struct dt_info *dti, int version);
+
+struct dt_info *dt_from_blob(const char *fname);
+
+/* Tree source */
+
+void dt_to_source(FILE *f, struct dt_info *dti);
+struct dt_info *dt_from_source(const char *f);
+
+/* YAML source */
+
+void dt_to_yaml(FILE *f, struct dt_info *dti);
+
+/* FS trees */
+
+struct dt_info *dt_from_fs(const char *dirname);
+
+#endif /* DTC_H */
diff --git a/dtdiff b/dtdiff
new file mode 100644
index 0000000..cdbf079
--- /dev/null
+++ b/dtdiff
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# This script uses the bash <(...) extension.
+# If you want to change this to work with a generic /bin/sh, make sure
+# you fix that.
+
+
+DTC=dtc
+
+source_and_sort () {
+    DT="$1"
+    if [ -d "$DT" ]; then
+	IFORMAT=fs
+    elif [ -f "$DT" ]; then
+	case "$DT" in
+	    *.dts)
+		IFORMAT=dts
+		;;
+	    *.dtb)
+		IFORMAT=dtb
+		;;
+	esac
+    fi
+
+    if [ -z "$IFORMAT" ]; then
+	echo "Unrecognized format for $DT" >&2
+	exit 2
+    fi
+
+    $DTC -I $IFORMAT -O dts -qq -f -s -o - "$DT"
+}
+
+if [ $# != 2 ]; then
+    echo "Usage: dtdiff <device tree> <device tree>" >&2
+    exit 1
+fi
+
+diff -u <(source_and_sort "$1") <(source_and_sort "$2")
diff --git a/fdtdump.c b/fdtdump.c
new file mode 100644
index 0000000..9613bef
--- /dev/null
+++ b/fdtdump.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include <libfdt.h>
+#include <libfdt_env.h>
+#include <fdt.h>
+
+#include "util.h"
+
+#define FDT_MAGIC_SIZE	4
+#define MAX_VERSION 17
+
+#define ALIGN(x, a)	(((x) + ((a) - 1)) & ~((a) - 1))
+#define PALIGN(p, a)	((void *)(ALIGN((unsigned long)(p), (a))))
+#define GET_CELL(p)	(p += 4, *((const fdt32_t *)(p-4)))
+
+static const char *tagname(uint32_t tag)
+{
+	static const char * const names[] = {
+#define TN(t) [t] = #t
+		TN(FDT_BEGIN_NODE),
+		TN(FDT_END_NODE),
+		TN(FDT_PROP),
+		TN(FDT_NOP),
+		TN(FDT_END),
+#undef TN
+	};
+	if (tag < ARRAY_SIZE(names))
+		if (names[tag])
+			return names[tag];
+	return "FDT_???";
+}
+
+#define dumpf(fmt, args...) \
+	do { if (debug) printf("// " fmt, ## args); } while (0)
+
+static void dump_blob(void *blob, bool debug)
+{
+	uintptr_t blob_off = (uintptr_t)blob;
+	struct fdt_header *bph = blob;
+	uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap);
+	uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct);
+	uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings);
+	struct fdt_reserve_entry *p_rsvmap =
+		(struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
+	const char *p_struct = (const char *)blob + off_dt;
+	const char *p_strings = (const char *)blob + off_str;
+	uint32_t version = fdt32_to_cpu(bph->version);
+	uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
+	uint32_t tag;
+	const char *p, *s, *t;
+	int depth, sz, shift;
+	int i;
+	uint64_t addr, size;
+
+	depth = 0;
+	shift = 4;
+
+	printf("/dts-v1/;\n");
+	printf("// magic:\t\t0x%"PRIx32"\n", fdt32_to_cpu(bph->magic));
+	printf("// totalsize:\t\t0x%"PRIx32" (%"PRIu32")\n",
+	       totalsize, totalsize);
+	printf("// off_dt_struct:\t0x%"PRIx32"\n", off_dt);
+	printf("// off_dt_strings:\t0x%"PRIx32"\n", off_str);
+	printf("// off_mem_rsvmap:\t0x%"PRIx32"\n", off_mem_rsvmap);
+	printf("// version:\t\t%"PRIu32"\n", version);
+	printf("// last_comp_version:\t%"PRIu32"\n",
+	       fdt32_to_cpu(bph->last_comp_version));
+	if (version >= 2)
+		printf("// boot_cpuid_phys:\t0x%"PRIx32"\n",
+		       fdt32_to_cpu(bph->boot_cpuid_phys));
+
+	if (version >= 3)
+		printf("// size_dt_strings:\t0x%"PRIx32"\n",
+		       fdt32_to_cpu(bph->size_dt_strings));
+	if (version >= 17)
+		printf("// size_dt_struct:\t0x%"PRIx32"\n",
+		       fdt32_to_cpu(bph->size_dt_struct));
+	printf("\n");
+
+	for (i = 0; ; i++) {
+		addr = fdt64_to_cpu(p_rsvmap[i].address);
+		size = fdt64_to_cpu(p_rsvmap[i].size);
+		if (addr == 0 && size == 0)
+			break;
+
+		printf("/memreserve/ %#"PRIx64" %#"PRIx64";\n",
+		       addr, size);
+	}
+
+	p = p_struct;
+	while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
+
+		dumpf("%04"PRIxPTR": tag: 0x%08"PRIx32" (%s)\n",
+		        (uintptr_t)p - blob_off - 4, tag, tagname(tag));
+
+		if (tag == FDT_BEGIN_NODE) {
+			s = p;
+			p = PALIGN(p + strlen(s) + 1, 4);
+
+			if (*s == '\0')
+				s = "/";
+
+			printf("%*s%s {\n", depth * shift, "", s);
+
+			depth++;
+			continue;
+		}
+
+		if (tag == FDT_END_NODE) {
+			depth--;
+
+			printf("%*s};\n", depth * shift, "");
+			continue;
+		}
+
+		if (tag == FDT_NOP) {
+			printf("%*s// [NOP]\n", depth * shift, "");
+			continue;
+		}
+
+		if (tag != FDT_PROP) {
+			fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
+			break;
+		}
+		sz = fdt32_to_cpu(GET_CELL(p));
+		s = p_strings + fdt32_to_cpu(GET_CELL(p));
+		if (version < 16 && sz >= 8)
+			p = PALIGN(p, 8);
+		t = p;
+
+		p = PALIGN(p + sz, 4);
+
+		dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
+		dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
+		printf("%*s%s", depth * shift, "", s);
+		utilfdt_print_data(t, sz);
+		printf(";\n");
+	}
+}
+
+/* Usage related data. */
+static const char usage_synopsis[] = "fdtdump [options] <file>";
+static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
+static struct option const usage_long_opts[] = {
+	{"debug",            no_argument, NULL, 'd'},
+	{"scan",             no_argument, NULL, 's'},
+	USAGE_COMMON_LONG_OPTS
+};
+static const char * const usage_opts_help[] = {
+	"Dump debug information while decoding the file",
+	"Scan for an embedded fdt in file",
+	USAGE_COMMON_OPTS_HELP
+};
+
+static bool valid_header(char *p, off_t len)
+{
+	if (len < sizeof(struct fdt_header) ||
+	    fdt_magic(p) != FDT_MAGIC ||
+	    fdt_version(p) > MAX_VERSION ||
+	    fdt_last_comp_version(p) > MAX_VERSION ||
+	    fdt_totalsize(p) >= len ||
+	    fdt_off_dt_struct(p) >= len ||
+	    fdt_off_dt_strings(p) >= len)
+		return 0;
+	else
+		return 1;
+}
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	const char *file;
+	char *buf;
+	bool debug = false;
+	bool scan = false;
+	size_t len;
+
+	fprintf(stderr, "\n"
+"**** fdtdump is a low-level debugging tool, not meant for general use.\n"
+"**** If you want to decompile a dtb, you probably want\n"
+"****     dtc -I dtb -O dts <filename>\n\n"
+		);
+	while ((opt = util_getopt_long()) != EOF) {
+		switch (opt) {
+		case_USAGE_COMMON_FLAGS
+
+		case 'd':
+			debug = true;
+			break;
+		case 's':
+			scan = true;
+			break;
+		}
+	}
+	if (optind != argc - 1)
+		usage("missing input filename");
+	file = argv[optind];
+
+	buf = utilfdt_read(file, &len);
+	if (!buf)
+		die("could not read: %s\n", file);
+
+	/* try and locate an embedded fdt in a bigger blob */
+	if (scan) {
+		unsigned char smagic[FDT_MAGIC_SIZE];
+		char *p = buf;
+		char *endp = buf + len;
+
+		fdt_set_magic(smagic, FDT_MAGIC);
+
+		/* poor man's memmem */
+		while ((endp - p) >= FDT_MAGIC_SIZE) {
+			p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
+			if (!p)
+				break;
+			if (fdt_magic(p) == FDT_MAGIC) {
+				/* try and validate the main struct */
+				off_t this_len = endp - p;
+				if (valid_header(p, this_len))
+					break;
+				if (debug)
+					printf("%s: skipping fdt magic at offset %#tx\n",
+						file, p - buf);
+			}
+			++p;
+		}
+		if (!p || endp - p < sizeof(struct fdt_header))
+			die("%s: could not locate fdt magic\n", file);
+		printf("%s: found fdt at offset %#tx\n", file, p - buf);
+		buf = p;
+	} else if (!valid_header(buf, len))
+		die("%s: header is not valid\n", file);
+
+	dump_blob(buf, debug);
+
+	return 0;
+}
diff --git a/fdtget.c b/fdtget.c
new file mode 100644
index 0000000..777582e
--- /dev/null
+++ b/fdtget.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * Portions from U-Boot cmd_fdt.c (C) Copyright 2007
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ * Based on code written by:
+ *   Pantelis Antoniou <pantelis.antoniou@gmail.com> and
+ *   Matthew McClintock <msm@freescale.com>
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+enum display_mode {
+	MODE_SHOW_VALUE,	/* show values for node properties */
+	MODE_LIST_PROPS,	/* list the properties for a node */
+	MODE_LIST_SUBNODES,	/* list the subnodes of a node */
+};
+
+/* Holds information which controls our output and options */
+struct display_info {
+	int type;		/* data type (s/i/u/x or 0 for default) */
+	int size;		/* data size (1/2/4) */
+	enum display_mode mode;	/* display mode that we are using */
+	const char *default_val; /* default value if node/property not found */
+};
+
+static void report_error(const char *where, int err)
+{
+	fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
+}
+
+/**
+ * Shows a list of cells in the requested format
+ *
+ * @param disp		Display information / options
+ * @param data		Data to display
+ * @param len		Maximum length of buffer
+ * @param size		Data size to use for display (e.g. 4 for 32-bit)
+ * @return 0 if ok, -1 on error
+ */
+static int show_cell_list(struct display_info *disp, const char *data, int len,
+			  int size)
+{
+	const uint8_t *p = (const uint8_t *)data;
+	char fmt[3];
+	int value;
+	int i;
+
+	fmt[0] = '%';
+	fmt[1] = disp->type ? disp->type : 'd';
+	fmt[2] = '\0';
+	for (i = 0; i < len; i += size, p += size) {
+		if (i)
+			printf(" ");
+		value = size == 4 ? fdt32_ld((const fdt32_t *)p) :
+			size == 2 ? (*p << 8) | p[1] : *p;
+		printf(fmt, value);
+	}
+
+	return 0;
+}
+
+/**
+ * Displays data of a given length according to selected options
+ *
+ * If a specific data type is provided in disp, then this is used. Otherwise
+ * we try to guess the data type / size from the contents.
+ *
+ * @param disp		Display information / options
+ * @param data		Data to display
+ * @param len		Maximum length of buffer
+ * @return 0 if ok, -1 if data does not match format
+ */
+static int show_data(struct display_info *disp, const char *data, int len)
+{
+	int size;
+	const char *s;
+	int is_string;
+
+	/* no data, don't print */
+	if (len == 0)
+		return 0;
+
+	is_string = (disp->type) == 's' ||
+		(!disp->type && util_is_printable_string(data, len));
+	if (is_string) {
+		if (data[len - 1] != '\0') {
+			fprintf(stderr, "Unterminated string\n");
+			return -1;
+		}
+		for (s = data; s - data < len; s += strlen(s) + 1) {
+			if (s != data)
+				printf(" ");
+			printf("%s", (const char *)s);
+		}
+		return 0;
+	}
+	size = disp->size;
+	if (size == -1) {
+		size = (len % 4) == 0 ? 4 : 1;
+	} else if (len % size) {
+		fprintf(stderr, "Property length must be a multiple of "
+				"selected data size\n");
+		return -1;
+	}
+
+	return show_cell_list(disp, data, len, size);
+}
+
+/**
+ * List all properties in a node, one per line.
+ *
+ * @param blob		FDT blob
+ * @param node		Node to display
+ * @return 0 if ok, or FDT_ERR... if not.
+ */
+static int list_properties(const void *blob, int node)
+{
+	const char *name;
+	int prop;
+
+	prop = fdt_first_property_offset(blob, node);
+	do {
+		/* Stop silently when there are no more properties */
+		if (prop < 0)
+			return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
+		fdt_getprop_by_offset(blob, prop, &name, NULL);
+		if (name)
+			puts(name);
+		prop = fdt_next_property_offset(blob, prop);
+	} while (1);
+}
+
+#define MAX_LEVEL	32		/* how deeply nested we will go */
+
+/**
+ * List all subnodes in a node, one per line
+ *
+ * @param blob		FDT blob
+ * @param node		Node to display
+ * @return 0 if ok, or FDT_ERR... if not.
+ */
+static int list_subnodes(const void *blob, int node)
+{
+	int nextoffset;		/* next node offset from libfdt */
+	uint32_t tag;		/* current tag */
+	int level = 0;		/* keep track of nesting level */
+	const char *pathp;
+	int depth = 1;		/* the assumed depth of this node */
+
+	while (level >= 0) {
+		tag = fdt_next_tag(blob, node, &nextoffset);
+		switch (tag) {
+		case FDT_BEGIN_NODE:
+			pathp = fdt_get_name(blob, node, NULL);
+			if (level <= depth) {
+				if (pathp == NULL)
+					pathp = "/* NULL pointer error */";
+				if (*pathp == '\0')
+					pathp = "/";	/* root is nameless */
+				if (level == 1)
+					puts(pathp);
+			}
+			level++;
+			if (level >= MAX_LEVEL) {
+				printf("Nested too deep, aborting.\n");
+				return 1;
+			}
+			break;
+		case FDT_END_NODE:
+			level--;
+			if (level == 0)
+				level = -1;		/* exit the loop */
+			break;
+		case FDT_END:
+			return 1;
+		case FDT_PROP:
+			break;
+		default:
+			if (level <= depth)
+				printf("Unknown tag 0x%08X\n", tag);
+			return 1;
+		}
+		node = nextoffset;
+	}
+	return 0;
+}
+
+/**
+ * Show the data for a given node (and perhaps property) according to the
+ * display option provided.
+ *
+ * @param blob		FDT blob
+ * @param disp		Display information / options
+ * @param node		Node to display
+ * @param property	Name of property to display, or NULL if none
+ * @return 0 if ok, -ve on error
+ */
+static int show_data_for_item(const void *blob, struct display_info *disp,
+		int node, const char *property)
+{
+	const void *value = NULL;
+	int len, err = 0;
+
+	switch (disp->mode) {
+	case MODE_LIST_PROPS:
+		err = list_properties(blob, node);
+		break;
+
+	case MODE_LIST_SUBNODES:
+		err = list_subnodes(blob, node);
+		break;
+
+	default:
+		assert(property);
+		value = fdt_getprop(blob, node, property, &len);
+		if (value) {
+			if (show_data(disp, value, len))
+				err = -1;
+			else
+				printf("\n");
+		} else if (disp->default_val) {
+			puts(disp->default_val);
+		} else {
+			report_error(property, len);
+			err = -1;
+		}
+		break;
+	}
+
+	return err;
+}
+
+/**
+ * Run the main fdtget operation, given a filename and valid arguments
+ *
+ * @param disp		Display information / options
+ * @param filename	Filename of blob file
+ * @param arg		List of arguments to process
+ * @param arg_count	Number of arguments
+ * @return 0 if ok, -ve on error
+ */
+static int do_fdtget(struct display_info *disp, const char *filename,
+		     char **arg, int arg_count, int args_per_step)
+{
+	char *blob;
+	const char *prop;
+	int i, node;
+
+	blob = utilfdt_read(filename, NULL);
+	if (!blob)
+		return -1;
+
+	for (i = 0; i + args_per_step <= arg_count; i += args_per_step) {
+		node = fdt_path_offset(blob, arg[i]);
+		if (node < 0) {
+			if (disp->default_val) {
+				puts(disp->default_val);
+				continue;
+			} else {
+				report_error(arg[i], node);
+				free(blob);
+				return -1;
+			}
+		}
+		prop = args_per_step == 1 ? NULL : arg[i + 1];
+
+		if (show_data_for_item(blob, disp, node, prop)) {
+			free(blob);
+			return -1;
+		}
+	}
+
+	free(blob);
+
+	return 0;
+}
+
+/* Usage related data. */
+static const char usage_synopsis[] =
+	"read values from device tree\n"
+	"	fdtget <options> <dt file> [<node> <property>]...\n"
+	"	fdtget -p <options> <dt file> [<node> ]...\n"
+	"\n"
+	"Each value is printed on a new line.\n"
+	USAGE_TYPE_MSG;
+static const char usage_short_opts[] = "t:pld:" USAGE_COMMON_SHORT_OPTS;
+static struct option const usage_long_opts[] = {
+	{"type",              a_argument, NULL, 't'},
+	{"properties",       no_argument, NULL, 'p'},
+	{"list",             no_argument, NULL, 'l'},
+	{"default",           a_argument, NULL, 'd'},
+	USAGE_COMMON_LONG_OPTS,
+};
+static const char * const usage_opts_help[] = {
+	"Type of data",
+	"List properties for each node",
+	"List subnodes for each node",
+	"Default value to display when the property is missing",
+	USAGE_COMMON_OPTS_HELP
+};
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	char *filename = NULL;
+	struct display_info disp;
+	int args_per_step = 2;
+
+	/* set defaults */
+	memset(&disp, '\0', sizeof(disp));
+	disp.size = -1;
+	disp.mode = MODE_SHOW_VALUE;
+	while ((opt = util_getopt_long()) != EOF) {
+		switch (opt) {
+		case_USAGE_COMMON_FLAGS
+
+		case 't':
+			if (utilfdt_decode_type(optarg, &disp.type,
+					&disp.size))
+				usage("invalid type string");
+			break;
+
+		case 'p':
+			disp.mode = MODE_LIST_PROPS;
+			args_per_step = 1;
+			break;
+
+		case 'l':
+			disp.mode = MODE_LIST_SUBNODES;
+			args_per_step = 1;
+			break;
+
+		case 'd':
+			disp.default_val = optarg;
+			break;
+		}
+	}
+
+	if (optind < argc)
+		filename = argv[optind++];
+	if (!filename)
+		usage("missing filename");
+
+	argv += optind;
+	argc -= optind;
+
+	/* Allow no arguments, and silently succeed */
+	if (!argc)
+		return 0;
+
+	/* Check for node, property arguments */
+	if (args_per_step == 2 && (argc % 2))
+		usage("must have an even number of arguments");
+
+	if (do_fdtget(&disp, filename, argv, argc, args_per_step))
+		return 1;
+	return 0;
+}
diff --git a/fdtoverlay.c b/fdtoverlay.c
new file mode 100644
index 0000000..8a1d11b
--- /dev/null
+++ b/fdtoverlay.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Konsulko Group Inc. All rights reserved.
+ *
+ * Author:
+ *	 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+#define BUF_INCREMENT	65536
+
+/* Usage related data. */
+static const char usage_synopsis[] =
+	"apply a number of overlays to a base blob\n"
+	"	fdtoverlay <options> [<overlay.dtbo> [<overlay.dtbo>]]\n"
+	"\n"
+	USAGE_TYPE_MSG;
+static const char usage_short_opts[] = "i:o:v" USAGE_COMMON_SHORT_OPTS;
+static struct option const usage_long_opts[] = {
+	{"input",            required_argument, NULL, 'i'},
+	{"output",	     required_argument, NULL, 'o'},
+	{"verbose",	           no_argument, NULL, 'v'},
+	USAGE_COMMON_LONG_OPTS,
+};
+static const char * const usage_opts_help[] = {
+	"Input base DT blob",
+	"Output DT blob",
+	"Verbose messages",
+	USAGE_COMMON_OPTS_HELP
+};
+
+int verbose = 0;
+
+static void *apply_one(char *base, const char *overlay, size_t *buf_len,
+		       const char *name)
+{
+	char *tmp = NULL;
+	char *tmpo;
+	int ret;
+
+	/*
+	 * We take a copies first, because a a failed apply can trash
+	 * both the base blob and the overlay
+	 */
+	tmpo = xmalloc(fdt_totalsize(overlay));
+
+	do {
+		tmp = xrealloc(tmp, *buf_len);
+		ret = fdt_open_into(base, tmp, *buf_len);
+		if (ret) {
+			fprintf(stderr,
+				"\nFailed to make temporary copy: %s\n",
+				fdt_strerror(ret));
+			goto fail;
+		}
+
+		memcpy(tmpo, overlay, fdt_totalsize(overlay));
+
+		ret = fdt_overlay_apply(tmp, tmpo);
+		if (ret == -FDT_ERR_NOSPACE) {
+			*buf_len += BUF_INCREMENT;
+		}
+	} while (ret == -FDT_ERR_NOSPACE);
+
+	if (ret) {
+		fprintf(stderr, "\nFailed to apply '%s': %s\n",
+			name, fdt_strerror(ret));
+		goto fail;
+	}
+
+	free(base);
+	free(tmpo);
+	return tmp;
+
+fail:
+	free(tmpo);
+	if (tmp)
+		free(tmp);
+
+	return NULL;
+}
+static int do_fdtoverlay(const char *input_filename,
+			 const char *output_filename,
+			 int argc, char *argv[])
+{
+	char *blob = NULL;
+	char **ovblob = NULL;
+	size_t buf_len;
+	int i, ret = -1;
+
+	blob = utilfdt_read(input_filename, &buf_len);
+	if (!blob) {
+		fprintf(stderr, "\nFailed to read '%s'\n", input_filename);
+		goto out_err;
+	}
+	if (fdt_totalsize(blob) > buf_len) {
+		fprintf(stderr,
+ "\nBase blob is incomplete (%lu / %" PRIu32 " bytes read)\n",
+			(unsigned long)buf_len, fdt_totalsize(blob));
+		goto out_err;
+	}
+	ret = 0;
+
+	/* allocate blob pointer array */
+	ovblob = xmalloc(sizeof(*ovblob) * argc);
+	memset(ovblob, 0, sizeof(*ovblob) * argc);
+
+	/* read and keep track of the overlay blobs */
+	for (i = 0; i < argc; i++) {
+		size_t ov_len;
+		ovblob[i] = utilfdt_read(argv[i], &ov_len);
+		if (!ovblob[i]) {
+			fprintf(stderr, "\nFailed to read '%s'\n", argv[i]);
+			goto out_err;
+		}
+		if (fdt_totalsize(ovblob[i]) > ov_len) {
+			fprintf(stderr,
+"\nOverlay '%s' is incomplete (%lu / %" PRIu32 " bytes read)\n",
+				argv[i], (unsigned long)ov_len,
+				fdt_totalsize(ovblob[i]));
+			goto out_err;
+		}
+	}
+
+	buf_len = fdt_totalsize(blob);
+
+	/* apply the overlays in sequence */
+	for (i = 0; i < argc; i++) {
+		blob = apply_one(blob, ovblob[i], &buf_len, argv[i]);
+		if (!blob)
+			goto out_err;
+	}
+
+	fdt_pack(blob);
+	ret = utilfdt_write(output_filename, blob);
+	if (ret)
+		fprintf(stderr, "\nFailed to write '%s'\n",
+			output_filename);
+
+out_err:
+	if (ovblob) {
+		for (i = 0; i < argc; i++) {
+			if (ovblob[i])
+				free(ovblob[i]);
+		}
+		free(ovblob);
+	}
+	free(blob);
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, i;
+	char *input_filename = NULL;
+	char *output_filename = NULL;
+
+	while ((opt = util_getopt_long()) != EOF) {
+		switch (opt) {
+		case_USAGE_COMMON_FLAGS
+
+		case 'i':
+			input_filename = optarg;
+			break;
+		case 'o':
+			output_filename = optarg;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		}
+	}
+
+	if (!input_filename)
+		usage("missing input file");
+
+	if (!output_filename)
+		usage("missing output file");
+
+	argv += optind;
+	argc -= optind;
+
+	if (argc <= 0)
+		usage("missing overlay file(s)");
+
+	if (verbose) {
+		printf("input  = %s\n", input_filename);
+		printf("output = %s\n", output_filename);
+		for (i = 0; i < argc; i++)
+			printf("overlay[%d] = %s\n", i, argv[i]);
+	}
+
+	if (do_fdtoverlay(input_filename, output_filename, argc, argv))
+		return 1;
+
+	return 0;
+}
diff --git a/fdtput.c b/fdtput.c
new file mode 100644
index 0000000..428745a
--- /dev/null
+++ b/fdtput.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+/* These are the operations we support */
+enum oper_type {
+	OPER_WRITE_PROP,		/* Write a property in a node */
+	OPER_CREATE_NODE,		/* Create a new node */
+	OPER_REMOVE_NODE,		/* Delete a node */
+	OPER_DELETE_PROP,		/* Delete a property in a node */
+};
+
+struct display_info {
+	enum oper_type oper;	/* operation to perform */
+	int type;		/* data type (s/i/u/x or 0 for default) */
+	int size;		/* data size (1/2/4) */
+	int verbose;		/* verbose output */
+	int auto_path;		/* automatically create all path components */
+};
+
+
+/**
+ * Report an error with a particular node.
+ *
+ * @param name		Node name to report error on
+ * @param namelen	Length of node name, or -1 to use entire string
+ * @param err		Error number to report (-FDT_ERR_...)
+ */
+static void report_error(const char *name, int namelen, int err)
+{
+	if (namelen == -1)
+		namelen = strlen(name);
+	fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
+		fdt_strerror(err));
+}
+
+/**
+ * Encode a series of arguments in a property value.
+ *
+ * @param disp		Display information / options
+ * @param arg		List of arguments from command line
+ * @param arg_count	Number of arguments (may be 0)
+ * @param valuep	Returns buffer containing value
+ * @param value_len	Returns length of value encoded
+ */
+static int encode_value(struct display_info *disp, char **arg, int arg_count,
+			char **valuep, int *value_len)
+{
+	char *value = NULL;	/* holding area for value */
+	int value_size = 0;	/* size of holding area */
+	char *ptr;		/* pointer to current value position */
+	int len;		/* length of this cell/string/byte */
+	int ival;
+	int upto;	/* the number of bytes we have written to buf */
+	char fmt[3];
+
+	upto = 0;
+
+	if (disp->verbose)
+		fprintf(stderr, "Decoding value:\n");
+
+	fmt[0] = '%';
+	fmt[1] = disp->type ? disp->type : 'd';
+	fmt[2] = '\0';
+	for (; arg_count > 0; arg++, arg_count--, upto += len) {
+		/* assume integer unless told otherwise */
+		if (disp->type == 's')
+			len = strlen(*arg) + 1;
+		else
+			len = disp->size == -1 ? 4 : disp->size;
+
+		/* enlarge our value buffer by a suitable margin if needed */
+		if (upto + len > value_size) {
+			value_size = (upto + len) + 500;
+			value = xrealloc(value, value_size);
+		}
+
+		ptr = value + upto;
+		if (disp->type == 's') {
+			memcpy(ptr, *arg, len);
+			if (disp->verbose)
+				fprintf(stderr, "\tstring: '%s'\n", ptr);
+		} else {
+			fdt32_t *iptr = (fdt32_t *)ptr;
+			sscanf(*arg, fmt, &ival);
+			if (len == 4)
+				*iptr = cpu_to_fdt32(ival);
+			else
+				*ptr = (uint8_t)ival;
+			if (disp->verbose) {
+				fprintf(stderr, "\t%s: %d\n",
+					disp->size == 1 ? "byte" :
+					disp->size == 2 ? "short" : "int",
+					ival);
+			}
+		}
+	}
+	*value_len = upto;
+	*valuep = value;
+	if (disp->verbose)
+		fprintf(stderr, "Value size %d\n", upto);
+	return 0;
+}
+
+#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
+
+static char *realloc_fdt(char *fdt, int delta)
+{
+	int new_sz = fdt_totalsize(fdt) + delta;
+	fdt = xrealloc(fdt, new_sz);
+	fdt_open_into(fdt, fdt, new_sz);
+	return fdt;
+}
+
+static char *realloc_node(char *fdt, const char *name)
+{
+	int delta;
+	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
+	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
+			+ FDT_TAGSIZE;
+	return realloc_fdt(fdt, delta);
+}
+
+static char *realloc_property(char *fdt, int nodeoffset,
+		const char *name, int newlen)
+{
+	int delta = 0;
+	int oldlen = 0;
+
+	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
+		/* strings + property header */
+		delta = sizeof(struct fdt_property) + strlen(name) + 1;
+
+	if (newlen > oldlen)
+		/* actual value in off_struct */
+		delta += ALIGN(newlen) - ALIGN(oldlen);
+
+	return realloc_fdt(fdt, delta);
+}
+
+static int store_key_value(char **blob, const char *node_name,
+		const char *property, const char *buf, int len)
+{
+	int node;
+	int err;
+
+	node = fdt_path_offset(*blob, node_name);
+	if (node < 0) {
+		report_error(node_name, -1, node);
+		return -1;
+	}
+
+	err = fdt_setprop(*blob, node, property, buf, len);
+	if (err == -FDT_ERR_NOSPACE) {
+		*blob = realloc_property(*blob, node, property, len);
+		err = fdt_setprop(*blob, node, property, buf, len);
+	}
+	if (err) {
+		report_error(property, -1, err);
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * Create paths as needed for all components of a path
+ *
+ * Any components of the path that do not exist are created. Errors are
+ * reported.
+ *
+ * @param blob		FDT blob to write into
+ * @param in_path	Path to process
+ * @return 0 if ok, -1 on error
+ */
+static int create_paths(char **blob, const char *in_path)
+{
+	const char *path = in_path;
+	const char *sep;
+	int node, offset = 0;
+
+	/* skip leading '/' */
+	while (*path == '/')
+		path++;
+
+	for (sep = path; *sep; path = sep + 1, offset = node) {
+		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
+		sep = strchr(path, '/');
+		if (!sep)
+			sep = path + strlen(path);
+
+		node = fdt_subnode_offset_namelen(*blob, offset, path,
+				sep - path);
+		if (node == -FDT_ERR_NOTFOUND) {
+			*blob = realloc_node(*blob, path);
+			node = fdt_add_subnode_namelen(*blob, offset, path,
+						       sep - path);
+		}
+		if (node < 0) {
+			report_error(path, sep - path, node);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Create a new node in the fdt.
+ *
+ * This will overwrite the node_name string. Any error is reported.
+ *
+ * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
+ *
+ * @param blob		FDT blob to write into
+ * @param node_name	Name of node to create
+ * @return new node offset if found, or -1 on failure
+ */
+static int create_node(char **blob, const char *node_name)
+{
+	int node = 0;
+	char *p;
+
+	p = strrchr(node_name, '/');
+	if (!p) {
+		report_error(node_name, -1, -FDT_ERR_BADPATH);
+		return -1;
+	}
+	*p = '\0';
+
+	*blob = realloc_node(*blob, p + 1);
+
+	if (p > node_name) {
+		node = fdt_path_offset(*blob, node_name);
+		if (node < 0) {
+			report_error(node_name, -1, node);
+			return -1;
+		}
+	}
+
+	node = fdt_add_subnode(*blob, node, p + 1);
+	if (node < 0) {
+		report_error(p + 1, -1, node);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Delete a property of a node in the fdt.
+ *
+ * @param blob		FDT blob to write into
+ * @param node_name	Path to node containing the property to delete
+ * @param prop_name	Name of property to delete
+ * @return 0 on success, or -1 on failure
+ */
+static int delete_prop(char *blob, const char *node_name, const char *prop_name)
+{
+	int node = 0;
+
+	node = fdt_path_offset(blob, node_name);
+	if (node < 0) {
+		report_error(node_name, -1, node);
+		return -1;
+	}
+
+	node = fdt_delprop(blob, node, prop_name);
+	if (node < 0) {
+		report_error(node_name, -1, node);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Delete a node in the fdt.
+ *
+ * @param blob		FDT blob to write into
+ * @param node_name	Name of node to delete
+ * @return 0 on success, or -1 on failure
+ */
+static int delete_node(char *blob, const char *node_name)
+{
+	int node = 0;
+
+	node = fdt_path_offset(blob, node_name);
+	if (node < 0) {
+		report_error(node_name, -1, node);
+		return -1;
+	}
+
+	node = fdt_del_node(blob, node);
+	if (node < 0) {
+		report_error(node_name, -1, node);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int do_fdtput(struct display_info *disp, const char *filename,
+		    char **arg, int arg_count)
+{
+	char *value = NULL;
+	char *blob;
+	char *node;
+	int len, ret = 0;
+
+	blob = utilfdt_read(filename, NULL);
+	if (!blob)
+		return -1;
+
+	switch (disp->oper) {
+	case OPER_WRITE_PROP:
+		/*
+		 * Convert the arguments into a single binary value, then
+		 * store them into the property.
+		 */
+		assert(arg_count >= 2);
+		if (disp->auto_path && create_paths(&blob, *arg))
+			return -1;
+		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
+			store_key_value(&blob, *arg, arg[1], value, len))
+			ret = -1;
+		break;
+	case OPER_CREATE_NODE:
+		for (; ret >= 0 && arg_count--; arg++) {
+			if (disp->auto_path)
+				ret = create_paths(&blob, *arg);
+			else
+				ret = create_node(&blob, *arg);
+		}
+		break;
+	case OPER_REMOVE_NODE:
+		for (; ret >= 0 && arg_count--; arg++)
+			ret = delete_node(blob, *arg);
+		break;
+	case OPER_DELETE_PROP:
+		node = *arg;
+		for (arg++; ret >= 0 && arg_count-- > 1; arg++)
+			ret = delete_prop(blob, node, *arg);
+		break;
+	}
+	if (ret >= 0) {
+		fdt_pack(blob);
+		ret = utilfdt_write(filename, blob);
+	}
+
+	free(blob);
+
+	if (value) {
+		free(value);
+	}
+
+	return ret;
+}
+
+/* Usage related data. */
+static const char usage_synopsis[] =
+	"write a property value to a device tree\n"
+	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
+	"	fdtput -c <options> <dt file> [<node>...]\n"
+	"	fdtput -r <options> <dt file> [<node>...]\n"
+	"	fdtput -d <options> <dt file> <node> [<property>...]\n"
+	"\n"
+	"The command line arguments are joined together into a single value.\n"
+	USAGE_TYPE_MSG;
+static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
+static struct option const usage_long_opts[] = {
+	{"create",           no_argument, NULL, 'c'},
+	{"remove",	     no_argument, NULL, 'r'},
+	{"delete",	     no_argument, NULL, 'd'},
+	{"auto-path",        no_argument, NULL, 'p'},
+	{"type",              a_argument, NULL, 't'},
+	{"verbose",          no_argument, NULL, 'v'},
+	USAGE_COMMON_LONG_OPTS,
+};
+static const char * const usage_opts_help[] = {
+	"Create nodes if they don't already exist",
+	"Delete nodes (and any subnodes) if they already exist",
+	"Delete properties if they already exist",
+	"Automatically create nodes as needed for the node path",
+	"Type of data",
+	"Display each value decoded from command line",
+	USAGE_COMMON_OPTS_HELP
+};
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	struct display_info disp;
+	char *filename = NULL;
+
+	memset(&disp, '\0', sizeof(disp));
+	disp.size = -1;
+	disp.oper = OPER_WRITE_PROP;
+	while ((opt = util_getopt_long()) != EOF) {
+		/*
+		 * TODO: add options to:
+		 * - rename node
+		 * - pack fdt before writing
+		 * - set amount of free space when writing
+		 */
+		switch (opt) {
+		case_USAGE_COMMON_FLAGS
+
+		case 'c':
+			disp.oper = OPER_CREATE_NODE;
+			break;
+		case 'r':
+			disp.oper = OPER_REMOVE_NODE;
+			break;
+		case 'd':
+			disp.oper = OPER_DELETE_PROP;
+			break;
+		case 'p':
+			disp.auto_path = 1;
+			break;
+		case 't':
+			if (utilfdt_decode_type(optarg, &disp.type,
+					&disp.size))
+				usage("Invalid type string");
+			break;
+
+		case 'v':
+			disp.verbose = 1;
+			break;
+		}
+	}
+
+	if (optind < argc)
+		filename = argv[optind++];
+	if (!filename)
+		usage("missing filename");
+
+	argv += optind;
+	argc -= optind;
+
+	if (disp.oper == OPER_WRITE_PROP) {
+		if (argc < 1)
+			usage("missing node");
+		if (argc < 2)
+			usage("missing property");
+	}
+
+	if (disp.oper == OPER_DELETE_PROP)
+		if (argc < 1)
+			usage("missing node");
+
+	if (do_fdtput(&disp, filename, argv, argc))
+		return 1;
+	return 0;
+}
diff --git a/flattree.c b/flattree.c
new file mode 100644
index 0000000..bd6977e
--- /dev/null
+++ b/flattree.c
@@ -0,0 +1,925 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include "dtc.h"
+#include "srcpos.h"
+
+#define FTF_FULLPATH	0x1
+#define FTF_VARALIGN	0x2
+#define FTF_NAMEPROPS	0x4
+#define FTF_BOOTCPUID	0x8
+#define FTF_STRTABSIZE	0x10
+#define FTF_STRUCTSIZE	0x20
+#define FTF_NOPS	0x40
+
+static struct version_info {
+	int version;
+	int last_comp_version;
+	int hdr_size;
+	int flags;
+} version_table[] = {
+	{1, 1, FDT_V1_SIZE,
+	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS},
+	{2, 1, FDT_V2_SIZE,
+	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID},
+	{3, 1, FDT_V3_SIZE,
+	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE},
+	{16, 16, FDT_V3_SIZE,
+	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS},
+	{17, 16, FDT_V17_SIZE,
+	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS},
+};
+
+struct emitter {
+	void (*cell)(void *, cell_t);
+	void (*string)(void *, const char *, int);
+	void (*align)(void *, int);
+	void (*data)(void *, struct data);
+	void (*beginnode)(void *, struct label *labels);
+	void (*endnode)(void *, struct label *labels);
+	void (*property)(void *, struct label *labels);
+};
+
+static void bin_emit_cell(void *e, cell_t val)
+{
+	struct data *dtbuf = e;
+
+	*dtbuf = data_append_cell(*dtbuf, val);
+}
+
+static void bin_emit_string(void *e, const char *str, int len)
+{
+	struct data *dtbuf = e;
+
+	if (len == 0)
+		len = strlen(str);
+
+	*dtbuf = data_append_data(*dtbuf, str, len);
+	*dtbuf = data_append_byte(*dtbuf, '\0');
+}
+
+static void bin_emit_align(void *e, int a)
+{
+	struct data *dtbuf = e;
+
+	*dtbuf = data_append_align(*dtbuf, a);
+}
+
+static void bin_emit_data(void *e, struct data d)
+{
+	struct data *dtbuf = e;
+
+	*dtbuf = data_append_data(*dtbuf, d.val, d.len);
+}
+
+static void bin_emit_beginnode(void *e, struct label *labels)
+{
+	bin_emit_cell(e, FDT_BEGIN_NODE);
+}
+
+static void bin_emit_endnode(void *e, struct label *labels)
+{
+	bin_emit_cell(e, FDT_END_NODE);
+}
+
+static void bin_emit_property(void *e, struct label *labels)
+{
+	bin_emit_cell(e, FDT_PROP);
+}
+
+static struct emitter bin_emitter = {
+	.cell = bin_emit_cell,
+	.string = bin_emit_string,
+	.align = bin_emit_align,
+	.data = bin_emit_data,
+	.beginnode = bin_emit_beginnode,
+	.endnode = bin_emit_endnode,
+	.property = bin_emit_property,
+};
+
+static void emit_label(FILE *f, const char *prefix, const char *label)
+{
+	fprintf(f, "\t.globl\t%s_%s\n", prefix, label);
+	fprintf(f, "%s_%s:\n", prefix, label);
+	fprintf(f, "_%s_%s:\n", prefix, label);
+}
+
+static void emit_offset_label(FILE *f, const char *label, int offset)
+{
+	fprintf(f, "\t.globl\t%s\n", label);
+	fprintf(f, "%s\t= . + %d\n", label, offset);
+}
+
+#define ASM_EMIT_BELONG(f, fmt, ...) \
+	{ \
+		fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \
+		fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \
+		fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \
+		fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \
+	}
+
+static void asm_emit_cell(void *e, cell_t val)
+{
+	FILE *f = e;
+
+	fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n",
+		(val >> 24) & 0xff, (val >> 16) & 0xff,
+		(val >> 8) & 0xff, val & 0xff);
+}
+
+static void asm_emit_string(void *e, const char *str, int len)
+{
+	FILE *f = e;
+
+	if (len != 0)
+		fprintf(f, "\t.string\t\"%.*s\"\n", len, str);
+	else
+		fprintf(f, "\t.string\t\"%s\"\n", str);
+}
+
+static void asm_emit_align(void *e, int a)
+{
+	FILE *f = e;
+
+	fprintf(f, "\t.balign\t%d, 0\n", a);
+}
+
+static void asm_emit_data(void *e, struct data d)
+{
+	FILE *f = e;
+	int off = 0;
+	struct marker *m = d.markers;
+
+	for_each_marker_of_type(m, LABEL)
+		emit_offset_label(f, m->ref, m->offset);
+
+	while ((d.len - off) >= sizeof(uint32_t)) {
+		asm_emit_cell(e, fdt32_to_cpu(*((fdt32_t *)(d.val+off))));
+		off += sizeof(uint32_t);
+	}
+
+	while ((d.len - off) >= 1) {
+		fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]);
+		off += 1;
+	}
+
+	assert(off == d.len);
+}
+
+static void asm_emit_beginnode(void *e, struct label *labels)
+{
+	FILE *f = e;
+	struct label *l;
+
+	for_each_label(labels, l) {
+		fprintf(f, "\t.globl\t%s\n", l->label);
+		fprintf(f, "%s:\n", l->label);
+	}
+	fprintf(f, "\t/* FDT_BEGIN_NODE */\n");
+	asm_emit_cell(e, FDT_BEGIN_NODE);
+}
+
+static void asm_emit_endnode(void *e, struct label *labels)
+{
+	FILE *f = e;
+	struct label *l;
+
+	fprintf(f, "\t/* FDT_END_NODE */\n");
+	asm_emit_cell(e, FDT_END_NODE);
+	for_each_label(labels, l) {
+		fprintf(f, "\t.globl\t%s_end\n", l->label);
+		fprintf(f, "%s_end:\n", l->label);
+	}
+}
+
+static void asm_emit_property(void *e, struct label *labels)
+{
+	FILE *f = e;
+	struct label *l;
+
+	for_each_label(labels, l) {
+		fprintf(f, "\t.globl\t%s\n", l->label);
+		fprintf(f, "%s:\n", l->label);
+	}
+	fprintf(f, "\t/* FDT_PROP */\n");
+	asm_emit_cell(e, FDT_PROP);
+}
+
+static struct emitter asm_emitter = {
+	.cell = asm_emit_cell,
+	.string = asm_emit_string,
+	.align = asm_emit_align,
+	.data = asm_emit_data,
+	.beginnode = asm_emit_beginnode,
+	.endnode = asm_emit_endnode,
+	.property = asm_emit_property,
+};
+
+static int stringtable_insert(struct data *d, const char *str)
+{
+	int i;
+
+	/* FIXME: do this more efficiently? */
+
+	for (i = 0; i < d->len; i++) {
+		if (streq(str, d->val + i))
+			return i;
+	}
+
+	*d = data_append_data(*d, str, strlen(str)+1);
+	return i;
+}
+
+static void flatten_tree(struct node *tree, struct emitter *emit,
+			 void *etarget, struct data *strbuf,
+			 struct version_info *vi)
+{
+	struct property *prop;
+	struct node *child;
+	bool seen_name_prop = false;
+
+	if (tree->deleted)
+		return;
+
+	emit->beginnode(etarget, tree->labels);
+
+	if (vi->flags & FTF_FULLPATH)
+		emit->string(etarget, tree->fullpath, 0);
+	else
+		emit->string(etarget, tree->name, 0);
+
+	emit->align(etarget, sizeof(cell_t));
+
+	for_each_property(tree, prop) {
+		int nameoff;
+
+		if (streq(prop->name, "name"))
+			seen_name_prop = true;
+
+		nameoff = stringtable_insert(strbuf, prop->name);
+
+		emit->property(etarget, prop->labels);
+		emit->cell(etarget, prop->val.len);
+		emit->cell(etarget, nameoff);
+
+		if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8))
+			emit->align(etarget, 8);
+
+		emit->data(etarget, prop->val);
+		emit->align(etarget, sizeof(cell_t));
+	}
+
+	if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) {
+		emit->property(etarget, NULL);
+		emit->cell(etarget, tree->basenamelen+1);
+		emit->cell(etarget, stringtable_insert(strbuf, "name"));
+
+		if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8))
+			emit->align(etarget, 8);
+
+		emit->string(etarget, tree->name, tree->basenamelen);
+		emit->align(etarget, sizeof(cell_t));
+	}
+
+	for_each_child(tree, child) {
+		flatten_tree(child, emit, etarget, strbuf, vi);
+	}
+
+	emit->endnode(etarget, tree->labels);
+}
+
+static struct data flatten_reserve_list(struct reserve_info *reservelist,
+				 struct version_info *vi)
+{
+	struct reserve_info *re;
+	struct data d = empty_data;
+	int    j;
+
+	for (re = reservelist; re; re = re->next) {
+		d = data_append_re(d, re->address, re->size);
+	}
+	/*
+	 * Add additional reserved slots if the user asked for them.
+	 */
+	for (j = 0; j < reservenum; j++) {
+		d = data_append_re(d, 0, 0);
+	}
+
+	return d;
+}
+
+static void make_fdt_header(struct fdt_header *fdt,
+			    struct version_info *vi,
+			    int reservesize, int dtsize, int strsize,
+			    int boot_cpuid_phys)
+{
+	int reserve_off;
+
+	reservesize += sizeof(struct fdt_reserve_entry);
+
+	memset(fdt, 0xff, sizeof(*fdt));
+
+	fdt->magic = cpu_to_fdt32(FDT_MAGIC);
+	fdt->version = cpu_to_fdt32(vi->version);
+	fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version);
+
+	/* Reserve map should be doubleword aligned */
+	reserve_off = ALIGN(vi->hdr_size, 8);
+
+	fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off);
+	fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize);
+	fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize
+					  + dtsize);
+	fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize);
+
+	if (vi->flags & FTF_BOOTCPUID)
+		fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys);
+	if (vi->flags & FTF_STRTABSIZE)
+		fdt->size_dt_strings = cpu_to_fdt32(strsize);
+	if (vi->flags & FTF_STRUCTSIZE)
+		fdt->size_dt_struct = cpu_to_fdt32(dtsize);
+}
+
+void dt_to_blob(FILE *f, struct dt_info *dti, int version)
+{
+	struct version_info *vi = NULL;
+	int i;
+	struct data blob       = empty_data;
+	struct data reservebuf = empty_data;
+	struct data dtbuf      = empty_data;
+	struct data strbuf     = empty_data;
+	struct fdt_header fdt;
+	int padlen = 0;
+
+	for (i = 0; i < ARRAY_SIZE(version_table); i++) {
+		if (version_table[i].version == version)
+			vi = &version_table[i];
+	}
+	if (!vi)
+		die("Unknown device tree blob version %d\n", version);
+
+	flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
+	bin_emit_cell(&dtbuf, FDT_END);
+
+	reservebuf = flatten_reserve_list(dti->reservelist, vi);
+
+	/* Make header */
+	make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
+			dti->boot_cpuid_phys);
+
+	/*
+	 * If the user asked for more space than is used, adjust the totalsize.
+	 */
+	if (minsize > 0) {
+		padlen = minsize - fdt32_to_cpu(fdt.totalsize);
+		if (padlen < 0) {
+			padlen = 0;
+			if (quiet < 1)
+				fprintf(stderr,
+					"Warning: blob size %"PRIu32" >= minimum size %d\n",
+					fdt32_to_cpu(fdt.totalsize), minsize);
+		}
+	}
+
+	if (padsize > 0)
+		padlen = padsize;
+
+	if (alignsize > 0)
+		padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize)
+			- fdt32_to_cpu(fdt.totalsize);
+
+	if (padlen > 0) {
+		int tsize = fdt32_to_cpu(fdt.totalsize);
+		tsize += padlen;
+		fdt.totalsize = cpu_to_fdt32(tsize);
+	}
+
+	/*
+	 * Assemble the blob: start with the header, add with alignment
+	 * the reserve buffer, add the reserve map terminating zeroes,
+	 * the device tree itself, and finally the strings.
+	 */
+	blob = data_append_data(blob, &fdt, vi->hdr_size);
+	blob = data_append_align(blob, 8);
+	blob = data_merge(blob, reservebuf);
+	blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry));
+	blob = data_merge(blob, dtbuf);
+	blob = data_merge(blob, strbuf);
+
+	/*
+	 * If the user asked for more space than is used, pad out the blob.
+	 */
+	if (padlen > 0)
+		blob = data_append_zeroes(blob, padlen);
+
+	if (fwrite(blob.val, blob.len, 1, f) != 1) {
+		if (ferror(f))
+			die("Error writing device tree blob: %s\n",
+			    strerror(errno));
+		else
+			die("Short write on device tree blob\n");
+	}
+
+	/*
+	 * data_merge() frees the right-hand element so only the blob
+	 * remains to be freed.
+	 */
+	data_free(blob);
+}
+
+static void dump_stringtable_asm(FILE *f, struct data strbuf)
+{
+	const char *p;
+	int len;
+
+	p = strbuf.val;
+
+	while (p < (strbuf.val + strbuf.len)) {
+		len = strlen(p);
+		fprintf(f, "\t.string \"%s\"\n", p);
+		p += len+1;
+	}
+}
+
+void dt_to_asm(FILE *f, struct dt_info *dti, int version)
+{
+	struct version_info *vi = NULL;
+	int i;
+	struct data strbuf = empty_data;
+	struct reserve_info *re;
+	const char *symprefix = "dt";
+
+	for (i = 0; i < ARRAY_SIZE(version_table); i++) {
+		if (version_table[i].version == version)
+			vi = &version_table[i];
+	}
+	if (!vi)
+		die("Unknown device tree blob version %d\n", version);
+
+	fprintf(f, "/* autogenerated by dtc, do not edit */\n\n");
+
+	emit_label(f, symprefix, "blob_start");
+	emit_label(f, symprefix, "header");
+	fprintf(f, "\t/* magic */\n");
+	asm_emit_cell(f, FDT_MAGIC);
+	fprintf(f, "\t/* totalsize */\n");
+	ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start",
+			symprefix, symprefix);
+	fprintf(f, "\t/* off_dt_struct */\n");
+	ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start",
+		symprefix, symprefix);
+	fprintf(f, "\t/* off_dt_strings */\n");
+	ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start",
+		symprefix, symprefix);
+	fprintf(f, "\t/* off_mem_rsvmap */\n");
+	ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start",
+		symprefix, symprefix);
+	fprintf(f, "\t/* version */\n");
+	asm_emit_cell(f, vi->version);
+	fprintf(f, "\t/* last_comp_version */\n");
+	asm_emit_cell(f, vi->last_comp_version);
+
+	if (vi->flags & FTF_BOOTCPUID) {
+		fprintf(f, "\t/* boot_cpuid_phys */\n");
+		asm_emit_cell(f, dti->boot_cpuid_phys);
+	}
+
+	if (vi->flags & FTF_STRTABSIZE) {
+		fprintf(f, "\t/* size_dt_strings */\n");
+		ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start",
+				symprefix, symprefix);
+	}
+
+	if (vi->flags & FTF_STRUCTSIZE) {
+		fprintf(f, "\t/* size_dt_struct */\n");
+		ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start",
+			symprefix, symprefix);
+	}
+
+	/*
+	 * Reserve map entries.
+	 * Align the reserve map to a doubleword boundary.
+	 * Each entry is an (address, size) pair of u64 values.
+	 * Always supply a zero-sized temination entry.
+	 */
+	asm_emit_align(f, 8);
+	emit_label(f, symprefix, "reserve_map");
+
+	fprintf(f, "/* Memory reserve map from source file */\n");
+
+	/*
+	 * Use .long on high and low halves of u64s to avoid .quad
+	 * as it appears .quad isn't available in some assemblers.
+	 */
+	for (re = dti->reservelist; re; re = re->next) {
+		struct label *l;
+
+		for_each_label(re->labels, l) {
+			fprintf(f, "\t.globl\t%s\n", l->label);
+			fprintf(f, "%s:\n", l->label);
+		}
+		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->address >> 32));
+		ASM_EMIT_BELONG(f, "0x%08x",
+				(unsigned int)(re->address & 0xffffffff));
+		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size >> 32));
+		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size & 0xffffffff));
+	}
+	for (i = 0; i < reservenum; i++) {
+		fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
+	}
+
+	fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
+
+	emit_label(f, symprefix, "struct_start");
+	flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
+
+	fprintf(f, "\t/* FDT_END */\n");
+	asm_emit_cell(f, FDT_END);
+	emit_label(f, symprefix, "struct_end");
+
+	emit_label(f, symprefix, "strings_start");
+	dump_stringtable_asm(f, strbuf);
+	emit_label(f, symprefix, "strings_end");
+
+	emit_label(f, symprefix, "blob_end");
+
+	/*
+	 * If the user asked for more space than is used, pad it out.
+	 */
+	if (minsize > 0) {
+		fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n",
+			minsize, symprefix, symprefix);
+	}
+	if (padsize > 0) {
+		fprintf(f, "\t.space\t%d, 0\n", padsize);
+	}
+	if (alignsize > 0)
+		asm_emit_align(f, alignsize);
+	emit_label(f, symprefix, "blob_abs_end");
+
+	data_free(strbuf);
+}
+
+struct inbuf {
+	char *base, *limit, *ptr;
+};
+
+static void inbuf_init(struct inbuf *inb, void *base, void *limit)
+{
+	inb->base = base;
+	inb->limit = limit;
+	inb->ptr = inb->base;
+}
+
+static void flat_read_chunk(struct inbuf *inb, void *p, int len)
+{
+	if ((inb->ptr + len) > inb->limit)
+		die("Premature end of data parsing flat device tree\n");
+
+	memcpy(p, inb->ptr, len);
+
+	inb->ptr += len;
+}
+
+static uint32_t flat_read_word(struct inbuf *inb)
+{
+	fdt32_t val;
+
+	assert(((inb->ptr - inb->base) % sizeof(val)) == 0);
+
+	flat_read_chunk(inb, &val, sizeof(val));
+
+	return fdt32_to_cpu(val);
+}
+
+static void flat_realign(struct inbuf *inb, int align)
+{
+	int off = inb->ptr - inb->base;
+
+	inb->ptr = inb->base + ALIGN(off, align);
+	if (inb->ptr > inb->limit)
+		die("Premature end of data parsing flat device tree\n");
+}
+
+static char *flat_read_string(struct inbuf *inb)
+{
+	int len = 0;
+	const char *p = inb->ptr;
+	char *str;
+
+	do {
+		if (p >= inb->limit)
+			die("Premature end of data parsing flat device tree\n");
+		len++;
+	} while ((*p++) != '\0');
+
+	str = xstrdup(inb->ptr);
+
+	inb->ptr += len;
+
+	flat_realign(inb, sizeof(uint32_t));
+
+	return str;
+}
+
+static struct data flat_read_data(struct inbuf *inb, int len)
+{
+	struct data d = empty_data;
+
+	if (len == 0)
+		return empty_data;
+
+	d = data_grow_for(d, len);
+	d.len = len;
+
+	flat_read_chunk(inb, d.val, len);
+
+	flat_realign(inb, sizeof(uint32_t));
+
+	return d;
+}
+
+static char *flat_read_stringtable(struct inbuf *inb, int offset)
+{
+	const char *p;
+
+	p = inb->base + offset;
+	while (1) {
+		if (p >= inb->limit || p < inb->base)
+			die("String offset %d overruns string table\n",
+			    offset);
+
+		if (*p == '\0')
+			break;
+
+		p++;
+	}
+
+	return xstrdup(inb->base + offset);
+}
+
+static struct property *flat_read_property(struct inbuf *dtbuf,
+					   struct inbuf *strbuf, int flags)
+{
+	uint32_t proplen, stroff;
+	char *name;
+	struct data val;
+
+	proplen = flat_read_word(dtbuf);
+	stroff = flat_read_word(dtbuf);
+
+	name = flat_read_stringtable(strbuf, stroff);
+
+	if ((flags & FTF_VARALIGN) && (proplen >= 8))
+		flat_realign(dtbuf, 8);
+
+	val = flat_read_data(dtbuf, proplen);
+
+	return build_property(name, val, NULL);
+}
+
+
+static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
+{
+	struct reserve_info *reservelist = NULL;
+	struct reserve_info *new;
+	struct fdt_reserve_entry re;
+
+	/*
+	 * Each entry is a pair of u64 (addr, size) values for 4 cell_t's.
+	 * List terminates at an entry with size equal to zero.
+	 *
+	 * First pass, count entries.
+	 */
+	while (1) {
+		uint64_t address, size;
+
+		flat_read_chunk(inb, &re, sizeof(re));
+		address  = fdt64_to_cpu(re.address);
+		size = fdt64_to_cpu(re.size);
+		if (size == 0)
+			break;
+
+		new = build_reserve_entry(address, size);
+		reservelist = add_reserve_entry(reservelist, new);
+	}
+
+	return reservelist;
+}
+
+
+static char *nodename_from_path(const char *ppath, const char *cpath)
+{
+	int plen;
+
+	plen = strlen(ppath);
+
+	if (!strstarts(cpath, ppath))
+		die("Path \"%s\" is not valid as a child of \"%s\"\n",
+		    cpath, ppath);
+
+	/* root node is a special case */
+	if (!streq(ppath, "/"))
+		plen++;
+
+	return xstrdup(cpath + plen);
+}
+
+static struct node *unflatten_tree(struct inbuf *dtbuf,
+				   struct inbuf *strbuf,
+				   const char *parent_flatname, int flags)
+{
+	struct node *node;
+	char *flatname;
+	uint32_t val;
+
+	node = build_node(NULL, NULL, NULL);
+
+	flatname = flat_read_string(dtbuf);
+
+	if (flags & FTF_FULLPATH)
+		node->name = nodename_from_path(parent_flatname, flatname);
+	else
+		node->name = flatname;
+
+	do {
+		struct property *prop;
+		struct node *child;
+
+		val = flat_read_word(dtbuf);
+		switch (val) {
+		case FDT_PROP:
+			if (node->children)
+				fprintf(stderr, "Warning: Flat tree input has "
+					"subnodes preceding a property.\n");
+			prop = flat_read_property(dtbuf, strbuf, flags);
+			add_property(node, prop);
+			break;
+
+		case FDT_BEGIN_NODE:
+			child = unflatten_tree(dtbuf,strbuf, flatname, flags);
+			add_child(node, child);
+			break;
+
+		case FDT_END_NODE:
+			break;
+
+		case FDT_END:
+			die("Premature FDT_END in device tree blob\n");
+			break;
+
+		case FDT_NOP:
+			if (!(flags & FTF_NOPS))
+				fprintf(stderr, "Warning: NOP tag found in flat tree"
+					" version <16\n");
+
+			/* Ignore */
+			break;
+
+		default:
+			die("Invalid opcode word %08x in device tree blob\n",
+			    val);
+		}
+	} while (val != FDT_END_NODE);
+
+	if (node->name != flatname) {
+		free(flatname);
+	}
+
+	return node;
+}
+
+
+struct dt_info *dt_from_blob(const char *fname)
+{
+	FILE *f;
+	fdt32_t magic_buf, totalsize_buf;
+	uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
+	uint32_t off_dt, off_str, off_mem_rsvmap;
+	int rc;
+	char *blob;
+	struct fdt_header *fdt;
+	char *p;
+	struct inbuf dtbuf, strbuf;
+	struct inbuf memresvbuf;
+	int sizeleft;
+	struct reserve_info *reservelist;
+	struct node *tree;
+	uint32_t val;
+	int flags = 0;
+
+	f = srcfile_relative_open(fname, NULL);
+
+	rc = fread(&magic_buf, sizeof(magic_buf), 1, f);
+	if (ferror(f))
+		die("Error reading DT blob magic number: %s\n",
+		    strerror(errno));
+	if (rc < 1) {
+		if (feof(f))
+			die("EOF reading DT blob magic number\n");
+		else
+			die("Mysterious short read reading magic number\n");
+	}
+
+	magic = fdt32_to_cpu(magic_buf);
+	if (magic != FDT_MAGIC)
+		die("Blob has incorrect magic number\n");
+
+	rc = fread(&totalsize_buf, sizeof(totalsize_buf), 1, f);
+	if (ferror(f))
+		die("Error reading DT blob size: %s\n", strerror(errno));
+	if (rc < 1) {
+		if (feof(f))
+			die("EOF reading DT blob size\n");
+		else
+			die("Mysterious short read reading blob size\n");
+	}
+
+	totalsize = fdt32_to_cpu(totalsize_buf);
+	if (totalsize < FDT_V1_SIZE)
+		die("DT blob size (%d) is too small\n", totalsize);
+
+	blob = xmalloc(totalsize);
+
+	fdt = (struct fdt_header *)blob;
+	fdt->magic = cpu_to_fdt32(magic);
+	fdt->totalsize = cpu_to_fdt32(totalsize);
+
+	sizeleft = totalsize - sizeof(magic) - sizeof(totalsize);
+	p = blob + sizeof(magic)  + sizeof(totalsize);
+
+	while (sizeleft) {
+		if (feof(f))
+			die("EOF before reading %d bytes of DT blob\n",
+			    totalsize);
+
+		rc = fread(p, 1, sizeleft, f);
+		if (ferror(f))
+			die("Error reading DT blob: %s\n",
+			    strerror(errno));
+
+		sizeleft -= rc;
+		p += rc;
+	}
+
+	off_dt = fdt32_to_cpu(fdt->off_dt_struct);
+	off_str = fdt32_to_cpu(fdt->off_dt_strings);
+	off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap);
+	version = fdt32_to_cpu(fdt->version);
+	boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys);
+
+	if (off_mem_rsvmap >= totalsize)
+		die("Mem Reserve structure offset exceeds total size\n");
+
+	if (off_dt >= totalsize)
+		die("DT structure offset exceeds total size\n");
+
+	if (off_str > totalsize)
+		die("String table offset exceeds total size\n");
+
+	if (version >= 3) {
+		uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings);
+		if ((off_str+size_str < off_str) || (off_str+size_str > totalsize))
+			die("String table extends past total size\n");
+		inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str);
+	} else {
+		inbuf_init(&strbuf, blob + off_str, blob + totalsize);
+	}
+
+	if (version >= 17) {
+		size_dt = fdt32_to_cpu(fdt->size_dt_struct);
+		if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize))
+			die("Structure block extends past total size\n");
+	}
+
+	if (version < 16) {
+		flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN;
+	} else {
+		flags |= FTF_NOPS;
+	}
+
+	inbuf_init(&memresvbuf,
+		   blob + off_mem_rsvmap, blob + totalsize);
+	inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
+
+	reservelist = flat_read_mem_reserve(&memresvbuf);
+
+	val = flat_read_word(&dtbuf);
+
+	if (val != FDT_BEGIN_NODE)
+		die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
+
+	tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
+
+	val = flat_read_word(&dtbuf);
+	if (val != FDT_END)
+		die("Device tree blob doesn't end with FDT_END\n");
+
+	free(blob);
+
+	fclose(f);
+
+	return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
+}
diff --git a/fstree.c b/fstree.c
new file mode 100644
index 0000000..9871689
--- /dev/null
+++ b/fstree.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include "dtc.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+static struct node *read_fstree(const char *dirname)
+{
+	DIR *d;
+	struct dirent *de;
+	struct stat st;
+	struct node *tree;
+
+	d = opendir(dirname);
+	if (!d)
+		die("Couldn't opendir() \"%s\": %s\n", dirname, strerror(errno));
+
+	tree = build_node(NULL, NULL, NULL);
+
+	while ((de = readdir(d)) != NULL) {
+		char *tmpname;
+
+		if (streq(de->d_name, ".")
+		    || streq(de->d_name, ".."))
+			continue;
+
+		tmpname = join_path(dirname, de->d_name);
+
+		if (lstat(tmpname, &st) < 0)
+			die("stat(%s): %s\n", tmpname, strerror(errno));
+
+		if (S_ISREG(st.st_mode)) {
+			struct property *prop;
+			FILE *pfile;
+
+			pfile = fopen(tmpname, "rb");
+			if (! pfile) {
+				fprintf(stderr,
+					"WARNING: Cannot open %s: %s\n",
+					tmpname, strerror(errno));
+			} else {
+				prop = build_property(xstrdup(de->d_name),
+						      data_copy_file(pfile,
+								     st.st_size),
+						      NULL);
+				add_property(tree, prop);
+				fclose(pfile);
+			}
+		} else if (S_ISDIR(st.st_mode)) {
+			struct node *newchild;
+
+			newchild = read_fstree(tmpname);
+			newchild = name_node(newchild, xstrdup(de->d_name));
+			add_child(tree, newchild);
+		}
+
+		free(tmpname);
+	}
+
+	closedir(d);
+	return tree;
+}
+
+struct dt_info *dt_from_fs(const char *dirname)
+{
+	struct node *tree;
+
+	tree = read_fstree(dirname);
+	tree = name_node(tree, "");
+
+	return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
+}
diff --git a/libfdt/.gitignore b/libfdt/.gitignore
new file mode 100644
index 0000000..fed4603
--- /dev/null
+++ b/libfdt/.gitignore
@@ -0,0 +1 @@
+libfdt.so.1
diff --git a/libfdt/Makefile.libfdt b/libfdt/Makefile.libfdt
new file mode 100644
index 0000000..e546397
--- /dev/null
+++ b/libfdt/Makefile.libfdt
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+# Makefile.libfdt
+#
+# This is not a complete Makefile of itself.  Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
+LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
+LIBFDT_VERSION = version.lds
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
+	fdt_addresses.c fdt_overlay.c
+LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
+LIBFDT_LIB = libfdt-$(DTC_VERSION).$(SHAREDLIB_EXT)
+
+libfdt_clean:
+	@$(VECHO) CLEAN "(libfdt)"
+	rm -f $(STD_CLEANFILES:%=$(LIBFDT_dir)/%)
+	rm -f $(LIBFDT_dir)/$(LIBFDT_soname)
diff --git a/libfdt/TODO b/libfdt/TODO
new file mode 100644
index 0000000..288437e
--- /dev/null
+++ b/libfdt/TODO
@@ -0,0 +1,3 @@
+- Tree traversal functions
+- Graft function
+- Complete libfdt.h documenting comments
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
new file mode 100644
index 0000000..d6ce7c0
--- /dev/null
+++ b/libfdt/fdt.c
@@ -0,0 +1,291 @@
+// 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 (fdt_magic(fdt) == FDT_MAGIC) {
+		/* Complete tree */
+		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 (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;
+}
+
+int fdt_check_header(const void *fdt)
+{
+	size_t hdrsize;
+
+	if (fdt_magic(fdt) != FDT_MAGIC)
+		return -FDT_ERR_BADMAGIC;
+	hdrsize = fdt_header_size(fdt);
+	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;
+
+	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;
+
+	/* Bounds check structure block */
+	if (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 absoffset = offset + fdt_off_dt_struct(fdt);
+
+	if ((absoffset < offset)
+	    || ((absoffset + len) < absoffset)
+	    || (absoffset + len) > fdt_totalsize(fdt))
+		return NULL;
+
+	if (fdt_version(fdt) >= 0x11)
+		if (((offset + len) < offset)
+		    || ((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 (!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 (!p)
+			return FDT_END; /* premature end */
+		break;
+
+	case FDT_PROP:
+		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+		if (!lenp)
+			return FDT_END; /* premature end */
+		/* skip-name offset, length and value */
+		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+			+ fdt32_to_cpu(*lenp);
+		if (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 ((offset < 0) || (offset % FDT_TAGSIZE)
+	    || (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 ((offset < 0) || (offset % FDT_TAGSIZE)
+	    || (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)
+{
+	FDT_RO_PROBE(fdt);
+
+	if (fdt_totalsize(fdt) > bufsize)
+		return -FDT_ERR_NOSPACE;
+
+	memmove(buf, fdt, fdt_totalsize(fdt));
+	return 0;
+}
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
new file mode 100644
index 0000000..f2e6880
--- /dev/null
+++ b/libfdt/fdt.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef FDT_H
+#define FDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ */
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+	fdt32_t magic;			 /* magic word FDT_MAGIC */
+	fdt32_t totalsize;		 /* total size of DT block */
+	fdt32_t off_dt_struct;		 /* offset to structure */
+	fdt32_t off_dt_strings;		 /* offset to strings */
+	fdt32_t off_mem_rsvmap;		 /* offset to memory reserve map */
+	fdt32_t version;		 /* format version */
+	fdt32_t last_comp_version;	 /* last compatible version */
+
+	/* version 2 fields below */
+	fdt32_t boot_cpuid_phys;	 /* Which physical CPU id we're
+					    booting on */
+	/* version 3 fields below */
+	fdt32_t size_dt_strings;	 /* size of the strings block */
+
+	/* version 17 fields below */
+	fdt32_t size_dt_struct;		 /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+	fdt64_t address;
+	fdt64_t size;
+};
+
+struct fdt_node_header {
+	fdt32_t tag;
+	char name[0];
+};
+
+struct fdt_property {
+	fdt32_t tag;
+	fdt32_t len;
+	fdt32_t nameoff;
+	char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC	0xd00dfeed	/* 4: version, 4: total size */
+#define FDT_TAGSIZE	sizeof(fdt32_t)
+
+#define FDT_BEGIN_NODE	0x1		/* Start node: full name */
+#define FDT_END_NODE	0x2		/* End node */
+#define FDT_PROP	0x3		/* Property: name off,
+					   size, content */
+#define FDT_NOP		0x4		/* nop */
+#define FDT_END		0x9
+
+#define FDT_V1_SIZE	(7*sizeof(fdt32_t))
+#define FDT_V2_SIZE	(FDT_V1_SIZE + sizeof(fdt32_t))
+#define FDT_V3_SIZE	(FDT_V2_SIZE + sizeof(fdt32_t))
+#define FDT_V16_SIZE	FDT_V3_SIZE
+#define FDT_V17_SIZE	(FDT_V16_SIZE + sizeof(fdt32_t))
+
+#endif /* FDT_H */
diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c
new file mode 100644
index 0000000..9a82cd0
--- /dev/null
+++ b/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) || ((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/libfdt/fdt_empty_tree.c b/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000..49d54d4
--- /dev/null
+++ b/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/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c
new file mode 100644
index 0000000..be71873
--- /dev/null
+++ b/libfdt/fdt_overlay.c
@@ -0,0 +1,881 @@
+// 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);
+}
+
+/**
+ * overlay_get_target - retrieves the offset of a fragment's target
+ * @fdt: Base device tree blob
+ * @fdto: Device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ * @pathp: pointer which receives the path of the target (or NULL)
+ *
+ * overlay_get_target() retrieves the target offset in the base
+ * device tree of a fragment, no matter how the actual targeting is
+ * done (through a phandle or a path)
+ *
+ * returns:
+ *      the targeted node offset in the base device tree
+ *      Negative error code on error
+ */
+static int overlay_get_target(const void *fdt, const void *fdto,
+			      int fragment, 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);
+	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, "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;
+
+		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 / sizeof(uint32_t)); 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 = overlay_get_target(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;
+		} 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 = overlay_get_target(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 = overlay_get_target(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/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
new file mode 100644
index 0000000..a5c2797
--- /dev/null
+++ b/libfdt/fdt_ro.c
@@ -0,0 +1,898 @@
+// 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 = fdt_ro_probe_(fdt);
+	uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
+	size_t len;
+	int err;
+	const char *s, *n;
+
+	err = totalsize;
+	if (totalsize < 0)
+		goto fail;
+
+	err = -FDT_ERR_BADOFFSET;
+	if (absoffset >= totalsize)
+		goto fail;
+	len = totalsize - absoffset;
+
+	if (fdt_magic(fdt) == FDT_MAGIC) {
+		if (stroffset < 0)
+			goto fail;
+		if (fdt_version(fdt) >= 17) {
+			if (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) {
+		if ((stroffset >= 0)
+		    || (stroffset < -fdt_size_dt_strings(fdt)))
+			goto fail;
+		if ((-stroffset) < len)
+			len = -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)
+{
+	int offset = n * sizeof(struct fdt_reserve_entry);
+	int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
+
+	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 (!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 (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 ((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 (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;
+
+		if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
+			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 (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 (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;
+		name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
+				      &namelen);
+		if (!name) {
+			if (lenp)
+				*lenp = namelen;
+			return NULL;
+		}
+		*namep = name;
+	}
+
+	/* Handle realignment */
+	if (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 ((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 (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 == -1))
+		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() */
+}
+
+int fdt_check_full(const void *fdt, size_t bufsize)
+{
+	int err;
+	int num_memrsv;
+	int offset, nextoffset = 0;
+	uint32_t tag;
+	unsigned depth = 0;
+	const void *prop;
+	const char *propname;
+
+	if (bufsize < FDT_V1_SIZE)
+		return -FDT_ERR_TRUNCATED;
+	err = fdt_check_header(fdt);
+	if (err != 0)
+		return err;
+	if (bufsize < fdt_totalsize(fdt))
+		return -FDT_ERR_TRUNCATED;
+
+	num_memrsv = fdt_num_mem_rsv(fdt);
+	if (num_memrsv < 0)
+		return num_memrsv;
+
+	while (1) {
+		offset = nextoffset;
+		tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+		if (nextoffset < 0)
+			return nextoffset;
+
+		switch (tag) {
+		case FDT_NOP:
+			break;
+
+		case FDT_END:
+			if (depth != 0)
+				return -FDT_ERR_BADSTRUCTURE;
+			return 0;
+
+		case FDT_BEGIN_NODE:
+			depth++;
+			if (depth > INT_MAX)
+				return -FDT_ERR_BADSTRUCTURE;
+			break;
+
+		case FDT_END_NODE:
+			if (depth == 0)
+				return -FDT_ERR_BADSTRUCTURE;
+			depth--;
+			break;
+
+		case FDT_PROP:
+			prop = fdt_getprop_by_offset(fdt, offset, &propname,
+						     &err);
+			if (!prop)
+				return err;
+			break;
+
+		default:
+			return -FDT_ERR_INTERNAL;
+		}
+	}
+}
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
new file mode 100644
index 0000000..8795947
--- /dev/null
+++ b/libfdt/fdt_rw.c
@@ -0,0 +1,476 @@
+// 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)
+{
+	FDT_RO_PROBE(fdt);
+
+	if (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 (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 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;
+	char *end = (char *)fdt + fdt_data_size_(fdt);
+
+	if (((p + oldlen) < p) || ((p + oldlen) > end))
+		return -FDT_ERR_BADOFFSET;
+	if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
+		return -FDT_ERR_BADOFFSET;
+	if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
+		return -FDT_ERR_NOSPACE;
+	memmove(p + newlen, p + oldlen, end - 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;
+}
+
+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;
+
+	*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;
+
+	*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) {
+		if (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 */
+	fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+	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 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),
+		fdt_size_dt_strings(old));
+	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 (fdt_version(fdt) >= 17) {
+		struct_size = fdt_size_dt_struct(fdt);
+	} else {
+		struct_size = 0;
+		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+			;
+		if (struct_size < 0)
+			return struct_size;
+	}
+
+	if (!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);
+	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_set_totalsize(fdt, fdt_data_size_(fdt));
+
+	return 0;
+}
diff --git a/libfdt/fdt_strerror.c b/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..768db66
--- /dev/null
+++ b/libfdt/fdt_strerror.c
@@ -0,0 +1,59 @@
+// 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),
+};
+#define FDT_ERRTABSIZE	(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/libfdt/fdt_sw.c b/libfdt/fdt_sw.c
new file mode 100644
index 0000000..76bea22
--- /dev/null
+++ b/libfdt/fdt_sw.c
@@ -0,0 +1,376 @@
+// 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 (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_reservmap_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 (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 (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)
+{
+	int offset = fdt_size_dt_struct(fdt);
+	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 size_t 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);
+
+	headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+	tailsize = fdt_size_dt_strings(fdt);
+
+	if ((headsize + tailsize) > fdt_totalsize(fdt))
+		return -FDT_ERR_INTERNAL;
+
+	if ((headsize + tailsize) > 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);
+	int strtabsize = fdt_size_dt_strings(fdt);
+	int len = strlen(s) + 1;
+	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_FIRST_SUPPORTED_VERSION);
+	fdt_set_magic(fdt, FDT_MAGIC);
+
+	return 0;
+}
diff --git a/libfdt/fdt_wip.c b/libfdt/fdt_wip.c
new file mode 100644
index 0000000..f64139e
--- /dev/null
+++ b/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 (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/libfdt/libfdt.h b/libfdt/libfdt.h
new file mode 100644
index 0000000..8037f39
--- /dev/null
+++ b/libfdt/libfdt.h
@@ -0,0 +1,2071 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_H
+#define LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <libfdt_env.h>
+#include <fdt.h>
+
+#define FDT_FIRST_SUPPORTED_VERSION	0x02
+#define FDT_LAST_SUPPORTED_VERSION	0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND	1
+	/* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS		2
+	/* FDT_ERR_EXISTS: Attempted to create a node or property which
+	 * already exists */
+#define FDT_ERR_NOSPACE		3
+	/* FDT_ERR_NOSPACE: Operation needed to expand the device
+	 * tree, but its buffer did not have sufficient space to
+	 * contain the expanded tree. Use fdt_open_into() to move the
+	 * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET	4
+	/* FDT_ERR_BADOFFSET: Function was passed a structure block
+	 * offset which is out-of-bounds, or which points to an
+	 * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH		5
+	/* FDT_ERR_BADPATH: Function was passed a badly formatted path
+	 * (e.g. missing a leading / for a function which requires an
+	 * absolute path) */
+#define FDT_ERR_BADPHANDLE	6
+	/* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
+	 * This can be caused either by an invalid phandle property
+	 * length, or the phandle value was either 0 or -1, which are
+	 * not permitted. */
+#define FDT_ERR_BADSTATE	7
+	/* FDT_ERR_BADSTATE: Function was passed an incomplete device
+	 * tree created by the sequential-write functions, which is
+	 * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED	8
+	/* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly
+	 * terminated (overflows, goes outside allowed bounds, or
+	 * isn't properly terminated).  */
+#define FDT_ERR_BADMAGIC	9
+	/* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+	 * device tree at all - it is missing the flattened device
+	 * tree magic number. */
+#define FDT_ERR_BADVERSION	10
+	/* FDT_ERR_BADVERSION: Given device tree has a version which
+	 * can't be handled by the requested operation.  For
+	 * read-write functions, this may mean that fdt_open_into() is
+	 * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE	11
+	/* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+	 * structure block or other serious error (e.g. misnested
+	 * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT	12
+	/* FDT_ERR_BADLAYOUT: For read-write functions, the given
+	 * device tree has it's sub-blocks in an order that the
+	 * function can't handle (memory reserve map, then structure,
+	 * then strings).  Use fdt_open_into() to reorganize the tree
+	 * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL	13
+	/* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+	 * Should never be returned, if it is, it indicates a bug in
+	 * libfdt itself. */
+
+/* Errors in device tree content */
+#define FDT_ERR_BADNCELLS	14
+	/* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
+	 * or similar property with a bad format or value */
+
+#define FDT_ERR_BADVALUE	15
+	/* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
+	 * value. For example: a property expected to contain a string list
+	 * is not NUL-terminated within the length of its value. */
+
+#define FDT_ERR_BADOVERLAY	16
+	/* FDT_ERR_BADOVERLAY: The device tree overlay, while
+	 * correctly structured, cannot be applied due to some
+	 * unexpected or missing value, property or node. */
+
+#define FDT_ERR_NOPHANDLES	17
+	/* FDT_ERR_NOPHANDLES: The device tree doesn't have any
+	 * phandle available anymore without causing an overflow */
+
+#define FDT_ERR_BADFLAGS	18
+	/* FDT_ERR_BADFLAGS: The function was passed a flags field that
+	 * contains invalid flags or an invalid combination of flags. */
+
+#define FDT_ERR_MAX		18
+
+/* constants */
+#define FDT_MAX_PHANDLE 0xfffffffe
+	/* Valid values for phandles range from 1 to 2^32-2. */
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these)                */
+/**********************************************************************/
+
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
+#endif
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+	return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/*
+ * Alignment helpers:
+ *     These helpers access words from a device tree blob.  They're
+ *     built to work even with unaligned pointers on platforms (ike
+ *     ARM) that don't like unaligned loads and stores
+ */
+
+static inline uint32_t fdt32_ld(const fdt32_t *p)
+{
+	const uint8_t *bp = (const uint8_t *)p;
+
+	return ((uint32_t)bp[0] << 24)
+		| ((uint32_t)bp[1] << 16)
+		| ((uint32_t)bp[2] << 8)
+		| bp[3];
+}
+
+static inline void fdt32_st(void *property, uint32_t value)
+{
+	uint8_t *bp = property;
+
+	bp[0] = value >> 24;
+	bp[1] = (value >> 16) & 0xff;
+	bp[2] = (value >> 8) & 0xff;
+	bp[3] = value & 0xff;
+}
+
+static inline uint64_t fdt64_ld(const fdt64_t *p)
+{
+	const uint8_t *bp = (const uint8_t *)p;
+
+	return ((uint64_t)bp[0] << 56)
+		| ((uint64_t)bp[1] << 48)
+		| ((uint64_t)bp[2] << 40)
+		| ((uint64_t)bp[3] << 32)
+		| ((uint64_t)bp[4] << 24)
+		| ((uint64_t)bp[5] << 16)
+		| ((uint64_t)bp[6] << 8)
+		| bp[7];
+}
+
+static inline void fdt64_st(void *property, uint64_t value)
+{
+	uint8_t *bp = property;
+
+	bp[0] = value >> 56;
+	bp[1] = (value >> 48) & 0xff;
+	bp[2] = (value >> 40) & 0xff;
+	bp[3] = (value >> 32) & 0xff;
+	bp[4] = (value >> 24) & 0xff;
+	bp[5] = (value >> 16) & 0xff;
+	bp[6] = (value >> 8) & 0xff;
+	bp[7] = value & 0xff;
+}
+
+/**********************************************************************/
+/* Traversal functions                                                */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**
+ * fdt_first_subnode() - get offset of first direct subnode
+ *
+ * @fdt:	FDT blob
+ * @offset:	Offset of node to check
+ * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none
+ */
+int fdt_first_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_next_subnode() - get offset of next direct subnode
+ *
+ * After first calling fdt_first_subnode(), call this function repeatedly to
+ * get direct subnodes of a parent node.
+ *
+ * @fdt:	FDT blob
+ * @offset:	Offset of previous subnode
+ * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more
+ * subnodes
+ */
+int fdt_next_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_subnode - iterate over all subnodes of a parent
+ *
+ * @node:	child node (int, lvalue)
+ * @fdt:	FDT blob (const void *)
+ * @parent:	parent node (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ *	fdt_for_each_subnode(node, fdt, parent) {
+ *		Use node
+ *		...
+ *	}
+ *
+ *	if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
+ *		Error handling
+ *	}
+ *
+ * Note that this is implemented as a macro and @node is used as
+ * iterator in the loop. The parent variable be constant or even a
+ * literal.
+ *
+ */
+#define fdt_for_each_subnode(node, fdt, parent)		\
+	for (node = fdt_first_subnode(fdt, parent);	\
+	     node >= 0;					\
+	     node = fdt_next_subnode(fdt, node))
+
+/**********************************************************************/
+/* General functions                                                  */
+/**********************************************************************/
+#define fdt_get_header(fdt, field) \
+	(fdt32_ld(&((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt)			(fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt)		(fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt)		(fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt)		(fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt)		(fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt)		(fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt)	(fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt)	(fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt)	(fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt)		(fdt_get_header(fdt, size_dt_struct))
+
+#define fdt_set_hdr_(name) \
+	static inline void fdt_set_##name(void *fdt, uint32_t val) \
+	{ \
+		struct fdt_header *fdth = (struct fdt_header *)fdt; \
+		fdth->name = cpu_to_fdt32(val); \
+	}
+fdt_set_hdr_(magic);
+fdt_set_hdr_(totalsize);
+fdt_set_hdr_(off_dt_struct);
+fdt_set_hdr_(off_dt_strings);
+fdt_set_hdr_(off_mem_rsvmap);
+fdt_set_hdr_(version);
+fdt_set_hdr_(last_comp_version);
+fdt_set_hdr_(boot_cpuid_phys);
+fdt_set_hdr_(size_dt_strings);
+fdt_set_hdr_(size_dt_struct);
+#undef fdt_set_hdr_
+
+/**
+ * fdt_header_size - return the size of the tree's header
+ * @fdt: pointer to a flattened device tree
+ */
+size_t fdt_header_size_(uint32_t version);
+static inline size_t fdt_header_size(const void *fdt)
+{
+	return fdt_header_size_(fdt_version(fdt));
+}
+
+/**
+ * fdt_check_header - sanity check a device tree header
+
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree, and that the header contains
+ * valid information (to the extent that can be determined from the
+ * header alone).
+ *
+ * returns:
+ *     0, if the buffer appears to contain a valid device tree
+ *     -FDT_ERR_BADMAGIC,
+ *     -FDT_ERR_BADVERSION,
+ *     -FDT_ERR_BADSTATE,
+ *     -FDT_ERR_TRUNCATED, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize.  The buffer may overlap
+ * with the existing device tree blob at fdt.  Therefore,
+ *     fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ *     0, on success
+ *     -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ *     -FDT_ERR_BADMAGIC,
+ *     -FDT_ERR_BADVERSION,
+ *     -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions                                                */
+/**********************************************************************/
+
+int fdt_check_full(const void *fdt, size_t bufsize);
+
+/**
+ * fdt_get_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ * @lenp: optional pointer to return the string's length
+ *
+ * fdt_get_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt, and optionally also
+ * returns the string's length in *lenp.
+ *
+ * returns:
+ *     a pointer to the string, on success
+ *     NULL, if stroffset is out of bounds, or doesn't point to a valid string
+ */
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp);
+
+/**
+ * fdt_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ *     a pointer to the string, on success
+ *     NULL, if stroffset is out of bounds, or doesn't point to a valid string
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_find_max_phandle - find and return the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ * @phandle: return location for the highest phandle value found in the tree
+ *
+ * fdt_find_max_phandle() finds the highest phandle value in the given device
+ * tree. The value returned in @phandle is only valid if the function returns
+ * success.
+ *
+ * returns:
+ *     0 on success or a negative error code on failure
+ */
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle);
+
+/**
+ * fdt_get_max_phandle - retrieves the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ *
+ * fdt_get_max_phandle retrieves the highest phandle in the given
+ * device tree. This will ignore badly formatted phandles, or phandles
+ * with a value of 0 or -1.
+ *
+ * This function is deprecated in favour of fdt_find_max_phandle().
+ *
+ * returns:
+ *      the highest phandle on success
+ *      0, if no phandle was found in the device tree
+ *      -1, if an error occurred
+ */
+static inline uint32_t fdt_get_max_phandle(const void *fdt)
+{
+	uint32_t phandle;
+	int err;
+
+	err = fdt_find_max_phandle(fdt, &phandle);
+	if (err < 0)
+		return (uint32_t)-1;
+
+	return phandle;
+}
+
+/**
+ * fdt_generate_phandle - return a new, unused phandle for a device tree blob
+ * @fdt: pointer to the device tree blob
+ * @phandle: return location for the new phandle
+ *
+ * Walks the device tree blob and looks for the highest phandle value. On
+ * success, the new, unused phandle value (one higher than the previously
+ * highest phandle value in the device tree blob) will be returned in the
+ * @phandle parameter.
+ *
+ * Returns:
+ *   0 on success or a negative error-code on failure
+ */
+int fdt_generate_phandle(const void *fdt, uint32_t *phandle);
+
+/**
+ * fdt_num_mem_rsv - retrieve the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map.  This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ *     the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retrieve one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ *     0, on success
+ *     -FDT_ERR_BADMAGIC,
+ *     -FDT_ERR_BADVERSION,
+ *     -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name.  This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+			       const char *name, int namelen);
+#endif
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name.  name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ *	structure block offset of the requested subnode (>=0), on success
+ *	-FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ *	-FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ *		tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset_namelen - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ * @namelen: number of characters of path to consider
+ *
+ * Identical to fdt_path_offset(), but only consider the first namelen
+ * characters of path as the path name.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
+#endif
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ *	structure block offset of the node with the requested path (>=0), on
+ *		success
+ *	-FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ *	-FDT_ERR_NOTFOUND, if the requested node does not exist
+ *      -FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retrieve the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset.  If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ *	pointer to the node's name, on success
+ *		If lenp is non-NULL, *lenp contains the length of that name
+ *			(>=0)
+ *	NULL, on error
+ *		if lenp is non-NULL *lenp contains an error code (<0):
+ *		-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ *			tag
+ *		-FDT_ERR_BADMAGIC,
+ *		-FDT_ERR_BADVERSION,
+ *		-FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_first_property_offset - find the offset of a node's first property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ *
+ * fdt_first_property_offset() finds the first property of the node at
+ * the given structure block offset.
+ *
+ * returns:
+ *	structure block offset of the property (>=0), on success
+ *	-FDT_ERR_NOTFOUND, if the requested node has no properties
+ *	-FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
+ *      -FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_first_property_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_next_property_offset - step through a node's properties
+ * @fdt: pointer to the device tree blob
+ * @offset: structure block offset of a property
+ *
+ * fdt_next_property_offset() finds the property immediately after the
+ * one at the given structure block offset.  This will be a property
+ * of the same node as the given property.
+ *
+ * returns:
+ *	structure block offset of the next property (>=0), on success
+ *	-FDT_ERR_NOTFOUND, if the given property is the last in its node
+ *	-FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
+ *      -FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_next_property_offset(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_property_offset - iterate over all properties of a node
+ *
+ * @property_offset:	property offset (int, lvalue)
+ * @fdt:		FDT blob (const void *)
+ * @node:		node offset (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ *	fdt_for_each_property_offset(property, fdt, node) {
+ *		Use property
+ *		...
+ *	}
+ *
+ *	if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) {
+ *		Error handling
+ *	}
+ *
+ * Note that this is implemented as a macro and property is used as
+ * iterator in the loop. The node variable can be constant or even a
+ * literal.
+ */
+#define fdt_for_each_property_offset(property, fdt, node)	\
+	for (property = fdt_first_property_offset(fdt, node);	\
+	     property >= 0;					\
+	     property = fdt_next_property_offset(fdt, property))
+
+/**
+ * fdt_get_property_by_offset - retrieve the property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to retrieve
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property_by_offset() retrieves a pointer to the
+ * fdt_property structure within the device tree blob at the given
+ * offset.  If lenp is non-NULL, the length of the property value is
+ * also returned, in the integer pointed to by lenp.
+ *
+ * Note that this code only works on device tree versions >= 16. fdt_getprop()
+ * works on all versions.
+ *
+ * returns:
+ *	pointer to the structure representing the property
+ *		if lenp is non-NULL, *lenp contains the length of the property
+ *		value (>=0)
+ *	NULL, on error
+ *		if lenp is non-NULL, *lenp contains an error code (<0):
+ *		-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ *		-FDT_ERR_BADMAGIC,
+ *		-FDT_ERR_BADVERSION,
+ *		-FDT_ERR_BADSTATE,
+ *		-FDT_ERR_BADSTRUCTURE,
+ *		-FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+						      int offset,
+						      int *lenp);
+
+/**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+						    int nodeoffset,
+						    const char *name,
+						    int namelen, int *lenp);
+#endif
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset.  If lenp is
+ * non-NULL, the length of the property value is also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ *	pointer to the structure representing the property
+ *		if lenp is non-NULL, *lenp contains the length of the property
+ *		value (>=0)
+ *	NULL, on error
+ *		if lenp is non-NULL, *lenp contains an error code (<0):
+ *		-FDT_ERR_NOTFOUND, node does not have named property
+ *		-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ *			tag
+ *		-FDT_ERR_BADMAGIC,
+ *		-FDT_ERR_BADVERSION,
+ *		-FDT_ERR_BADSTATE,
+ *		-FDT_ERR_BADSTRUCTURE,
+ *		-FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+					    const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+						      const char *name,
+						      int *lenp)
+{
+	return (struct fdt_property *)(uintptr_t)
+		fdt_get_property(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_getprop_by_offset - retrieve the value of a property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to read
+ * @namep: pointer to a string variable (will be overwritten) or NULL
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop_by_offset() retrieves a pointer to the value of the
+ * property at structure block offset 'offset' (this will be a pointer
+ * to within the device blob itself, not a copy of the value).  If
+ * lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp.  If namep is non-NULL,
+ * the property's namne will also be returned in the char * pointed to
+ * by namep (this will be a pointer to within the device tree's string
+ * block, not a new copy of the name).
+ *
+ * returns:
+ *	pointer to the property's value
+ *		if lenp is non-NULL, *lenp contains the length of the property
+ *		value (>=0)
+ *		if namep is non-NULL *namep contiains a pointer to the property
+ *		name.
+ *	NULL, on error
+ *		if lenp is non-NULL, *lenp contains an error code (<0):
+ *		-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ *		-FDT_ERR_BADMAGIC,
+ *		-FDT_ERR_BADVERSION,
+ *		-FDT_ERR_BADSTATE,
+ *		-FDT_ERR_BADSTRUCTURE,
+ *		-FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+				  const char **namep, int *lenp);
+#endif
+
+/**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+				const char *name, int namelen, int *lenp);
+static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
+					  const char *name, int namelen,
+					  int *lenp)
+{
+	return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
+						      namelen, lenp);
+}
+#endif
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ *	pointer to the property's value
+ *		if lenp is non-NULL, *lenp contains the length of the property
+ *		value (>=0)
+ *	NULL, on error
+ *		if lenp is non-NULL, *lenp contains an error code (<0):
+ *		-FDT_ERR_NOTFOUND, node does not have named property
+ *		-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ *			tag
+ *		-FDT_ERR_BADMAGIC,
+ *		-FDT_ERR_BADVERSION,
+ *		-FDT_ERR_BADSTATE,
+ *		-FDT_ERR_BADSTRUCTURE,
+ *		-FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+			const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+				  const char *name, int *lenp)
+{
+	return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retrieve the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ *	the phandle of the node at nodeoffset, on success (!= 0, != -1)
+ *	0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_alias_namelen - get alias based on substring
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_get_alias(), but only examine the first namelen
+ * characters of name for matching the alias name.
+ */
+#ifndef SWIG /* Not available in Python */
+const char *fdt_get_alias_namelen(const void *fdt,
+				  const char *name, int namelen);
+#endif
+
+/**
+ * fdt_get_alias - retrieve the path referenced by a given alias
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ *
+ * fdt_get_alias() retrieves the value of a given alias.  That is, the
+ * value of the property named 'name' in the node /aliases.
+ *
+ * returns:
+ *	a pointer to the expansion of the alias named 'name', if it exists
+ *	NULL, if the given alias or the /aliases node does not exist
+ */
+const char *fdt_get_alias(const void *fdt, const char *name);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ *	0, on success
+ *		buf contains the absolute path of the node at
+ *		nodeoffset, as a NUL-terminated string.
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ *		characters and will not fit in the given buffer.
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth).  So
+ *	fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node.  If the node at
+ * nodeoffset has depth D, then:
+ *	fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ *	structure block offset of the node at node offset's ancestor
+ *		of depth supernodedepth (>=0), on success
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
+ *		nodeoffset
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+				 int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node.  The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ *	depth of the node at nodeoffset (>=0), on success
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ *	structure block offset of the parent of the node at nodeoffset
+ *		(>=0), on success
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ *	offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ *					       propval, proplen);
+ *	while (offset != -FDT_ERR_NOTFOUND) {
+ *		// other code here
+ *		offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ *						       propval, proplen);
+ *	}
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ *	structure block offset of the located node (>= 0, >startoffset),
+ *		 on success
+ *	-FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ *		tree after startoffset
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+				  const char *propname,
+				  const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_phandle() returns the offset of the node
+ * which has the given phandle value.  If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ *	structure block offset of the located node (>= 0), on success
+ *	-FDT_ERR_NOTFOUND, no node with that phandle exists
+ *	-FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ *	0, if the node has a 'compatible' property listing the given string
+ *	1, if the node has a 'compatible' property, but it does not list
+ *		the given string
+ *	-FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ *	-FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+			      const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ *	offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ *	while (offset != -FDT_ERR_NOTFOUND) {
+ *		// other code here
+ *		offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ *	}
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ *	structure block offset of the located node (>= 0, >startoffset),
+ *		 on success
+ *	-FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ *		tree after startoffset
+ *	-FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+				  const char *compatible);
+
+/**
+ * fdt_stringlist_contains - check a string list property for a string
+ * @strlist: Property containing a list of strings to check
+ * @listlen: Length of property
+ * @str: String to search for
+ *
+ * This is a utility function provided for convenience. The list contains
+ * one or more strings, each terminated by \0, as is found in a device tree
+ * "compatible" property.
+ *
+ * @return: 1 if the string is found in the list, 0 not found, or invalid list
+ */
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+
+/**
+ * fdt_stringlist_count - count the number of strings in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @return:
+ *   the number of strings in the given property
+ *   -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ *   -FDT_ERR_NOTFOUND if the property does not exist
+ */
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
+
+/**
+ * fdt_stringlist_search - find a string in a string list and return its index
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @string: string to look up in the string list
+ *
+ * Note that it is possible for this function to succeed on property values
+ * that are not NUL-terminated. That's because the function will stop after
+ * finding the first occurrence of @string. This can for example happen with
+ * small-valued cell properties, such as #address-cells, when searching for
+ * the empty string.
+ *
+ * @return:
+ *   the index of the string in the list of strings
+ *   -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ *   -FDT_ERR_NOTFOUND if the property does not exist or does not contain
+ *                     the given string
+ */
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+			  const char *string);
+
+/**
+ * fdt_stringlist_get() - obtain the string at a given index in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @index: index of the string to return
+ * @lenp: return location for the string length or an error code on failure
+ *
+ * Note that this will successfully extract strings from properties with
+ * non-NUL-terminated values. For example on small-valued cell properties
+ * this function will return the empty string.
+ *
+ * If non-NULL, the length of the string (on success) or a negative error-code
+ * (on failure) will be stored in the integer pointer to by lenp.
+ *
+ * @return:
+ *   A pointer to the string at the given index in the string list or NULL on
+ *   failure. On success the length of the string will be stored in the memory
+ *   location pointed to by the lenp parameter, if non-NULL. On failure one of
+ *   the following negative error codes will be returned in the lenp parameter
+ *   (if non-NULL):
+ *     -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ *     -FDT_ERR_NOTFOUND if the property does not exist
+ */
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+			       const char *property, int index,
+			       int *lenp);
+
+/**********************************************************************/
+/* Read-only functions (addressing related)                           */
+/**********************************************************************/
+
+/**
+ * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
+ *
+ * This is the maximum value for #address-cells, #size-cells and
+ * similar properties that will be processed by libfdt.  IEE1275
+ * requires that OF implementations handle values up to 4.
+ * Implementations may support larger values, but in practice higher
+ * values aren't used.
+ */
+#define FDT_MAX_NCELLS		4
+
+/**
+ * fdt_address_cells - retrieve address size for a bus represented in the tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address size for
+ *
+ * When the node has a valid #address-cells property, returns its value.
+ *
+ * returns:
+ *	0 <= n < FDT_MAX_NCELLS, on success
+ *      2, if the node has no #address-cells property
+ *      -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ *		#address-cells property
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_address_cells(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_size_cells - retrieve address range size for a bus represented in the
+ *                  tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address range size for
+ *
+ * When the node has a valid #size-cells property, returns its value.
+ *
+ * returns:
+ *	0 <= n < FDT_MAX_NCELLS, on success
+ *      1, if the node has no #size-cells property
+ *      -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ *		#size-cells property
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_size_cells(const void *fdt, int nodeoffset);
+
+
+/**********************************************************************/
+/* Write-in-place functions                                           */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace_namelen_partial - change a property's value,
+ *                                       but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: number of characters of name to consider
+ * @idx: index of the property to change in the array
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * Identical to fdt_setprop_inplace(), but modifies the given property
+ * starting from the given index, and using only the first characters
+ * of the name. It is useful when you want to manipulate only one value of
+ * an array and you have a string that doesn't end with \0.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+					const char *name, int namelen,
+					uint32_t idx, const void *val,
+					int len);
+#endif
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len.  This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ *	-FDT_ERR_NOTFOUND, node does not have the named property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+			const void *val, int len);
+#endif
+
+/**
+ * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u32() replaces the value of a given property
+ * with the 32-bit integer value in val, converting val to big-endian
+ * if necessary.  This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ *	-FDT_ERR_NOTFOUND, node does not have the named property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset,
+					  const char *name, uint32_t val)
+{
+	fdt32_t tmp = cpu_to_fdt32(val);
+	return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u64() replaces the value of a given property
+ * with the 64-bit integer value in val, converting val to big-endian
+ * if necessary.  This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 8.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, if the property's length is not equal to 8
+ *	-FDT_ERR_NOTFOUND, node does not have the named property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset,
+					  const char *name, uint64_t val)
+{
+	fdt64_t tmp = cpu_to_fdt64(val);
+	return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ *
+ * This is an alternative name for fdt_setprop_inplace_u32()
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+					   const char *name, uint32_t val)
+{
+	return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOTFOUND, node does not have the named property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions                                         */
+/**********************************************************************/
+
+/* fdt_create_with_flags flags */
+#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1
+	/* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property
+	 * names in the fdt. This can result in faster creation times, but
+	 * a larger fdt. */
+
+#define FDT_CREATE_FLAGS_ALL	(FDT_CREATE_FLAG_NO_NAME_DEDUP)
+
+/**
+ * fdt_create_with_flags - begin creation of a new fdt
+ * @fdt: pointer to memory allocated where fdt will be created
+ * @bufsize: size of the memory space at fdt
+ * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0.
+ *
+ * fdt_create_with_flags() begins the process of creating a new fdt with
+ * the sequential write interface.
+ *
+ * fdt creation process must end with fdt_finished() to produce a valid fdt.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
+ *	-FDT_ERR_BADFLAGS, flags is not valid
+ */
+int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags);
+
+/**
+ * fdt_create - begin creation of a new fdt
+ * @fdt: pointer to memory allocated where fdt will be created
+ * @bufsize: size of the memory space at fdt
+ *
+ * fdt_create() is equivalent to fdt_create_with_flags() with flags=0.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
+ */
+int fdt_create(void *buf, int bufsize);
+
+int fdt_resize(void *fdt, void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+	fdt32_t tmp = cpu_to_fdt32(val);
+	return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
+{
+	fdt64_t tmp = cpu_to_fdt64(val);
+	return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+
+#ifndef SWIG /* Not available in Python */
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+	return fdt_property_u32(fdt, name, val);
+}
+#endif
+
+/**
+ * fdt_property_placeholder - add a new property and return a ptr to its value
+ *
+ * @fdt: pointer to the device tree blob
+ * @name: name of property to add
+ * @len: length of property value in bytes
+ * @valp: returns a pointer to where where the value should be placed
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_NOSPACE, standard meanings
+ */
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp);
+
+#define fdt_property_string(fdt, name, str) \
+	fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions                                               */
+/**********************************************************************/
+
+int fdt_create_empty_tree(void *buf, int bufsize);
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new reservation entry
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ *		are less than n+1 reserve map entries)
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string.  NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ *		to contain the new name
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+		const void *val, int len);
+
+/**
+ * fdt_setprop_placeholder - allocate space for a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @len: length of the property value
+ * @prop_data: return pointer to property data
+ *
+ * fdt_setprop_placeholer() allocates the named property in the given node.
+ * If the property exists it is resized. In either case a pointer to the
+ * property data is returned.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
+			    int len, void **prop_data);
+
+/**
+ * fdt_setprop_u32 - set a property to a 32-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u32() sets the value of the named property in the given
+ * node to the given 32-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name,
+				  uint32_t val)
+{
+	fdt32_t tmp = cpu_to_fdt32(val);
+	return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_u64 - set a property to a 64-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u64() sets the value of the named property in the given
+ * node to the given 64-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name,
+				  uint64_t val)
+{
+	fdt64_t tmp = cpu_to_fdt64(val);
+	return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ *
+ * This is an alternative name for fdt_setprop_u32()
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+				   uint32_t val)
+{
+	return fdt_setprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+	fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+
+/**
+ * fdt_setprop_empty - set a property to an empty value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ *
+ * fdt_setprop_empty() sets the value of the named property in the
+ * given node to an empty (zero length) value, or creates a new empty
+ * property if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_empty(fdt, nodeoffset, name) \
+	fdt_setprop((fdt), (nodeoffset), (name), NULL, 0)
+
+/**
+ * fdt_appendprop - append to or create a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to append to
+ * @val: pointer to data to append to the property value
+ * @len: length of the data to append to the property value
+ *
+ * fdt_appendprop() appends the value to the named property in the
+ * given node, creating the property if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+		   const void *val, int len);
+
+/**
+ * fdt_appendprop_u32 - append a 32-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u32() appends the given 32-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u32(void *fdt, int nodeoffset,
+				     const char *name, uint32_t val)
+{
+	fdt32_t tmp = cpu_to_fdt32(val);
+	return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_u64 - append a 64-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u64() appends the given 64-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u64(void *fdt, int nodeoffset,
+				     const char *name, uint64_t val)
+{
+	fdt64_t tmp = cpu_to_fdt64(val);
+	return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_cell - append a single cell value to a property
+ *
+ * This is an alternative name for fdt_appendprop_u32()
+ */
+static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
+				      const char *name, uint32_t val)
+{
+	return fdt_appendprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_appendprop_string - append a string to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value to append to the property
+ *
+ * fdt_appendprop_string() appends the given string to the value of
+ * the named property in the given node, or creates a new property
+ * with that value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain the new property value
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_appendprop_string(fdt, nodeoffset, name, str) \
+	fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_appendprop_addrrange - append a address range property
+ * @fdt: pointer to the device tree blob
+ * @parent: offset of the parent node
+ * @nodeoffset: offset of the node to add a property at
+ * @name: name of property
+ * @addr: start address of a given range
+ * @size: size of a given range
+ *
+ * fdt_appendprop_addrrange() appends an address range value (start
+ * address and size) to the value of the named property in the given
+ * node, or creates a new property with that value if it does not
+ * already exist.
+ * If "name" is not specified, a default "reg" is used.
+ * Cell sizes are determined by parent's #address-cells and #size-cells.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ *		#address-cells property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size
+ *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ *		contain a new property
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
+			     const char *name, uint64_t addr, uint64_t size);
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOTFOUND, node does not have the named property
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node.  This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+			    const char *name, int namelen);
+#endif
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ *	structure block offset of the created nodeequested subnode (>=0), on
+ *		success
+ *	-FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ *	-FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ *		tag
+ *	-FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ *		the given name
+ *	-FDT_ERR_NOSPACE, if there is insufficient free space in the
+ *		blob to contain the new node
+ *	-FDT_ERR_NOSPACE
+ *	-FDT_ERR_BADLAYOUT
+ *      -FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**
+ * fdt_overlay_apply - Applies a DT overlay on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdto: pointer to the device tree overlay blob
+ *
+ * fdt_overlay_apply() will apply the given device tree overlay on the
+ * given base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ *	-FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ *		properties in the base DT
+ *	-FDT_ERR_BADPHANDLE,
+ *	-FDT_ERR_BADOVERLAY,
+ *	-FDT_ERR_NOPHANDLES,
+ *	-FDT_ERR_INTERNAL,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADOFFSET,
+ *	-FDT_ERR_BADPATH,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_overlay_apply(void *fdt, void *fdto);
+
+/**********************************************************************/
+/* Debugging / informational functions                                */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#endif /* LIBFDT_H */
diff --git a/libfdt/libfdt_env.h b/libfdt/libfdt_env.h
new file mode 100644
index 0000000..73b6d40
--- /dev/null
+++ b/libfdt/libfdt_env.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_ENV_H
+#define LIBFDT_ENV_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef __CHECKER__
+#define FDT_FORCE __attribute__((force))
+#define FDT_BITWISE __attribute__((bitwise))
+#else
+#define FDT_FORCE
+#define FDT_BITWISE
+#endif
+
+typedef uint16_t FDT_BITWISE fdt16_t;
+typedef uint32_t FDT_BITWISE fdt32_t;
+typedef uint64_t FDT_BITWISE fdt64_t;
+
+#define EXTRACT_BYTE(x, n)	((unsigned long long)((uint8_t *)&x)[n])
+#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
+#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
+			 (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
+#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
+			 (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
+			 (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
+			 (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
+
+static inline uint16_t fdt16_to_cpu(fdt16_t x)
+{
+	return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
+}
+static inline fdt16_t cpu_to_fdt16(uint16_t x)
+{
+	return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
+}
+
+static inline uint32_t fdt32_to_cpu(fdt32_t x)
+{
+	return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
+}
+static inline fdt32_t cpu_to_fdt32(uint32_t x)
+{
+	return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
+}
+
+static inline uint64_t fdt64_to_cpu(fdt64_t x)
+{
+	return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
+}
+static inline fdt64_t cpu_to_fdt64(uint64_t x)
+{
+	return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
+}
+#undef CPU_TO_FDT64
+#undef CPU_TO_FDT32
+#undef CPU_TO_FDT16
+#undef EXTRACT_BYTE
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+
+/* strnlen() is not available on Mac OS < 10.7 */
+# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \
+                                         MAC_OS_X_VERSION_10_7)
+
+#define strnlen fdt_strnlen
+
+/*
+ * fdt_strnlen: returns the length of a string or max_count - which ever is
+ * smallest.
+ * Input 1 string: the string whose size is to be determined
+ * Input 2 max_count: the maximum value returned by this function
+ * Output: length of the string or max_count (the smallest of the two)
+ */
+static inline size_t fdt_strnlen(const char *string, size_t max_count)
+{
+    const char *p = memchr(string, 0, max_count);
+    return p ? p - string : max_count;
+}
+
+#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED <
+          MAC_OS_X_VERSION_10_7) */
+
+#endif /* __APPLE__ */
+
+#endif /* LIBFDT_ENV_H */
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..741eeb3
--- /dev/null
+++ b/libfdt/libfdt_internal.h
@@ -0,0 +1,51 @@
+/* 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))
+
+int fdt_ro_probe_(const void *fdt);
+#define FDT_RO_PROBE(fdt)					\
+	{							\
+		int 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);
+}
+
+#define FDT_SW_MAGIC		(~FDT_MAGIC)
+
+#endif /* LIBFDT_INTERNAL_H */
diff --git a/libfdt/version.lds b/libfdt/version.lds
new file mode 100644
index 0000000..ae32924
--- /dev/null
+++ b/libfdt/version.lds
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+LIBFDT_1.2 {
+	global:
+		fdt_next_node;
+		fdt_check_header;
+		fdt_move;
+		fdt_string;
+		fdt_num_mem_rsv;
+		fdt_get_mem_rsv;
+		fdt_subnode_offset_namelen;
+		fdt_subnode_offset;
+		fdt_path_offset_namelen;
+		fdt_path_offset;
+		fdt_get_name;
+		fdt_get_property_namelen;
+		fdt_get_property;
+		fdt_getprop_namelen;
+		fdt_getprop;
+		fdt_get_phandle;
+		fdt_get_alias_namelen;
+		fdt_get_alias;
+		fdt_get_path;
+		fdt_supernode_atdepth_offset;
+		fdt_node_depth;
+		fdt_parent_offset;
+		fdt_node_offset_by_prop_value;
+		fdt_node_offset_by_phandle;
+		fdt_node_check_compatible;
+		fdt_node_offset_by_compatible;
+		fdt_setprop_inplace;
+		fdt_nop_property;
+		fdt_nop_node;
+		fdt_create;
+		fdt_add_reservemap_entry;
+		fdt_finish_reservemap;
+		fdt_begin_node;
+		fdt_property;
+		fdt_end_node;
+		fdt_finish;
+		fdt_open_into;
+		fdt_pack;
+		fdt_add_mem_rsv;
+		fdt_del_mem_rsv;
+		fdt_set_name;
+		fdt_setprop;
+		fdt_delprop;
+		fdt_add_subnode_namelen;
+		fdt_add_subnode;
+		fdt_del_node;
+		fdt_strerror;
+		fdt_offset_ptr;
+		fdt_next_tag;
+		fdt_appendprop;
+		fdt_create_empty_tree;
+		fdt_first_property_offset;
+		fdt_get_property_by_offset;
+		fdt_getprop_by_offset;
+		fdt_next_property_offset;
+		fdt_first_subnode;
+		fdt_next_subnode;
+		fdt_address_cells;
+		fdt_size_cells;
+		fdt_stringlist_contains;
+		fdt_stringlist_count;
+		fdt_stringlist_search;
+		fdt_stringlist_get;
+		fdt_resize;
+		fdt_overlay_apply;
+		fdt_get_string;
+		fdt_find_max_phandle;
+		fdt_generate_phandle;
+		fdt_check_full;
+		fdt_setprop_placeholder;
+		fdt_property_placeholder;
+		fdt_header_size_;
+		fdt_appendprop_addrrange;
+		fdt_setprop_inplace_namelen_partial;
+		fdt_create_with_flags;
+	local:
+		*;
+};
diff --git a/livetree.c b/livetree.c
new file mode 100644
index 0000000..032df58
--- /dev/null
+++ b/livetree.c
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include "dtc.h"
+#include "srcpos.h"
+
+/*
+ * Tree building functions
+ */
+
+void add_label(struct label **labels, char *label)
+{
+	struct label *new;
+
+	/* Make sure the label isn't already there */
+	for_each_label_withdel(*labels, new)
+		if (streq(new->label, label)) {
+			new->deleted = 0;
+			return;
+		}
+
+	new = xmalloc(sizeof(*new));
+	memset(new, 0, sizeof(*new));
+	new->label = label;
+	new->next = *labels;
+	*labels = new;
+}
+
+void delete_labels(struct label **labels)
+{
+	struct label *label;
+
+	for_each_label(*labels, label)
+		label->deleted = 1;
+}
+
+struct property *build_property(char *name, struct data val,
+				struct srcpos *srcpos)
+{
+	struct property *new = xmalloc(sizeof(*new));
+
+	memset(new, 0, sizeof(*new));
+
+	new->name = name;
+	new->val = val;
+	new->srcpos = srcpos_copy(srcpos);
+
+	return new;
+}
+
+struct property *build_property_delete(char *name)
+{
+	struct property *new = xmalloc(sizeof(*new));
+
+	memset(new, 0, sizeof(*new));
+
+	new->name = name;
+	new->deleted = 1;
+
+	return new;
+}
+
+struct property *chain_property(struct property *first, struct property *list)
+{
+	assert(first->next == NULL);
+
+	first->next = list;
+	return first;
+}
+
+struct property *reverse_properties(struct property *first)
+{
+	struct property *p = first;
+	struct property *head = NULL;
+	struct property *next;
+
+	while (p) {
+		next = p->next;
+		p->next = head;
+		head = p;
+		p = next;
+	}
+	return head;
+}
+
+struct node *build_node(struct property *proplist, struct node *children,
+			struct srcpos *srcpos)
+{
+	struct node *new = xmalloc(sizeof(*new));
+	struct node *child;
+
+	memset(new, 0, sizeof(*new));
+
+	new->proplist = reverse_properties(proplist);
+	new->children = children;
+	new->srcpos = srcpos_copy(srcpos);
+
+	for_each_child(new, child) {
+		child->parent = new;
+	}
+
+	return new;
+}
+
+struct node *build_node_delete(struct srcpos *srcpos)
+{
+	struct node *new = xmalloc(sizeof(*new));
+
+	memset(new, 0, sizeof(*new));
+
+	new->deleted = 1;
+	new->srcpos = srcpos_copy(srcpos);
+
+	return new;
+}
+
+struct node *name_node(struct node *node, char *name)
+{
+	assert(node->name == NULL);
+
+	node->name = name;
+
+	return node;
+}
+
+struct node *omit_node_if_unused(struct node *node)
+{
+	node->omit_if_unused = 1;
+
+	return node;
+}
+
+struct node *reference_node(struct node *node)
+{
+	node->is_referenced = 1;
+
+	return node;
+}
+
+struct node *merge_nodes(struct node *old_node, struct node *new_node)
+{
+	struct property *new_prop, *old_prop;
+	struct node *new_child, *old_child;
+	struct label *l;
+
+	old_node->deleted = 0;
+
+	/* Add new node labels to old node */
+	for_each_label_withdel(new_node->labels, l)
+		add_label(&old_node->labels, l->label);
+
+	/* Move properties from the new node to the old node.  If there
+	 * is a collision, replace the old value with the new */
+	while (new_node->proplist) {
+		/* Pop the property off the list */
+		new_prop = new_node->proplist;
+		new_node->proplist = new_prop->next;
+		new_prop->next = NULL;
+
+		if (new_prop->deleted) {
+			delete_property_by_name(old_node, new_prop->name);
+			free(new_prop);
+			continue;
+		}
+
+		/* Look for a collision, set new value if there is */
+		for_each_property_withdel(old_node, old_prop) {
+			if (streq(old_prop->name, new_prop->name)) {
+				/* Add new labels to old property */
+				for_each_label_withdel(new_prop->labels, l)
+					add_label(&old_prop->labels, l->label);
+
+				old_prop->val = new_prop->val;
+				old_prop->deleted = 0;
+				free(old_prop->srcpos);
+				old_prop->srcpos = new_prop->srcpos;
+				free(new_prop);
+				new_prop = NULL;
+				break;
+			}
+		}
+
+		/* if no collision occurred, add property to the old node. */
+		if (new_prop)
+			add_property(old_node, new_prop);
+	}
+
+	/* Move the override child nodes into the primary node.  If
+	 * there is a collision, then merge the nodes. */
+	while (new_node->children) {
+		/* Pop the child node off the list */
+		new_child = new_node->children;
+		new_node->children = new_child->next_sibling;
+		new_child->parent = NULL;
+		new_child->next_sibling = NULL;
+
+		if (new_child->deleted) {
+			delete_node_by_name(old_node, new_child->name);
+			free(new_child);
+			continue;
+		}
+
+		/* Search for a collision.  Merge if there is */
+		for_each_child_withdel(old_node, old_child) {
+			if (streq(old_child->name, new_child->name)) {
+				merge_nodes(old_child, new_child);
+				new_child = NULL;
+				break;
+			}
+		}
+
+		/* if no collision occurred, add child to the old node. */
+		if (new_child)
+			add_child(old_node, new_child);
+	}
+
+	old_node->srcpos = srcpos_extend(old_node->srcpos, new_node->srcpos);
+
+	/* The new node contents are now merged into the old node.  Free
+	 * the new node. */
+	free(new_node);
+
+	return old_node;
+}
+
+struct node * add_orphan_node(struct node *dt, struct node *new_node, char *ref)
+{
+	static unsigned int next_orphan_fragment = 0;
+	struct node *node;
+	struct property *p;
+	struct data d = empty_data;
+	char *name;
+
+	if (ref[0] == '/') {
+		d = data_add_marker(d, TYPE_STRING, ref);
+		d = data_append_data(d, ref, strlen(ref) + 1);
+
+		p = build_property("target-path", d, NULL);
+	} else {
+		d = data_add_marker(d, REF_PHANDLE, ref);
+		d = data_append_integer(d, 0xffffffff, 32);
+
+		p = build_property("target", d, NULL);
+	}
+
+	xasprintf(&name, "fragment@%u",
+			next_orphan_fragment++);
+	name_node(new_node, "__overlay__");
+	node = build_node(p, new_node, NULL);
+	name_node(node, name);
+
+	add_child(dt, node);
+	return dt;
+}
+
+struct node *chain_node(struct node *first, struct node *list)
+{
+	assert(first->next_sibling == NULL);
+
+	first->next_sibling = list;
+	return first;
+}
+
+void add_property(struct node *node, struct property *prop)
+{
+	struct property **p;
+
+	prop->next = NULL;
+
+	p = &node->proplist;
+	while (*p)
+		p = &((*p)->next);
+
+	*p = prop;
+}
+
+void delete_property_by_name(struct node *node, char *name)
+{
+	struct property *prop = node->proplist;
+
+	while (prop) {
+		if (streq(prop->name, name)) {
+			delete_property(prop);
+			return;
+		}
+		prop = prop->next;
+	}
+}
+
+void delete_property(struct property *prop)
+{
+	prop->deleted = 1;
+	delete_labels(&prop->labels);
+}
+
+void add_child(struct node *parent, struct node *child)
+{
+	struct node **p;
+
+	child->next_sibling = NULL;
+	child->parent = parent;
+
+	p = &parent->children;
+	while (*p)
+		p = &((*p)->next_sibling);
+
+	*p = child;
+}
+
+void delete_node_by_name(struct node *parent, char *name)
+{
+	struct node *node = parent->children;
+
+	while (node) {
+		if (streq(node->name, name)) {
+			delete_node(node);
+			return;
+		}
+		node = node->next_sibling;
+	}
+}
+
+void delete_node(struct node *node)
+{
+	struct property *prop;
+	struct node *child;
+
+	node->deleted = 1;
+	for_each_child(node, child)
+		delete_node(child);
+	for_each_property(node, prop)
+		delete_property(prop);
+	delete_labels(&node->labels);
+}
+
+void append_to_property(struct node *node,
+			char *name, const void *data, int len,
+			enum markertype type)
+{
+	struct data d;
+	struct property *p;
+
+	p = get_property(node, name);
+	if (p) {
+		d = data_add_marker(p->val, type, name);
+		d = data_append_data(d, data, len);
+		p->val = d;
+	} else {
+		d = data_add_marker(empty_data, type, name);
+		d = data_append_data(d, data, len);
+		p = build_property(name, d, NULL);
+		add_property(node, p);
+	}
+}
+
+struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size)
+{
+	struct reserve_info *new = xmalloc(sizeof(*new));
+
+	memset(new, 0, sizeof(*new));
+
+	new->address = address;
+	new->size = size;
+
+	return new;
+}
+
+struct reserve_info *chain_reserve_entry(struct reserve_info *first,
+					struct reserve_info *list)
+{
+	assert(first->next == NULL);
+
+	first->next = list;
+	return first;
+}
+
+struct reserve_info *add_reserve_entry(struct reserve_info *list,
+				      struct reserve_info *new)
+{
+	struct reserve_info *last;
+
+	new->next = NULL;
+
+	if (! list)
+		return new;
+
+	for (last = list; last->next; last = last->next)
+		;
+
+	last->next = new;
+
+	return list;
+}
+
+struct dt_info *build_dt_info(unsigned int dtsflags,
+			      struct reserve_info *reservelist,
+			      struct node *tree, uint32_t boot_cpuid_phys)
+{
+	struct dt_info *dti;
+
+	dti = xmalloc(sizeof(*dti));
+	dti->dtsflags = dtsflags;
+	dti->reservelist = reservelist;
+	dti->dt = tree;
+	dti->boot_cpuid_phys = boot_cpuid_phys;
+
+	return dti;
+}
+
+/*
+ * Tree accessor functions
+ */
+
+const char *get_unitname(struct node *node)
+{
+	if (node->name[node->basenamelen] == '\0')
+		return "";
+	else
+		return node->name + node->basenamelen + 1;
+}
+
+struct property *get_property(struct node *node, const char *propname)
+{
+	struct property *prop;
+
+	for_each_property(node, prop)
+		if (streq(prop->name, propname))
+			return prop;
+
+	return NULL;
+}
+
+cell_t propval_cell(struct property *prop)
+{
+	assert(prop->val.len == sizeof(cell_t));
+	return fdt32_to_cpu(*((fdt32_t *)prop->val.val));
+}
+
+cell_t propval_cell_n(struct property *prop, int n)
+{
+	assert(prop->val.len / sizeof(cell_t) >= n);
+	return fdt32_to_cpu(*((fdt32_t *)prop->val.val + n));
+}
+
+struct property *get_property_by_label(struct node *tree, const char *label,
+				       struct node **node)
+{
+	struct property *prop;
+	struct node *c;
+
+	*node = tree;
+
+	for_each_property(tree, prop) {
+		struct label *l;
+
+		for_each_label(prop->labels, l)
+			if (streq(l->label, label))
+				return prop;
+	}
+
+	for_each_child(tree, c) {
+		prop = get_property_by_label(c, label, node);
+		if (prop)
+			return prop;
+	}
+
+	*node = NULL;
+	return NULL;
+}
+
+struct marker *get_marker_label(struct node *tree, const char *label,
+				struct node **node, struct property **prop)
+{
+	struct marker *m;
+	struct property *p;
+	struct node *c;
+
+	*node = tree;
+
+	for_each_property(tree, p) {
+		*prop = p;
+		m = p->val.markers;
+		for_each_marker_of_type(m, LABEL)
+			if (streq(m->ref, label))
+				return m;
+	}
+
+	for_each_child(tree, c) {
+		m = get_marker_label(c, label, node, prop);
+		if (m)
+			return m;
+	}
+
+	*prop = NULL;
+	*node = NULL;
+	return NULL;
+}
+
+struct node *get_subnode(struct node *node, const char *nodename)
+{
+	struct node *child;
+
+	for_each_child(node, child)
+		if (streq(child->name, nodename))
+			return child;
+
+	return NULL;
+}
+
+struct node *get_node_by_path(struct node *tree, const char *path)
+{
+	const char *p;
+	struct node *child;
+
+	if (!path || ! (*path)) {
+		if (tree->deleted)
+			return NULL;
+		return tree;
+	}
+
+	while (path[0] == '/')
+		path++;
+
+	p = strchr(path, '/');
+
+	for_each_child(tree, child) {
+		if (p && strprefixeq(path, p - path, child->name))
+			return get_node_by_path(child, p+1);
+		else if (!p && streq(path, child->name))
+			return child;
+	}
+
+	return NULL;
+}
+
+struct node *get_node_by_label(struct node *tree, const char *label)
+{
+	struct node *child, *node;
+	struct label *l;
+
+	assert(label && (strlen(label) > 0));
+
+	for_each_label(tree->labels, l)
+		if (streq(l->label, label))
+			return tree;
+
+	for_each_child(tree, child) {
+		node = get_node_by_label(child, label);
+		if (node)
+			return node;
+	}
+
+	return NULL;
+}
+
+struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
+{
+	struct node *child, *node;
+
+	if ((phandle == 0) || (phandle == -1)) {
+		assert(generate_fixups);
+		return NULL;
+	}
+
+	if (tree->phandle == phandle) {
+		if (tree->deleted)
+			return NULL;
+		return tree;
+	}
+
+	for_each_child(tree, child) {
+		node = get_node_by_phandle(child, phandle);
+		if (node)
+			return node;
+	}
+
+	return NULL;
+}
+
+struct node *get_node_by_ref(struct node *tree, const char *ref)
+{
+	if (streq(ref, "/"))
+		return tree;
+	else if (ref[0] == '/')
+		return get_node_by_path(tree, ref);
+	else
+		return get_node_by_label(tree, ref);
+}
+
+cell_t get_node_phandle(struct node *root, struct node *node)
+{
+	static cell_t phandle = 1; /* FIXME: ick, static local */
+	struct data d = empty_data;
+
+	if ((node->phandle != 0) && (node->phandle != -1))
+		return node->phandle;
+
+	while (get_node_by_phandle(root, phandle))
+		phandle++;
+
+	node->phandle = phandle;
+
+	d = data_add_marker(d, TYPE_UINT32, NULL);
+	d = data_append_cell(d, phandle);
+
+	if (!get_property(node, "linux,phandle")
+	    && (phandle_format & PHANDLE_LEGACY))
+		add_property(node, build_property("linux,phandle", d, NULL));
+
+	if (!get_property(node, "phandle")
+	    && (phandle_format & PHANDLE_EPAPR))
+		add_property(node, build_property("phandle", d, NULL));
+
+	/* If the node *does* have a phandle property, we must
+	 * be dealing with a self-referencing phandle, which will be
+	 * fixed up momentarily in the caller */
+
+	return node->phandle;
+}
+
+uint32_t guess_boot_cpuid(struct node *tree)
+{
+	struct node *cpus, *bootcpu;
+	struct property *reg;
+
+	cpus = get_node_by_path(tree, "/cpus");
+	if (!cpus)
+		return 0;
+
+
+	bootcpu = cpus->children;
+	if (!bootcpu)
+		return 0;
+
+	reg = get_property(bootcpu, "reg");
+	if (!reg || (reg->val.len != sizeof(uint32_t)))
+		return 0;
+
+	/* FIXME: Sanity check node? */
+
+	return propval_cell(reg);
+}
+
+static int cmp_reserve_info(const void *ax, const void *bx)
+{
+	const struct reserve_info *a, *b;
+
+	a = *((const struct reserve_info * const *)ax);
+	b = *((const struct reserve_info * const *)bx);
+
+	if (a->address < b->address)
+		return -1;
+	else if (a->address > b->address)
+		return 1;
+	else if (a->size < b->size)
+		return -1;
+	else if (a->size > b->size)
+		return 1;
+	else
+		return 0;
+}
+
+static void sort_reserve_entries(struct dt_info *dti)
+{
+	struct reserve_info *ri, **tbl;
+	int n = 0, i = 0;
+
+	for (ri = dti->reservelist;
+	     ri;
+	     ri = ri->next)
+		n++;
+
+	if (n == 0)
+		return;
+
+	tbl = xmalloc(n * sizeof(*tbl));
+
+	for (ri = dti->reservelist;
+	     ri;
+	     ri = ri->next)
+		tbl[i++] = ri;
+
+	qsort(tbl, n, sizeof(*tbl), cmp_reserve_info);
+
+	dti->reservelist = tbl[0];
+	for (i = 0; i < (n-1); i++)
+		tbl[i]->next = tbl[i+1];
+	tbl[n-1]->next = NULL;
+
+	free(tbl);
+}
+
+static int cmp_prop(const void *ax, const void *bx)
+{
+	const struct property *a, *b;
+
+	a = *((const struct property * const *)ax);
+	b = *((const struct property * const *)bx);
+
+	return strcmp(a->name, b->name);
+}
+
+static void sort_properties(struct node *node)
+{
+	int n = 0, i = 0;
+	struct property *prop, **tbl;
+
+	for_each_property_withdel(node, prop)
+		n++;
+
+	if (n == 0)
+		return;
+
+	tbl = xmalloc(n * sizeof(*tbl));
+
+	for_each_property_withdel(node, prop)
+		tbl[i++] = prop;
+
+	qsort(tbl, n, sizeof(*tbl), cmp_prop);
+
+	node->proplist = tbl[0];
+	for (i = 0; i < (n-1); i++)
+		tbl[i]->next = tbl[i+1];
+	tbl[n-1]->next = NULL;
+
+	free(tbl);
+}
+
+static int cmp_subnode(const void *ax, const void *bx)
+{
+	const struct node *a, *b;
+
+	a = *((const struct node * const *)ax);
+	b = *((const struct node * const *)bx);
+
+	return strcmp(a->name, b->name);
+}
+
+static void sort_subnodes(struct node *node)
+{
+	int n = 0, i = 0;
+	struct node *subnode, **tbl;
+
+	for_each_child_withdel(node, subnode)
+		n++;
+
+	if (n == 0)
+		return;
+
+	tbl = xmalloc(n * sizeof(*tbl));
+
+	for_each_child_withdel(node, subnode)
+		tbl[i++] = subnode;
+
+	qsort(tbl, n, sizeof(*tbl), cmp_subnode);
+
+	node->children = tbl[0];
+	for (i = 0; i < (n-1); i++)
+		tbl[i]->next_sibling = tbl[i+1];
+	tbl[n-1]->next_sibling = NULL;
+
+	free(tbl);
+}
+
+static void sort_node(struct node *node)
+{
+	struct node *c;
+
+	sort_properties(node);
+	sort_subnodes(node);
+	for_each_child_withdel(node, c)
+		sort_node(c);
+}
+
+void sort_tree(struct dt_info *dti)
+{
+	sort_reserve_entries(dti);
+	sort_node(dti->dt);
+}
+
+/* utility helper to avoid code duplication */
+static struct node *build_and_name_child_node(struct node *parent, char *name)
+{
+	struct node *node;
+
+	node = build_node(NULL, NULL, NULL);
+	name_node(node, xstrdup(name));
+	add_child(parent, node);
+
+	return node;
+}
+
+static struct node *build_root_node(struct node *dt, char *name)
+{
+	struct node *an;
+
+	an = get_subnode(dt, name);
+	if (!an)
+		an = build_and_name_child_node(dt, name);
+
+	if (!an)
+		die("Could not build root node /%s\n", name);
+
+	return an;
+}
+
+static bool any_label_tree(struct dt_info *dti, struct node *node)
+{
+	struct node *c;
+
+	if (node->labels)
+		return true;
+
+	for_each_child(node, c)
+		if (any_label_tree(dti, c))
+			return true;
+
+	return false;
+}
+
+static void generate_label_tree_internal(struct dt_info *dti,
+					 struct node *an, struct node *node,
+					 bool allocph)
+{
+	struct node *dt = dti->dt;
+	struct node *c;
+	struct property *p;
+	struct label *l;
+
+	/* if there are labels */
+	if (node->labels) {
+
+		/* now add the label in the node */
+		for_each_label(node->labels, l) {
+
+			/* check whether the label already exists */
+			p = get_property(an, l->label);
+			if (p) {
+				fprintf(stderr, "WARNING: label %s already"
+					" exists in /%s", l->label,
+					an->name);
+				continue;
+			}
+
+			/* insert it */
+			p = build_property(l->label,
+				data_copy_escape_string(node->fullpath,
+						strlen(node->fullpath)),
+				NULL);
+			add_property(an, p);
+		}
+
+		/* force allocation of a phandle for this node */
+		if (allocph)
+			(void)get_node_phandle(dt, node);
+	}
+
+	for_each_child(node, c)
+		generate_label_tree_internal(dti, an, c, allocph);
+}
+
+static bool any_fixup_tree(struct dt_info *dti, struct node *node)
+{
+	struct node *c;
+	struct property *prop;
+	struct marker *m;
+
+	for_each_property(node, prop) {
+		m = prop->val.markers;
+		for_each_marker_of_type(m, REF_PHANDLE) {
+			if (!get_node_by_ref(dti->dt, m->ref))
+				return true;
+		}
+	}
+
+	for_each_child(node, c) {
+		if (any_fixup_tree(dti, c))
+			return true;
+	}
+
+	return false;
+}
+
+static void add_fixup_entry(struct dt_info *dti, struct node *fn,
+			    struct node *node, struct property *prop,
+			    struct marker *m)
+{
+	char *entry;
+
+	/* m->ref can only be a REF_PHANDLE, but check anyway */
+	assert(m->type == REF_PHANDLE);
+
+	/* there shouldn't be any ':' in the arguments */
+	if (strchr(node->fullpath, ':') || strchr(prop->name, ':'))
+		die("arguments should not contain ':'\n");
+
+	xasprintf(&entry, "%s:%s:%u",
+			node->fullpath, prop->name, m->offset);
+	append_to_property(fn, m->ref, entry, strlen(entry) + 1, TYPE_STRING);
+
+	free(entry);
+}
+
+static void generate_fixups_tree_internal(struct dt_info *dti,
+					  struct node *fn,
+					  struct node *node)
+{
+	struct node *dt = dti->dt;
+	struct node *c;
+	struct property *prop;
+	struct marker *m;
+	struct node *refnode;
+
+	for_each_property(node, prop) {
+		m = prop->val.markers;
+		for_each_marker_of_type(m, REF_PHANDLE) {
+			refnode = get_node_by_ref(dt, m->ref);
+			if (!refnode)
+				add_fixup_entry(dti, fn, node, prop, m);
+		}
+	}
+
+	for_each_child(node, c)
+		generate_fixups_tree_internal(dti, fn, c);
+}
+
+static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
+{
+	struct node *c;
+	struct property *prop;
+	struct marker *m;
+
+	for_each_property(node, prop) {
+		m = prop->val.markers;
+		for_each_marker_of_type(m, REF_PHANDLE) {
+			if (get_node_by_ref(dti->dt, m->ref))
+				return true;
+		}
+	}
+
+	for_each_child(node, c) {
+		if (any_local_fixup_tree(dti, c))
+			return true;
+	}
+
+	return false;
+}
+
+static void add_local_fixup_entry(struct dt_info *dti,
+		struct node *lfn, struct node *node,
+		struct property *prop, struct marker *m,
+		struct node *refnode)
+{
+	struct node *wn, *nwn;	/* local fixup node, walk node, new */
+	fdt32_t value_32;
+	char **compp;
+	int i, depth;
+
+	/* walk back retrieving depth */
+	depth = 0;
+	for (wn = node; wn; wn = wn->parent)
+		depth++;
+
+	/* allocate name array */
+	compp = xmalloc(sizeof(*compp) * depth);
+
+	/* store names in the array */
+	for (wn = node, i = depth - 1; wn; wn = wn->parent, i--)
+		compp[i] = wn->name;
+
+	/* walk the path components creating nodes if they don't exist */
+	for (wn = lfn, i = 1; i < depth; i++, wn = nwn) {
+		/* if no node exists, create it */
+		nwn = get_subnode(wn, compp[i]);
+		if (!nwn)
+			nwn = build_and_name_child_node(wn, compp[i]);
+	}
+
+	free(compp);
+
+	value_32 = cpu_to_fdt32(m->offset);
+	append_to_property(wn, prop->name, &value_32, sizeof(value_32), TYPE_UINT32);
+}
+
+static void generate_local_fixups_tree_internal(struct dt_info *dti,
+						struct node *lfn,
+						struct node *node)
+{
+	struct node *dt = dti->dt;
+	struct node *c;
+	struct property *prop;
+	struct marker *m;
+	struct node *refnode;
+
+	for_each_property(node, prop) {
+		m = prop->val.markers;
+		for_each_marker_of_type(m, REF_PHANDLE) {
+			refnode = get_node_by_ref(dt, m->ref);
+			if (refnode)
+				add_local_fixup_entry(dti, lfn, node, prop, m, refnode);
+		}
+	}
+
+	for_each_child(node, c)
+		generate_local_fixups_tree_internal(dti, lfn, c);
+}
+
+void generate_label_tree(struct dt_info *dti, char *name, bool allocph)
+{
+	if (!any_label_tree(dti, dti->dt))
+		return;
+	generate_label_tree_internal(dti, build_root_node(dti->dt, name),
+				     dti->dt, allocph);
+}
+
+void generate_fixups_tree(struct dt_info *dti, char *name)
+{
+	if (!any_fixup_tree(dti, dti->dt))
+		return;
+	generate_fixups_tree_internal(dti, build_root_node(dti->dt, name),
+				      dti->dt);
+}
+
+void generate_local_fixups_tree(struct dt_info *dti, char *name)
+{
+	if (!any_local_fixup_tree(dti, dti->dt))
+		return;
+	generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name),
+					    dti->dt);
+}
diff --git a/pylibfdt/.gitignore b/pylibfdt/.gitignore
new file mode 100644
index 0000000..f63a8f1
--- /dev/null
+++ b/pylibfdt/.gitignore
@@ -0,0 +1,3 @@
+libfdt.py
+*.pyc
+libfdt_wrap.c
diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
new file mode 100644
index 0000000..6866a0b
--- /dev/null
+++ b/pylibfdt/Makefile.pylibfdt
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+# Makefile.pylibfdt
+#
+
+PYLIBFDT_srcs = $(PYLIBFDT_dir)/libfdt.i
+PYMODULE = $(PYLIBFDT_dir)/_libfdt.so
+PYLIBFDT_CLEANFILES_L = libfdt_wrap.c libfdt.py *.pyc *.so
+PYLIBFDT_CLEANFILES = $(PYLIBFDT_CLEANFILES_L:%=$(PYLIBFDT_dir)/%)
+PYLIBFDT_CLEANDIRS_L = build __pycache__
+PYLIBFDT_CLEANDIRS = $(PYLIBFDT_CLEANDIRS_L:%=$(PYLIBFDT_dir)/%)
+
+SETUP = $(PYLIBFDT_dir)/setup.py
+SETUPFLAGS =
+
+ifndef V
+SETUPFLAGS += --quiet
+endif
+
+$(PYMODULE): $(PYLIBFDT_srcs) $(LIBFDT_archive) $(SETUP) $(VERSION_FILE)
+	@$(VECHO) PYMOD $@
+	$(PYTHON) $(SETUP) $(SETUPFLAGS) build_ext --build-lib=../$(PYLIBFDT_dir)
+
+install_pylibfdt: $(PYMODULE)
+	@$(VECHO) INSTALL-PYLIB
+	$(PYTHON) $(SETUP) $(SETUPFLAGS) install --prefix=$(PREFIX)
+
+pylibfdt_clean:
+	@$(VECHO) CLEAN "(pylibfdt)"
+	rm -f $(PYLIBFDT_CLEANFILES)
+	rm -rf $(PYLIBFDT_CLEANDIRS)
diff --git a/pylibfdt/libfdt.i b/pylibfdt/libfdt.i
new file mode 100644
index 0000000..feb8c9c
--- /dev/null
+++ b/pylibfdt/libfdt.i
@@ -0,0 +1,1118 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * pylibfdt - Flat Device Tree manipulation in Python
+ * Copyright (C) 2017 Google, Inc.
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+%module libfdt
+
+%include <stdint.i>
+
+%{
+#define SWIG_FILE_WITH_INIT
+#include "libfdt.h"
+
+/*
+ * We rename this function here to avoid problems with swig, since we also have
+ * a struct called fdt_property. That struct causes swig to create a class in
+ * libfdt.py called fdt_property(), which confuses things.
+ */
+static int fdt_property_stub(void *fdt, const char *name, const char *val,
+                             int len)
+{
+    return fdt_property(fdt, name, val, len);
+}
+
+%}
+
+%pythoncode %{
+
+import struct
+
+# Error codes, corresponding to FDT_ERR_... in libfdt.h
+(NOTFOUND,
+        EXISTS,
+        NOSPACE,
+        BADOFFSET,
+        BADPATH,
+        BADPHANDLE,
+        BADSTATE,
+        TRUNCATED,
+        BADMAGIC,
+        BADVERSION,
+        BADSTRUCTURE,
+        BADLAYOUT,
+        INTERNAL,
+        BADNCELLS,
+        BADVALUE,
+        BADOVERLAY,
+        NOPHANDLES) = QUIET_ALL = range(1, 18)
+# QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
+# altogether. All # functions passed this value will return an error instead
+# of raising an exception.
+
+# Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
+# instead of raising an exception.
+QUIET_NOTFOUND = (NOTFOUND,)
+QUIET_NOSPACE = (NOSPACE,)
+
+
+class FdtException(Exception):
+    """An exception caused by an error such as one of the codes above"""
+    def __init__(self, err):
+        self.err = err
+
+    def __str__(self):
+        return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
+
+def strerror(fdt_err):
+    """Get the string for an error number
+
+    Args:
+        fdt_err: Error number (-ve)
+
+    Returns:
+        String containing the associated error
+    """
+    return fdt_strerror(fdt_err)
+
+def check_err(val, quiet=()):
+    """Raise an error if the return value is -ve
+
+    This is used to check for errors returned by libfdt C functions.
+
+    Args:
+        val: Return value from a libfdt function
+        quiet: Errors to ignore (empty to raise on all errors)
+
+    Returns:
+        val if val >= 0
+
+    Raises
+        FdtException if val < 0
+    """
+    if isinstance(val, int) and val < 0:
+        if -val not in quiet:
+            raise FdtException(val)
+    return val
+
+def check_err_null(val, quiet=()):
+    """Raise an error if the return value is NULL
+
+    This is used to check for a NULL return value from certain libfdt C
+    functions
+
+    Args:
+        val: Return value from a libfdt function
+        quiet: Errors to ignore (empty to raise on all errors)
+
+    Returns:
+        val if val is a list, None if not
+
+    Raises
+        FdtException if val indicates an error was reported and the error
+        is not in @quiet.
+    """
+    # Normally a list is returned which contains the data and its length.
+    # If we get just an integer error code, it means the function failed.
+    if not isinstance(val, list):
+        if -val not in quiet:
+            raise FdtException(val)
+    return val
+
+class FdtRo(object):
+    """Class for a read-only device-tree
+
+    This is a base class used by FdtRw (read-write access) and FdtSw
+    (sequential-write access). It implements read-only access to the
+    device tree.
+
+    Here are the three classes and when you should use them:
+
+        FdtRo - read-only access to an existing FDT
+        FdtRw - read-write access to an existing FDT (most common case)
+        FdtSw - for creating a new FDT, as well as allowing read-only access
+    """
+    def __init__(self, data):
+        self._fdt = bytearray(data)
+        check_err(fdt_check_header(self._fdt));
+
+    def as_bytearray(self):
+        """Get the device tree contents as a bytearray
+
+        This can be passed directly to libfdt functions that access a
+        const void * for the device tree.
+
+        Returns:
+            bytearray containing the device tree
+        """
+        return bytearray(self._fdt)
+
+    def next_node(self, nodeoffset, depth, quiet=()):
+        """Find the next subnode
+
+        Args:
+            nodeoffset: Node offset of previous node
+            depth: The depth of the node at nodeoffset. This is used to
+                calculate the depth of the returned node
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Typle:
+                Offset of the next node, if any, else a -ve error
+                Depth of the returned node, if any, else undefined
+
+        Raises:
+            FdtException if no more nodes found or other error occurs
+        """
+        return check_err(fdt_next_node(self._fdt, nodeoffset, depth), quiet)
+
+    def first_subnode(self, nodeoffset, quiet=()):
+        """Find the first subnode of a parent node
+
+        Args:
+            nodeoffset: Node offset of parent node
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the first subnode, if any
+
+        Raises:
+            FdtException if no subnodes found or other error occurs
+        """
+        return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
+
+    def next_subnode(self, nodeoffset, quiet=()):
+        """Find the next subnode
+
+        Args:
+            nodeoffset: Node offset of previous subnode
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the next subnode, if any
+
+        Raises:
+            FdtException if no more subnodes found or other error occurs
+        """
+        return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
+
+    def magic(self):
+        """Return the magic word from the header
+
+        Returns:
+            Magic word
+        """
+        return fdt_magic(self._fdt)
+
+    def totalsize(self):
+        """Return the total size of the device tree
+
+        Returns:
+            Total tree size in bytes
+        """
+        return fdt_totalsize(self._fdt)
+
+    def off_dt_struct(self):
+        """Return the start of the device-tree struct area
+
+        Returns:
+            Start offset of struct area
+        """
+        return fdt_off_dt_struct(self._fdt)
+
+    def off_dt_strings(self):
+        """Return the start of the device-tree string area
+
+        Returns:
+            Start offset of string area
+        """
+        return fdt_off_dt_strings(self._fdt)
+
+    def off_mem_rsvmap(self):
+        """Return the start of the memory reserve map
+
+        Returns:
+            Start offset of memory reserve map
+        """
+        return fdt_off_mem_rsvmap(self._fdt)
+
+    def version(self):
+        """Return the version of the device tree
+
+        Returns:
+            Version number of the device tree
+        """
+        return fdt_version(self._fdt)
+
+    def last_comp_version(self):
+        """Return the last compatible version of the device tree
+
+        Returns:
+            Last compatible version number of the device tree
+        """
+        return fdt_last_comp_version(self._fdt)
+
+    def boot_cpuid_phys(self):
+        """Return the physical boot CPU ID
+
+        Returns:
+            Physical boot CPU ID
+        """
+        return fdt_boot_cpuid_phys(self._fdt)
+
+    def size_dt_strings(self):
+        """Return the start of the device-tree string area
+
+        Returns:
+            Start offset of string area
+        """
+        return fdt_size_dt_strings(self._fdt)
+
+    def size_dt_struct(self):
+        """Return the start of the device-tree struct area
+
+        Returns:
+            Start offset of struct area
+        """
+        return fdt_size_dt_struct(self._fdt)
+
+    def num_mem_rsv(self, quiet=()):
+        """Return the number of memory reserve-map records
+
+        Returns:
+            Number of memory reserve-map records
+        """
+        return check_err(fdt_num_mem_rsv(self._fdt), quiet)
+
+    def get_mem_rsv(self, index, quiet=()):
+        """Return the indexed memory reserve-map record
+
+        Args:
+            index: Record to return (0=first)
+
+        Returns:
+            Number of memory reserve-map records
+        """
+        return check_err(fdt_get_mem_rsv(self._fdt, index), quiet)
+
+    def subnode_offset(self, parentoffset, name, quiet=()):
+        """Get the offset of a named subnode
+
+        Args:
+            parentoffset: Offset of the parent node to check
+            name: Name of the required subnode, e.g. 'subnode@1'
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The node offset of the found node, if any
+
+        Raises
+            FdtException if there is no node with that name, or other error
+        """
+        return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
+                         quiet)
+
+    def path_offset(self, path, quiet=()):
+        """Get the offset for a given path
+
+        Args:
+            path: Path to the required node, e.g. '/node@3/subnode@1'
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Node offset
+
+        Raises
+            FdtException if the path is not valid or not found
+        """
+        return check_err(fdt_path_offset(self._fdt, path), quiet)
+
+    def get_name(self, nodeoffset):
+        """Get the name of a node
+
+        Args:
+            nodeoffset: Offset of node to check
+
+        Returns:
+            Node name
+
+        Raises:
+            FdtException on error (e.g. nodeoffset is invalid)
+        """
+        return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
+
+    def first_property_offset(self, nodeoffset, quiet=()):
+        """Get the offset of the first property in a node offset
+
+        Args:
+            nodeoffset: Offset to the node to check
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Offset of the first property
+
+        Raises
+            FdtException if the associated node has no properties, or some
+                other error occurred
+        """
+        return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
+                         quiet)
+
+    def next_property_offset(self, prop_offset, quiet=()):
+        """Get the next property in a node
+
+        Args:
+            prop_offset: Offset of the previous property
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Offset of the next property
+
+        Raises:
+            FdtException if the associated node has no more properties, or
+                some other error occurred
+        """
+        return check_err(fdt_next_property_offset(self._fdt, prop_offset),
+                         quiet)
+
+    def get_property_by_offset(self, prop_offset, quiet=()):
+        """Obtains a property that can be examined
+
+        Args:
+            prop_offset: Offset of property (e.g. from first_property_offset())
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Property object, or None if not found
+
+        Raises:
+            FdtException on error (e.g. invalid prop_offset or device
+            tree format)
+        """
+        pdata = check_err_null(
+                fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
+        if isinstance(pdata, (int)):
+            return pdata
+        return Property(pdata[0], pdata[1])
+
+    def getprop(self, nodeoffset, prop_name, quiet=()):
+        """Get a property from a node
+
+        Args:
+            nodeoffset: Node offset containing property to get
+            prop_name: Name of property to get
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Value of property as a Property object (which can be used as a
+               bytearray/string), or -ve error number. On failure, returns an
+               integer error
+
+        Raises:
+            FdtError if any error occurs (e.g. the property is not found)
+        """
+        pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
+                               quiet)
+        if isinstance(pdata, (int)):
+            return pdata
+        return Property(prop_name, bytearray(pdata[0]))
+
+    def get_phandle(self, nodeoffset):
+        """Get the phandle of a node
+
+        Args:
+            nodeoffset: Node offset to check
+
+        Returns:
+            phandle of node, or 0 if the node has no phandle or another error
+            occurs
+        """
+        return fdt_get_phandle(self._fdt, nodeoffset)
+
+    def get_alias(self, name):
+        """Get the full path referenced by a given alias
+
+        Args:
+            name: name of the alias to lookup
+
+        Returns:
+            Full path to the node for the alias named 'name', if it exists
+            None, if the given alias or the /aliases node does not exist
+        """
+        return fdt_get_alias(self._fdt, name)
+
+    def parent_offset(self, nodeoffset, quiet=()):
+        """Get the offset of a node's parent
+
+        Args:
+            nodeoffset: Node offset to check
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the parent node, if any
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
+
+    def node_offset_by_phandle(self, phandle, quiet=()):
+        """Get the offset of a node with the given phandle
+
+        Args:
+            phandle: Phandle to search for
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of node with that phandle, if any
+
+        Raises:
+            FdtException if no node found or other error occurs
+        """
+        return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
+
+
+class Fdt(FdtRo):
+    """Device tree class, supporting all operations
+
+    The Fdt object is created is created from a device tree binary file,
+    e.g. with something like:
+
+       fdt = Fdt(open("filename.dtb").read())
+
+    Operations can then be performed using the methods in this class. Each
+    method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
+
+    All methods raise an FdtException if an error occurs. To avoid this
+    behaviour a 'quiet' parameter is provided for some functions. This
+    defaults to empty, but you can pass a list of errors that you expect.
+    If one of these errors occurs, the function will return an error number
+    (e.g. -NOTFOUND).
+    """
+    def __init__(self, data):
+        FdtRo.__init__(self, data)
+
+    @staticmethod
+    def create_empty_tree(size, quiet=()):
+        """Create an empty device tree ready for use
+
+        Args:
+            size: Size of device tree in bytes
+
+        Returns:
+            Fdt object containing the device tree
+        """
+        data = bytearray(size)
+        err = check_err(fdt_create_empty_tree(data, size), quiet)
+        if err:
+            return err
+        return Fdt(data)
+
+    def resize(self, size, quiet=()):
+        """Move the device tree into a larger or smaller space
+
+        This creates a new device tree of size @size and moves the existing
+        device tree contents over to that. It can be used to create more space
+        in a device tree. Note that the Fdt object remains the same, but it
+        now has a new bytearray holding the contents.
+
+        Args:
+            size: Required new size of device tree in bytes
+        """
+        fdt = bytearray(size)
+        err = check_err(fdt_open_into(self._fdt, fdt, size), quiet)
+        if err:
+            return err
+        self._fdt = fdt
+
+    def pack(self, quiet=()):
+        """Pack the device tree to remove unused space
+
+        This adjusts the tree in place.
+
+        Args:
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if any error occurs
+        """
+        err = check_err(fdt_pack(self._fdt), quiet)
+        if err:
+            return err
+        del self._fdt[self.totalsize():]
+        return err
+
+    def set_name(self, nodeoffset, name, quiet=()):
+        """Set the name of a node
+
+        Args:
+            nodeoffset: Node offset of node to update
+            name: New node name (string without \0)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        if chr(0) in name:
+            raise ValueError('Property contains embedded nul characters')
+        return check_err(fdt_set_name(self._fdt, nodeoffset, name), quiet)
+
+    def setprop(self, nodeoffset, prop_name, val, quiet=()):
+        """Set the value of a property
+
+        Args:
+            nodeoffset: Node offset containing the property to create/update
+            prop_name: Name of property
+            val: Value to write (string or bytearray)
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name, val,
+                                     len(val)), quiet)
+
+    def setprop_u32(self, nodeoffset, prop_name, val, quiet=()):
+        """Set the value of a property
+
+        Args:
+            nodeoffset: Node offset containing the property to create/update
+            prop_name: Name of property
+            val: Value to write (integer)
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        return check_err(fdt_setprop_u32(self._fdt, nodeoffset, prop_name, val),
+                         quiet)
+
+    def setprop_u64(self, nodeoffset, prop_name, val, quiet=()):
+        """Set the value of a property
+
+        Args:
+            nodeoffset: Node offset containing the property to create/update
+            prop_name: Name of property
+            val: Value to write (integer)
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        return check_err(fdt_setprop_u64(self._fdt, nodeoffset, prop_name, val),
+                         quiet)
+
+    def setprop_str(self, nodeoffset, prop_name, val, quiet=()):
+        """Set the string value of a property
+
+        The property is set to the string, with a nul terminator added
+
+        Args:
+            nodeoffset: Node offset containing the property to create/update
+            prop_name: Name of property
+            val: Value to write (string without nul terminator). Unicode is
+                supposed by encoding to UTF-8
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        val = val.encode('utf-8') + b'\0'
+        return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name,
+                                     val, len(val)), quiet)
+
+    def delprop(self, nodeoffset, prop_name, quiet=()):
+        """Delete a property from a node
+
+        Args:
+            nodeoffset: Node offset containing property to delete
+            prop_name: Name of property to delete
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtError if the property does not exist, or another error occurs
+        """
+        return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name), quiet)
+
+    def add_subnode(self, parentoffset, name, quiet=()):
+        """Add a new subnode to a node
+
+        Args:
+            parentoffset: Parent offset to add the subnode to
+            name: Name of node to add
+
+        Returns:
+            offset of the node created, or negative error code on failure
+
+        Raises:
+            FdtError if there is not enough space, or another error occurs
+        """
+        return check_err(fdt_add_subnode(self._fdt, parentoffset, name), quiet)
+
+    def del_node(self, nodeoffset, quiet=()):
+        """Delete a node
+
+        Args:
+            nodeoffset: Offset of node to delete
+
+        Returns:
+            Error code, or 0 if OK
+
+        Raises:
+            FdtError if an error occurs
+        """
+        return check_err(fdt_del_node(self._fdt, nodeoffset), quiet)
+
+
+class Property(bytearray):
+    """Holds a device tree property name and value.
+
+    This holds a copy of a property taken from the device tree. It does not
+    reference the device tree, so if anything changes in the device tree,
+    a Property object will remain valid.
+
+    Properties:
+        name: Property name
+        value: Property value as a bytearray
+    """
+    def __init__(self, name, value):
+        bytearray.__init__(self, value)
+        self.name = name
+
+    def as_cell(self, fmt):
+        return struct.unpack('>' + fmt, self)[0]
+
+    def as_uint32(self):
+        return self.as_cell('L')
+
+    def as_int32(self):
+        return self.as_cell('l')
+
+    def as_uint64(self):
+        return self.as_cell('Q')
+
+    def as_int64(self):
+        return self.as_cell('q')
+
+    def as_str(self):
+        """Unicode is supported by decoding from UTF-8"""
+        if self[-1] != 0:
+            raise ValueError('Property lacks nul termination')
+        if 0 in self[:-1]:
+            raise ValueError('Property contains embedded nul characters')
+        return self[:-1].decode('utf-8')
+
+
+class FdtSw(FdtRo):
+    """Software interface to create a device tree from scratch
+
+    The methods in this class work by adding to an existing 'partial' device
+    tree buffer of a fixed size created by instantiating this class. When the
+    tree is complete, call as_fdt() to obtain a device tree ready to be used.
+
+    Similarly with nodes, a new node is started with begin_node() and finished
+    with end_node().
+
+    The context manager functions can be used to make this a bit easier:
+
+    # First create the device tree with a node and property:
+    sw = FdtSw()
+    sw.finish_reservemap()
+    with sw.add_node(''):
+        with sw.add_node('node'):
+            sw.property_u32('reg', 2)
+    fdt = sw.as_fdt()
+
+    # Now we can use it as a real device tree
+    fdt.setprop_u32(0, 'reg', 3)
+
+    The size hint provides a starting size for the space to be used by the
+    device tree. This will be increased automatically as needed as new items
+    are added to the tree.
+    """
+    INC_SIZE = 1024  # Expand size by this much when out of space
+
+    def __init__(self, size_hint=None):
+        """Create a new FdtSw object
+
+        Args:
+            size_hint: A hint as to the initial size to use
+
+        Raises:
+            ValueError if size_hint is negative
+
+        Returns:
+            FdtSw object on success, else integer error code (if not raising)
+        """
+        if not size_hint:
+            size_hint = self.INC_SIZE
+        fdtsw = bytearray(size_hint)
+        err = check_err(fdt_create(fdtsw, size_hint))
+        if err:
+            return err
+        self._fdt = fdtsw
+
+    def as_fdt(self):
+        """Convert a FdtSw into an Fdt so it can be accessed as normal
+
+        Creates a new Fdt object from the work-in-progress device tree. This
+        does not call fdt_finish() on the current object, so it is possible to
+        add more nodes/properties and call as_fdt() again to get an updated
+        tree.
+
+        Returns:
+            Fdt object allowing access to the newly created device tree
+        """
+        fdtsw = bytearray(self._fdt)
+        check_err(fdt_finish(fdtsw))
+        return Fdt(fdtsw)
+
+    def check_space(self, val):
+        """Check if we need to add more space to the FDT
+
+        This should be called with the error code from an operation. If this is
+        -NOSPACE then the FDT will be expanded to have more space, and True will
+        be returned, indicating that the operation needs to be tried again.
+
+        Args:
+            val: Return value from the operation that was attempted
+
+        Returns:
+            True if the operation must be retried, else False
+        """
+        if check_err(val, QUIET_NOSPACE) < 0:
+            self.resize(len(self._fdt) + self.INC_SIZE)
+            return True
+        return False
+
+    def resize(self, size):
+        """Resize the buffer to accommodate a larger tree
+
+        Args:
+            size: New size of tree
+
+        Raises:
+            FdtException on any error
+        """
+        fdt = bytearray(size)
+        err = check_err(fdt_resize(self._fdt, fdt, size))
+        self._fdt = fdt
+
+    def add_reservemap_entry(self, addr, size):
+        """Add a new memory reserve map entry
+
+        Once finished adding, you must call finish_reservemap().
+
+        Args:
+            addr: 64-bit start address
+            size: 64-bit size
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_add_reservemap_entry(self._fdt, addr,
+                                                        size)):
+            pass
+
+    def finish_reservemap(self):
+        """Indicate that there are no more reserve map entries to add
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_finish_reservemap(self._fdt)):
+            pass
+
+    def begin_node(self, name):
+        """Begin a new node
+
+        Use this before adding properties to the node. Then call end_node() to
+        finish it. You can also use the context manager as shown in the FdtSw
+        class comment.
+
+        Args:
+            name: Name of node to begin
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_begin_node(self._fdt, name)):
+            pass
+
+    def property_string(self, name, string):
+        """Add a property with a string value
+
+        The string will be nul-terminated when written to the device tree
+
+        Args:
+            name: Name of property to add
+            string: String value of property
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_property_string(self._fdt, name, string)):
+            pass
+
+    def property_u32(self, name, val):
+        """Add a property with a 32-bit value
+
+        Write a single-cell value to the device tree
+
+        Args:
+            name: Name of property to add
+            val: Value of property
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_property_u32(self._fdt, name, val)):
+            pass
+
+    def property_u64(self, name, val):
+        """Add a property with a 64-bit value
+
+        Write a double-cell value to the device tree in big-endian format
+
+        Args:
+            name: Name of property to add
+            val: Value of property
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_property_u64(self._fdt, name, val)):
+            pass
+
+    def property_cell(self, name, val):
+        """Add a property with a single-cell value
+
+        Write a single-cell value to the device tree
+
+        Args:
+            name: Name of property to add
+            val: Value of property
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_property_cell(self._fdt, name, val)):
+            pass
+
+    def property(self, name, val):
+        """Add a property
+
+        Write a new property with the given value to the device tree. The value
+        is taken as is and is not nul-terminated
+
+        Args:
+            name: Name of property to add
+            val: Value of property
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_property_stub(self._fdt, name, val,
+                                                 len(val))):
+            pass
+
+    def end_node(self):
+        """End a node
+
+        Use this after adding properties to a node to close it off. You can also
+        use the context manager as shown in the FdtSw class comment.
+
+        Args:
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Raises:
+            FdtException on any error
+        """
+        while self.check_space(fdt_end_node(self._fdt)):
+            pass
+
+    def add_node(self, name):
+        """Create a new context for adding a node
+
+        When used in a 'with' clause this starts a new node and finishes it
+        afterward.
+
+        Args:
+            name: Name of node to add
+        """
+        return NodeAdder(self, name)
+
+
+class NodeAdder():
+    """Class to provide a node context
+
+    This allows you to add nodes in a more natural way:
+
+        with fdtsw.add_node('name'):
+            fdtsw.property_string('test', 'value')
+
+    The node is automatically completed with a call to end_node() when the
+    context exits.
+    """
+    def __init__(self, fdtsw, name):
+        self._fdt = fdtsw
+        self._name = name
+
+    def __enter__(self):
+        self._fdt.begin_node(self._name)
+
+    def __exit__(self, type, value, traceback):
+        self._fdt.end_node()
+%}
+
+%rename(fdt_property) fdt_property_func;
+
+/*
+ * fdt32_t is a big-endian 32-bit value defined to uint32_t in libfdt_env.h
+ * so use the same type here.
+ */
+typedef uint32_t fdt32_t;
+
+%include "fdt.h"
+
+%include "typemaps.i"
+
+/* Most functions don't change the device tree, so use a const void * */
+%typemap(in) (const void *)(const void *fdt) {
+	if (!PyByteArray_Check($input)) {
+		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+			"', argument " "$argnum"" of type '" "$type""'");
+	}
+	$1 = (void *)PyByteArray_AsString($input);
+        fdt = $1;
+        fdt = fdt; /* avoid unused variable warning */
+}
+
+/* Some functions do change the device tree, so use void * */
+%typemap(in) (void *)(const void *fdt) {
+	if (!PyByteArray_Check($input)) {
+		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+			"', argument " "$argnum"" of type '" "$type""'");
+	}
+	$1 = PyByteArray_AsString($input);
+        fdt = $1;
+        fdt = fdt; /* avoid unused variable warning */
+}
+
+/* typemap used for fdt_get_property_by_offset() */
+%typemap(out) (struct fdt_property *) {
+	PyObject *buff;
+
+	if ($1) {
+		resultobj = PyString_FromString(
+			fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
+		buff = PyByteArray_FromStringAndSize(
+			(const char *)($1 + 1), fdt32_to_cpu($1->len));
+		resultobj = SWIG_Python_AppendOutput(resultobj, buff);
+	}
+}
+
+%apply int *OUTPUT { int *lenp };
+
+/* typemap used for fdt_getprop() */
+%typemap(out) (const void *) {
+	if (!$1)
+		$result = Py_None;
+	else
+        %#if PY_VERSION_HEX >= 0x03000000
+            $result = Py_BuildValue("y#", $1, *arg4);
+        %#else
+            $result = Py_BuildValue("s#", $1, *arg4);
+        %#endif
+}
+
+/* typemap used for fdt_setprop() */
+%typemap(in) (const void *val) {
+    %#if PY_VERSION_HEX >= 0x03000000
+        if (!PyBytes_Check($input)) {
+            SWIG_exception_fail(SWIG_TypeError, "bytes expected in method '" "$symname"
+                "', argument " "$argnum"" of type '" "$type""'");
+        }
+        $1 = PyBytes_AsString($input);
+    %#else
+        $1 = PyString_AsString($input);   /* char *str */
+    %#endif
+}
+
+/* typemaps used for fdt_next_node() */
+%typemap(in, numinputs=1) int *depth (int depth) {
+   depth = (int) PyInt_AsLong($input);
+   $1 = &depth;
+}
+
+%typemap(argout) int *depth {
+        PyObject *val = Py_BuildValue("i", *arg$argnum);
+        resultobj = SWIG_Python_AppendOutput(resultobj, val);
+}
+
+%apply int *depth { int *depth };
+
+/* typemaps for fdt_get_mem_rsv */
+%typemap(in, numinputs=0) uint64_t * (uint64_t temp) {
+   $1 = &temp;
+}
+
+%typemap(argout) uint64_t * {
+        PyObject *val = PyLong_FromUnsignedLongLong(*arg$argnum);
+        if (!result) {
+           if (PyTuple_GET_SIZE(resultobj) == 0)
+              resultobj = val;
+           else
+              resultobj = SWIG_Python_AppendOutput(resultobj, val);
+        }
+}
+
+/* We have both struct fdt_property and a function fdt_property() */
+%warnfilter(302) fdt_property;
+
+/* These are macros in the header so have to be redefined here */
+uint32_t fdt_magic(const void *fdt);
+uint32_t fdt_totalsize(const void *fdt);
+uint32_t fdt_off_dt_struct(const void *fdt);
+uint32_t fdt_off_dt_strings(const void *fdt);
+uint32_t fdt_off_mem_rsvmap(const void *fdt);
+uint32_t fdt_version(const void *fdt);
+uint32_t fdt_last_comp_version(const void *fdt);
+uint32_t fdt_boot_cpuid_phys(const void *fdt);
+uint32_t fdt_size_dt_strings(const void *fdt);
+uint32_t fdt_size_dt_struct(const void *fdt);
+
+int fdt_property_string(void *fdt, const char *name, const char *val);
+int fdt_property_cell(void *fdt, const char *name, uint32_t val);
+
+/*
+ * This function has a stub since the name fdt_property is used for both a
+  * function and a struct, which confuses SWIG.
+ */
+int fdt_property_stub(void *fdt, const char *name, const char *val, int len);
+
+%include <libfdt.h>
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
new file mode 100755
index 0000000..a00bf8b
--- /dev/null
+++ b/pylibfdt/setup.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python2
+# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+
+"""
+setup.py file for SWIG libfdt
+Copyright (C) 2017 Google, Inc.
+Written by Simon Glass <sjg@chromium.org>
+"""
+
+from distutils.core import setup, Extension
+import os
+import re
+import sys
+
+
+VERSION_PATTERN = '^#define DTC_VERSION "DTC ([^"]*)"$'
+
+
+def get_version():
+    version_file = "../version_gen.h"
+    f = open(version_file, 'rt')
+    m = re.match(VERSION_PATTERN, f.readline())
+    return m.group(1)
+
+
+setupdir = os.path.dirname(os.path.abspath(sys.argv[0]))
+os.chdir(setupdir)
+
+libfdt_module = Extension(
+    '_libfdt',
+    sources=['libfdt.i'],
+    include_dirs=['../libfdt'],
+    libraries=['fdt'],
+    library_dirs=['../libfdt'],
+    swig_opts=['-I../libfdt'],
+)
+
+setup(
+    name='libfdt',
+    version=get_version(),
+    author='Simon Glass <sjg@chromium.org>',
+    description='Python binding for libfdt',
+    ext_modules=[libfdt_module],
+    py_modules=['libfdt'],
+)
diff --git a/scripts/kup-dtc b/scripts/kup-dtc
new file mode 100755
index 0000000..3c3376c
--- /dev/null
+++ b/scripts/kup-dtc
@@ -0,0 +1,32 @@
+#! /bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+REMOTE_GIT=/pub/scm/utils/dtc/dtc.git
+REMOTE_PATH=/pub/software/utils/dtc
+
+set -e
+
+kup_one () {
+    VERSION="$1"
+
+    TAG="v$VERSION"
+
+    PREFIX="dtc-$VERSION/"
+    TAR="dtc-$VERSION.tar"
+    SIG="$TAR.sign"
+
+    git archive --format=tar --prefix="$PREFIX" -o "$TAR" "$TAG"
+    gpg --detach-sign --armor -o "$SIG" "$TAR"
+
+    ls -l "$TAR"*
+
+    # Verify the signature as a sanity check
+    gpg --verify "$SIG" "$TAR"
+
+    kup put --tar --prefix="$PREFIX" "$REMOTE_GIT" "$TAG" "$SIG" "$REMOTE_PATH/$TAR.gz" 
+}
+
+for version; do
+    kup_one $version
+done
+
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
new file mode 100755
index 0000000..ea333e7
--- /dev/null
+++ b/scripts/setlocalversion
@@ -0,0 +1,23 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Print additional version information for non-release trees.
+
+usage() {
+	echo "Usage: $0 [srctree]" >&2
+	exit 1
+}
+
+cd "${1:-.}" || usage
+
+# Check for git and a git repo.
+if head=`git rev-parse --verify HEAD 2>/dev/null`; then
+	# Do we have an untagged version?
+	if git name-rev --tags HEAD | grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then
+		printf '%s%s' -g `echo "$head" | cut -c1-8`
+	fi
+
+	# Are there uncommitted changes?
+	if git diff-index HEAD | read dummy; then
+		printf '%s' -dirty
+	fi
+fi
diff --git a/srcpos.c b/srcpos.c
new file mode 100644
index 0000000..f5205fb
--- /dev/null
+++ b/srcpos.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+
+#include "dtc.h"
+#include "srcpos.h"
+
+/* A node in our list of directories to search for source/include files */
+struct search_path {
+	struct search_path *next;	/* next node in list, NULL for end */
+	const char *dirname;		/* name of directory to search */
+};
+
+/* This is the list of directories that we search for source files */
+static struct search_path *search_path_head, **search_path_tail;
+
+/* Detect infinite include recursion. */
+#define MAX_SRCFILE_DEPTH     (100)
+static int srcfile_depth; /* = 0 */
+
+static char *get_dirname(const char *path)
+{
+	const char *slash = strrchr(path, '/');
+
+	if (slash) {
+		int len = slash - path;
+		char *dir = xmalloc(len + 1);
+
+		memcpy(dir, path, len);
+		dir[len] = '\0';
+		return dir;
+	}
+	return NULL;
+}
+
+FILE *depfile; /* = NULL */
+struct srcfile_state *current_srcfile; /* = NULL */
+static char *initial_path; /* = NULL */
+static int initial_pathlen; /* = 0 */
+static bool initial_cpp = true;
+
+static void set_initial_path(char *fname)
+{
+	int i, len = strlen(fname);
+
+	xasprintf(&initial_path, "%s", fname);
+	initial_pathlen = 0;
+	for (i = 0; i != len; i++)
+		if (initial_path[i] == '/')
+			initial_pathlen++;
+}
+
+static char *shorten_to_initial_path(char *fname)
+{
+	char *p1, *p2, *prevslash1 = NULL;
+	int slashes = 0;
+
+	for (p1 = fname, p2 = initial_path; *p1 && *p2; p1++, p2++) {
+		if (*p1 != *p2)
+			break;
+		if (*p1 == '/') {
+			prevslash1 = p1;
+			slashes++;
+		}
+	}
+	p1 = prevslash1 + 1;
+	if (prevslash1) {
+		int diff = initial_pathlen - slashes, i, j;
+		int restlen = strlen(fname) - (p1 - fname);
+		char *res;
+
+		res = xmalloc((3 * diff) + restlen + 1);
+		for (i = 0, j = 0; i != diff; i++) {
+			res[j++] = '.';
+			res[j++] = '.';
+			res[j++] = '/';
+		}
+		strcpy(res + j, p1);
+		return res;
+	}
+	return NULL;
+}
+
+/**
+ * Try to open a file in a given directory.
+ *
+ * If the filename is an absolute path, then dirname is ignored. If it is a
+ * relative path, then we look in that directory for the file.
+ *
+ * @param dirname	Directory to look in, or NULL for none
+ * @param fname		Filename to look for
+ * @param fp		Set to NULL if file did not open
+ * @return allocated filename on success (caller must free), NULL on failure
+ */
+static char *try_open(const char *dirname, const char *fname, FILE **fp)
+{
+	char *fullname;
+
+	if (!dirname || fname[0] == '/')
+		fullname = xstrdup(fname);
+	else
+		fullname = join_path(dirname, fname);
+
+	*fp = fopen(fullname, "rb");
+	if (!*fp) {
+		free(fullname);
+		fullname = NULL;
+	}
+
+	return fullname;
+}
+
+/**
+ * Open a file for read access
+ *
+ * If it is a relative filename, we search the full search path for it.
+ *
+ * @param fname	Filename to open
+ * @param fp	Returns pointer to opened FILE, or NULL on failure
+ * @return pointer to allocated filename, which caller must free
+ */
+static char *fopen_any_on_path(const char *fname, FILE **fp)
+{
+	const char *cur_dir = NULL;
+	struct search_path *node;
+	char *fullname;
+
+	/* Try current directory first */
+	assert(fp);
+	if (current_srcfile)
+		cur_dir = current_srcfile->dir;
+	fullname = try_open(cur_dir, fname, fp);
+
+	/* Failing that, try each search path in turn */
+	for (node = search_path_head; !*fp && node; node = node->next)
+		fullname = try_open(node->dirname, fname, fp);
+
+	return fullname;
+}
+
+FILE *srcfile_relative_open(const char *fname, char **fullnamep)
+{
+	FILE *f;
+	char *fullname;
+
+	if (streq(fname, "-")) {
+		f = stdin;
+		fullname = xstrdup("<stdin>");
+	} else {
+		fullname = fopen_any_on_path(fname, &f);
+		if (!f)
+			die("Couldn't open \"%s\": %s\n", fname,
+			    strerror(errno));
+	}
+
+	if (depfile)
+		fprintf(depfile, " %s", fullname);
+
+	if (fullnamep)
+		*fullnamep = fullname;
+	else
+		free(fullname);
+
+	return f;
+}
+
+void srcfile_push(const char *fname)
+{
+	struct srcfile_state *srcfile;
+
+	if (srcfile_depth++ >= MAX_SRCFILE_DEPTH)
+		die("Includes nested too deeply");
+
+	srcfile = xmalloc(sizeof(*srcfile));
+
+	srcfile->f = srcfile_relative_open(fname, &srcfile->name);
+	srcfile->dir = get_dirname(srcfile->name);
+	srcfile->prev = current_srcfile;
+
+	srcfile->lineno = 1;
+	srcfile->colno = 1;
+
+	current_srcfile = srcfile;
+
+	if (srcfile_depth == 1)
+		set_initial_path(srcfile->name);
+}
+
+bool srcfile_pop(void)
+{
+	struct srcfile_state *srcfile = current_srcfile;
+
+	assert(srcfile);
+
+	current_srcfile = srcfile->prev;
+
+	if (fclose(srcfile->f))
+		die("Error closing \"%s\": %s\n", srcfile->name,
+		    strerror(errno));
+
+	/* FIXME: We allow the srcfile_state structure to leak,
+	 * because it could still be referenced from a location
+	 * variable being carried through the parser somewhere.  To
+	 * fix this we could either allocate all the files from a
+	 * table, or use a pool allocator. */
+
+	return current_srcfile ? true : false;
+}
+
+void srcfile_add_search_path(const char *dirname)
+{
+	struct search_path *node;
+
+	/* Create the node */
+	node = xmalloc(sizeof(*node));
+	node->next = NULL;
+	node->dirname = xstrdup(dirname);
+
+	/* Add to the end of our list */
+	if (search_path_tail)
+		*search_path_tail = node;
+	else
+		search_path_head = node;
+	search_path_tail = &node->next;
+}
+
+void srcpos_update(struct srcpos *pos, const char *text, int len)
+{
+	int i;
+
+	pos->file = current_srcfile;
+
+	pos->first_line = current_srcfile->lineno;
+	pos->first_column = current_srcfile->colno;
+
+	for (i = 0; i < len; i++)
+		if (text[i] == '\n') {
+			current_srcfile->lineno++;
+			current_srcfile->colno = 1;
+		} else {
+			current_srcfile->colno++;
+		}
+
+	pos->last_line = current_srcfile->lineno;
+	pos->last_column = current_srcfile->colno;
+}
+
+struct srcpos *
+srcpos_copy(struct srcpos *pos)
+{
+	struct srcpos *pos_new;
+	struct srcfile_state *srcfile_state;
+
+	if (!pos)
+		return NULL;
+
+	pos_new = xmalloc(sizeof(struct srcpos));
+	assert(pos->next == NULL);
+	memcpy(pos_new, pos, sizeof(struct srcpos));
+
+	/* allocate without free */
+	srcfile_state = xmalloc(sizeof(struct srcfile_state));
+	memcpy(srcfile_state, pos->file, sizeof(struct srcfile_state));
+	pos_new->file = srcfile_state;
+
+	return pos_new;
+}
+
+struct srcpos *srcpos_extend(struct srcpos *pos, struct srcpos *newtail)
+{
+	struct srcpos *p;
+
+	if (!pos)
+		return newtail;
+
+	for (p = pos; p->next != NULL; p = p->next);
+	p->next = newtail;
+	return pos;
+}
+
+char *
+srcpos_string(struct srcpos *pos)
+{
+	const char *fname = "<no-file>";
+	char *pos_str;
+
+	if (pos->file && pos->file->name)
+		fname = pos->file->name;
+
+
+	if (pos->first_line != pos->last_line)
+		xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
+			  pos->first_line, pos->first_column,
+			  pos->last_line, pos->last_column);
+	else if (pos->first_column != pos->last_column)
+		xasprintf(&pos_str, "%s:%d.%d-%d", fname,
+			  pos->first_line, pos->first_column,
+			  pos->last_column);
+	else
+		xasprintf(&pos_str, "%s:%d.%d", fname,
+			  pos->first_line, pos->first_column);
+
+	return pos_str;
+}
+
+static char *
+srcpos_string_comment(struct srcpos *pos, bool first_line, int level)
+{
+	char *pos_str, *fname, *first, *rest;
+	bool fresh_fname = false;
+
+	if (!pos) {
+		if (level > 1) {
+			xasprintf(&pos_str, "<no-file>:<no-line>");
+			return pos_str;
+		} else {
+			return NULL;
+		}
+	}
+
+	if (!pos->file)
+		fname = "<no-file>";
+	else if (!pos->file->name)
+		fname = "<no-filename>";
+	else if (level > 1)
+		fname = pos->file->name;
+	else {
+		fname = shorten_to_initial_path(pos->file->name);
+		if (fname)
+			fresh_fname = true;
+		else
+			fname = pos->file->name;
+	}
+
+	if (level > 1)
+		xasprintf(&first, "%s:%d:%d-%d:%d", fname,
+			  pos->first_line, pos->first_column,
+			  pos->last_line, pos->last_column);
+	else
+		xasprintf(&first, "%s:%d", fname,
+			  first_line ? pos->first_line : pos->last_line);
+
+	if (fresh_fname)
+		free(fname);
+
+	if (pos->next != NULL) {
+		rest = srcpos_string_comment(pos->next, first_line, level);
+		xasprintf(&pos_str, "%s, %s", first, rest);
+		free(first);
+		free(rest);
+	} else {
+		pos_str = first;
+	}
+
+	return pos_str;
+}
+
+char *srcpos_string_first(struct srcpos *pos, int level)
+{
+	return srcpos_string_comment(pos, true, level);
+}
+
+char *srcpos_string_last(struct srcpos *pos, int level)
+{
+	return srcpos_string_comment(pos, false, level);
+}
+
+void srcpos_verror(struct srcpos *pos, const char *prefix,
+		   const char *fmt, va_list va)
+{
+	char *srcstr;
+
+	srcstr = srcpos_string(pos);
+
+	fprintf(stderr, "%s: %s ", prefix, srcstr);
+	vfprintf(stderr, fmt, va);
+	fprintf(stderr, "\n");
+
+	free(srcstr);
+}
+
+void srcpos_error(struct srcpos *pos, const char *prefix,
+		  const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	srcpos_verror(pos, prefix, fmt, va);
+	va_end(va);
+}
+
+void srcpos_set_line(char *f, int l)
+{
+	current_srcfile->name = f;
+	current_srcfile->lineno = l;
+
+	if (initial_cpp) {
+		initial_cpp = false;
+		set_initial_path(f);
+	}
+}
diff --git a/srcpos.h b/srcpos.h
new file mode 100644
index 0000000..4318d7a
--- /dev/null
+++ b/srcpos.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
+ */
+
+#ifndef SRCPOS_H
+#define SRCPOS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include "util.h"
+
+struct srcfile_state {
+	FILE *f;
+	char *name;
+	char *dir;
+	int lineno, colno;
+	struct srcfile_state *prev;
+};
+
+extern FILE *depfile; /* = NULL */
+extern struct srcfile_state *current_srcfile; /* = NULL */
+
+/**
+ * Open a source file.
+ *
+ * If the source file is a relative pathname, then it is searched for in the
+ * current directory (the directory of the last source file read) and after
+ * that in the search path.
+ *
+ * We work through the search path in order from the first path specified to
+ * the last.
+ *
+ * If the file is not found, then this function does not return, but calls
+ * die().
+ *
+ * @param fname		Filename to search
+ * @param fullnamep	If non-NULL, it is set to the allocated filename of the
+ *			file that was opened. The caller is then responsible
+ *			for freeing the pointer.
+ * @return pointer to opened FILE
+ */
+FILE *srcfile_relative_open(const char *fname, char **fullnamep);
+
+void srcfile_push(const char *fname);
+bool srcfile_pop(void);
+
+/**
+ * Add a new directory to the search path for input files
+ *
+ * The new path is added at the end of the list.
+ *
+ * @param dirname	Directory to add
+ */
+void srcfile_add_search_path(const char *dirname);
+
+struct srcpos {
+    int first_line;
+    int first_column;
+    int last_line;
+    int last_column;
+    struct srcfile_state *file;
+    struct srcpos *next;
+};
+
+#define YYLTYPE struct srcpos
+
+#define YYLLOC_DEFAULT(Current, Rhs, N)						\
+	do {									\
+		if (N) {							\
+			(Current).first_line = YYRHSLOC(Rhs, 1).first_line;	\
+			(Current).first_column = YYRHSLOC(Rhs, 1).first_column;	\
+			(Current).last_line = YYRHSLOC(Rhs, N).last_line;	\
+			(Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+			(Current).file = YYRHSLOC(Rhs, N).file;			\
+		} else {							\
+			(Current).first_line = (Current).last_line =		\
+				YYRHSLOC(Rhs, 0).last_line;			\
+			(Current).first_column = (Current).last_column =	\
+				YYRHSLOC(Rhs, 0).last_column;			\
+			(Current).file = YYRHSLOC (Rhs, 0).file;		\
+		}								\
+		(Current).next = NULL;						\
+	} while (0)
+
+
+extern void srcpos_update(struct srcpos *pos, const char *text, int len);
+extern struct srcpos *srcpos_copy(struct srcpos *pos);
+extern struct srcpos *srcpos_extend(struct srcpos *new_srcpos,
+				    struct srcpos *old_srcpos);
+extern char *srcpos_string(struct srcpos *pos);
+extern char *srcpos_string_first(struct srcpos *pos, int level);
+extern char *srcpos_string_last(struct srcpos *pos, int level);
+
+
+extern void PRINTF(3, 0) srcpos_verror(struct srcpos *pos, const char *prefix,
+					const char *fmt, va_list va);
+extern void PRINTF(3, 4) srcpos_error(struct srcpos *pos, const char *prefix,
+				      const char *fmt, ...);
+
+extern void srcpos_set_line(char *f, int l);
+
+#endif /* SRCPOS_H */
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..d3f1434
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,75 @@
+*.dtb
+*.dts.test.s
+*.test.dts
+*.test.dt.yaml
+tmp.*
+/fs/
+/add_subnode_with_nops
+/addr_size_cells
+/addr_size_cells2
+/appendprop[12]
+/appendprop_addrrange
+/asm_tree_dump
+/boot-cpuid
+/char_literal
+/check_full
+/check_header
+/check_path
+/del_node
+/del_property
+/dtbs_equal_ordered
+/dtbs_equal_unordered
+/dtb_reverse
+/dumptrees
+/extra-terminating-null
+/find_property
+/get_alias
+/get_mem_rsv
+/get_name
+/get_path
+/get_phandle
+/getprop
+/get_prop_offset
+/incbin
+/integer-expressions
+/fs_tree1
+/mangle-layout
+/move_and_save
+/node_check_compatible
+/node_offset_by_compatible
+/node_offset_by_phandle
+/node_offset_by_prop_value
+/nop_node
+/nop_property
+/nopulate
+/notfound
+/open_pack
+/overlay
+/overlay_bad_fixup
+/parent_offset
+/path-references
+/path_offset
+/path_offset_aliases
+/phandle_format
+/property_iterate
+/propname_escapes
+/references
+/root_node
+/rw_tree1
+/rw_oom
+/set_name
+/setprop
+/setprop_inplace
+/sized_cells
+/string_escapes
+/stringlist
+/subnode_iterate
+/subnode_offset
+/supernode_atdepth_offset
+/sw_tree1
+/sw_states
+/truncated_property
+/truncated_string
+/truncated_memrsv
+/utilfdt_test
+/value-labels
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
new file mode 100644
index 0000000..5093aaa
--- /dev/null
+++ b/tests/Makefile.tests
@@ -0,0 +1,100 @@
+LIB_TESTS_L = get_mem_rsv \
+	root_node find_property subnode_offset path_offset \
+	get_name getprop get_prop_offset get_phandle \
+	get_path supernode_atdepth_offset parent_offset \
+	node_offset_by_prop_value node_offset_by_phandle \
+	node_check_compatible node_offset_by_compatible \
+	get_alias \
+	char_literal \
+	sized_cells \
+	notfound \
+	addr_size_cells \
+	addr_size_cells2 \
+	appendprop_addrrange \
+	stringlist \
+	setprop_inplace nop_property nop_node \
+	sw_tree1 sw_states \
+	move_and_save mangle-layout nopulate \
+	open_pack rw_tree1 rw_oom set_name setprop del_property del_node \
+	appendprop1 appendprop2 propname_escapes \
+	string_escapes references path-references phandle_format \
+	boot-cpuid incbin \
+	extra-terminating-null \
+	dtbs_equal_ordered \
+	dtb_reverse dtbs_equal_unordered \
+	add_subnode_with_nops path_offset_aliases \
+	utilfdt_test \
+	integer-expressions \
+	property_iterate \
+	subnode_iterate \
+	overlay overlay_bad_fixup \
+	check_path check_header check_full \
+	fs_tree1
+LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
+
+LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv
+LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
+
+DL_LIB_TESTS_L = asm_tree_dump value-labels
+DL_LIB_TESTS = $(DL_LIB_TESTS_L:%=$(TESTS_PREFIX)%)
+
+TESTS = $(LIB_TESTS) $(LIBTREE_TESTS) $(DL_LIB_TESTS)
+
+TESTS_TREES_L = test_tree1.dtb
+TESTS_TREES = $(TESTS_TREES_L:%=$(TESTS_PREFIX)%)
+
+TESTS_TARGETS = $(TESTS) $(TESTS_TREES)
+
+TESTS_DEPFILES = $(TESTS:%=%.d) \
+	$(addprefix $(TESTS_PREFIX),testutils.d trees.d dumptrees.d)
+
+TESTS_CLEANFILES_L = $(STD_CLEANFILES) \
+	*.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* \
+	dumptrees
+TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
+TESTS_CLEANDIRS_L = fs
+TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%)
+
+.PHONY: tests
+tests:	$(TESTS) $(TESTS_TREES)
+
+$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_lib)
+
+# Not necessary on all platforms; allow -ldl to be excluded instead of forcing
+# other platforms to patch it out.
+LIBDL = -ldl
+$(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_lib)
+	@$(VECHO) LD [libdl] $@
+	$(LINK.c) -o $@ $^ $(LIBDL)
+
+$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o \
+		util.o $(LIBFDT_lib)
+
+$(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o
+
+$(TESTS_TREES): $(TESTS_PREFIX)dumptrees
+	@$(VECHO) DUMPTREES
+	cd $(TESTS_PREFIX); ./dumptrees >/dev/null
+
+tests_clean:
+	@$(VECHO) CLEAN "(tests)"
+	rm -f $(TESTS_CLEANFILES)
+	rm -rf $(TESTS_CLEANDIRS)
+
+check:	tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
+	cd $(TESTS_PREFIX); PYTHON=$(PYTHON) ./run_tests.sh
+
+ifeq ($(NO_VALGRIND),1)
+checkm:
+	@echo "make checkm requires valgrind, but NO_VALGRIND=1"
+else
+checkm: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
+	cd $(TESTS_PREFIX); PYTHON=$(PYTHON) ./run_tests.sh -m
+endif
+
+checkv:	tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
+	cd $(TESTS_PREFIX); PYTHON=$(PYTHON) ./run_tests.sh -v
+
+ifneq ($(DEPTARGETS),)
+-include $(TESTS_DEPFILES)
+endif
diff --git a/tests/add_subnode_with_nops.c b/tests/add_subnode_with_nops.c
new file mode 100644
index 0000000..29bd34b
--- /dev/null
+++ b/tests/add_subnode_with_nops.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE	65536
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+#define OFF_CHECK(off, code) \
+	{ \
+		(off) = (code); \
+		if (off < 0) \
+			FAIL(#code ": %s", fdt_strerror(off)); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int err;
+	int offset;
+
+	test_init(argc, argv);
+
+	fdt = xmalloc(SPACE);
+
+	CHECK(fdt_create(fdt, SPACE));
+
+	CHECK(fdt_finish_reservemap(fdt));
+	CHECK(fdt_begin_node(fdt, ""));
+	CHECK(fdt_property_cell(fdt, "prop1", TEST_VALUE_1));
+	CHECK(fdt_property_cell(fdt, "prop2", TEST_VALUE_2));
+	CHECK(fdt_end_node(fdt));
+	CHECK(fdt_finish(fdt));
+
+	verbose_printf("Built empty tree, totalsize = %d\n",
+		       fdt_totalsize(fdt));
+
+	CHECK(fdt_open_into(fdt, fdt, SPACE));
+
+	check_getprop_cell(fdt, 0, "prop1", TEST_VALUE_1);
+	check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
+
+	CHECK(fdt_nop_property(fdt, 0, "prop1"));
+
+	check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
+
+	OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode"));
+
+	check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
+
+	PASS();
+}
diff --git a/tests/addr_size_cells.c b/tests/addr_size_cells.c
new file mode 100644
index 0000000..783574d
--- /dev/null
+++ b/tests/addr_size_cells.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for #address-cells and #size-cells handling
+ * Copyright (C) 2014 David Gibson, <david@gibson.dropbear.id.au>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_node(const void *fdt, const char *path, int ac, int sc)
+{
+	int offset;
+	int xac, xsc;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	xac = fdt_address_cells(fdt, offset);
+	xsc = fdt_size_cells(fdt, offset);
+
+	if (xac != ac)
+		FAIL("Address cells for %s is %d instead of %d\n",
+		     path, xac, ac);
+	if (xsc != sc)
+		FAIL("Size cells for %s is %d instead of %d\n",
+		     path, xsc, sc);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>\n", argv[0]);
+
+	test_init(argc, argv);
+	fdt = load_blob(argv[1]);
+
+	check_node(fdt, "/", 2, 2);
+	check_node(fdt, "/identity-bus@0", 2, 1);
+	check_node(fdt, "/simple-bus@1000000", 2, 1);
+	check_node(fdt, "/discrete-bus@2000000", 1, 0);
+	check_node(fdt, "/c0", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
+	check_node(fdt, "/c1", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
+	check_node(fdt, "/c2", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
+	check_node(fdt, "/c3", -FDT_ERR_BADNCELLS, 0);
+	PASS();
+}
diff --git a/tests/addr_size_cells2.c b/tests/addr_size_cells2.c
new file mode 100644
index 0000000..d97541b
--- /dev/null
+++ b/tests/addr_size_cells2.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for #address-cells and #size-cells handling
+ * Copyright (C) 2014 David Gibson, <david@gibson.dropbear.id.au>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_node(const void *fdt, const char *path, int ac, int sc)
+{
+	int offset;
+	int xac, xsc;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	xac = fdt_address_cells(fdt, offset);
+	xsc = fdt_size_cells(fdt, offset);
+
+	if (xac != ac)
+		FAIL("Address cells for %s is %d instead of %d\n",
+		     path, xac, ac);
+	if (xsc != sc)
+		FAIL("Size cells for %s is %d instead of %d\n",
+		     path, xsc, sc);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>\n", argv[0]);
+
+	test_init(argc, argv);
+	fdt = load_blob(argv[1]);
+
+	check_node(fdt, "/", 2, 1);
+	PASS();
+}
diff --git a/tests/addresses.dts b/tests/addresses.dts
new file mode 100644
index 0000000..1b307ab
--- /dev/null
+++ b/tests/addresses.dts
@@ -0,0 +1,40 @@
+/dts-v1/;
+
+/ {
+	compatible = "test_addresses";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	identity-bus@0 {
+	};
+
+	simple-bus@1000000 {
+		#address-cells = <2>;
+		#size-cells = <1>;
+	};
+
+	discrete-bus@2000000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	c0@0 {
+		#address-cells = <1 1>;
+		#size-cells = <1 1>;
+	};
+
+	c1@0 {
+		#address-cells = <0x80000000>;
+		#size-cells = <0x80000000>;
+	};
+
+	c2@0 {
+		#address-cells = <5>;
+		#size-cells = <5>;
+	};
+
+	c3@0 {
+		#address-cells = <0>;
+		#size-cells = <0>;
+	};
+};
diff --git a/tests/aliases.dts b/tests/aliases.dts
new file mode 100644
index 0000000..853479a
--- /dev/null
+++ b/tests/aliases.dts
@@ -0,0 +1,25 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	aliases {
+		s1 = &sub1;
+		ss1 = &subsub1;
+		sss1 = &subsubsub1;
+	};
+
+	sub1: subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+
+		subsub1: subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+
+			subsubsub1: subsubsubnode {
+				compatible = "subsubsubnode1", "subsubsubnode";
+			};
+		};
+	};
+};
diff --git a/tests/appendprop.dts b/tests/appendprop.dts
new file mode 100644
index 0000000..f4bc730
--- /dev/null
+++ b/tests/appendprop.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	prop-str = "hello world", "nastystring: \a\b\t\n\v\f\r\\\"";
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef 0xdeadbeef01abcdef>;
+	prop-int = <0xdeadbeef 123456789>;
+	prop-bytes = [00010203040001020304];
+};
diff --git a/tests/appendprop1.c b/tests/appendprop1.c
new file mode 100644
index 0000000..a7b502a
--- /dev/null
+++ b/tests/appendprop1.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_appendprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE		65536
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int err;
+	uint8_t bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04};
+
+	test_init(argc, argv);
+
+	/* Create an empty tree first */
+	fdt = xmalloc(SPACE);
+	CHECK(fdt_create(fdt, SPACE));
+	CHECK(fdt_finish_reservemap(fdt));
+	CHECK(fdt_begin_node(fdt, ""));
+	CHECK(fdt_end_node(fdt));
+	CHECK(fdt_finish(fdt));
+
+	/* Now use appendprop to add properties */
+	CHECK(fdt_open_into(fdt, fdt, SPACE));
+
+	CHECK(fdt_appendprop(fdt, 0, "prop-bytes", bytes, sizeof(bytes)));
+	CHECK(fdt_appendprop_cell(fdt, 0, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_appendprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
+	CHECK(fdt_appendprop_string(fdt, 0, "prop-str", TEST_STRING_1));
+
+	CHECK(fdt_pack(fdt));
+
+	save_blob("appendprop1.test.dtb", fdt);
+
+	PASS();
+}
diff --git a/tests/appendprop2.c b/tests/appendprop2.c
new file mode 100644
index 0000000..a0c1f6f
--- /dev/null
+++ b/tests/appendprop2.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_appendprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE		65536
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt, *buf;
+	int err;
+	uint8_t bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04};
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	buf = xmalloc(SPACE);
+	CHECK(fdt_open_into(fdt, buf, SPACE));
+	fdt = buf;
+
+	CHECK(fdt_appendprop(fdt, 0, "prop-bytes", bytes, sizeof(bytes)));
+	CHECK(fdt_appendprop_cell(fdt, 0, "prop-int", TEST_VALUE_2));
+	CHECK(fdt_appendprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
+	CHECK(fdt_appendprop_string(fdt, 0, "prop-str", TEST_STRING_2));
+
+	CHECK(fdt_pack(fdt));
+
+	save_blob("appendprop2.test.dtb", fdt);
+
+	PASS();
+}
diff --git a/tests/appendprop_addrrange.c b/tests/appendprop_addrrange.c
new file mode 100644
index 0000000..538afcf
--- /dev/null
+++ b/tests/appendprop_addrrange.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_appendprop_addrrange()
+ * Copyright (C) 2018 AKASHI Takahiro, Linaro Limited
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt, *buf;
+	int offset, xac, xsc, num, i, err;
+	uint64_t addr, size;
+
+	if (argc != 5)
+		CONFIG("Usage: %s <dtb file> <address-cells> <size-cells> <num>\n",
+		       argv[0]);
+
+	test_init(argc, argv);
+	fdt = load_blob(argv[1]);
+	xac = strtol(argv[2], NULL, 10);
+	xsc = strtol(argv[3], NULL, 10);
+	num = strtol(argv[4], NULL, 10);
+
+	buf = xmalloc(0x1000);
+	if (!buf)
+		FAIL("Couldn't allocate temporary buffer");
+	err = fdt_open_into(fdt, buf, 0x1000);
+	if (err)
+		FAIL("fdt_open_into(): %s", fdt_strerror(err));
+
+	fdt = buf;
+
+	/* Set up */
+	err = fdt_setprop_cell(fdt, 0, "#address-cells", xac);
+	if (err)
+		FAIL("fdt_setprop_cell(\"#address-cells\"): %s",
+		     fdt_strerror(err));
+	err = fdt_setprop_cell(fdt, 0, "#size-cells", xsc);
+	if (err)
+		FAIL("fdt_setprop_cell(\"#size-cells\"): %s",
+		     fdt_strerror(err));
+
+	offset = fdt_path_offset(fdt, "/node@1");
+	if (offset < 0)
+		FAIL("Couldn't find path %s", "/node@1");
+
+	addr = TEST_MEMREGION_ADDR;
+	if (xac > 1)
+		addr += TEST_MEMREGION_ADDR_HI;
+	size = TEST_MEMREGION_SIZE;
+	if (xsc > 1)
+		size += TEST_MEMREGION_SIZE_HI;
+
+	/*
+	 * Do test
+	 */
+	/* 1. repeat append's */
+	for (i = 0; i < num; i++) {
+		err = fdt_appendprop_addrrange(fdt, 0, offset,
+					       "prop-memregion", addr, size);
+		if (err)
+			FAIL("Failed to append[%d] \"prop-memregion\": %s",
+			     i, fdt_strerror(err));
+
+		check_getprop_addrrange(fdt, 0, offset, "prop-memregion",
+					i + 1);
+
+		addr += size;
+		size += TEST_MEMREGION_SIZE_INC;
+	}
+
+	/* 2. default property name */
+	addr = TEST_MEMREGION_ADDR;
+	if (xac > 1)
+		addr += TEST_MEMREGION_ADDR_HI;
+	size = TEST_MEMREGION_SIZE;
+	if (xsc > 1)
+		size += TEST_MEMREGION_SIZE_HI;
+
+	err = fdt_appendprop_addrrange(fdt, 0, offset, "reg", addr, size);
+	if (err)
+		FAIL("Failed to set \"reg\": %s", fdt_strerror(err));
+	check_getprop_addrrange(fdt, 0, offset, "reg", 1);
+
+	PASS();
+}
diff --git a/tests/asm_tree_dump.c b/tests/asm_tree_dump.c
new file mode 100644
index 0000000..8236172
--- /dev/null
+++ b/tests/asm_tree_dump.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests if an asm tree built into a shared object matches a given dtb
+ * Copyright (C) 2008 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <dlfcn.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *sohandle;
+	void *fdt;
+	int err;
+
+	test_init(argc, argv);
+	if (argc != 3)
+		CONFIG("Usage: %s <so file> <dtb file>", argv[0]);
+
+	sohandle = dlopen(argv[1], RTLD_NOW);
+	if (!sohandle)
+		FAIL("Couldn't dlopen() %s", argv[1]);
+
+	fdt = dlsym(sohandle, "dt_blob_start");
+	if (!fdt)
+		FAIL("Couldn't locate \"dt_blob_start\" symbol in %s",
+		     argv[1]);
+
+	err = fdt_check_header(fdt);
+	if (err != 0)
+		FAIL("%s contains invalid tree: %s", argv[1],
+		     fdt_strerror(err));
+
+	save_blob(argv[2], fdt);
+
+	PASS();
+}
diff --git a/tests/bad-chosen.dts b/tests/bad-chosen.dts
new file mode 100644
index 0000000..d6f53c6
--- /dev/null
+++ b/tests/bad-chosen.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+	node2 {
+		chosen {
+			bootargs = <0xdeadbeef>;
+			stdout-path = <1>;
+		};
+	};
+};
diff --git a/tests/bad-empty-ranges.dts b/tests/bad-empty-ranges.dts
new file mode 100644
index 0000000..2be7bc8
--- /dev/null
+++ b/tests/bad-empty-ranges.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	node {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+	};
+};
diff --git a/tests/bad-gpio.dts b/tests/bad-gpio.dts
new file mode 100644
index 0000000..6b77be4
--- /dev/null
+++ b/tests/bad-gpio.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	gpio: gpio-controller {
+		#gpio-cells = <3>;
+	};
+
+	node {
+		nr-gpios = <1>;
+		foo-gpios = <&gpio>;
+		bar-gpio = <&gpio 1 2 3>;
+	};
+};
diff --git a/tests/bad-graph.dts b/tests/bad-graph.dts
new file mode 100644
index 0000000..522da0e
--- /dev/null
+++ b/tests/bad-graph.dts
@@ -0,0 +1,24 @@
+/dts-v1/;
+
+/ {
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		bad_endpoint: port-a@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			endpoint@d0 {
+				reg = <0>;
+				remote-endpoint = <0xdeadbeef>;
+			};
+
+		};
+
+		port@1 {
+			reg = <0>;
+		};
+	};
+};
diff --git a/tests/bad-interrupt-cells.dts b/tests/bad-interrupt-cells.dts
new file mode 100644
index 0000000..39fc78f
--- /dev/null
+++ b/tests/bad-interrupt-cells.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	interrupt-parent = <&intc>;
+	intc: interrupt-controller {
+		#interrupt-cells = <3>;
+	};
+
+	node {
+		interrupts = <1>;
+	};
+};
diff --git a/tests/bad-name-property.dts b/tests/bad-name-property.dts
new file mode 100644
index 0000000..4fde4be
--- /dev/null
+++ b/tests/bad-name-property.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	node@0 {
+		name = "badthing";
+	};
+};
diff --git a/tests/bad-ncells.dts b/tests/bad-ncells.dts
new file mode 100644
index 0000000..636198c
--- /dev/null
+++ b/tests/bad-ncells.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	#address-cells = "badthing";
+	#size-cells = "badthing";
+	#interrupt-cells = "badthing";
+};
diff --git a/tests/bad-octal-literal.dts b/tests/bad-octal-literal.dts
new file mode 100644
index 0000000..26558a2
--- /dev/null
+++ b/tests/bad-octal-literal.dts
@@ -0,0 +1,5 @@
+/dts-v1/;
+
+/ {
+	x = <09>;
+};
diff --git a/tests/bad-phandle-cells.dts b/tests/bad-phandle-cells.dts
new file mode 100644
index 0000000..7f7c6a2
--- /dev/null
+++ b/tests/bad-phandle-cells.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	intc: interrupt-controller {
+		#interrupt-cells = <3>;
+	};
+
+	node {
+		interrupts-extended = <&intc>;
+	};
+};
diff --git a/tests/bad-reg-ranges.dts b/tests/bad-reg-ranges.dts
new file mode 100644
index 0000000..77419f5
--- /dev/null
+++ b/tests/bad-reg-ranges.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	node {
+		reg = <0 0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0 0>;
+	};
+};
diff --git a/tests/bad-size-cells.dts b/tests/bad-size-cells.dts
new file mode 100644
index 0000000..515c0cc
--- /dev/null
+++ b/tests/bad-size-cells.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	mangled {
+		#address-cells = <0x0>;
+		#size-cells = <0x0>;
+
+		valid {
+            reg = <0x0 0x4000000>;
+        };
+    };
+};
diff --git a/tests/bad-string-props.dts b/tests/bad-string-props.dts
new file mode 100644
index 0000000..6694704
--- /dev/null
+++ b/tests/bad-string-props.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	device_type = <0xdeadbeef>;
+	model = <0xdeadbeef>;
+	status = <0xdeadbeef>;
+	label = <0xdeadbeef>;
+
+	foobar-names = "foo", <1>;
+
+	node {
+		compatible = "good", <0xdeadbeef>;
+	};
+};
diff --git a/tests/base01.asm b/tests/base01.asm
new file mode 100644
index 0000000..266e446
--- /dev/null
+++ b/tests/base01.asm
@@ -0,0 +1,175 @@
+/* autogenerated by dtc, do not edit */
+
+#define OF_DT_HEADER 0xd00dfeed
+#define OF_DT_BEGIN_NODE 0x1
+#define OF_DT_END_NODE 0x2
+#define OF_DT_PROP 0x3
+#define OF_DT_END 0x9
+
+	.globl	dt_blob_start
+dt_blob_start:
+_dt_blob_start:
+	.globl	dt_header
+dt_header:
+_dt_header:
+	.long	OF_DT_HEADER /* magic */
+	.long	_dt_blob_end - _dt_blob_start /* totalsize */
+	.long	_dt_struct_start - _dt_blob_start /* off_dt_struct */
+	.long	_dt_strings_start - _dt_blob_start /* off_dt_strings */
+	.long	_dt_reserve_map - _dt_blob_start /* off_dt_strings */
+	.long	16 /* version */
+	.long	16 /* last_comp_version */
+	.long	0	/*boot_cpuid_phys*/
+	.long	_dt_strings_end - _dt_strings_start	/* size_dt_strings */
+	.balign	8
+	.globl	dt_reserve_map
+dt_reserve_map:
+_dt_reserve_map:
+/* Memory reserve map from source file */
+	.long	0, 0
+	.long	0, 0
+	.globl	dt_struct_start
+dt_struct_start:
+_dt_struct_start:
+	.long	OF_DT_BEGIN_NODE
+	.string	""
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0xa
+	.long	0x0
+	.long	0x536f6d65
+	.long	0x4d6f6465
+	.short	0x6c00
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x8
+	.long	0x6
+	.long	0x4e6f7468
+	.long	0x696e6700
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x11
+	.long	0x2
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x20
+	.long	0x2
+	.balign	4
+	.long	OF_DT_BEGIN_NODE
+	.string	"memory@0"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x7
+	.long	0x2c
+	.long	0x6d656d6f
+	.short	0x7279
+	.byte	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x10
+	.long	0x38
+	.long	0x0
+	.long	0x0
+	.long	0x0
+	.long	0x20000000
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_BEGIN_NODE
+	.string	"cpus"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x11
+	.long	0x1
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x20
+	.long	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x3c
+	.long	0xa
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x40
+	.long	0x17
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x44
+	.long	0x5
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x49
+	.long	0xf
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x4d
+	.long	0xd00d
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x10
+	.long	0x53
+	.long	0x4d2
+	.long	0x162e
+	.long	0x2334
+	.long	0xd80
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x59
+	.long	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x61
+	.long	0xffffffff
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x69
+	.long	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x71
+	.long	0xffffffff
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_END_NODE
+	.long	OF_DT_END
+	.globl	dt_struct_end
+dt_struct_end:
+_dt_struct_end:
+	.globl	dt_strings_start
+dt_strings_start:
+_dt_strings_start:
+	.string "model"
+	.string "compatible"
+	.string "#address-cells"
+	.string "#size-cells"
+	.string "device_type"
+	.string "reg"
+	.string "d10"
+	.string "d23"
+	.string "b101"
+	.string "o17"
+	.string "hd00d"
+	.string "stuff"
+	.string "bad-d-1"
+	.string "bad-d-2"
+	.string "bad-o-1"
+	.string "bad-o-2"
+	.globl	dt_strings_end
+dt_strings_end:
+_dt_strings_end:
+	.globl	dt_blob_end
+dt_blob_end:
+_dt_blob_end:
diff --git a/tests/base01.cmd b/tests/base01.cmd
new file mode 100644
index 0000000..e1fce6c
--- /dev/null
+++ b/tests/base01.cmd
@@ -0,0 +1 @@
+dtc -f -b 0 -V 16 -I dts -O asm
diff --git a/tests/base01.dts b/tests/base01.dts
new file mode 100644
index 0000000..97a5dd5
--- /dev/null
+++ b/tests/base01.dts
@@ -0,0 +1,33 @@
+/dts-v1/;
+
+/ {
+	model = "SomeModel";
+	compatible = "Nothing";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+        memory@0 {
+                device_type = "memory";
+		reg = <0x00000000 0x00000000 0x00000000 0x20000000>;
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		d10 = < 10>;		// hex: 0xa
+		d23 = < 23>;		// hex: 0x17
+		b101 = < 0x5>;	// hex: 0x5
+		o17 = < 017>;		// hex: 0xf
+		hd00d = < 0xd00d>;	// hex: 0xd00d
+
+		//   hex:  0x4d2    0x163e      0x2334    0xd80
+		stuff = < 1234     5678     9012     3456>;
+
+
+		bad-d-1 = < 0>;		// Hrm. 0
+		bad-d-2 = < 123456789012345>;
+		bad-o-1 = < 00>;
+		bad-o-2 = < 0123456123456>;
+	};
+
+};
diff --git a/tests/base01.stderr b/tests/base01.stderr
new file mode 100644
index 0000000..0510b0f
--- /dev/null
+++ b/tests/base01.stderr
@@ -0,0 +1,6 @@
+DTC: dts->asm  on file "tests/base01.dts"
+Line 26: Invalid cell value '123456789012345'; -1 assumed
+Line 27: Invalid cell value '891'; 0 assumed
+Line 28: Invalid cell value '123456123456'; -1 assumed
+ERROR: Missing /chosen node
+Input tree has errors
diff --git a/tests/boot-cpuid.c b/tests/boot-cpuid.c
new file mode 100644
index 0000000..5ed4f9a
--- /dev/null
+++ b/tests/boot-cpuid.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright (C) 2008 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	uint32_t cpuid;
+
+	test_init(argc, argv);
+
+	if (argc != 3)
+		CONFIG("Usage: %s <dtb file> <cpuid>", argv[0]);
+
+	fdt = load_blob(argv[1]);
+	cpuid = strtoul(argv[2], NULL, 0);
+
+	if (fdt_boot_cpuid_phys(fdt) != cpuid)
+		FAIL("Incorrect boot_cpuid_phys (0x%x instead of 0x%x)",
+		     fdt_boot_cpuid_phys(fdt), cpuid);
+
+	PASS();
+}
diff --git a/tests/boot-cpuid.dts b/tests/boot-cpuid.dts
new file mode 100644
index 0000000..7021a24
--- /dev/null
+++ b/tests/boot-cpuid.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	cpus {
+		cpu@10 {
+			device_type = "cpu";
+			compatible = "fake-cpu";
+			reg = <0x10>;
+		};
+		cpu@11 {
+			device_type = "cpu";
+			compatible = "fake-cpu";
+			reg = <0x11>;
+		};
+	};
+};
diff --git a/tests/char_literal.c b/tests/char_literal.c
new file mode 100644
index 0000000..3a69e28
--- /dev/null
+++ b/tests/char_literal.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for character literals in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright (C) 2011 The Chromium Authors. All rights reserved.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	fdt32_t expected_cells[5];
+
+	expected_cells[0] = cpu_to_fdt32((unsigned char)TEST_CHAR1);
+	expected_cells[1] = cpu_to_fdt32((unsigned char)TEST_CHAR2);
+	expected_cells[2] = cpu_to_fdt32((unsigned char)TEST_CHAR3);
+	expected_cells[3] = cpu_to_fdt32((unsigned char)TEST_CHAR4);
+	expected_cells[4] = cpu_to_fdt32((unsigned char)TEST_CHAR5);
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop(fdt, 0, "char-literal-cells",
+		      sizeof(expected_cells), expected_cells);
+
+	PASS();
+}
diff --git a/tests/char_literal.dts b/tests/char_literal.dts
new file mode 100644
index 0000000..22e17ed
--- /dev/null
+++ b/tests/char_literal.dts
@@ -0,0 +1,5 @@
+/dts-v1/;
+
+/ {
+	char-literal-cells = <'\r' 'b' '\0' '\'' '\xff'>;
+};
diff --git a/tests/check_full.c b/tests/check_full.c
new file mode 100644
index 0000000..b6d5fc3
--- /dev/null
+++ b/tests/check_full.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests if two given dtbs are structurally equal (including order)
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int expect_bad; /* = 0 */
+
+int main(int argc, char *argv[])
+{
+	const char *filename;
+	char *fdt;
+	size_t len;
+	int err;
+
+	test_init(argc, argv);
+	if ((argc != 2)
+	    && ((argc != 3) || !streq(argv[1], "-n")))
+		CONFIG("Usage: %s [-n] <dtb file>", argv[0]);
+	if (argc == 3)
+		expect_bad = 1;
+
+	filename = argv[argc-1];
+	err = utilfdt_read_err(filename, &fdt, &len);
+	if (err)
+		CONFIG("Couldn't open blob from \"%s\": %s",
+		       filename, strerror(err));
+
+	vg_prepare_blob(fdt, len);
+
+	err = fdt_check_full(fdt, len);
+
+	if (expect_bad && (err == 0))
+		FAIL("fdt_check_full() succeeded unexpectedly");
+	else if (!expect_bad && (err != 0))
+		FAIL("fdt_check_full() failed: %s", fdt_strerror(err));
+
+	PASS();
+}
diff --git a/tests/check_header.c b/tests/check_header.c
new file mode 100644
index 0000000..ca26ec1
--- /dev/null
+++ b/tests/check_header.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_check_header
+ * Copyright (C) 2018 David Gibson
+ */
+
+#include <stdio.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+
+static void *dtdup(void *dt)
+{
+	size_t bufsize = fdt_totalsize(dt);
+	void *buf = xmalloc(bufsize);
+	fdt_move(dt, buf, bufsize);
+	return buf;
+}
+
+#define CHECK_MANGLE(exerr, code)					\
+	do {								\
+		void *fdt = dtdup(template);				\
+		{ code }						\
+		err = fdt_check_header(fdt);				\
+		verbose_printf("\"%s\" => %s\n", #code, fdt_strerror(err)); \
+		if (err != (exerr))					\
+			FAIL("fdt_check_header() didn't catch mangle %s", \
+			     #code);					\
+		free(fdt);						\
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	void *template;
+	int err;
+
+	test_init(argc, argv);
+	template = load_blob(argv[1]);
+
+	/* Check that the base dt is valid before mangling it */
+	err = fdt_check_header(template);
+	if (err != 0)
+		FAIL("Base tree fails: %s", fdt_strerror(err));
+
+	/* Check a no-op mangle doesn't break things */
+	CHECK_MANGLE(0, ; );
+
+	/* Mess up the magic number */
+	CHECK_MANGLE(-FDT_ERR_BADMAGIC,
+		fdt_set_magic(fdt, fdt_magic(fdt) ^ 0x1);
+	);
+	CHECK_MANGLE(-FDT_ERR_BADMAGIC,
+		fdt_set_magic(fdt, fdt_magic(fdt) ^ 0x80000000);
+	);
+
+	/* Mess up the version */
+	CHECK_MANGLE(-FDT_ERR_BADVERSION,
+		fdt_set_version(fdt, FDT_FIRST_SUPPORTED_VERSION - 1);
+		fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION - 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_BADVERSION,
+		fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION + 1);
+		fdt_set_last_comp_version(fdt, FDT_LAST_SUPPORTED_VERSION + 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_BADVERSION,
+		fdt_set_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+		fdt_set_last_comp_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+	);
+
+	/* Out of bounds sizes */
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, FDT_V1_SIZE - 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		     fdt_set_totalsize(fdt, (uint32_t)INT_MAX + 1);
+	);
+
+	/* Truncate within various blocks */
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_dt_struct(fdt) - 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_dt_strings(fdt) - 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_mem_rsvmap(fdt) - 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_dt_struct(fdt) + 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_dt_strings(fdt) + 1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_totalsize(fdt, fdt_off_mem_rsvmap(fdt) + 1);
+	);
+
+	/* Negative block sizes */
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_size_dt_struct(fdt, (uint32_t)-1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_size_dt_strings(fdt, (uint32_t)-1);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		     fdt_set_size_dt_struct(fdt, (uint32_t)INT_MIN);
+	);
+	CHECK_MANGLE(-FDT_ERR_TRUNCATED,
+		fdt_set_size_dt_strings(fdt, (uint32_t)INT_MIN);
+	);
+
+	PASS();
+}
diff --git a/tests/check_path.c b/tests/check_path.c
new file mode 100644
index 0000000..cc9757a
--- /dev/null
+++ b/tests/check_path.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for node existence
+ * Copyright (C) 2016 Konsulko Inc.
+ */
+
+#include <stdio.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+
+#define CHECK(code) \
+	{ \
+		int err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+/* 4k ought to be enough for anybody */
+#define FDT_COPY_SIZE	(4 * 1024)
+
+static void *open_dt(char *path)
+{
+	void *dt, *copy;
+
+	dt = load_blob(path);
+	copy = xmalloc(FDT_COPY_SIZE);
+
+	/*
+	 * Resize our DTs to 4k so that we have room to operate on
+	 */
+	CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE));
+
+	return copy;
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt_base;
+	int fail_config, exists, check_exists;
+
+	test_init(argc, argv);
+	fail_config = 0;
+
+	if (argc != 4)
+		fail_config = 1;
+
+	if (!fail_config) {
+		if (!strcmp(argv[2], "exists"))
+			check_exists = 1;
+		else if (!strcmp(argv[2], "not-exists"))
+			check_exists = 0;
+		else
+			fail_config = 1;
+	}
+
+	if (fail_config)
+		CONFIG("Usage: %s <base dtb> <[exists|not-exists]> <node-path>", argv[0]);
+
+	fdt_base = open_dt(argv[1]);
+
+	exists = fdt_path_offset(fdt_base, argv[3]) >= 0;
+
+	if (exists == check_exists)
+		PASS();
+	else
+		FAIL();
+}
diff --git a/tests/comments-cmp.dts b/tests/comments-cmp.dts
new file mode 100644
index 0000000..4ee9f52
--- /dev/null
+++ b/tests/comments-cmp.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	prop1;
+	prop2;
+	prop3;
+	prop4;
+	prop5;
+	prop6;
+	prop7;
+	prop8;
+	prop9;
+	prop10;
+	child {
+	};
+};
diff --git a/tests/comments.dts b/tests/comments.dts
new file mode 100644
index 0000000..0b04b6b
--- /dev/null
+++ b/tests/comments.dts
@@ -0,0 +1,39 @@
+/* regexps for lexing comments are.. tricky.  Check if we've actually
+ * got it right */
+/dts-v1/;
+
+/ {
+	// line comment
+	prop1;
+	/* comment */
+	prop2;
+	/* multiline
+
+	notaprop1;
+
+	   comment */
+	prop3;
+	/**/
+	prop4;
+	/***/
+	prop5;
+	/****/
+	prop6;
+	/* another
+	 * multiline
+	 * comment */
+	prop7;
+	/* yet
+	 * another
+	 * multline
+	 * comment
+	 */
+	prop8;
+	/** try this */
+	prop9;
+	/* and this **/
+	prop10;
+	child /* finally */ {
+	};
+};
+/* final comment */
diff --git a/tests/data.S b/tests/data.S
new file mode 100644
index 0000000..86ad539
--- /dev/null
+++ b/tests/data.S
@@ -0,0 +1,3 @@
+/* Used in combination with dtc -Oasm output to embed
+ * a device tree in the data section of a .o */
+	.data
diff --git a/tests/default-addr-size.dts b/tests/default-addr-size.dts
new file mode 100644
index 0000000..e964a55
--- /dev/null
+++ b/tests/default-addr-size.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	node {
+		reg = <0 0 0>;
+	};
+};
diff --git a/tests/del_node.c b/tests/del_node.c
new file mode 100644
index 0000000..10846df
--- /dev/null
+++ b/tests/del_node.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset, subsubnode2_offset;
+	int err;
+	int oldsize, delsize, newsize;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	fdt = open_blob_rw(fdt);
+
+	oldsize = fdt_totalsize(fdt);
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset < 0)
+		FAIL("Couldn't find \"/subnode@1\": %s",
+		     fdt_strerror(subnode1_offset));
+	check_getprop_cell(fdt, subnode1_offset, "prop-int", TEST_VALUE_1);
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode@2\": %s",
+		     fdt_strerror(subnode2_offset));
+	check_getprop_cell(fdt, subnode2_offset, "prop-int", TEST_VALUE_2);
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode@2/subsubnode\": %s",
+		     fdt_strerror(subsubnode2_offset));
+	check_getprop_cell(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2);
+
+	err = fdt_del_node(fdt, subnode1_offset);
+	if (err)
+		FAIL("fdt_del_node(subnode1): %s", fdt_strerror(err));
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode1_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode2\": %s",
+		     fdt_strerror(subnode2_offset));
+	check_getprop_cell(fdt, subnode2_offset, "prop-int", TEST_VALUE_2);
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode@2/subsubnode\": %s",
+		     fdt_strerror(subsubnode2_offset));
+	check_getprop_cell(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2);
+
+	err = fdt_del_node(fdt, subnode2_offset);
+	if (err)
+		FAIL("fdt_del_node(subnode2): %s", fdt_strerror(err));
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode1_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode2) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode2_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subsubnode2) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subsubnode2_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	delsize = fdt_totalsize(fdt);
+
+	err = fdt_pack(fdt);
+	if (err)
+		FAIL("fdt_pack(): %s", fdt_strerror(err));
+
+	newsize = fdt_totalsize(fdt);
+
+	verbose_printf("oldsize = %d, delsize = %d, newsize = %d\n",
+		       oldsize, delsize, newsize);
+
+	if (newsize >= oldsize)
+		FAIL("Tree failed to shrink after deletions");
+
+	PASS();
+}
diff --git a/tests/del_property.c b/tests/del_property.c
new file mode 100644
index 0000000..37e8303
--- /dev/null
+++ b/tests/del_property.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_delprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const uint32_t *intp;
+	const char *strp;
+	int err, lenerr;
+	int oldsize, delsize, newsize;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	fdt = open_blob_rw(fdt);
+
+	oldsize = fdt_totalsize(fdt);
+
+	intp = check_getprop_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+	verbose_printf("int value was 0x%08x\n", *intp);
+
+	err = fdt_delprop(fdt, 0, "prop-int");
+	if (err)
+		FAIL("Failed to delete \"prop-int\": %s", fdt_strerror(err));
+
+	intp = fdt_getprop(fdt, 0, "prop-int", &lenerr);
+	if (intp)
+		FAIL("prop-int still present after deletion");
+	if (lenerr != -FDT_ERR_NOTFOUND)
+		FAIL("Unexpected error on second getprop: %s",
+		     fdt_strerror(lenerr));
+
+	strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1,
+			     TEST_STRING_1);
+	verbose_printf("string value was \"%s\"\n", strp);
+	err = fdt_delprop(fdt, 0, "prop-str");
+	if (err)
+		FAIL("Failed to delete \"prop-str\": %s", fdt_strerror(err));
+
+	strp = fdt_getprop(fdt, 0, "prop-str", &lenerr);
+	if (strp)
+		FAIL("prop-str still present after deletion");
+	if (lenerr != -FDT_ERR_NOTFOUND)
+		FAIL("Unexpected error on second getprop: %s",
+		     fdt_strerror(lenerr));
+
+	delsize = fdt_totalsize(fdt);
+
+	err = fdt_pack(fdt);
+	if (err)
+		FAIL("fdt_pack(): %s\n", fdt_strerror(err));
+
+	newsize = fdt_totalsize(fdt);
+
+	verbose_printf("oldsize = %d, delsize = %d, newsize = %d\n",
+		       oldsize, delsize, newsize);
+
+	if (newsize >= oldsize)
+		FAIL("Tree failed to shrink after deletions");
+
+	PASS();
+}
diff --git a/tests/delete_reinstate_multilabel.dts b/tests/delete_reinstate_multilabel.dts
new file mode 100644
index 0000000..281a6b2
--- /dev/null
+++ b/tests/delete_reinstate_multilabel.dts
@@ -0,0 +1,37 @@
+/dts-v1/;
+
+/* Create some nodes and properties with multiple labels */
+
+/ {
+	label1: label2: prop = "value";
+
+	label3: label4: node {
+		label5: label6: prop = "value";
+	};
+};
+
+/* Delete them, and everything that's part of them, i.e. the labels */
+
+/ {
+	/delete-property/ prop;
+	/delete-node/ node;
+};
+
+/*
+ * Re-instate them. None of the old labels should come back
+ *
+ * Note: Do not add any new/extra labels here. As of the time of writing,
+ * when dtc adds labels to an object, they are added to the head of the list
+ * of labels, and this test is specifically about ensuring the correct
+ * handling of lists of labels where the first label in the list is marked as
+ * deleted. Failure to observe this note may result in the test passing when
+ * it should not.
+ */
+
+/ {
+	prop = "value";
+
+	node {
+		prop = "value";
+	};
+};
diff --git a/tests/delete_reinstate_multilabel_ref.dts b/tests/delete_reinstate_multilabel_ref.dts
new file mode 100644
index 0000000..28fa117
--- /dev/null
+++ b/tests/delete_reinstate_multilabel_ref.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+	prop = "value";
+
+	node {
+		prop = "value";
+	};
+};
diff --git a/tests/dependencies.cmp b/tests/dependencies.cmp
new file mode 100644
index 0000000..bcd9432
--- /dev/null
+++ b/tests/dependencies.cmp
@@ -0,0 +1 @@
+dependencies.test.dtb: dependencies.dts deps_inc1.dtsi deps_inc2.dtsi
diff --git a/tests/dependencies.dts b/tests/dependencies.dts
new file mode 100644
index 0000000..2cfe31b
--- /dev/null
+++ b/tests/dependencies.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/include/ "deps_inc1.dtsi"
+
+/ {
+};
diff --git a/tests/deps_inc1.dtsi b/tests/deps_inc1.dtsi
new file mode 100644
index 0000000..5c607dc
--- /dev/null
+++ b/tests/deps_inc1.dtsi
@@ -0,0 +1 @@
+/include/ "deps_inc2.dtsi"
diff --git a/tests/deps_inc2.dtsi b/tests/deps_inc2.dtsi
new file mode 100644
index 0000000..710cecc
--- /dev/null
+++ b/tests/deps_inc2.dtsi
@@ -0,0 +1 @@
+/* Empty */
diff --git a/tests/division-by-zero.dts b/tests/division-by-zero.dts
new file mode 100644
index 0000000..2984b29
--- /dev/null
+++ b/tests/division-by-zero.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	prop-div = < (1/0) >;
+	prop-mod = < (1%0) >;
+};
diff --git a/tests/dtb_reverse.c b/tests/dtb_reverse.c
new file mode 100644
index 0000000..95b6c1c
--- /dev/null
+++ b/tests/dtb_reverse.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests if two given dtbs are structurally equal (including order)
+ * Copyright (C) 2010 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+static void reverse_reservemap(void *in, void *out, int n)
+{
+	int err;
+	uint64_t addr, size;
+
+	verbose_printf("reverse_reservemap(): %d/%d\n",
+		       n, fdt_num_mem_rsv(in));
+
+	if (n < (fdt_num_mem_rsv(in)-1))
+		reverse_reservemap(in, out, n+1);
+
+	CHECK(fdt_get_mem_rsv(in, n, &addr, &size));
+	CHECK(fdt_add_reservemap_entry(out, addr, size));
+	verbose_printf("Added entry 0x%llx 0x%llx\n",
+		       (unsigned long long)addr, (unsigned long long)size);
+}
+
+static void reverse_properties(void *in, void *out, int offset)
+{
+	int err;
+	int len;
+	const char *name;
+	const void *data;
+
+	data = fdt_getprop_by_offset(in, offset, &name, &len);
+	if (!data)
+		FAIL("fdt_getprop_by_offset(): %s\n", fdt_strerror(len));
+
+	verbose_printf("reverse_properties(): offset=%d  name=%s\n",
+		       offset, name);
+
+	offset = fdt_next_property_offset(in, offset);
+	if (offset >= 0)
+		reverse_properties(in, out, offset);
+	else if (offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_next_property_offset(): %s\n", fdt_strerror(offset));
+
+	CHECK(fdt_property(out, name, data, len));
+	verbose_printf("  -> output property %s\n", name);
+}
+
+static void reverse_node(void *in, void *out, int nodeoffset);
+
+static void reverse_children(void *in, void *out, int offset)
+{
+	int err;
+	int nextoffset = offset;
+	int depth = 1;
+
+	do {
+		char path[PATH_MAX];
+
+		CHECK(fdt_get_path(in, nextoffset, path, sizeof(path)));
+		verbose_printf("reverse_children() offset=%d nextoffset=%d [%s]"
+			       " depth=%d\n", offset, nextoffset, path, depth);
+
+		nextoffset = fdt_next_node(in, nextoffset, &depth);
+	} while ((depth >= 0) && (depth != 1));
+
+	if (depth == 1)
+		reverse_children(in, out, nextoffset);
+
+	reverse_node(in, out, offset);
+}
+
+static void reverse_node(void *in, void *out, int nodeoffset)
+{
+	const char *name = fdt_get_name(in, nodeoffset, NULL);
+	char path[PATH_MAX];
+	int err;
+	int offset;
+	int depth = 0;
+
+	CHECK(fdt_get_path(in, nodeoffset, path, sizeof(path)));
+	verbose_printf("reverse_node(): nodeoffset=%d [%s]\n",
+		       nodeoffset, path);
+
+	CHECK(fdt_begin_node(out, name));
+
+	offset = fdt_first_property_offset(in, nodeoffset);
+	if (offset >= 0)
+		reverse_properties(in, out, offset);
+	else if (offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_first_property(): %s\n", fdt_strerror(offset));
+
+	offset = fdt_next_node(in, nodeoffset, &depth);
+
+	if (depth == 1)
+		reverse_children(in, out, offset);
+
+	CHECK(fdt_end_node(out));
+}
+
+int main(int argc, char *argv[])
+{
+	void *in, *out;
+	char outname[PATH_MAX];
+	int bufsize;
+	int err;
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>", argv[0]);
+
+	in = load_blob(argv[1]);
+	sprintf(outname, "%s.reversed.test.dtb", argv[1]);
+
+	bufsize = fdt_totalsize(in);
+	out = xmalloc(bufsize);
+
+	CHECK(fdt_create(out, bufsize));
+
+	fdt_set_boot_cpuid_phys(out, fdt_boot_cpuid_phys(in));
+
+	reverse_reservemap(in, out, 0);
+	CHECK(fdt_finish_reservemap(out));
+
+	reverse_node(in, out, 0);
+
+	CHECK(fdt_finish(out));
+
+	save_blob(outname, out);
+
+	PASS();
+}
diff --git a/tests/dtbs_equal_ordered.c b/tests/dtbs_equal_ordered.c
new file mode 100644
index 0000000..90c7344
--- /dev/null
+++ b/tests/dtbs_equal_ordered.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests if two given dtbs are structurally equal (including order)
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int notequal; /* = 0 */
+
+#define MISMATCH(fmt, ...)			\
+	do { \
+		if (notequal) \
+			PASS(); \
+		else \
+			FAIL(fmt, ##__VA_ARGS__);	\
+	} while (0)
+
+#define MATCH()			\
+	do { \
+		if (!notequal) \
+			PASS(); \
+		else \
+			FAIL("Trees match which shouldn't");	\
+	} while (0)
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+static void compare_mem_rsv(const void *fdt1, const void *fdt2)
+{
+	int i;
+	uint64_t addr1, size1, addr2, size2;
+	int err;
+
+	if (fdt_num_mem_rsv(fdt1) != fdt_num_mem_rsv(fdt2))
+		MISMATCH("Trees have different number of reserve entries");
+	for (i = 0; i < fdt_num_mem_rsv(fdt1); i++) {
+		CHECK(fdt_get_mem_rsv(fdt1, i, &addr1, &size1));
+		CHECK(fdt_get_mem_rsv(fdt2, i, &addr2, &size2));
+
+		if ((addr1 != addr2) || (size1 != size2))
+			MISMATCH("Mismatch in reserve entry %d: "
+				 "(0x%llx, 0x%llx) != (0x%llx, 0x%llx)", i,
+				 (unsigned long long)addr1,
+				 (unsigned long long)size1,
+				 (unsigned long long)addr2,
+				 (unsigned long long)size2);
+	}
+}
+
+static void compare_structure(const void *fdt1, const void *fdt2)
+{
+	int nextoffset1 = 0, nextoffset2 = 0;
+	int offset1, offset2;
+	uint32_t tag1, tag2;
+	const char *name1, *name2;
+	int err;
+	const struct fdt_property *prop1, *prop2;
+	int len1, len2;
+
+	while (1) {
+		do {
+			offset1 = nextoffset1;
+			tag1 = fdt_next_tag(fdt1, offset1, &nextoffset1);
+		} while (tag1 == FDT_NOP);
+		do {
+			offset2 = nextoffset2;
+			tag2 = fdt_next_tag(fdt2, offset2, &nextoffset2);
+		} while (tag2 == FDT_NOP);
+
+		if (tag1 != tag2)
+			MISMATCH("Tag mismatch (%d != %d) at (%d, %d)",
+			     tag1, tag2, offset1, offset2);
+
+		switch (tag1) {
+		case FDT_BEGIN_NODE:
+			name1 = fdt_get_name(fdt1, offset1, &err);
+			if (!name1)
+				FAIL("fdt_get_name(fdt1, %d, ..): %s",
+				     offset1, fdt_strerror(err));
+			name2 = fdt_get_name(fdt2, offset2, NULL);
+			if (!name2)
+				FAIL("fdt_get_name(fdt2, %d, ..): %s",
+				     offset2, fdt_strerror(err));
+
+			if (!streq(name1, name2))
+			    MISMATCH("Name mismatch (\"%s\" != \"%s\") at (%d, %d)",
+				     name1, name2, offset1, offset2);
+			break;
+
+		case FDT_PROP:
+			prop1 = fdt_offset_ptr(fdt1, offset1, sizeof(*prop1));
+			if (!prop1)
+				FAIL("Could get fdt1 property at %d", offset1);
+			prop2 = fdt_offset_ptr(fdt2, offset2, sizeof(*prop2));
+			if (!prop2)
+				FAIL("Could get fdt2 property at %d", offset2);
+
+			name1 = fdt_string(fdt1, fdt32_to_cpu(prop1->nameoff));
+			name2 = fdt_string(fdt2, fdt32_to_cpu(prop2->nameoff));
+			if (!streq(name1, name2))
+				MISMATCH("Property name mismatch \"%s\" != \"%s\" "
+					 "at (%d, %d)", name1, name2, offset1, offset2);
+			len1 = fdt32_to_cpu(prop1->len);
+			len2 = fdt32_to_cpu(prop2->len);
+			if (len1 != len2)
+				MISMATCH("Property length mismatch %u != %u "
+					 "at (%d, %d)", len1, len2, offset1, offset2);
+
+			if (memcmp(prop1->data, prop2->data, len1) != 0)
+				MISMATCH("Property value mismatch at (%d, %d)",
+					 offset1, offset2);
+			break;
+
+		case FDT_END:
+			return;
+		}
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt1, *fdt2;
+	uint32_t cpuid1, cpuid2;
+
+	test_init(argc, argv);
+	if ((argc != 3)
+	    && ((argc != 4) || !streq(argv[1], "-n")))
+		CONFIG("Usage: %s [-n] <dtb file> <dtb file>", argv[0]);
+	if (argc == 4)
+		notequal = 1;
+
+	fdt1 = load_blob(argv[argc-2]);
+	fdt2 = load_blob(argv[argc-1]);
+
+	compare_mem_rsv(fdt1, fdt2);
+	compare_structure(fdt1, fdt2);
+
+	cpuid1 = fdt_boot_cpuid_phys(fdt1);
+	cpuid2 = fdt_boot_cpuid_phys(fdt2);
+	if (cpuid1 != cpuid2)
+		MISMATCH("boot_cpuid_phys mismatch 0x%x != 0x%x",
+			 cpuid1, cpuid2);
+
+	MATCH();
+}
diff --git a/tests/dtbs_equal_unordered.c b/tests/dtbs_equal_unordered.c
new file mode 100644
index 0000000..e5ff9e8
--- /dev/null
+++ b/tests/dtbs_equal_unordered.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests if two given dtbs are structurally equal (including order)
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int notequal; /* = 0 */
+static int ignore_memrsv; /* = 0 */
+
+#define MISMATCH(fmt, ...)			\
+	do { \
+		if (notequal) \
+			PASS(); \
+		else \
+			FAIL(fmt, ##__VA_ARGS__);	\
+	} while (0)
+
+#define MATCH()			\
+	do { \
+		if (!notequal) \
+			PASS(); \
+		else \
+			FAIL("Trees match which shouldn't");	\
+	} while (0)
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+static int mem_rsv_cmp(const void *p1, const void *p2)
+{
+	const struct fdt_reserve_entry *re1 = p1;
+	const struct fdt_reserve_entry *re2 = p2;
+
+	if (fdt64_to_cpu(re1->address) < fdt64_to_cpu(re2->address))
+		return -1;
+	else if (fdt64_to_cpu(re1->address) > fdt64_to_cpu(re2->address))
+		return 1;
+
+	if (fdt64_to_cpu(re1->size) < fdt64_to_cpu(re2->size))
+		return -1;
+	else if (fdt64_to_cpu(re1->size) > fdt64_to_cpu(re2->size))
+		return 1;
+
+	return 0;
+}
+
+static void compare_mem_rsv(void *fdt1, void *fdt2)
+{
+	int i;
+	uint64_t addr1, size1, addr2, size2;
+	int err;
+
+	if (fdt_num_mem_rsv(fdt1) != fdt_num_mem_rsv(fdt2))
+		MISMATCH("Trees have different number of reserve entries");
+
+	qsort((char *)fdt1 + fdt_off_mem_rsvmap(fdt1), fdt_num_mem_rsv(fdt1),
+	      sizeof(struct fdt_reserve_entry), mem_rsv_cmp);
+	qsort((char *)fdt2 + fdt_off_mem_rsvmap(fdt2), fdt_num_mem_rsv(fdt2),
+	      sizeof(struct fdt_reserve_entry), mem_rsv_cmp);
+
+	for (i = 0; i < fdt_num_mem_rsv(fdt1); i++) {
+		CHECK(fdt_get_mem_rsv(fdt1, i, &addr1, &size1));
+		CHECK(fdt_get_mem_rsv(fdt2, i, &addr2, &size2));
+
+		if ((addr1 != addr2) || (size1 != size2))
+			MISMATCH("Mismatch in reserve entry %d: "
+			     "(0x%llx, 0x%llx) != (0x%llx, 0x%llx)", i,
+			     (unsigned long long)addr1,
+			     (unsigned long long)size1,
+			     (unsigned long long)addr2,
+			     (unsigned long long)size2);
+	}
+}
+
+static void compare_properties(const void *fdt1, int offset1,
+			       const void *fdt2, int offset2)
+{
+	int offset = offset1;
+
+	/* Check the properties */
+	for (offset = fdt_first_property_offset(fdt1, offset1);
+	     offset >= 0;
+	     offset = fdt_next_property_offset(fdt1, offset)) {
+		const char *name;
+		int len1, len2;
+		const void *data1, *data2;
+		int i;
+
+		data1 = fdt_getprop_by_offset(fdt1, offset, &name, &len1);
+		if (!data1)
+			FAIL("fdt_getprop_by_offset(): %s\n",
+			     fdt_strerror(len1));
+
+		verbose_printf("Property '%s'\n", name);
+
+		data2 = fdt_getprop(fdt2, offset2, name, &len2);
+		if (!data2) {
+			if (len2 == -FDT_ERR_NOTFOUND)
+				MISMATCH("Property '%s' missing\n", name);
+			else
+				FAIL("fdt_get_property(): %s\n",
+				     fdt_strerror(len2));
+		}
+
+		verbose_printf("len1=%d data1=", len1);
+		for (i = 0; i < len1; i++)
+			verbose_printf(" %02x", ((const char *)data1)[i]);
+		verbose_printf("\nlen2=%d data2=", len2);
+		for (i = 0; i < len1; i++)
+			verbose_printf(" %02x", ((const char *)data2)[i]);
+		verbose_printf("\n");
+
+		if (len1 != len2)
+			MISMATCH("Property '%s' mismatched length %d vs. %d\n",
+			     name, len1, len2);
+		else if (memcmp(data1, data2, len1) != 0)
+			MISMATCH("Property '%s' mismatched value\n", name);
+	}
+}
+
+static void compare_node(const void *fdt1, int offset1,
+			 const void *fdt2, int offset2);
+
+static void compare_subnodes(const void *fdt1, int offset1,
+			     const void *fdt2, int offset2,
+			     int recurse)
+{
+	int coffset1, coffset2, depth;
+
+	for (depth = 0, coffset1 = offset1;
+	     (coffset1 >= 0) && (depth >= 0);
+	      coffset1 = fdt_next_node(fdt1, coffset1, &depth))
+		if (depth == 1) {
+			const char *name = fdt_get_name(fdt1, coffset1, NULL);
+
+			verbose_printf("Subnode %s\n", name);
+			coffset2 = fdt_subnode_offset(fdt2, offset2, name);
+			if (coffset2 == -FDT_ERR_NOTFOUND)
+				MISMATCH("Subnode %s missing\n", name);
+			else if (coffset2 < 0)
+				FAIL("fdt_subnode_offset(): %s\n",
+				     fdt_strerror(coffset2));
+
+			if (recurse)
+				compare_node(fdt1, coffset1, fdt2, coffset2);
+		}
+}
+
+static void compare_node(const void *fdt1, int offset1,
+			 const void *fdt2, int offset2)
+{
+	int err;
+	char path1[PATH_MAX], path2[PATH_MAX];
+
+	CHECK(fdt_get_path(fdt1, offset1, path1, sizeof(path1)));
+	CHECK(fdt_get_path(fdt2, offset2, path2, sizeof(path2)));
+
+	if (!streq(path1, path2))
+		TEST_BUG("Path mismatch %s vs. %s\n", path1, path2);
+
+	verbose_printf("Checking %s\n", path1);
+
+	compare_properties(fdt1, offset1, fdt2, offset2);
+	compare_properties(fdt2, offset2, fdt1, offset1);
+
+	compare_subnodes(fdt1, offset1, fdt2, offset2, 1);
+	compare_subnodes(fdt2, offset2, fdt1, offset1, 0);
+}
+
+static void badargs(char **argv)
+{
+	CONFIG("Usage: %s [-n] [-m] <dtb file> <dtb file>", argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt1, *fdt2;
+	uint32_t cpuid1, cpuid2;
+	char **args;
+	int argsleft;
+
+	test_init(argc, argv);
+
+	args = &argv[1];
+	argsleft = argc - 1;
+
+	while (argsleft > 2) {
+		if (streq(args[0], "-n"))
+			notequal = 1;
+		else if (streq(args[0], "-m"))
+			ignore_memrsv = 1;
+		else
+			badargs(argv);
+		args++;
+		argsleft--;
+	}
+	if (argsleft != 2)
+		badargs(argv);
+
+	fdt1 = load_blob(args[0]);
+	fdt2 = load_blob(args[1]);
+
+	if (!ignore_memrsv)
+		compare_mem_rsv(fdt1, fdt2);
+	compare_node(fdt1, 0, fdt2, 0);
+
+	cpuid1 = fdt_boot_cpuid_phys(fdt1);
+	cpuid2 = fdt_boot_cpuid_phys(fdt2);
+	if (cpuid1 != cpuid2)
+		MISMATCH("boot_cpuid_phys mismatch 0x%x != 0x%x",
+		     cpuid1, cpuid2);
+
+	MATCH();
+}
diff --git a/tests/dtc-checkfails.sh b/tests/dtc-checkfails.sh
new file mode 100755
index 0000000..0e8beb4
--- /dev/null
+++ b/tests/dtc-checkfails.sh
@@ -0,0 +1,44 @@
+#! /bin/sh
+
+. ./testutils.sh
+
+for x; do
+    shift
+    if [ "$x" = "-n" ]; then
+	for x; do
+	    shift
+	    if [ "$x" = "--" ]; then
+		break;
+	    fi
+	    NOCHECKS="$NOCHECKS $x"
+	done
+	break;
+    fi
+    if [ "$x" = "--" ]; then
+	break;
+    fi
+    YESCHECKS="$YESCHECKS $x"
+done
+
+LOG=tmp.log.$$
+rm -f $LOG
+trap "rm -f $LOG" 0
+
+verbose_run_log "$LOG" $VALGRIND "$DTC" -o /dev/null "$@"
+ret="$?"
+
+FAIL_IF_SIGNAL $ret
+
+for c in $YESCHECKS; do
+    if ! grep -E "(ERROR|Warning) \($c\):" $LOG > /dev/null; then
+	FAIL "Failed to trigger check \"$c\""
+    fi
+done
+
+for c in $NOCHECKS; do
+    if grep -E "(ERROR|Warning) \($c\):" $LOG > /dev/null; then
+	FAIL "Incorrectly triggered check \"$c\""
+    fi
+done
+
+PASS
diff --git a/tests/dtc-fails.sh b/tests/dtc-fails.sh
new file mode 100755
index 0000000..4543203
--- /dev/null
+++ b/tests/dtc-fails.sh
@@ -0,0 +1,30 @@
+#! /bin/sh
+
+. ./testutils.sh
+
+if [ "$1" = "-n" ]; then
+    NEG="$1"
+    shift
+fi
+
+OUTPUT="$1"
+shift
+
+verbose_run $VALGRIND "$DTC" -o "$OUTPUT" "$@"
+ret="$?"
+
+FAIL_IF_SIGNAL $ret
+
+if [ -n "$NEG" ]; then
+    if [ ! -e "$OUTPUT" ]; then
+	FAIL "Produced no output"
+    fi
+else
+    if [ -e "$OUTPUT" ]; then
+	FAIL "Incorrectly produced output"
+    fi
+fi
+
+rm -f "$OUTPUT"
+
+PASS
diff --git a/tests/dtc-fatal.sh b/tests/dtc-fatal.sh
new file mode 100644
index 0000000..22eea69
--- /dev/null
+++ b/tests/dtc-fatal.sh
@@ -0,0 +1,14 @@
+#! /bin/sh
+
+. ./testutils.sh
+
+verbose_run $VALGRIND "$DTC" -o/dev/null "$@"
+ret="$?"
+
+if [ "$ret" -gt 127 ]; then
+    FAIL "dtc killed by signal (ret=$ret)"
+elif [ "$ret" != "1" ]; then
+    FAIL "dtc returned incorrect status $ret instead of 1"
+fi
+
+PASS
diff --git a/tests/dumptrees.c b/tests/dumptrees.c
new file mode 100644
index 0000000..b2f5b26
--- /dev/null
+++ b/tests/dumptrees.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dumptrees - utility for libfdt testing
+ *
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2006.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "testdata.h"
+
+static struct {
+	void *blob;
+	const char *filename;
+} trees[] = {
+#define TREE(name)	{ &name, #name ".dtb" }
+	TREE(test_tree1),
+	TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char),
+	TREE(ovf_size_strings),
+	TREE(truncated_property), TREE(truncated_string),
+	TREE(truncated_memrsv),
+};
+
+#define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
+
+int main(int argc, char *argv[])
+{
+	int i;
+
+	for (i = 0; i < NUM_TREES; i++) {
+		void *blob = trees[i].blob;
+		const char *filename = trees[i].filename;
+		int size;
+		int fd;
+		int ret;
+
+		size = fdt_totalsize(blob);
+
+		printf("Tree \"%s\", %d bytes\n", filename, size);
+
+		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (fd < 0)
+			perror("open()");
+
+		ret = write(fd, blob, size);
+		if (ret != size)
+			perror("write()");
+
+		close(fd);
+	}
+	exit(0);
+}
diff --git a/tests/dup-nodename.dts b/tests/dup-nodename.dts
new file mode 100644
index 0000000..2a3aa75
--- /dev/null
+++ b/tests/dup-nodename.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	node {
+	};
+	node {
+	};
+};
diff --git a/tests/dup-phandle.dts b/tests/dup-phandle.dts
new file mode 100644
index 0000000..c266c61
--- /dev/null
+++ b/tests/dup-phandle.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+	node1 {
+		linux,phandle = <1>;
+	};
+	node2 {
+		linux,phandle = <1>;
+	};
+};
diff --git a/tests/dup-propname.dts b/tests/dup-propname.dts
new file mode 100644
index 0000000..8145f6e
--- /dev/null
+++ b/tests/dup-propname.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	prop;
+	prop;
+};
diff --git a/tests/embedded_nul.dts b/tests/embedded_nul.dts
new file mode 100644
index 0000000..7b4993c
--- /dev/null
+++ b/tests/embedded_nul.dts
Binary files differ
diff --git a/tests/embedded_nul_equiv.dts b/tests/embedded_nul_equiv.dts
new file mode 100644
index 0000000..e978204
--- /dev/null
+++ b/tests/embedded_nul_equiv.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	reserved-names = "aaaaaaaaaaaaaaaaaa\0bbbbbb\0ccccccccccccc";
+	reserved-ranges = < 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0  >;
+};
diff --git a/tests/empty.dts b/tests/empty.dts
new file mode 100644
index 0000000..e160dad
--- /dev/null
+++ b/tests/empty.dts
@@ -0,0 +1,4 @@
+/dts-v1/;
+
+/ {
+};
diff --git a/tests/escapes.dts b/tests/escapes.dts
new file mode 100644
index 0000000..e05ab46
--- /dev/null
+++ b/tests/escapes.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	compatible = "test_string_escapes";
+	escape-str = "nastystring: \a\b\t\n\v\f\r\\\"";
+	escape-str-2 = "\xde\xad\xbe\xef";
+};
diff --git a/tests/extra-terminating-null.c b/tests/extra-terminating-null.c
new file mode 100644
index 0000000..0fa2ca8
--- /dev/null
+++ b/tests/extra-terminating-null.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for properties with more than one terminating null
+ * Copyright (C) 2009 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_extranull(void *fdt, const char *prop, const char *str, int numnulls)
+{
+	int len = strlen(str);
+	char checkbuf[len+numnulls];
+
+	memset(checkbuf, 0, sizeof(checkbuf));
+	memcpy(checkbuf, TEST_STRING_1, len);
+
+	check_getprop(fdt, 0, prop, len+numnulls, checkbuf);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+
+	fdt = load_blob_arg(argc, argv);
+
+	check_extranull(fdt, "extranull0", TEST_STRING_1, 1);
+	check_extranull(fdt, "extranull1,1", TEST_STRING_1, 2);
+	check_extranull(fdt, "extranull1,2", TEST_STRING_1, 2);
+	check_extranull(fdt, "extranull2,1", TEST_STRING_1, 3);
+	check_extranull(fdt, "extranull2,2", TEST_STRING_1, 3);
+	check_extranull(fdt, "extranull2,3", TEST_STRING_1, 3);
+	check_extranull(fdt, "extranull2,4", TEST_STRING_1, 3);
+
+	PASS();
+}
diff --git a/tests/extra-terminating-null.dts b/tests/extra-terminating-null.dts
new file mode 100644
index 0000000..b6cc19c
--- /dev/null
+++ b/tests/extra-terminating-null.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	extranull0 = "hello world";
+	extranull1,1 = "hello world\0";
+	extranull1,2 = "hello world", "";
+	extranull2,1 = "hello world\0\0";
+	extranull2,2 = "hello world", "", "";
+	extranull2,3 = "hello world\0", "";
+	extranull2,4 = "hello world", "\0";
+};
diff --git a/tests/fdtdump-runtest.sh b/tests/fdtdump-runtest.sh
new file mode 100644
index 0000000..616c737
--- /dev/null
+++ b/tests/fdtdump-runtest.sh
@@ -0,0 +1,30 @@
+#! /bin/sh
+
+# Arguments:
+#   $1 - source file to compile and compare with fdtdump output of the
+#	  compiled file.
+
+. ./testutils.sh
+
+dts="$1"
+dtb="${dts}.dtb"
+out="${dts}.out"
+LOG=tmp.log.$$
+
+files="$dtb $out $LOG"
+
+rm -f $files
+trap "rm -f $files" 0
+
+verbose_run_log_check "$LOG" $VALGRIND $DTC -O dtb $dts -o $dtb
+$FDTDUMP ${dtb} | grep -v "//" >${out}
+
+if diff -w $dts $out >/dev/null; then
+    PASS
+else
+    if [ -z "$QUIET_TEST" ]; then
+	echo "DIFF :-:"
+	diff -u -w $dts $out
+    fi
+    FAIL "Results differ from expected"
+fi
diff --git a/tests/fdtdump.dts b/tests/fdtdump.dts
new file mode 100644
index 0000000..b83b7df
--- /dev/null
+++ b/tests/fdtdump.dts
@@ -0,0 +1,38 @@
+/dts-v1/;
+
+/memreserve/ 0 0xe;
+/ {
+	model = "MyBoardName";
+	compatible = "MyBoardName", "MyBoardFamilyName";
+	#address-cells = <0x00000002>;
+	#size-cells = <0x00000002>;
+	cpus {
+		linux,phandle = <0x00000001>;
+		#address-cells = <0x00000001>;
+		#size-cells = <0x00000000>;
+		PowerPC,970@0 {
+			device_type = "cpu";
+			reg = <0x00000000>;
+			linux,boot-cpu;
+			};
+		PowerPC,970@1 {
+			device_type = "cpu";
+			reg = <0x00000001>;
+		};
+	};
+	randomnode {
+		string =  "foo", "stuff";
+		bytes = [61 62 63 64 65];
+		nbytes = [80 ff];
+		child {
+		};
+	};
+	memory@0 {
+		device_type = "memory";
+		reg = <0x00000000 0x00000123 0x00000456 0x87654321>;
+	};
+	chosen {
+		bootargs = "root=/dev/sda2";
+		linux,platform = <0x00000600>;
+	};
+};
diff --git a/tests/fdtget-runtest.sh b/tests/fdtget-runtest.sh
new file mode 100755
index 0000000..21044f3
--- /dev/null
+++ b/tests/fdtget-runtest.sh
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+. ./testutils.sh
+
+LOG=tmp.log.$$
+EXPECT=tmp.expect.$$
+rm -f $LOG $EXPECT
+trap "rm -f $LOG $EXPECT" 0
+
+expect="$1"
+printf '%b\n' "$expect" > $EXPECT
+shift
+
+verbose_run_log_check "$LOG" $VALGRIND $DTGET "$@"
+
+if cmp $EXPECT $LOG>/dev/null; then
+    PASS
+else
+    if [ -z "$QUIET_TEST" ]; then
+	echo "EXPECTED :-:"
+	cat $EXPECT
+    fi
+    FAIL "Results differ from expected"
+fi
diff --git a/tests/fdtoverlay-runtest.sh b/tests/fdtoverlay-runtest.sh
new file mode 100644
index 0000000..7c54beb
--- /dev/null
+++ b/tests/fdtoverlay-runtest.sh
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+# Run script for fdtoverlay tests
+# We run fdtoverlay to generate a target device tree, then fdtget to check it
+
+# Usage
+#    fdtoverlay-runtest.sh name expected_output dtb_file node property flags value
+
+. ./testutils.sh
+
+LOG=tmp.log.$$
+EXPECT=tmp.expect.$$
+rm -f $LOG $EXPECT
+trap "rm -f $LOG $EXPECT" 0
+
+expect="$1"
+echo $expect >$EXPECT
+node="$2"
+property="$3"
+flags="$4"
+basedtb="$5"
+targetdtb="$6"
+shift 6
+overlays="$@"
+
+# First run fdtoverlay
+verbose_run_check $VALGRIND "$FDTOVERLAY" -i "$basedtb" -o "$targetdtb" $overlays
+
+# Now fdtget to read the value
+verbose_run_log_check "$LOG" $VALGRIND "$DTGET" "$targetdtb" "$node" "$property" $flags
+
+if cmp $EXPECT $LOG >/dev/null; then
+    PASS
+else
+    if [ -z "$QUIET_TEST" ]; then
+	echo "EXPECTED :-:"
+	cat $EXPECT
+    fi
+    FAIL "Results differ from expected"
+fi
diff --git a/tests/fdtput-runtest.sh b/tests/fdtput-runtest.sh
new file mode 100644
index 0000000..a1f7b85
--- /dev/null
+++ b/tests/fdtput-runtest.sh
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+# Run script for fdtput tests
+# We run fdtput to update the device tree, then fdtget to check it
+
+# Usage
+#    fdtput-runtest.sh name expected_output dtb_file node property flags value
+
+. ./testutils.sh
+
+LOG=tmp.log.$$
+EXPECT=tmp.expect.$$
+rm -f $LOG $EXPECT
+trap "rm -f $LOG $EXPECT" 0
+
+expect="$1"
+echo $expect >$EXPECT
+dtb="$2"
+node="$3"
+property="$4"
+flags="$5"
+shift 5
+value="$@"
+
+# First run fdtput
+verbose_run_check $VALGRIND "$DTPUT" "$dtb" "$node" "$property" $value $flags
+
+# Now fdtget to read the value
+verbose_run_log_check "$LOG" $VALGRIND "$DTGET" "$dtb" "$node" "$property" $flags
+
+if cmp $EXPECT $LOG >/dev/null; then
+    PASS
+else
+    if [ -z "$QUIET_TEST" ]; then
+	echo "EXPECTED :-:"
+	cat $EXPECT
+    fi
+    FAIL "Results differ from expected"
+fi
diff --git a/tests/find_property.c b/tests/find_property.c
new file mode 100644
index 0000000..0404ea0
--- /dev/null
+++ b/tests/find_property.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_property_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_property_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+	check_property(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1, TEST_STRING_1);
+
+	PASS();
+}
diff --git a/tests/fs_tree1.c b/tests/fs_tree1.c
new file mode 100644
index 0000000..dff3880
--- /dev/null
+++ b/tests/fs_tree1.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase/tool constructing an fs tree for further test
+ * Copyright (C) 2018 David Gibson, Red Hat Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void start_dir(const char *name)
+{
+	int rc;
+
+	rc = mkdir(name, 0777);
+	if (rc != 0)
+		FAIL("mkdir(\"%s\"): %s", name, strerror(errno));
+
+	rc = chdir(name);
+	if (rc != 0)
+		FAIL("chdir(\"%s\"): %s", name, strerror(errno));
+}
+
+static void end_dir(void)
+{
+	int rc;
+
+	rc = chdir("..");
+	if (rc != 0)
+		FAIL("chdir(..): %s", strerror(errno));
+}
+	
+static void mkfile(const char *name, void *data, size_t len)
+{
+	int fd;
+	int rc;
+
+	fd = open(name, O_WRONLY|O_CREAT, 0666);
+	if (fd < 0)
+		FAIL("open(\"%s\"): %s", name, strerror(errno));
+
+	rc = write(fd, data, len);
+	if (rc < 0)
+		FAIL("write(\"%s\"): %s", name, strerror(errno));
+	if (rc != len)
+		FAIL("write(\"%s\"): short write", name);
+	
+	rc = close(fd);
+	if (rc != 0)
+		FAIL("close(\"%s\"): %s", name, strerror(errno));
+}
+
+#define mkfile_str(name, s)			  \
+	do {					  \
+		char str[] = s;			  \
+		mkfile((name), str, sizeof(str)); \
+	} while (0)
+
+static void mkfile_u32(const char *name, uint32_t val)
+{
+	val = cpu_to_fdt32(val);
+	mkfile(name, &val, sizeof(val));
+}
+
+static void mkfile_u64(const char *name, uint64_t val)
+{
+	val = cpu_to_fdt64(val);
+	mkfile(name, &val, sizeof(val));
+}
+
+int main(int argc, char *argv[])
+{
+	const char *base;
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <path>", argv[0]);
+
+	base = argv[1];
+
+	start_dir(base);
+	mkfile_str("compatible", "test_tree1");
+	mkfile_u32("prop-int", TEST_VALUE_1);
+	mkfile_u64("prop-int64", 0xdeadbeef01abcdefULL);
+	mkfile_str("prop-str", "hello world");
+	mkfile_u32("#address-cells", 1);
+	mkfile_u32("#size-cells", 0);
+
+	{
+		start_dir("subnode@1");
+
+		mkfile_str("compatible", "subnode1");
+		mkfile_u32("reg", 1);
+		mkfile_u32("prop-int", TEST_VALUE_1);
+
+		{
+			start_dir("subsubnode");
+
+			mkfile_str("compatible", "subsubnode1\0subsubnode");
+			mkfile_str("placeholder", "this is a placeholder string\0string2");
+			mkfile_u32("prop-int", TEST_VALUE_1);
+			
+			end_dir();
+		}
+
+		{
+			start_dir("ss1");
+			end_dir();
+		}
+		
+		end_dir();
+	}
+
+	{
+		start_dir("subnode@2");
+
+		mkfile_u32("reg", 2);
+		mkfile_u32("linux,phandle", 0x2000);
+		mkfile_u32("prop-int", TEST_VALUE_2);
+		mkfile_u32("#address-cells", 1);
+		mkfile_u32("#size-cells", 0);
+
+		{
+			start_dir("subsubnode@0");
+
+			mkfile_u32("reg", 0);
+			mkfile_u32("phandle", 0x2001);
+			mkfile_str("compatible", "subsubnode2\0subsubnode");
+			mkfile_u32("prop-int", TEST_VALUE_2);
+			
+			end_dir();
+		}
+
+		{
+			start_dir("ss2");
+			end_dir();
+		}
+		
+		end_dir();
+	}
+
+	PASS();
+}
diff --git a/tests/get_alias.c b/tests/get_alias.c
new file mode 100644
index 0000000..fb2c38c
--- /dev/null
+++ b/tests/get_alias.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_get_alias()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_alias(void *fdt, const char *path, const char *alias)
+{
+	const char *aliaspath;
+
+	aliaspath = fdt_get_alias(fdt, alias);
+
+	if (path && !aliaspath)
+		FAIL("fdt_get_alias(%s) failed\n", alias);
+
+	if (strcmp(aliaspath, path) != 0)
+		FAIL("fdt_get_alias(%s) returned %s instead of %s\n",
+		     alias, aliaspath, path);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_alias(fdt, "/subnode@1", "s1");
+	check_alias(fdt, "/subnode@1/subsubnode", "ss1");
+	check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1");
+
+	PASS();
+}
diff --git a/tests/get_mem_rsv.c b/tests/get_mem_rsv.c
new file mode 100644
index 0000000..f977d19
--- /dev/null
+++ b/tests/get_mem_rsv.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_get_mem_rsv() and fdt_num_mem_rsv()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int rc;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	rc = fdt_num_mem_rsv(fdt);
+	if (rc < 0)
+		FAIL("fdt_num_mem_rsv(): %s", fdt_strerror(rc));
+	if (rc != 2)
+		FAIL("fdt_num_mem_rsv() returned %d instead of 2", rc);
+
+	check_mem_rsv(fdt, 0, TEST_ADDR_1, TEST_SIZE_1);
+	check_mem_rsv(fdt, 1, TEST_ADDR_2, TEST_SIZE_2);
+	PASS();
+}
diff --git a/tests/get_name.c b/tests/get_name.c
new file mode 100644
index 0000000..5a35103
--- /dev/null
+++ b/tests/get_name.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_get_name()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_name(void *fdt, const char *path)
+{
+	int offset;
+	const char *getname, *getname2, *checkname;
+	int len;
+
+	checkname = strrchr(path, '/');
+	if (!checkname)
+		TEST_BUG();
+	checkname += 1;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find %s", path);
+
+	getname = fdt_get_name(fdt, offset, &len);
+	verbose_printf("fdt_get_name(%d) returns \"%s\" (len=%d)\n",
+		       offset, getname, len);
+	if (!getname)
+		FAIL("fdt_get_name(%d): %s", offset, fdt_strerror(len));
+
+	if (strcmp(getname, checkname) != 0)
+		FAIL("fdt_get_name(%s) returned \"%s\" instead of \"%s\"",
+		     path, getname, checkname);
+
+	if (len != strlen(getname))
+		FAIL("fdt_get_name(%s) returned length %d instead of %zd",
+		     path, len, strlen(getname));
+
+	/* Now check that it doesn't break if we omit len */
+	getname2 = fdt_get_name(fdt, offset, NULL);
+	if (!getname2)
+		FAIL("fdt_get_name(%d, NULL) failed", offset);
+	if (strcmp(getname2, getname) != 0)
+		FAIL("fdt_get_name(%d, NULL) returned \"%s\" instead of \"%s\"",
+		     offset, getname2, getname);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_name(fdt, "/");
+	check_name(fdt, "/subnode@1");
+	check_name(fdt, "/subnode@2");
+	check_name(fdt, "/subnode@1/subsubnode");
+	check_name(fdt, "/subnode@2/subsubnode@0");
+
+	PASS();
+}
diff --git a/tests/get_path.c b/tests/get_path.c
new file mode 100644
index 0000000..7349898
--- /dev/null
+++ b/tests/get_path.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_get_path()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define POISON	('\xff')
+
+static void check_path_buf(void *fdt, const char *path, int pathlen, int buflen)
+{
+	int offset;
+	char buf[buflen+1];
+	int len;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path \"%s\": %s", path, fdt_strerror(offset));
+
+	memset(buf, POISON, sizeof(buf)); /* poison the buffer */
+
+	len = fdt_get_path(fdt, offset, buf, buflen);
+	verbose_printf("get_path() %s -> %d -> %s\n", path, offset,
+		       len >= 0 ? buf : "<error>");
+
+	if (buflen <= pathlen) {
+		if (len != -FDT_ERR_NOSPACE)
+			FAIL("fdt_get_path([%d bytes]) returns %d with "
+			     "insufficient buffer space", buflen, len);
+	} else {
+		if (len < 0)
+			FAIL("fdt_get_path([%d bytes]): %s", buflen,
+			     fdt_strerror(len));
+		if (len != 0)
+			FAIL("fdt_get_path([%d bytes]) returns %d "
+			     "instead of 0", buflen, len);
+		if (strcmp(buf, path) != 0)
+			FAIL("fdt_get_path([%d bytes]) returns \"%s\" "
+			     "instead of \"%s\"", buflen, buf, path);
+	}
+
+	if (buf[buflen] != POISON)
+		FAIL("fdt_get_path([%d bytes]) overran buffer", buflen);
+}
+
+static void check_path(void *fdt, const char *path)
+{
+	int pathlen = strlen(path);
+
+	check_path_buf(fdt, path, pathlen, 1024);
+	check_path_buf(fdt, path, pathlen, pathlen+1);
+	check_path_buf(fdt, path, pathlen, pathlen);
+	check_path_buf(fdt, path, pathlen, 0);
+	check_path_buf(fdt, path, pathlen, 2);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_path(fdt, "/");
+	check_path(fdt, "/subnode@1");
+	check_path(fdt, "/subnode@2");
+	check_path(fdt, "/subnode@1/subsubnode");
+	check_path(fdt, "/subnode@2/subsubnode@0");
+
+	PASS();
+}
diff --git a/tests/get_phandle.c b/tests/get_phandle.c
new file mode 100644
index 0000000..157b522
--- /dev/null
+++ b/tests/get_phandle.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_get_phandle()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_phandle(void *fdt, const char *path, uint32_t checkhandle)
+{
+	int offset;
+	uint32_t phandle;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find %s", path);
+
+	phandle = fdt_get_phandle(fdt, offset);
+	if (phandle != checkhandle)
+		FAIL("fdt_get_phandle(%s) returned 0x%x instead of 0x%x\n",
+		     path, phandle, checkhandle);
+}
+
+static void check_phandle_unique(const void *fdt, uint32_t checkhandle)
+{
+	uint32_t phandle;
+	int offset = -1;
+
+	while (true) {
+		offset = fdt_next_node(fdt, offset, NULL);
+		if (offset < 0) {
+			if (offset == -FDT_ERR_NOTFOUND)
+				break;
+
+			FAIL("error looking for phandle %#x", checkhandle);
+		}
+
+		phandle = fdt_get_phandle(fdt, offset);
+
+		if (phandle == checkhandle)
+			FAIL("generated phandle already exists");
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	uint32_t max, phandle;
+	void *fdt;
+	int err;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_phandle(fdt, "/", 0);
+	check_phandle(fdt, "/subnode@2", PHANDLE_1);
+	check_phandle(fdt, "/subnode@2/subsubnode@0", PHANDLE_2);
+
+	err = fdt_find_max_phandle(fdt, &max);
+	if (err < 0)
+		FAIL("fdt_find_max_phandle returned %d instead of 0\n", err);
+
+	if (max != PHANDLE_2)
+		FAIL("fdt_find_max_phandle found 0x%x instead of 0x%x", max,
+		     PHANDLE_2);
+
+	max = fdt_get_max_phandle(fdt);
+	if (max != PHANDLE_2)
+		FAIL("fdt_get_max_phandle returned 0x%x instead of 0x%x\n",
+		     max, PHANDLE_2);
+
+	err = fdt_generate_phandle(fdt, &phandle);
+	if (err < 0)
+		FAIL("failed to generate phandle: %d", err);
+
+	check_phandle_unique(fdt, phandle);
+
+	PASS();
+}
diff --git a/tests/get_prop_offset.c b/tests/get_prop_offset.c
new file mode 100644
index 0000000..cff3c18
--- /dev/null
+++ b/tests/get_prop_offset.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_getprop_by_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	bool found_prop_int = false;
+	bool found_prop_str = false;
+	int poffset;
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	fdt_for_each_property_offset(poffset, fdt, 0) {
+		if (check_get_prop_offset_cell(fdt, poffset, "prop-int",
+					       TEST_VALUE_1))
+			found_prop_int = true;
+		if (check_get_prop_offset(fdt, poffset, "prop-str",
+					  strlen(TEST_STRING_1) + 1,
+					  TEST_STRING_1))
+			found_prop_str = true;
+	}
+	if (!found_prop_int)
+		FAIL("Property 'prop-int' not found");
+	if (!found_prop_str)
+		FAIL("Property 'prop-str' not found");
+
+	PASS();
+}
diff --git a/tests/getprop.c b/tests/getprop.c
new file mode 100644
index 0000000..cccc8e2
--- /dev/null
+++ b/tests/getprop.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_getprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+	check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1, TEST_STRING_1);
+
+	PASS();
+}
diff --git a/tests/incbin.bin b/tests/incbin.bin
new file mode 100644
index 0000000..e6e3e48
--- /dev/null
+++ b/tests/incbin.bin
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
\ No newline at end of file
diff --git a/tests/incbin.c b/tests/incbin.c
new file mode 100644
index 0000000..fd02609
--- /dev/null
+++ b/tests/incbin.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for string escapes in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define CHUNKSIZE	1024
+
+static char *load_file(const char *name, int *len)
+{
+	FILE *f;
+	char *buf = NULL;
+	int bufsize = 0, n;
+
+	*len = 0;
+
+	f = fopen(name, "r");
+	if (!f)
+		FAIL("Couldn't open \"%s\": %s", name, strerror(errno));
+
+	while (!feof(f)) {
+		if (bufsize < (*len + CHUNKSIZE)) {
+			buf = xrealloc(buf, *len + CHUNKSIZE);
+			bufsize = *len + CHUNKSIZE;
+		}
+
+		n = fread(buf + *len, 1, CHUNKSIZE, f);
+		if (ferror(f))
+			FAIL("Error reading \"%s\": %s", name, strerror(errno));
+		*len += n;
+	}
+
+	return buf;
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	char *incbin;
+	int len;
+
+	test_init(argc, argv);
+
+	incbin = load_file("incbin.bin", &len);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop(fdt, 0, "incbin", len, incbin);
+	check_getprop(fdt, 0, "incbin-partial", 17, incbin + 13);
+
+	PASS();
+}
diff --git a/tests/incbin.dts b/tests/incbin.dts
new file mode 100644
index 0000000..7c30e0e
--- /dev/null
+++ b/tests/incbin.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	incbin = /incbin/("incbin.bin");
+	incbin-partial = /incbin/("incbin.bin", 13, 17);
+};
diff --git a/tests/include0.dts b/tests/include0.dts
new file mode 100644
index 0000000..355ed6a
--- /dev/null
+++ b/tests/include0.dts
@@ -0,0 +1 @@
+/include/ "include1.dts"
diff --git a/tests/include1.dts b/tests/include1.dts
new file mode 100644
index 0000000..0b4b773
--- /dev/null
+++ b/tests/include1.dts
@@ -0,0 +1,32 @@
+/dts-v1/;
+
+/include/ "include2.dts"
+/memreserve/ /include/ "include3.dts";
+
+/ {
+	/include/ "include4.dts"
+	/include/ "include5.dts" = <0xdeadbeef>;
+	prop-int64 /include/ "include5a.dts";
+	prop-str = /include/ "include6.dts";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	/include/ "include7.dts"
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		/include/ "include8.dts"
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/include2.dts b/tests/include2.dts
new file mode 100644
index 0000000..7e189dd
--- /dev/null
+++ b/tests/include2.dts
@@ -0,0 +1 @@
+/memreserve/ 0xdeadbeef00000000 0x100000;
diff --git a/tests/include3.dts b/tests/include3.dts
new file mode 100644
index 0000000..ee9d277
--- /dev/null
+++ b/tests/include3.dts
@@ -0,0 +1 @@
+123456789 010000
diff --git a/tests/include4.dts b/tests/include4.dts
new file mode 100644
index 0000000..b2ddbe5
--- /dev/null
+++ b/tests/include4.dts
@@ -0,0 +1 @@
+	compatible = "test_tree1";
diff --git a/tests/include5.dts b/tests/include5.dts
new file mode 100644
index 0000000..9a35dc5
--- /dev/null
+++ b/tests/include5.dts
@@ -0,0 +1 @@
+prop-int
diff --git a/tests/include5a.dts b/tests/include5a.dts
new file mode 100644
index 0000000..39ddba4
--- /dev/null
+++ b/tests/include5a.dts
@@ -0,0 +1 @@
+= /bits/ 64 <0xdeadbeef01abcdef>
\ No newline at end of file
diff --git a/tests/include6.dts b/tests/include6.dts
new file mode 100644
index 0000000..cd4bc1a
--- /dev/null
+++ b/tests/include6.dts
@@ -0,0 +1 @@
+"hello world"
diff --git a/tests/include7.dts b/tests/include7.dts
new file mode 100644
index 0000000..ab2c948
--- /dev/null
+++ b/tests/include7.dts
@@ -0,0 +1,14 @@
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
diff --git a/tests/include8.dts b/tests/include8.dts
new file mode 100644
index 0000000..7532ef5
--- /dev/null
+++ b/tests/include8.dts
@@ -0,0 +1,2 @@
+subsubnode@0 {
+	reg = <0>;
diff --git a/tests/integer-expressions.c b/tests/integer-expressions.c
new file mode 100644
index 0000000..6f33d81
--- /dev/null
+++ b/tests/integer-expressions.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Testcase for dtc expression support
+ *
+ * Copyright (C) 2008 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static struct test_expr {
+	const char *expr;
+	uint32_t result;
+} expr_table[] = {
+#define TE(expr)	{ #expr, (expr) }
+	TE(0xdeadbeef),
+	TE(-0x21524111),
+	TE(1+1),
+	TE(2*3),
+	TE(4/2),
+	TE(10/3),
+	TE(19%4),
+	TE(1 << 13),
+	TE(0x1000 >> 4),
+	TE(3*2+1), TE(3*(2+1)),
+	TE(1+2*3), TE((1+2)*3),
+	TE(1 < 2), TE(2 < 1), TE(1 < 1),
+	TE(1 <= 2), TE(2 <= 1), TE(1 <= 1),
+	TE(1 > 2), TE(2 > 1), TE(1 > 1),
+	TE(1 >= 2), TE(2 >= 1), TE(1 >= 1),
+	TE(1 == 1), TE(1 == 2),
+	TE(1 != 1), TE(1 != 2),
+	TE(0xabcdabcd & 0xffff0000),
+	TE(0xdead4110 ^ 0xf0f0f0f0),
+	TE(0xabcd0000 | 0x0000abcd),
+	TE(~0x21524110),
+	TE(~~0xdeadbeef),
+	TE(0 && 0), TE(17 && 0), TE(0 && 17), TE(17 && 17),
+	TE(0 || 0), TE(17 || 0), TE(0 || 17), TE(17 || 17),
+	TE(!0), TE(!1), TE(!17), TE(!!0), TE(!!17),
+	TE(0 ? 17 : 39), TE(1 ? 17 : 39), TE(17 ? 0xdeadbeef : 0xabcd1234),
+	TE(11 * 257 * 1321517ULL),
+	TE(123456790 - 4/2 + 17%4),
+};
+
+#define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const fdt32_t *res;
+	int reslen;
+	int i;
+
+	test_init(argc, argv);
+
+	if ((argc == 3) && (strcmp(argv[1], "-g") == 0)) {
+		FILE *f = fopen(argv[2], "w");
+
+		if (!f)
+			FAIL("Couldn't open \"%s\" for output: %s\n",
+			     argv[2], strerror(errno));
+
+		fprintf(f, "/dts-v1/;\n");
+		fprintf(f, "/ {\n");
+		fprintf(f, "\texpressions = <\n");
+		for (i = 0; i < ARRAY_SIZE(expr_table); i++)
+			fprintf(f, "\t\t(%s)\n", expr_table[i].expr);
+		fprintf(f, "\t>;\n");
+		fprintf(f, "};\n");
+		fclose(f);
+	} else {
+		fdt = load_blob_arg(argc, argv);
+
+		res = fdt_getprop(fdt, 0, "expressions", &reslen);
+
+		if (!res)
+			FAIL("Error retrieving expression results: %s\n",
+		     fdt_strerror(reslen));
+
+		if (reslen != (ARRAY_SIZE(expr_table) * sizeof(uint32_t)))
+			FAIL("Unexpected length of results %d instead of %zd\n",
+			     reslen, ARRAY_SIZE(expr_table) * sizeof(uint32_t));
+
+		for (i = 0; i < ARRAY_SIZE(expr_table); i++)
+			if (fdt32_to_cpu(res[i]) != expr_table[i].result)
+				FAIL("Incorrect result for expression \"%s\","
+				     " 0x%x instead of 0x%x\n",
+				     expr_table[i].expr, fdt32_to_cpu(res[i]),
+				     expr_table[i].result);
+	}
+
+	PASS();
+}
diff --git a/tests/label01.dts b/tests/label01.dts
new file mode 100644
index 0000000..a895803
--- /dev/null
+++ b/tests/label01.dts
@@ -0,0 +1,63 @@
+/dts-v1/;
+
+/memreserve/ 0x1000000000000000 0x0000000002000000;
+memrsv2: /memreserve/ 0x2000000000000000 0x0100000000000000;
+/memreserve/ 0x0000000000000000 0x0000000000000014;
+
+/ {
+	model = "MyBoardName";
+	compatible = "MyBoardName", "MyBoardFamilyName";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	cpus {
+		linux,phandle = <0x1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		PowerPC,970@0 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000000>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			linux,boot-cpu;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+		};
+
+		PowerPC,970@1 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000001>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+		};
+
+	};
+
+	node: randomnode {
+		prop: string = str: "foo", str_mid: "stuffstuff\t\t\t\n\n\n" str_end: ;
+		blob = [byte: 0a 0b 0c 0d byte_mid: de ea ad be ef byte_end: ];
+		ref = < cell: &{/memory@0} 0x0 cell_mid: 0xffffffff cell_end: >;
+		mixed = "abc", pre: [1234] post: , gap: < aligned: 0xa 0xb 0xc>;
+		tricky1 = [61 lt1: 62 63 00];
+		subnode: child {
+		};
+		/* subnode_end: is auto-generated by node emit */
+	};
+	/* node_end: is auto-generated by node emit */
+
+	memory@0 {
+		device_type = "memory";
+		memreg: reg = <0x00000000 0x00000000 0x00000000 0x20000000>;
+	};
+
+	chosen {
+		bootargs = "root=/dev/sda2";
+		linux,platform = <0x600>;
+	};
+
+};
+
diff --git a/tests/label_repeated.dts b/tests/label_repeated.dts
new file mode 100644
index 0000000..34225d3
--- /dev/null
+++ b/tests/label_repeated.dts
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+	l0: prop = "foo";
+
+	l1: node {
+	};
+};
+
+/ {
+	l0: prop = "foo";
+
+	l1: node {
+	};
+};
diff --git a/tests/line_directives.dts b/tests/line_directives.dts
new file mode 100644
index 0000000..67b5e08
--- /dev/null
+++ b/tests/line_directives.dts
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/* common format */
+#line 3 "foo.dts"
+/* newer gcc format */
+# 9 "baz.dts" 1
+/* flags are optional */
+# 6 "bar.dts"
+
+/ {
+/*
+ * Make sure optional flags don't consume integer data on next line. The issue
+ * was that the {WS} in the trailing ({WS}+[0-9]+)? could cross the * line-
+ * break, and consume the leading "0" of the hex constant, leaving "x12345678"
+ * to be parsed as a number, which is invalid syntax.
+ */
+	prop1 = <
+# 10 "qux.dts"
+		0x12345678
+	>;
+/*
+ * Check processing of escapes in filenames
+ */
+# 100 "\".dts"
+# 200 "\\.dts"
+};
diff --git a/tests/lorem.txt b/tests/lorem.txt
new file mode 100644
index 0000000..acff698
--- /dev/null
+++ b/tests/lorem.txt
@@ -0,0 +1,35 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris eros
+arcu, egestas non pellentesque non, euismod eu nibh. Proin arcu metus,
+dapibus vitae sodales rhoncus, suscipit vel nulla. Etiam lorem est,
+aliquam ut fringilla sit amet, condimentum et quam. Duis eu arcu odio,
+at pulvinar nisi. Morbi condimentum eros ut turpis rhoncus
+pharetra. Pellentesque habitant morbi tristique senectus et netus et
+malesuada fames ac turpis egestas. Nam et nulla enim. Etiam fringilla
+eleifend neque, at posuere ante lacinia a. Duis orci tortor, dictum ac
+gravida ac, euismod eu leo. Sed eget dolor tortor. Pellentesque
+venenatis, lectus eu vulputate porta, libero ipsum convallis mi, sit
+amet vehicula arcu elit sit amet odio.
+
+Fusce iaculis massa metus, id sagittis diam. Praesent molestie ante
+vel odio tincidunt auctor. Cum sociis natoque penatibus et magnis dis
+parturient montes, nascetur ridiculus mus. Duis rutrum vehicula nisl
+eget condimentum. In in justo nisl. Nullam id arcu at nisl eleifend
+pretium. Nulla interdum ligula id elit mollis dictum a sit amet
+quam. Nullam iaculis laoreet ipsum at tempus. Vestibulum in cursus
+dui. Curabitur porta lectus eget urna bibendum congue eget elementum
+nisi. Proin sit amet lectus ut neque iaculis consectetur eu sit amet
+nibh. Maecenas rhoncus dolor ac nunc gravida blandit. Fusce sem felis,
+aliquam a porttitor a, porta quis odio.
+
+Nunc purus lorem, sollicitudin non ultricies id, porta vitae
+enim. Nulla tristique gravida leo ut suscipit. Phasellus vitae turpis
+libero. Proin ac purus dolor, in suscipit magna. Sed et enim
+arcu. Morbi semper aliquet suscipit. Aenean laoreet condimentum massa,
+eu pharetra magna fermentum ut. Morbi euismod convallis tortor, eget
+fringilla lacus sagittis non. Nullam bibendum posuere feugiat.
+
+In at pulvinar massa. Mauris nunc lectus, mollis et malesuada
+pharetra, cursus sed lacus. Integer dolor urna, interdum a mollis at,
+vestibulum non nisl. Sed in urna tortor. Mauris arcu felis, volutpat
+quis euismod vel, congue sit amet ipsum. Morbi in aliquet purus. Duis
+cras amet.
diff --git a/tests/mangle-layout.c b/tests/mangle-layout.c
new file mode 100644
index 0000000..59b1604
--- /dev/null
+++ b/tests/mangle-layout.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase/tool for rearranging blocks of a dtb
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+struct bufstate {
+	char *buf;
+	int size;
+};
+
+static void expand_buf(struct bufstate *buf, int newsize)
+{
+	buf->buf = realloc(buf->buf, newsize);
+	if (!buf->buf)
+		CONFIG("Allocation failure");
+	buf->size = newsize;
+}
+
+static void new_header(struct bufstate *buf, int version, const void *fdt)
+{
+	int hdrsize = fdt_header_size_(version);
+
+	if ((version != 16) && (version != 17))
+		CONFIG("Bad version %d", version);
+
+	expand_buf(buf, hdrsize);
+	memset(buf->buf, 0, hdrsize);
+
+	fdt_set_magic(buf->buf, FDT_MAGIC);
+	fdt_set_version(buf->buf, version);
+	fdt_set_last_comp_version(buf->buf, 16);
+	fdt_set_boot_cpuid_phys(buf->buf, fdt_boot_cpuid_phys(fdt));
+}
+
+static void add_block(struct bufstate *buf, int version, char block, const void *fdt)
+{
+	int align, size, oldsize;
+	const void *src;
+	int offset;
+
+	switch (block) {
+	case 'm':
+		/* Memory reserve map */
+		align = 8;
+		src = (const char *)fdt + fdt_off_mem_rsvmap(fdt);
+		size = (fdt_num_mem_rsv(fdt) + 1)
+			* sizeof(struct fdt_reserve_entry);
+		break;
+
+	case 't':
+		/* Structure block */
+		align = 4;
+		src = (const char *)fdt + fdt_off_dt_struct(fdt);
+		size = fdt_size_dt_struct(fdt);
+		break;
+
+	case 's':
+		/* Strings block */
+		align = 1;
+		src = (const char *)fdt + fdt_off_dt_strings(fdt);
+		size = fdt_size_dt_strings(fdt);
+		break;
+	default:
+		CONFIG("Bad block '%c'", block);
+	}
+
+	oldsize = buf->size;
+	offset = ALIGN(oldsize, align);
+	expand_buf(buf, offset+size);
+	memset(buf->buf + oldsize, 0, offset - oldsize);
+
+	memcpy(buf->buf + offset, src, size);
+
+	switch (block) {
+	case 'm':
+		fdt_set_off_mem_rsvmap(buf->buf, offset);
+		break;
+
+	case 't':
+		fdt_set_off_dt_struct(buf->buf, offset);
+		if (version >= 17)
+			fdt_set_size_dt_struct(buf->buf, size);
+		break;
+
+	case 's':
+		fdt_set_off_dt_strings(buf->buf, offset);
+		fdt_set_size_dt_strings(buf->buf, size);
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int version;
+	const char *blockorder;
+	struct bufstate buf = {NULL, 0};
+	int err;
+	const char *inname;
+	char outname[PATH_MAX];
+
+	test_init(argc, argv);
+	if (argc != 4)
+		CONFIG("Usage: %s <dtb file> <version> <block order>", argv[0]);
+
+	inname = argv[1];
+	fdt = load_blob(argv[1]);
+	version = atoi(argv[2]);
+	blockorder = argv[3];
+	sprintf(outname, "v%d.%s.%s", version, blockorder, inname);
+
+	if ((version != 16) && (version != 17))
+		CONFIG("Version must be 16 or 17");
+
+	if (fdt_version(fdt) < 17)
+		CONFIG("Input tree must be v17");
+
+	new_header(&buf, version, fdt);
+
+	while (*blockorder) {
+		add_block(&buf, version, *blockorder, fdt);
+		blockorder++;
+	}
+
+	fdt_set_totalsize(buf.buf, buf.size);
+
+	err = fdt_check_header(buf.buf);
+	if (err)
+		FAIL("Output tree fails check: %s", fdt_strerror(err));
+
+	save_blob(outname, buf.buf);
+
+	PASS();
+}
diff --git a/tests/minusone-phandle.dts b/tests/minusone-phandle.dts
new file mode 100644
index 0000000..21d9986
--- /dev/null
+++ b/tests/minusone-phandle.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	node {
+		linux,phandle = <0xffffffff>;
+	};
+};
diff --git a/tests/move_and_save.c b/tests/move_and_save.c
new file mode 100644
index 0000000..a89f8de
--- /dev/null
+++ b/tests/move_and_save.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Basic testcase for read-only access
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt, *fdt1, *fdt2, *fdt3;
+	char *buf;
+	int shuntsize;
+	int bufsize;
+	int err;
+	const char *inname;
+	char outname[PATH_MAX];
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+	inname = argv[1];
+
+	shuntsize = ALIGN(fdt_totalsize(fdt) / 2, sizeof(uint64_t));
+	bufsize = fdt_totalsize(fdt) + shuntsize;
+	buf = xmalloc(bufsize);
+
+	fdt1 = buf;
+	err = fdt_move(fdt, fdt1, bufsize);
+	if (err)
+		FAIL("Failed to move tree into new buffer: %s",
+		     fdt_strerror(err));
+	sprintf(outname, "moved.%s", inname);
+	save_blob(outname, fdt1);
+
+	fdt2 = buf + shuntsize;
+	err = fdt_move(fdt1, fdt2, bufsize-shuntsize);
+	if (err)
+		FAIL("Failed to shunt tree %d bytes: %s",
+		     shuntsize, fdt_strerror(err));
+	sprintf(outname, "shunted.%s", inname);
+	save_blob(outname, fdt2);
+
+	fdt3 = buf;
+	err = fdt_move(fdt2, fdt3, bufsize);
+	if (err)
+		FAIL("Failed to deshunt tree %d bytes: %s",
+		     shuntsize, fdt_strerror(err));
+	sprintf(outname, "deshunted.%s", inname);
+	save_blob(outname, fdt3);
+
+	PASS();
+}
diff --git a/tests/multilabel.dts b/tests/multilabel.dts
new file mode 100644
index 0000000..d80ebe1
--- /dev/null
+++ b/tests/multilabel.dts
@@ -0,0 +1,49 @@
+/dts-v1/;
+
+m1: mq: /memreserve/ 0 0x1000;
+
+/ {
+	p0: pw: prop = "foo";
+
+	rref = <&{/}>;
+
+	/* Explicit phandles */
+	n1: nx: node1 {
+		linux,phandle = <0x2000>;
+		ref = <&{/node2}>; /* reference precedes target */
+		p1: px: lref = <&ny>;
+	};
+	ny: n2: node2 {
+		p2: py: phandle = <0x1>;
+		ref = <&{/node1}>; /* reference after target */
+		lref = <&nx>;
+	};
+
+	/* Implicit phandles */
+	n3: node3 {
+		p3: ref = <&{/node4}>;
+		lref = <&n4>;
+	};
+	n4: node4 {
+		p4: prop;
+	};
+
+	/* Explicit phandle with implicit value */
+	/* This self-reference is the standard way to tag a node as requiring
+	 * a phandle (perhaps for reference by nodes that will be dynamically
+	 * added) without explicitly allocating it a phandle.
+	 * The self-reference requires some special internal handling, though
+	 * so check it actually works */
+	n5: nz: node5 {
+		linux,phandle = <&n5>;
+		phandle = <&nz>;
+		n1 = &n1;
+		n2 = &n2;
+		n3 = &n3;
+	};
+
+	node6 {
+		linux,phandle = <0xfffffffe>;
+		phandle = <0xfffffffe>;
+	};
+};
diff --git a/tests/multilabel_merge.dts b/tests/multilabel_merge.dts
new file mode 100644
index 0000000..a27d856
--- /dev/null
+++ b/tests/multilabel_merge.dts
@@ -0,0 +1,75 @@
+/dts-v1/;
+
+m1: mq: /memreserve/ 0 0x1000;
+
+/ {
+	p0: pw: prop = "foo";
+
+	/* Explicit phandles */
+	n1: node1 {
+		linux,phandle = <0x2000>;
+		ref = <&{/node2}>; /* reference precedes target */
+		p1: lref;
+	};
+	node2 {
+		phandle = <0x1>;
+		ref = <&{/node1}>; /* reference after target */
+		lref = <&nx>;
+	};
+
+	/* Implicit phandles */
+	n3: node3 {
+		p3: ref = <&{/node4}>;
+		lref = <&n4>;
+	};
+	n4: node4 {
+		p4: prop = "foo";
+	};
+
+	/* Explicit phandle with implicit value */
+	/* This self-reference is the standard way to tag a node as requiring
+	 * a phandle (perhaps for reference by nodes that will be dynamically
+	 * added) without explicitly allocating it a phandle.
+	 * The self-reference requires some special internal handling, though
+	 * so check it actually works */
+	n5: nz: node5 {
+		linux,phandle = <&n5>;
+		phandle = <&nz>;
+		n1 = &n1;
+		n2 = &n2;
+		n3 = &n3;
+	};
+
+	node6 {
+		linux,phandle = <0xfffffffe>;
+		phandle = <0xfffffffe>;
+	};
+};
+
+/ {
+	/* Append labels (also changes property content) */
+	nx: node1 {
+		px: lref = <&ny>;
+	};
+
+	/* Add multiple labels */
+	ny: n2: node2 {
+		/* Add a label to a property */
+		p2: py: phandle = <0x1>;
+	};
+
+	/* Reassigning the same label should be a no-op */
+	n3: node3 {
+		p3: ref = <&{/node4}>;
+	};
+
+	/* Redefining a node/property should not remove labels */
+	node4 {
+		prop;
+	};
+
+};
+
+/ {
+	rref = <&{/}>;
+};
diff --git a/tests/node_check_compatible.c b/tests/node_check_compatible.c
new file mode 100644
index 0000000..81efe62
--- /dev/null
+++ b/tests/node_check_compatible.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_node_check_compatible()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_compatible(const void *fdt, const char *path,
+			     const char *compat)
+{
+	int offset, err;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("fdt_path_offset(%s): %s", path, fdt_strerror(offset));
+
+	err = fdt_node_check_compatible(fdt, offset, compat);
+	if (err < 0)
+		FAIL("fdt_node_check_compatible(%s): %s", path,
+		     fdt_strerror(err));
+	if (err != 0)
+		FAIL("%s is not compatible with \"%s\"", path, compat);
+}
+
+static void check_not_compatible(const void *fdt, const char *path,
+				 const char *compat)
+{
+	int offset, err;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("fdt_path_offset(%s): %s", path, fdt_strerror(offset));
+
+	err = fdt_node_check_compatible(fdt, offset, compat);
+	if (err < 0)
+		FAIL("fdt_node_check_compatible(%s): %s", path,
+		     fdt_strerror(err));
+	if (err == 0)
+		FAIL("%s is incorrectly compatible with \"%s\"", path, compat);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_compatible(fdt, "/", "test_tree1");
+	check_compatible(fdt, "/subnode@1/subsubnode", "subsubnode1");
+	check_compatible(fdt, "/subnode@1/subsubnode", "subsubnode");
+	check_not_compatible(fdt, "/subnode@1/subsubnode", "subsubnode2");
+	check_compatible(fdt, "/subnode@2/subsubnode", "subsubnode2");
+	check_compatible(fdt, "/subnode@2/subsubnode", "subsubnode");
+	check_not_compatible(fdt, "/subnode@2/subsubnode", "subsubnode1");
+
+	PASS();
+}
diff --git a/tests/node_offset_by_compatible.c b/tests/node_offset_by_compatible.c
new file mode 100644
index 0000000..a9e6783
--- /dev/null
+++ b/tests/node_offset_by_compatible.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_node_offset_by_compatible()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_search(void *fdt, const char *compat, ...)
+{
+	va_list ap;
+	int offset = -1, target;
+
+	va_start(ap, compat);
+	do {
+		target = va_arg(ap, int);
+		verbose_printf("Searching (target = %d): %d ->",
+			       target, offset);
+		offset = fdt_node_offset_by_compatible(fdt, offset, compat);
+		verbose_printf("%d\n", offset);
+
+		if (offset != target)
+			FAIL("fdt_node_offset_by_compatible(%s) returns %d "
+			     "instead of %d", compat, offset, target);
+	} while (target >= 0);
+
+	va_end(ap);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset;
+	int subsubnode1_offset, subsubnode2_offset;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	subsubnode1_offset = fdt_path_offset(fdt, "/subnode@1/subsubnode");
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode@0");
+
+	if ((subnode1_offset < 0) || (subnode2_offset < 0)
+	    || (subsubnode1_offset < 0) || (subsubnode2_offset < 0))
+		FAIL("Can't find required nodes");
+
+	check_search(fdt, "test_tree1", 0, -FDT_ERR_NOTFOUND);
+	check_search(fdt, "subnode1", subnode1_offset, -FDT_ERR_NOTFOUND);
+	check_search(fdt, "subsubnode1", subsubnode1_offset, -FDT_ERR_NOTFOUND);
+	check_search(fdt, "subsubnode2", subsubnode2_offset, -FDT_ERR_NOTFOUND);
+	/* Eek.. HACK to make this work whatever the order in the
+	 * example tree */
+	if (subsubnode1_offset < subsubnode2_offset)
+		check_search(fdt, "subsubnode", subsubnode1_offset,
+			     subsubnode2_offset, -FDT_ERR_NOTFOUND);
+	else
+		check_search(fdt, "subsubnode", subsubnode2_offset,
+			     subsubnode1_offset, -FDT_ERR_NOTFOUND);
+	check_search(fdt, "nothing-like-this", -FDT_ERR_NOTFOUND);
+
+	PASS();
+}
diff --git a/tests/node_offset_by_phandle.c b/tests/node_offset_by_phandle.c
new file mode 100644
index 0000000..60af78a
--- /dev/null
+++ b/tests/node_offset_by_phandle.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_node_offset_by_phandle()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_search(void *fdt, uint32_t phandle, int target)
+{
+	int offset;
+
+	offset = fdt_node_offset_by_phandle(fdt, phandle);
+
+	if (offset != target)
+		FAIL("fdt_node_offset_by_phandle(0x%x) returns %d "
+		     "instead of %d", phandle, offset, target);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode2_offset, subsubnode2_offset;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode@0");
+
+	if ((subnode2_offset < 0) || (subsubnode2_offset < 0))
+		FAIL("Can't find required nodes");
+
+	check_search(fdt, PHANDLE_1, subnode2_offset);
+	check_search(fdt, PHANDLE_2, subsubnode2_offset);
+	check_search(fdt, ~PHANDLE_1, -FDT_ERR_NOTFOUND);
+	check_search(fdt, 0, -FDT_ERR_BADPHANDLE);
+	check_search(fdt, -1, -FDT_ERR_BADPHANDLE);
+
+	PASS();
+}
diff --git a/tests/node_offset_by_prop_value.c b/tests/node_offset_by_prop_value.c
new file mode 100644
index 0000000..48ab1d9
--- /dev/null
+++ b/tests/node_offset_by_prop_value.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_path_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void vcheck_search(void *fdt, const char *propname, const void *propval,
+		  int proplen, va_list ap)
+{
+	int offset = -1, target;
+
+	do {
+		target = va_arg(ap, int);
+		verbose_printf("Searching (target = %d): %d ->",
+			       target, offset);
+		offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+						       propval, proplen);
+		verbose_printf("%d\n", offset);
+
+		if (offset != target)
+			FAIL("fdt_node_offset_by_prop_value() returns %d "
+			     "instead of %d", offset, target);
+	} while (target >= 0);
+}
+
+static void check_search(void *fdt, const char *propname, const void *propval,
+		  int proplen, ...)
+{
+	va_list ap;
+
+	va_start(ap, proplen);
+	vcheck_search(fdt, propname, propval, proplen, ap);
+	va_end(ap);
+}
+
+static void check_search_str(void *fdt, const char *propname,
+			     const char *propval, ...)
+{
+	va_list ap;
+
+	va_start(ap, propval);
+	vcheck_search(fdt, propname, propval, strlen(propval)+1, ap);
+	va_end(ap);
+}
+
+#define check_search_cell(fdt, propname, propval, ...) \
+	{ \
+		fdt32_t val = cpu_to_fdt32(propval); \
+		check_search((fdt), (propname), &val, sizeof(val), \
+			     ##__VA_ARGS__); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset;
+	int subsubnode1_offset, subsubnode2_offset;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	subsubnode1_offset = fdt_path_offset(fdt, "/subnode@1/subsubnode");
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode@0");
+
+	if ((subnode1_offset < 0) || (subnode2_offset < 0)
+	    || (subsubnode1_offset < 0) || (subsubnode2_offset < 0))
+		FAIL("Can't find required nodes");
+
+	check_search_cell(fdt, "prop-int", TEST_VALUE_1, 0, subnode1_offset,
+			  subsubnode1_offset, -FDT_ERR_NOTFOUND);
+
+	check_search_cell(fdt, "prop-int", TEST_VALUE_2, subnode2_offset,
+			  subsubnode2_offset, -FDT_ERR_NOTFOUND);
+
+	check_search_str(fdt, "prop-str", TEST_STRING_1, 0, -FDT_ERR_NOTFOUND);
+
+	check_search_str(fdt, "prop-str", "no such string", -FDT_ERR_NOTFOUND);
+
+	check_search_cell(fdt, "prop-int", TEST_VALUE_1+1, -FDT_ERR_NOTFOUND);
+
+	check_search(fdt, "no-such-prop", NULL, 0, -FDT_ERR_NOTFOUND);
+
+	PASS();
+}
diff --git a/tests/nonexist-label-ref.dts b/tests/nonexist-label-ref.dts
new file mode 100644
index 0000000..25927a1
--- /dev/null
+++ b/tests/nonexist-label-ref.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	ref = <&label>;
+	badref = <&nosuchlabel>;
+	label: node {
+	};
+};
diff --git a/tests/nonexist-node-ref.dts b/tests/nonexist-node-ref.dts
new file mode 100644
index 0000000..efd4140
--- /dev/null
+++ b/tests/nonexist-node-ref.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	ref = < &{/node} >;
+	badref = < &{/nosuchnode} >;
+	label: node {
+	};
+};
diff --git a/tests/nonexist-node-ref2.dts b/tests/nonexist-node-ref2.dts
new file mode 100644
index 0000000..44b4ebe
--- /dev/null
+++ b/tests/nonexist-node-ref2.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+	label: node {
+	};
+};
+
+/* Try to redefine a node using a non-existent label */
+&nosuchnode {
+};
diff --git a/tests/nop_node.c b/tests/nop_node.c
new file mode 100644
index 0000000..ee972d2
--- /dev/null
+++ b/tests/nop_node.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset, subsubnode2_offset;
+	int err;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset < 0)
+		FAIL("Couldn't find \"/subnode1\": %s",
+		     fdt_strerror(subnode1_offset));
+	check_getprop_cell(fdt, subnode1_offset, "prop-int", TEST_VALUE_1);
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode2\": %s",
+		     fdt_strerror(subnode2_offset));
+	check_getprop_cell(fdt, subnode2_offset, "prop-int", TEST_VALUE_2);
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode@2/subsubnode\": %s",
+		     fdt_strerror(subsubnode2_offset));
+	check_getprop_cell(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2);
+
+	err = fdt_nop_node(fdt, subnode1_offset);
+	if (err)
+		FAIL("fdt_nop_node(subnode1): %s", fdt_strerror(err));
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode1_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode2\": %s",
+		     fdt_strerror(subnode2_offset));
+	check_getprop_cell(fdt, subnode2_offset, "prop-int", TEST_VALUE_2);
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset < 0)
+		FAIL("Couldn't find \"/subnode@2/subsubnode\": %s",
+		     fdt_strerror(subsubnode2_offset));
+	check_getprop_cell(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2);
+
+	err = fdt_nop_node(fdt, subnode2_offset);
+	if (err)
+		FAIL("fdt_nop_node(subnode2): %s", fdt_strerror(err));
+
+	subnode1_offset = fdt_path_offset(fdt, "/subnode@1");
+	if (subnode1_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode1_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subnode2_offset = fdt_path_offset(fdt, "/subnode@2");
+	if (subnode2_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subnode2) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subnode2_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode");
+	if (subsubnode2_offset != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_path_offset(subsubnode2) returned \"%s\" instead of \"%s\"",
+		     fdt_strerror(subsubnode2_offset),
+		     fdt_strerror(-FDT_ERR_NOTFOUND));
+
+	PASS();
+}
diff --git a/tests/nop_property.c b/tests/nop_property.c
new file mode 100644
index 0000000..6593828
--- /dev/null
+++ b/tests/nop_property.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_property()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const uint32_t *intp;
+	const char *strp;
+	int err;
+	int lenerr;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	intp = check_getprop_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+	verbose_printf("int value was 0x%08x\n", *intp);
+
+	err = fdt_nop_property(fdt, 0, "prop-int");
+	if (err)
+		FAIL("Failed to nop \"prop-int\": %s", fdt_strerror(err));
+
+	intp = fdt_getprop(fdt, 0, "prop-int", &lenerr);
+	if (intp)
+		FAIL("prop-int still present after nopping");
+	if (lenerr != -FDT_ERR_NOTFOUND)
+		FAIL("Unexpected error on second getprop: %s", fdt_strerror(err));
+
+	strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1,
+			     TEST_STRING_1);
+	verbose_printf("string value was \"%s\"\n", strp);
+	err = fdt_nop_property(fdt, 0, "prop-str");
+	if (err)
+		FAIL("Failed to nop \"prop-str\": %s", fdt_strerror(err));
+
+	strp = fdt_getprop(fdt, 0, "prop-str", &lenerr);
+	if (strp)
+		FAIL("prop-str still present after nopping");
+	if (lenerr != -FDT_ERR_NOTFOUND)
+		FAIL("Unexpected error on second getprop: %s", fdt_strerror(err));
+
+	PASS();
+}
diff --git a/tests/nopulate.c b/tests/nopulate.c
new file mode 100644
index 0000000..2ae1753
--- /dev/null
+++ b/tests/nopulate.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase/tool for rearranging blocks of a dtb
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int nopulate_struct(char *buf, const char *fdt)
+{
+	int offset, nextoffset = 0;
+	uint32_t tag;
+	char *p;
+
+	p = buf;
+
+	do {
+		offset = nextoffset;
+		tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+		memcpy(p, (const char *)fdt + fdt_off_dt_struct(fdt) + offset,
+		       nextoffset - offset);
+		p += nextoffset - offset;
+
+		*((fdt32_t *)p) = cpu_to_fdt32(FDT_NOP);
+		p += FDT_TAGSIZE;
+
+	} while (tag != FDT_END);
+
+	return p - buf;
+}
+
+int main(int argc, char *argv[])
+{
+	char *fdt, *fdt2, *buf;
+	int newsize, struct_start, struct_end_old, struct_end_new, delta;
+	const char *inname;
+	char outname[PATH_MAX];
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>", argv[0]);
+
+	inname = argv[1];
+	fdt = load_blob(argv[1]);
+	sprintf(outname, "noppy.%s", inname);
+
+	if (fdt_version(fdt) < 17)
+		FAIL("Can't deal with version <17");
+
+	buf = xmalloc(2 * fdt_size_dt_struct(fdt));
+
+	newsize = nopulate_struct(buf, fdt);
+
+	verbose_printf("Nopulated structure block has new size %d\n", newsize);
+
+	/* Replace old strcutre block with the new */
+
+	fdt2 = xmalloc(fdt_totalsize(fdt) + newsize);
+
+	struct_start = fdt_off_dt_struct(fdt);
+	delta = newsize - fdt_size_dt_struct(fdt);
+	struct_end_old = struct_start + fdt_size_dt_struct(fdt);
+	struct_end_new = struct_start + newsize;
+
+	memcpy(fdt2, fdt, struct_start);
+	memcpy(fdt2 + struct_start, buf, newsize);
+	memcpy(fdt2 + struct_end_new, fdt + struct_end_old,
+	       fdt_totalsize(fdt) - struct_end_old);
+
+	fdt_set_totalsize(fdt2, fdt_totalsize(fdt) + delta);
+	fdt_set_size_dt_struct(fdt2, newsize);
+
+	if (fdt_off_mem_rsvmap(fdt) > struct_start)
+		fdt_set_off_mem_rsvmap(fdt2, fdt_off_mem_rsvmap(fdt) + delta);
+	if (fdt_off_dt_strings(fdt) > struct_start)
+		fdt_set_off_dt_strings(fdt2, fdt_off_dt_strings(fdt) + delta);
+
+	save_blob(outname, fdt2);
+
+	PASS();
+}
diff --git a/tests/notfound.c b/tests/notfound.c
new file mode 100644
index 0000000..70acbcd
--- /dev/null
+++ b/tests/notfound.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for behaviour on searching for a non-existent node
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_error(const char *s, int err)
+{
+	if (err != -FDT_ERR_NOTFOUND)
+		FAIL("%s return error %s instead of -FDT_ERR_NOTFOUND", s,
+		     fdt_strerror(err));
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int offset;
+	int subnode1_offset;
+	int lenerr;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	fdt_get_property(fdt, 0, "nonexistant-property", &lenerr);
+	check_error("fdt_get_property(\"nonexistant-property\")", lenerr);
+
+	fdt_getprop(fdt, 0, "nonexistant-property", &lenerr);
+	check_error("fdt_getprop(\"nonexistant-property\"", lenerr);
+
+	subnode1_offset = fdt_subnode_offset(fdt, 0, "subnode@1");
+	if (subnode1_offset < 0)
+		FAIL("Couldn't find subnode1: %s", fdt_strerror(subnode1_offset));
+
+	fdt_getprop(fdt, subnode1_offset, "prop-str", &lenerr);
+	check_error("fdt_getprop(\"prop-str\")", lenerr);
+
+	offset = fdt_subnode_offset(fdt, 0, "nonexistant-subnode");
+	check_error("fdt_subnode_offset(\"nonexistant-subnode\")", offset);
+
+	offset = fdt_subnode_offset(fdt, 0, "subsubnode");
+	check_error("fdt_subnode_offset(\"subsubnode\")", offset);
+
+	offset = fdt_path_offset(fdt, "/nonexistant-subnode");
+	check_error("fdt_path_offset(\"/nonexistant-subnode\")", offset);
+
+	PASS();
+}
diff --git a/tests/nul-in-escape.dts b/tests/nul-in-escape.dts
new file mode 100644
index 0000000..9bed351
--- /dev/null
+++ b/tests/nul-in-escape.dts
Binary files differ
diff --git a/tests/nul-in-line-info1.dts b/tests/nul-in-line-info1.dts
new file mode 100644
index 0000000..ceb7261
--- /dev/null
+++ b/tests/nul-in-line-info1.dts
Binary files differ
diff --git a/tests/nul-in-line-info2.dts b/tests/nul-in-line-info2.dts
new file mode 100644
index 0000000..1157d23
--- /dev/null
+++ b/tests/nul-in-line-info2.dts
@@ -0,0 +1 @@
+# 0 "\0"
diff --git a/tests/obsolete-chosen-interrupt-controller.dts b/tests/obsolete-chosen-interrupt-controller.dts
new file mode 100644
index 0000000..36dd6e8
--- /dev/null
+++ b/tests/obsolete-chosen-interrupt-controller.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	PIC: pic@0 {
+		reg = <0x0 0x10>;
+		interrupt-controller;
+	};
+	chosen {
+		interrupt-controller = <&PIC>;
+	};
+};
diff --git a/tests/omit-no-ref.dts b/tests/omit-no-ref.dts
new file mode 100644
index 0000000..8ace232
--- /dev/null
+++ b/tests/omit-no-ref.dts
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+	test-phandle = <&node3>;
+	test-path = &node4;
+
+	/omit-if-no-ref/ node1: node1 {
+		bar = <0xdeadbeef>;
+	};
+
+	node2: node2 {
+		foo = <0x42>;
+	};
+
+	node3: node3 {
+		test = "test";
+	};
+
+	node4: node4 {
+		test;
+	};
+};
+
+/omit-if-no-ref/ &node2;
+/omit-if-no-ref/ &node3;
+/omit-if-no-ref/ &node4;
diff --git a/tests/open_pack.c b/tests/open_pack.c
new file mode 100644
index 0000000..6ed4df7
--- /dev/null
+++ b/tests/open_pack.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Basic testcase for read-only access
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt, *fdt1;
+	void *buf;
+	int oldsize, bufsize, packsize;
+	int err;
+	const char *inname;
+	char outname[PATH_MAX];
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+	inname = argv[1];
+
+	oldsize = fdt_totalsize(fdt);
+
+	bufsize = oldsize * 2;
+
+	buf = xmalloc(bufsize);
+	/* don't leak uninitialized memory into our output */
+	memset(buf, 0, bufsize);
+
+	fdt1 = buf;
+	err = fdt_open_into(fdt, fdt1, bufsize);
+	if (err)
+		FAIL("fdt_open_into(): %s", fdt_strerror(err));
+	sprintf(outname, "opened.%s", inname);
+	save_blob(outname, fdt1);
+
+	err = fdt_pack(fdt1);
+	if (err)
+		FAIL("fdt_pack(): %s", fdt_strerror(err));
+	sprintf(outname, "repacked.%s", inname);
+	save_blob(outname, fdt1);
+
+	packsize = fdt_totalsize(fdt1);
+
+	verbose_printf("oldsize = %d, bufsize = %d, packsize = %d\n",
+		       oldsize, bufsize, packsize);
+	PASS();
+}
diff --git a/tests/overlay.c b/tests/overlay.c
new file mode 100644
index 0000000..91afa72
--- /dev/null
+++ b/tests/overlay.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for DT overlays()
+ * Copyright (C) 2016 Free Electrons
+ * Copyright (C) 2016 NextThing Co.
+ */
+
+#include <stdio.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+
+#define CHECK(code) \
+	{ \
+		int err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+/* 4k ought to be enough for anybody */
+#define FDT_COPY_SIZE	(4 * 1024)
+
+static int fdt_getprop_u32_by_poffset(void *fdt, const char *path,
+				      const char *name, int poffset,
+				      unsigned long *out)
+{
+	const fdt32_t *val;
+	int node_off;
+	int len;
+
+	node_off = fdt_path_offset(fdt, path);
+	if (node_off < 0)
+		return node_off;
+
+	val = fdt_getprop(fdt, node_off, name, &len);
+	if (!val || (len < (sizeof(uint32_t) * (poffset + 1))))
+		return -FDT_ERR_NOTFOUND;
+
+	*out = fdt32_to_cpu(*(val + poffset));
+
+	return 0;
+}
+
+static int check_getprop_string_by_name(void *fdt, const char *path,
+					const char *name, const char *val)
+{
+	int node_off;
+
+	node_off = fdt_path_offset(fdt, path);
+	if (node_off < 0)
+		return node_off;
+
+	check_getprop_string(fdt, node_off, name, val);
+
+	return 0;
+}
+
+static int check_getprop_u32_by_name(void *fdt, const char *path,
+				     const char *name, uint32_t val)
+{
+	int node_off;
+
+	node_off = fdt_path_offset(fdt, path);
+	CHECK(node_off < 0);
+
+	check_getprop_cell(fdt, node_off, name, val);
+
+	return 0;
+}
+
+static int check_getprop_null_by_name(void *fdt, const char *path,
+				      const char *name)
+{
+	int node_off;
+
+	node_off = fdt_path_offset(fdt, path);
+	CHECK(node_off < 0);
+
+	check_property(fdt, node_off, name, 0, NULL);
+
+	return 0;
+}
+
+static int fdt_overlay_change_int_property(void *fdt)
+{
+	return check_getprop_u32_by_name(fdt, "/test-node", "test-int-property",
+					 43);
+}
+
+static int fdt_overlay_change_str_property(void *fdt)
+{
+	return check_getprop_string_by_name(fdt, "/test-node",
+					    "test-str-property", "foobar");
+}
+
+static int fdt_overlay_add_str_property(void *fdt)
+{
+	return check_getprop_string_by_name(fdt, "/test-node",
+					    "test-str-property-2", "foobar2");
+}
+
+static int fdt_overlay_add_node(void *fdt)
+{
+	return check_getprop_null_by_name(fdt, "/test-node/new-node",
+					  "new-property");
+}
+
+static int fdt_overlay_add_subnode_property(void *fdt)
+{
+	check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
+				   "sub-test-property");
+	check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
+				   "new-sub-test-property");
+
+	return 0;
+}
+
+static int fdt_overlay_local_phandle(void *fdt)
+{
+	uint32_t local_phandle;
+	unsigned long val = 0;
+	int off;
+
+	off = fdt_path_offset(fdt, "/test-node/new-local-node");
+	CHECK(off < 0);
+
+	local_phandle = fdt_get_phandle(fdt, off);
+	CHECK(!local_phandle);
+
+	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
+					 "test-several-phandle",
+					 0, &val));
+	CHECK(val != local_phandle);
+
+	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
+					 "test-several-phandle",
+					 1, &val));
+	CHECK(val != local_phandle);
+
+	return 0;
+}
+
+static int fdt_overlay_local_phandles(void *fdt)
+{
+	uint32_t local_phandle, test_phandle;
+	unsigned long val = 0;
+	int off;
+
+	off = fdt_path_offset(fdt, "/test-node/new-local-node");
+	CHECK(off < 0);
+
+	local_phandle = fdt_get_phandle(fdt, off);
+	CHECK(!local_phandle);
+
+	off = fdt_path_offset(fdt, "/test-node");
+	CHECK(off < 0);
+
+	test_phandle = fdt_get_phandle(fdt, off);
+	CHECK(!test_phandle);
+
+	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
+					 "test-phandle", 0, &val));
+	CHECK(test_phandle != val);
+
+	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
+					 "test-phandle", 1, &val));
+	CHECK(local_phandle != val);
+
+	return 0;
+}
+
+static void *open_dt(char *path)
+{
+	void *dt, *copy;
+
+	dt = load_blob(path);
+	copy = xmalloc(FDT_COPY_SIZE);
+
+	/*
+	 * Resize our DTs to 4k so that we have room to operate on
+	 */
+	CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE));
+
+	return copy;
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt_base, *fdt_overlay;
+
+	test_init(argc, argv);
+	if (argc != 3)
+		CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]);
+
+	fdt_base = open_dt(argv[1]);
+	fdt_overlay = open_dt(argv[2]);
+
+	/* Apply the overlay */
+	CHECK(fdt_overlay_apply(fdt_base, fdt_overlay));
+
+	fdt_overlay_change_int_property(fdt_base);
+	fdt_overlay_change_str_property(fdt_base);
+	fdt_overlay_add_str_property(fdt_base);
+	fdt_overlay_add_node(fdt_base);
+	fdt_overlay_add_subnode_property(fdt_base);
+
+	/*
+	 * If the base tree has a __symbols__ node, do the tests that
+	 * are only successful with a proper phandle support, and thus
+	 * dtc -@
+	 */
+	if (fdt_path_offset(fdt_base, "/__symbols__") >= 0) {
+		fdt_overlay_local_phandle(fdt_base);
+		fdt_overlay_local_phandles(fdt_base);
+	}
+
+	PASS();
+}
diff --git a/tests/overlay_bad_fixup.c b/tests/overlay_bad_fixup.c
new file mode 100644
index 0000000..029bc79
--- /dev/null
+++ b/tests/overlay_bad_fixup.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for DT overlays()
+ * Copyright (C) 2016 Free Electrons
+ * Copyright (C) 2016 NextThing Co.
+ */
+
+#include <stdio.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+
+#define CHECK(code, expected)					\
+	{							\
+		err = (code);					\
+		if (err != expected)				\
+			FAIL(#code ": %s", fdt_strerror(err));	\
+	}
+
+/* 4k ought to be enough for anybody */
+#define FDT_COPY_SIZE	(4 * 1024)
+
+static void *open_dt(char *path)
+{
+	void *dt, *copy;
+	int err;
+
+	dt = load_blob(path);
+	copy = xmalloc(FDT_COPY_SIZE);
+
+	/*
+	 * Resize our DTs to 4k so that we have room to operate on
+	 */
+	CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE), 0);
+
+	return copy;
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt_base, *fdt_overlay;
+	int err;
+
+	test_init(argc, argv);
+	if (argc != 3)
+		CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]);
+
+	fdt_base = open_dt(argv[1]);
+	fdt_overlay = open_dt(argv[2]);
+
+	/* Apply the overlay */
+	CHECK(fdt_overlay_apply(fdt_base, fdt_overlay), -FDT_ERR_BADOVERLAY);
+
+	PASS();
+}
diff --git a/tests/overlay_bad_fixup_bad_index.dts b/tests/overlay_bad_fixup_bad_index.dts
new file mode 100644
index 0000000..b5cf131
--- /dev/null
+++ b/tests/overlay_bad_fixup_bad_index.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0:target:ab";
+	};
+};
diff --git a/tests/overlay_bad_fixup_base.dtsi b/tests/overlay_bad_fixup_base.dtsi
new file mode 100644
index 0000000..216bcab
--- /dev/null
+++ b/tests/overlay_bad_fixup_base.dtsi
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+
+/ {
+	fragment@0 {
+		target = <0xffffffff>;
+
+		__overlay__ {
+			test-property;
+		};
+	};
+};
diff --git a/tests/overlay_bad_fixup_empty.dts b/tests/overlay_bad_fixup_empty.dts
new file mode 100644
index 0000000..e111db4
--- /dev/null
+++ b/tests/overlay_bad_fixup_empty.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "";
+	};
+};
diff --git a/tests/overlay_bad_fixup_empty_index.dts b/tests/overlay_bad_fixup_empty_index.dts
new file mode 100644
index 0000000..9e12e21
--- /dev/null
+++ b/tests/overlay_bad_fixup_empty_index.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0:target:";
+	};
+};
diff --git a/tests/overlay_bad_fixup_index_trailing.dts b/tests/overlay_bad_fixup_index_trailing.dts
new file mode 100644
index 0000000..f586bef
--- /dev/null
+++ b/tests/overlay_bad_fixup_index_trailing.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0:target:0a";
+	};
+};
diff --git a/tests/overlay_bad_fixup_path_empty_prop.dts b/tests/overlay_bad_fixup_path_empty_prop.dts
new file mode 100644
index 0000000..608b5f9
--- /dev/null
+++ b/tests/overlay_bad_fixup_path_empty_prop.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0::";
+	};
+};
diff --git a/tests/overlay_bad_fixup_path_only.dts b/tests/overlay_bad_fixup_path_only.dts
new file mode 100644
index 0000000..2485dd9
--- /dev/null
+++ b/tests/overlay_bad_fixup_path_only.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0";
+	};
+};
diff --git a/tests/overlay_bad_fixup_path_only_sep.dts b/tests/overlay_bad_fixup_path_only_sep.dts
new file mode 100644
index 0000000..3cbf6c4
--- /dev/null
+++ b/tests/overlay_bad_fixup_path_only_sep.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0:";
+	};
+};
diff --git a/tests/overlay_bad_fixup_path_prop.dts b/tests/overlay_bad_fixup_path_prop.dts
new file mode 100644
index 0000000..ca79b52
--- /dev/null
+++ b/tests/overlay_bad_fixup_path_prop.dts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/include/ "overlay_bad_fixup_base.dtsi"
+
+/ {
+	__fixups__ {
+		test = "/fragment@0:target";
+	};
+};
diff --git a/tests/overlay_base.dts b/tests/overlay_base.dts
new file mode 100644
index 0000000..a5e55b2
--- /dev/null
+++ b/tests/overlay_base.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+
+/ {
+	test: test-node {
+		test-int-property = <42>;
+		test-str-property = "foo";
+
+		subtest: sub-test-node {
+			sub-test-property;
+
+			subtest_with_long_path: sub-test-node-with-very-long-target-path {
+				long-test-path-property;
+			};
+		};
+	};
+};
+
+
diff --git a/tests/overlay_base_manual_symbols.dts b/tests/overlay_base_manual_symbols.dts
new file mode 100644
index 0000000..7e4d17d
--- /dev/null
+++ b/tests/overlay_base_manual_symbols.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+
+/ {
+	test: test-node {
+		phandle = <&test>; /* Force phandle generation */
+		test-int-property = <42>;
+		test-str-property = "foo";
+
+		subtest: sub-test-node {
+			sub-test-property;
+		};
+	};
+	__symbols__ {
+		test = &test;
+	};
+};
+
+
diff --git a/tests/overlay_overlay.dts b/tests/overlay_overlay.dts
new file mode 100644
index 0000000..c4ef1d4
--- /dev/null
+++ b/tests/overlay_overlay.dts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+/plugin/;
+
+/* Test that we can change an int by another */
+&test {
+	test-int-property = <43>;
+};
+
+/* Test that we can replace a string by a longer one */
+&test {
+	test-str-property = "foobar";
+};
+
+/* Test that we add a new property */
+&test {
+	test-str-property-2 = "foobar2";
+};
+
+/* Test that we add a new node (by phandle) */
+&test {
+	new-node {
+		new-property;
+	};
+};
+
+&test {
+	local: new-local-node {
+		new-property;
+	};
+};
+
+&test {
+	test-phandle = <&test>, <&local>;
+};
+
+&test {
+	test-several-phandle = <&local>, <&local>;
+};
+
+&test {
+	sub-test-node {
+		new-sub-test-property;
+	};
+};
diff --git a/tests/overlay_overlay_bypath.dts b/tests/overlay_overlay_bypath.dts
new file mode 100644
index 0000000..f23e7b6
--- /dev/null
+++ b/tests/overlay_overlay_bypath.dts
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+/plugin/;
+
+/* Test that we can change an int by another */
+&{/test-node} {
+	test-int-property = <43>;
+};
+
+/* Test that we can replace a string by a longer one */
+&{/test-node} {
+	test-str-property = "foobar";
+};
+
+/* Test that we add a new property */
+&{/test-node} {
+	test-str-property-2 = "foobar2";
+};
+
+/* Test that we add a new node (by phandle) */
+&{/test-node} {
+	new-node {
+		new-property;
+	};
+};
+
+&{/} {
+	local: new-local-node {
+		new-property;
+	};
+};
+
+&{/} {
+	test-several-phandle = <&local>, <&local>;
+};
+
+&{/test-node} {
+	sub-test-node {
+		new-sub-test-property;
+	};
+};
diff --git a/tests/overlay_overlay_local_merge.dts b/tests/overlay_overlay_local_merge.dts
new file mode 100644
index 0000000..3ee622d
--- /dev/null
+++ b/tests/overlay_overlay_local_merge.dts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+/plugin/;
+
+&test {
+	parent: new-node {
+		parent-property;
+	};
+};
+
+&parent {
+	new-merged-property;
+	new-merged-node {
+		new-property;
+	};
+};
+
+&{/} {
+	new-root-node {
+		new-root-node-property;
+	};
+};
diff --git a/tests/overlay_overlay_long_path.dts b/tests/overlay_overlay_long_path.dts
new file mode 100644
index 0000000..4f0d40b
--- /dev/null
+++ b/tests/overlay_overlay_long_path.dts
@@ -0,0 +1,32 @@
+/dts-v1/;
+/plugin/;
+
+&subtest_with_long_path {
+	lpath_0: test-0 {
+		prop = "lpath";
+	};
+
+	lpath_1: test-1 {
+		prop = "lpath";
+	};
+
+	lpath_2: test-2 {
+		prop = "lpath";
+	};
+
+	lpath_3: test-3 {
+		prop = "lpath";
+	};
+
+	lpath_4: test-4 {
+		prop = "lpath";
+	};
+
+	lpath_5: test-5 {
+		prop = "lpath";
+	};
+
+	lpath_6: test-6 {
+		prop = "lpath";
+	};
+};
diff --git a/tests/overlay_overlay_manual_fixups.dts b/tests/overlay_overlay_manual_fixups.dts
new file mode 100644
index 0000000..a5715b6
--- /dev/null
+++ b/tests/overlay_overlay_manual_fixups.dts
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+
+/* Note no /plugin/ tag - we're manually generating the metadata for
+   testing purposes */
+
+/ {
+	/* Test that we can change an int by another */
+	fragment@0 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			test-int-property = <43>;
+		};
+	};
+
+	/* Test that we can replace a string by a longer one */
+	fragment@1 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			test-str-property = "foobar";
+		};
+	};
+
+	/* Test that we add a new property */
+	fragment@2 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			test-str-property-2 = "foobar2";
+		};
+	};
+
+	/* Test that we add a new node (by phandle) */
+	fragment@3 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			new-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			local: new-local-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			test-phandle = <0xffffffff /*&test*/>, <&local>;
+		};
+	};
+
+	fragment@6 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			test-several-phandle = <&local>, <&local>;
+		};
+	};
+
+	fragment@7 {
+		target = <0xffffffff /*&test*/>;
+
+		__overlay__ {
+			sub-test-node {
+				new-sub-test-property;
+			};
+		};
+	};
+
+	__fixups__ {
+		test = "/fragment@0:target:0",
+		       "/fragment@1:target:0",
+		       "/fragment@2:target:0",
+		       "/fragment@3:target:0",
+		       "/fragment@4:target:0",
+		       "/fragment@5:target:0",
+		       "/fragment@5/__overlay__:test-phandle:0",
+		       "/fragment@6:target:0",
+		       "/fragment@7:target:0";
+	};
+	__local_fixups__ {
+		fragment@5 {
+			__overlay__ {
+				test-phandle = <4>;
+			};
+		};
+		fragment@6 {
+			__overlay__ {
+				test-several-phandle = <0 4>;
+			};
+		};
+	};
+};
diff --git a/tests/overlay_overlay_no_fixups.dts b/tests/overlay_overlay_no_fixups.dts
new file mode 100644
index 0000000..e8d0f96
--- /dev/null
+++ b/tests/overlay_overlay_no_fixups.dts
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+
+/ {
+	fragment@0 {
+		target-path = "/test-node";
+
+		__overlay__ {
+			test-int-property = <43>;
+		};
+	};
+
+	/* Test that we can replace a string by a longer one */
+	fragment@1 {
+		target-path = "/test-node";
+
+		__overlay__ {
+			test-str-property = "foobar";
+		};
+	};
+
+	/* Test that we add a new property */
+	fragment@2 {
+		target-path = "/test-node";
+
+		__overlay__ {
+			test-str-property-2 = "foobar2";
+		};
+	};
+
+	fragment@3 {
+		target-path = "/test-node";
+
+		__overlay__ {
+			new-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@4 {
+		target-path = "/";
+
+		__overlay__ {
+			local: new-local-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@5 {
+		target-path = "/";
+
+		__overlay__ {
+			test-several-phandle = <&local>, <&local>;
+		};
+	};
+
+	fragment@6 {
+		target-path = "/test-node";
+
+		__overlay__ {
+			sub-test-node {
+				new-sub-test-property;
+			};
+		};
+	};
+
+	__local_fixups__ {
+		fragment@5 {
+			__overlay__ {
+				test-several-phandle = <0 4>;
+			};
+		};
+	};
+};
diff --git a/tests/overlay_overlay_nosugar.dts b/tests/overlay_overlay_nosugar.dts
new file mode 100644
index 0000000..b6d841b
--- /dev/null
+++ b/tests/overlay_overlay_nosugar.dts
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	/* Test that we can change an int by another */
+	fragment@0 {
+		target = <&test>;
+
+		__overlay__ {
+			test-int-property = <43>;
+		};
+	};
+
+	/* Test that we can replace a string by a longer one */
+	fragment@1 {
+		target = <&test>;
+
+		__overlay__ {
+			test-str-property = "foobar";
+		};
+	};
+
+	/* Test that we add a new property */
+	fragment@2 {
+		target = <&test>;
+
+		__overlay__ {
+			test-str-property-2 = "foobar2";
+		};
+	};
+
+	/* Test that we add a new node (by phandle) */
+	fragment@3 {
+		target = <&test>;
+
+		__overlay__ {
+			new-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&test>;
+
+		__overlay__ {
+			local: new-local-node {
+				new-property;
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&test>;
+
+		__overlay__ {
+			test-phandle = <&test>, <&local>;
+		};
+	};
+
+	fragment@7 {
+		target = <&test>;
+
+		__overlay__ {
+			test-several-phandle = <&local>, <&local>;
+		};
+	};
+
+	fragment@8 {
+		target = <&test>;
+
+		__overlay__ {
+			sub-test-node {
+				new-sub-test-property;
+			};
+		};
+	};
+};
diff --git a/tests/overlay_overlay_simple.dts b/tests/overlay_overlay_simple.dts
new file mode 100644
index 0000000..8657e1e
--- /dev/null
+++ b/tests/overlay_overlay_simple.dts
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2016 Konsulko Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+/plugin/;
+
+&test {
+	test-int-property = <43>;
+};
diff --git a/tests/parent_offset.c b/tests/parent_offset.c
new file mode 100644
index 0000000..a935a53
--- /dev/null
+++ b/tests/parent_offset.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_parent_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int path_parent_len(const char *path)
+{
+	const char *p = strrchr(path, '/');
+
+	if (!p)
+		TEST_BUG();
+	if (p == path)
+		return 1;
+	else
+		return p - path;
+}
+
+static void check_path(struct fdt_header *fdt, const char *path)
+{
+	char *parentpath;
+	int nodeoffset, parentoffset, parentpathoffset, pathparentlen;
+
+	pathparentlen = path_parent_len(path);
+	parentpath = alloca(pathparentlen + 1);
+	strncpy(parentpath, path, pathparentlen);
+	parentpath[pathparentlen] = '\0';
+
+	verbose_printf("Path: \"%s\"\tParent: \"%s\"\n", path, parentpath);
+
+	nodeoffset = fdt_path_offset(fdt, path);
+	if (nodeoffset < 0)
+		FAIL("fdt_path_offset(%s): %s", path, fdt_strerror(nodeoffset));
+
+	parentpathoffset = fdt_path_offset(fdt, parentpath);
+	if (parentpathoffset < 0)
+		FAIL("fdt_path_offset(%s): %s", parentpath,
+		     fdt_strerror(parentpathoffset));
+
+	parentoffset = fdt_parent_offset(fdt, nodeoffset);
+	if (parentoffset < 0)
+		FAIL("fdt_parent_offset(): %s", fdt_strerror(parentoffset));
+
+	if (parentoffset != parentpathoffset)
+		FAIL("fdt_parent_offset() returns %d instead of %d",
+		     parentoffset, parentpathoffset);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int err;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_path(fdt, "/subnode@1");
+	check_path(fdt, "/subnode@2");
+	check_path(fdt, "/subnode@1/subsubnode");
+	check_path(fdt, "/subnode@2/subsubnode@0");
+	err = fdt_parent_offset(fdt, 0);
+	if (err != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_parent_offset(/) returns %d instead of "
+		     "-FDT_ERR_NOTFOUND", err);
+
+	PASS();
+}
diff --git a/tests/path-references.c b/tests/path-references.c
new file mode 100644
index 0000000..4db61a5
--- /dev/null
+++ b/tests/path-references.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for string references in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_ref(const void *fdt, int node, const char *checkpath)
+{
+	const char *p;
+	int len;
+
+	p = fdt_getprop(fdt, node, "ref", &len);
+	if (!p)
+		FAIL("fdt_getprop(%d, \"ref\"): %s", node, fdt_strerror(len));
+	if (!streq(p, checkpath))
+		FAIL("'ref' in node at %d has value \"%s\" instead of \"%s\"",
+		     node, p, checkpath);
+
+	p = fdt_getprop(fdt, node, "lref", &len);
+	if (!p)
+		FAIL("fdt_getprop(%d, \"lref\"): %s", node, fdt_strerror(len));
+	if (!streq(p, checkpath))
+		FAIL("'lref' in node at %d has value \"%s\" instead of \"%s\"",
+		     node, p, checkpath);
+}
+
+static void check_rref(const void *fdt)
+{
+	const char *p;
+	int len;
+
+	/* Check reference to root node */
+	p = fdt_getprop(fdt, 0, "rref", &len);
+	if (!p)
+		FAIL("fdt_getprop(0, \"rref\"): %s", fdt_strerror(len));
+	if (!streq(p, "/"))
+		FAIL("'rref' in root node has value \"%s\" instead of \"/\"",
+		     p);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const char *p;
+	int len, multilen;
+	int n1, n2, n3, n4;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	n1 = fdt_path_offset(fdt, "/node1");
+	if (n1 < 0)
+		FAIL("fdt_path_offset(/node1): %s", fdt_strerror(n1));
+	n2 = fdt_path_offset(fdt, "/node2");
+	if (n2 < 0)
+		FAIL("fdt_path_offset(/node2): %s", fdt_strerror(n2));
+
+	check_ref(fdt, n1, "/node2");
+	check_ref(fdt, n2, "/node1");
+
+	/* Check multiple reference */
+	multilen = strlen("/node1") + strlen("/node2") + 2;
+	p = fdt_getprop(fdt, 0, "multiref", &len);
+	if (!p)
+		FAIL("fdt_getprop(0, \"multiref\"): %s", fdt_strerror(len));
+	if (len != multilen)
+		FAIL("multiref has wrong length, %d instead of %d",
+		     len, multilen);
+	if ((!streq(p, "/node1") || !streq(p + strlen("/node1") + 1, "/node2")))
+		FAIL("multiref has wrong value");
+
+	/* Check reference to nested nodes with common prefix */
+	n3 = fdt_path_offset(fdt, "/foo/baz");
+	if (n3 < 0)
+		FAIL("fdt_path_offset(/foo/baz): %s", fdt_strerror(n3));
+	n4 = fdt_path_offset(fdt, "/foobar/baz");
+	if (n4 < 0)
+		FAIL("fdt_path_offset(/foobar/baz): %s", fdt_strerror(n4));
+	check_ref(fdt, n3, "/foobar/baz");
+	check_ref(fdt, n4, "/foo/baz");
+
+	check_rref(fdt);
+
+	PASS();
+}
diff --git a/tests/path-references.dts b/tests/path-references.dts
new file mode 100644
index 0000000..1fb7d70
--- /dev/null
+++ b/tests/path-references.dts
@@ -0,0 +1,28 @@
+/dts-v1/;
+
+/ {
+	rref = &{/};
+	/* Check multiple references case */
+	multiref = &n1 , &n2;
+	n1: node1 {
+		ref = &{/node2}; /* reference precedes target */
+		lref = &n2;
+	};
+	n2: node2 {
+		ref = &{/node1}; /* reference after target */
+		lref = &n1;
+	};
+	/* Check references to nested nodes with common prefix */
+	foobar {
+		n3: baz {
+			ref = &{/foo/baz};
+			lref = start: &n4 end:;
+		};
+	};
+	foo {
+		n4: baz {
+			ref = &{/foobar/baz};
+			lref = &n3;
+		};
+	};
+};
diff --git a/tests/path_offset.c b/tests/path_offset.c
new file mode 100644
index 0000000..82527d4
--- /dev/null
+++ b/tests/path_offset.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_path_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int check_subnode(void *fdt, int parent, const char *name)
+{
+	int offset;
+	const struct fdt_node_header *nh;
+	uint32_t tag;
+
+	verbose_printf("Checking subnode \"%s\" of %d...", name, parent);
+	offset = fdt_subnode_offset(fdt, parent, name);
+	verbose_printf("offset %d...", offset);
+	if (offset < 0)
+		FAIL("fdt_subnode_offset(\"%s\"): %s", name, fdt_strerror(offset));
+	nh = fdt_offset_ptr(fdt, offset, sizeof(*nh));
+	verbose_printf("pointer %p\n", nh);
+	if (! nh)
+		FAIL("NULL retrieving subnode \"%s\"", name);
+
+	tag = fdt32_to_cpu(nh->tag);
+
+	if (tag != FDT_BEGIN_NODE)
+		FAIL("Incorrect tag 0x%08x on property \"%s\"", tag, name);
+	if (!nodename_eq(nh->name, name))
+		FAIL("Subnode name mismatch \"%s\" instead of \"%s\"",
+		     nh->name, name);
+
+	return offset;
+}
+
+static void check_path_offset(void *fdt, char *path, int offset)
+{
+	int rc;
+
+	verbose_printf("Checking offset of \"%s\" is %d...\n", path, offset);
+
+	rc = fdt_path_offset(fdt, path);
+	if (rc < 0)
+		FAIL("fdt_path_offset(\"%s\") failed: %s",
+		     path,  fdt_strerror(rc));
+	if (rc != offset)
+		FAIL("fdt_path_offset(\"%s\") returned incorrect offset"
+		     " %d instead of %d", path, rc, offset);
+}
+
+static void check_path_offset_namelen(void *fdt, char *path, int namelen,
+				      int offset)
+{
+	int rc;
+
+	verbose_printf("Checking offset of \"%s\" [first %d characters]"
+		       " is %d...\n", path, namelen, offset);
+
+	rc = fdt_path_offset_namelen(fdt, path, namelen);
+	if (rc == offset)
+		return;
+
+	if (rc < 0)
+		FAIL("fdt_path_offset_namelen(\"%s\", %d) failed: %s",
+		     path, namelen, fdt_strerror(rc));
+	else
+		FAIL("fdt_path_offset_namelen(\"%s\", %d) returned incorrect"
+		     " offset %d instead of %d", path, namelen, rc, offset);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset;
+	int subsubnode1_offset, subsubnode2_offset, subsubnode2_offset2;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_path_offset(fdt, "/", 0);
+
+	subnode1_offset = check_subnode(fdt, 0, "subnode@1");
+	subnode2_offset = check_subnode(fdt, 0, "subnode@2");
+
+	check_path_offset(fdt, "/subnode@1", subnode1_offset);
+	check_path_offset(fdt, "/subnode@2", subnode2_offset);
+
+	subsubnode1_offset = check_subnode(fdt, subnode1_offset, "subsubnode");
+	subsubnode2_offset = check_subnode(fdt, subnode2_offset, "subsubnode@0");
+	subsubnode2_offset2 = check_subnode(fdt, subnode2_offset, "subsubnode");
+
+	check_path_offset(fdt, "/subnode@1/subsubnode", subsubnode1_offset);
+	check_path_offset(fdt, "/subnode@2/subsubnode@0", subsubnode2_offset);
+	check_path_offset(fdt, "/subnode@2/subsubnode", subsubnode2_offset2);
+
+	/* Test paths with extraneous separators */
+	check_path_offset(fdt, "//", 0);
+	check_path_offset(fdt, "///", 0);
+	check_path_offset(fdt, "//subnode@1", subnode1_offset);
+	check_path_offset(fdt, "/subnode@1/", subnode1_offset);
+	check_path_offset(fdt, "//subnode@1///", subnode1_offset);
+	check_path_offset(fdt, "/subnode@2////subsubnode", subsubnode2_offset2);
+
+	/* Test fdt_path_offset_namelen() */
+	check_path_offset_namelen(fdt, "/subnode@1", 1, 0);
+	check_path_offset_namelen(fdt, "/subnode@1/subsubnode", 10, subnode1_offset);
+	check_path_offset_namelen(fdt, "/subnode@1/subsubnode", 11, subnode1_offset);
+	check_path_offset_namelen(fdt, "/subnode@2TRAILINGGARBAGE", 10, subnode2_offset);
+	check_path_offset_namelen(fdt, "/subnode@2TRAILINGGARBAGE", 11, -FDT_ERR_NOTFOUND);
+	check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 23, subsubnode2_offset2);
+	check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 22, -FDT_ERR_NOTFOUND);
+	check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 24, subsubnode2_offset2);
+	check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 25, -FDT_ERR_NOTFOUND);
+
+	PASS();
+}
diff --git a/tests/path_offset_aliases.c b/tests/path_offset_aliases.c
new file mode 100644
index 0000000..0112e72
--- /dev/null
+++ b/tests/path_offset_aliases.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_path_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2008 Kumar Gala, Freescale Semiconductor, Inc.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_alias(void *fdt, const char *full_path, const char *alias_path)
+{
+	int offset, offset_a;
+
+	offset = fdt_path_offset(fdt, full_path);
+	offset_a = fdt_path_offset(fdt, alias_path);
+
+	if (offset != offset_a)
+		FAIL("Mismatch between %s path_offset (%d) and %s path_offset alias (%d)",
+		     full_path, offset, alias_path, offset_a);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_alias(fdt, "/subnode@1", "s1");
+	check_alias(fdt, "/subnode@1/subsubnode", "ss1");
+	check_alias(fdt, "/subnode@1/subsubnode", "s1/subsubnode");
+	check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1");
+	check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "ss1/subsubsubnode");
+	check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "s1/subsubnode/subsubsubnode");
+
+	PASS();
+}
diff --git a/tests/pci-bridge-bad1.dts b/tests/pci-bridge-bad1.dts
new file mode 100644
index 0000000..17aac04
--- /dev/null
+++ b/tests/pci-bridge-bad1.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	compatible = "example,pci-bridge-ok";
+	#address-cells = < 2 >;
+	#size-cells = < 2 >;
+	abadname@0 {
+		device_type = "pci";
+		compatible = "example,pci-bridge";
+		#address-cells = < 3 >;
+		#size-cells = < 2 >;
+		reg = <0 0 0 0x1000>;
+		bus-range = <0 0xff>;
+		ranges = <0 0 0 0 0 0 0x10000>;
+	};
+};
diff --git a/tests/pci-bridge-bad2.dts b/tests/pci-bridge-bad2.dts
new file mode 100644
index 0000000..a7e5c05
--- /dev/null
+++ b/tests/pci-bridge-bad2.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	compatible = "example,pci-bridge-ok";
+	#address-cells = < 2 >;
+	#size-cells = < 2 >;
+	p@0 {
+		device_type = "pci";
+		compatible = "example,pci-bridge";
+		#address-cells = < 3 >;
+		#size-cells = < 2 >;
+		reg = <0 0 0 0x1000>;
+		bus-range = <0 0xff>;
+		ranges = <0 0 0 0 0 0 0x10000>;
+	};
+};
diff --git a/tests/pci-bridge-ok.dts b/tests/pci-bridge-ok.dts
new file mode 100644
index 0000000..02e32e0
--- /dev/null
+++ b/tests/pci-bridge-ok.dts
@@ -0,0 +1,25 @@
+/dts-v1/;
+
+/ {
+	compatible = "example,pci-bridge-ok";
+	#address-cells = < 2 >;
+	#size-cells = < 2 >;
+	pci@0 {
+		device_type = "pci";
+		compatible = "example,pci-bridge";
+		#address-cells = < 3 >;
+		#size-cells = < 2 >;
+		reg = <0 0 0 0x1000>;
+		bus-range = <0 0xff>;
+		ranges = <0 0 0 0 0 0 0x10000>;
+	};
+	pcie@10000000000 {
+		device_type = "pci";
+		compatible = "example,pcie-bridge";
+		#address-cells = < 3 >;
+		#size-cells = < 2 >;
+		reg = <0x10 0x00000000 0 0x1000>;
+		bus-range = <0 0xff>;
+		ranges = <0 0 0 0 0 0 0x10000>;
+	};
+};
diff --git a/tests/phandle_format.c b/tests/phandle_format.c
new file mode 100644
index 0000000..d00618f
--- /dev/null
+++ b/tests/phandle_format.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for phandle format options
+ * Copyright (C) 2009 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define PHANDLE_LEGACY	0x1
+#define PHANDLE_EPAPR	0x2
+#define PHANDLE_BOTH	0x3
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int phandle_format;
+	int n4;
+	uint32_t h4;
+
+	if (argc != 3)
+		CONFIG("Usage: %s <dtb file> <legacy|epapr|both>\n", argv[0]);
+
+	test_init(argc, argv);
+	fdt = load_blob(argv[1]);
+
+	if (streq(argv[2], "legacy"))
+		phandle_format = PHANDLE_LEGACY;
+	else if (streq(argv[2], "epapr"))
+		phandle_format = PHANDLE_EPAPR;
+	else if (streq(argv[2], "both"))
+		phandle_format = PHANDLE_BOTH;
+	else
+		CONFIG("Usage: %s <dtb file> <legacy|epapr|both>\n", argv[0]);
+
+	n4 = fdt_path_offset(fdt, "/node4");
+	if (n4 < 0)
+		FAIL("fdt_path_offset(/node4): %s", fdt_strerror(n4));
+
+	h4 = fdt_get_phandle(fdt, n4);
+	if ((h4 == 0) || (h4 == -1))
+		FAIL("/node4 has bad phandle 0x%x\n", h4);
+
+	if (phandle_format & PHANDLE_LEGACY)
+		check_getprop_cell(fdt, n4, "linux,phandle", h4);
+	else
+		if (fdt_getprop(fdt, n4, "linux,phandle", NULL))
+			FAIL("linux,phandle property present in non-legacy mode");
+
+	if (phandle_format & PHANDLE_EPAPR)
+		check_getprop_cell(fdt, n4, "phandle", h4);
+	else
+		if (fdt_getprop(fdt, n4, "phandle", NULL))
+			FAIL("phandle property present in legacy-only mode");
+
+	PASS();
+}
diff --git a/tests/prop-after-subnode.dts b/tests/prop-after-subnode.dts
new file mode 100644
index 0000000..6dd0b66
--- /dev/null
+++ b/tests/prop-after-subnode.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+	node1 {
+	};
+	prop;
+	node2 {
+	};
+};
diff --git a/tests/property_iterate.c b/tests/property_iterate.c
new file mode 100644
index 0000000..9a67f49
--- /dev/null
+++ b/tests/property_iterate.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests that fdt_next_subnode() works as expected
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void test_node(void *fdt, int parent_offset)
+{
+	uint32_t properties;
+	const fdt32_t *prop;
+	int offset, property;
+	int count;
+	int len;
+
+	/*
+	 * This property indicates the number of properties in our
+	 * test node to expect
+	 */
+	prop = fdt_getprop(fdt, parent_offset, "test-properties", &len);
+	if (!prop || len != sizeof(fdt32_t)) {
+		FAIL("Missing/invalid test-properties property at '%s'",
+		     fdt_get_name(fdt, parent_offset, NULL));
+	}
+	properties = fdt32_to_cpu(*prop);
+
+	count = 0;
+	offset = fdt_first_subnode(fdt, parent_offset);
+	if (offset < 0)
+		FAIL("Missing test node\n");
+
+	fdt_for_each_property_offset(property, fdt, offset)
+		count++;
+
+	if (count != properties) {
+		FAIL("Node '%s': Expected %d properties, got %d\n",
+		     fdt_get_name(fdt, parent_offset, NULL), properties,
+		     count);
+	}
+}
+
+static void check_fdt_next_subnode(void *fdt)
+{
+	int offset;
+	int count = 0;
+
+	fdt_for_each_subnode(offset, fdt, 0) {
+		test_node(fdt, offset);
+		count++;
+	}
+
+	if (count != 2)
+		FAIL("Expected %d tests, got %d\n", 2, count);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>", argv[0]);
+
+	fdt = load_blob(argv[1]);
+	if (!fdt)
+		FAIL("No device tree available");
+
+	check_fdt_next_subnode(fdt);
+
+	PASS();
+}
diff --git a/tests/property_iterate.dts b/tests/property_iterate.dts
new file mode 100644
index 0000000..e8f5f8f
--- /dev/null
+++ b/tests/property_iterate.dts
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	test1 {
+		test-properties = <3>;
+
+		test {
+			linux,phandle = <0x1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+	};
+
+	test2 {
+		test-properties = <0>;
+
+		test {
+		};
+	};
+};
diff --git a/tests/propname_escapes.c b/tests/propname_escapes.c
new file mode 100644
index 0000000..3e41e63
--- /dev/null
+++ b/tests/propname_escapes.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_getprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop_cell(fdt, 0, "#address-cells", 1);
+	check_getprop_cell(fdt, 0, "#gpio-cells", 2);
+
+	PASS();
+}
diff --git a/tests/propname_escapes.dts b/tests/propname_escapes.dts
new file mode 100644
index 0000000..9f70618
--- /dev/null
+++ b/tests/propname_escapes.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	\#gpio-cells = <2>;
+};
diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py
new file mode 100644
index 0000000..42f31ba
--- /dev/null
+++ b/tests/pylibfdt_tests.py
@@ -0,0 +1,589 @@
+# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+# pylibfdt - Tests for Flat Device Tree manipulation in Python
+# Copyright (C) 2017 Google, Inc.
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+import struct
+import sys
+import types
+import unittest
+
+sys.path.insert(0, '../pylibfdt')
+import libfdt
+from libfdt import Fdt, FdtSw, FdtException, QUIET_NOTFOUND, QUIET_ALL
+
+TEST_ADDR_1H = 0xdeadbeef
+TEST_ADDR_1L = 0x00000000
+TEST_ADDR_1 = (TEST_ADDR_1H << 32) | TEST_ADDR_1L
+TEST_ADDR_1 = 0x8000000000000000
+TEST_SIZE_1H = 0x00000000
+TEST_SIZE_1L = 0x00100000
+TEST_SIZE_1 = (TEST_SIZE_1H << 32) | TEST_SIZE_1L
+TEST_ADDR_2H = 0
+TEST_ADDR_2L = 123456789
+TEST_ADDR_2 = (TEST_ADDR_2H << 32) | TEST_ADDR_2L
+TEST_SIZE_2H = 0
+TEST_SIZE_2L = 0o10000
+TEST_SIZE_2 = (TEST_SIZE_2H << 32) | TEST_SIZE_2L
+
+TEST_VALUE_1 = 0xdeadbeef
+TEST_VALUE_2 = 123456789
+
+TEST_VALUE64_1H = 0xdeadbeef
+TEST_VALUE64_1L = 0x01abcdef
+TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L
+
+PHANDLE_1 = 0x2000
+PHANDLE_2 = 0x2001
+
+TEST_BYTES_1 = b'hello world'
+
+TEST_STRING_1 = 'hello world'
+TEST_STRING_2 = 'hi world'
+TEST_STRING_3 = u'unicode \u01d3'
+
+
+def get_err(err_code):
+    """Convert an error code into an error message
+
+    Args:
+        err_code: Error code value (FDT_ERR_...)
+
+    Returns:
+        String error code
+    """
+    return 'pylibfdt error %d: %s' % (-err_code, libfdt.strerror(-err_code))
+
+def _ReadFdt(fname):
+    """Read a device tree file into an Fdt object, ready for use
+
+    Args:
+        fname: Filename to read from
+
+    Returns:
+        Fdt bytearray suitable for passing to libfdt functions
+    """
+    return libfdt.Fdt(open(fname, mode='rb').read())
+
+class PyLibfdtBasicTests(unittest.TestCase):
+    """Test class for basic pylibfdt access functions
+
+    Properties:
+        fdt: Device tree file used for testing
+    """
+
+    def setUp(self):
+        """Read in the device tree we use for testing"""
+        self.fdt = _ReadFdt('test_tree1.dtb')
+        self.fdt2 = _ReadFdt('test_props.dtb')
+        self.fdt3 = _ReadFdt('aliases.dtb')
+
+    def GetPropList(self, node_path):
+        """Read a list of properties from a node
+
+        Args:
+            node_path: Full path to node, e.g. '/subnode@1/subsubnode'
+
+        Returns:
+            List of property names for that node, e.g. ['compatible', 'reg']
+        """
+        prop_list = []
+        node = self.fdt.path_offset(node_path)
+        poffset = self.fdt.first_property_offset(node, QUIET_NOTFOUND)
+        while poffset > 0:
+            prop = self.fdt.get_property_by_offset(poffset)
+            prop_list.append(prop.name)
+            poffset = self.fdt.next_property_offset(poffset, QUIET_NOTFOUND)
+        return prop_list
+
+    def GetSubnodes(self, node_path):
+        """Read a list of subnodes from a node
+
+        Args:
+            node_path: Full path to node, e.g. '/subnode@1/subsubnode'
+
+        Returns:
+            List of subnode names for that node, e.g. ['subsubnode', 'ss1']
+        """
+        subnode_list = []
+        node = self.fdt.path_offset(node_path)
+        offset = self.fdt.first_subnode(node, QUIET_NOTFOUND)
+        while offset > 0:
+            name = self.fdt.get_name(offset)
+            subnode_list.append(name)
+            offset = self.fdt.next_subnode(offset, QUIET_NOTFOUND)
+        return subnode_list
+
+    def testImport(self):
+        """Check that we can import the library correctly"""
+        self.assertEquals(type(libfdt), types.ModuleType)
+
+    def testBadFdt(self):
+        """Check that a filename provided accidentally is not accepted"""
+        with self.assertRaises(FdtException) as e:
+            fdt = libfdt.Fdt(b'a string')
+        self.assertEquals(e.exception.err, -libfdt.BADMAGIC)
+
+    def testSubnodeOffset(self):
+        """check that we can locate a subnode by name"""
+        node1 = self.fdt.path_offset('/subnode@1')
+        self.assertEquals(self.fdt.subnode_offset(0, 'subnode@1'), node1)
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.subnode_offset(0, 'missing')
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node2 = self.fdt.path_offset('/subnode@1/subsubnode')
+        self.assertEquals(self.fdt.subnode_offset(node1, 'subsubnode'), node2)
+
+    def testPathOffset(self):
+        """Check that we can find the offset of a node"""
+        self.assertEquals(self.fdt.path_offset('/'), 0)
+        self.assertTrue(self.fdt.path_offset('/subnode@1') > 0)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.path_offset('/wibble')
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+        self.assertEquals(self.fdt.path_offset('/wibble', QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+
+    def testPropertyOffset(self):
+        """Walk through all the properties in the root node"""
+        offset = self.fdt.first_property_offset(0)
+        self.assertTrue(offset > 0)
+        for i in range(5):
+            next_offset = self.fdt.next_property_offset(offset)
+            self.assertTrue(next_offset > offset)
+            offset = next_offset
+        self.assertEquals(self.fdt.next_property_offset(offset, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+
+    def testPropertyOffsetExceptions(self):
+        """Check that exceptions are raised as expected"""
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(107)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+        # Quieten the NOTFOUND exception and check that a BADOFFSET
+        # exception is still raised.
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(107, QUIET_NOTFOUND)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_property_offset(107, QUIET_NOTFOUND)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+        # Check that NOTFOUND can be quietened.
+        node = self.fdt.path_offset('/subnode@1/ss1')
+        self.assertEquals(self.fdt.first_property_offset(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+    def testGetName(self):
+        """Check that we can get the name of a node"""
+        self.assertEquals(self.fdt.get_name(0), '')
+        node = self.fdt.path_offset('/subnode@1/subsubnode')
+        self.assertEquals(self.fdt.get_name(node), 'subsubnode')
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_name(-2)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+    def testGetPropertyByOffset(self):
+        """Check that we can read the name and contents of a property"""
+        root = 0
+        poffset = self.fdt.first_property_offset(root)
+        prop = self.fdt.get_property_by_offset(poffset)
+        self.assertEquals(prop.name, 'compatible')
+        self.assertEquals(prop, b'test_tree1\0')
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_property_by_offset(-2)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        self.assertEquals(
+                -libfdt.BADOFFSET,
+                self.fdt.get_property_by_offset(-2, [libfdt.BADOFFSET]))
+
+    def testGetProp(self):
+        """Check that we can read the contents of a property by name"""
+        root = self.fdt.path_offset('/')
+        value = self.fdt.getprop(root, "compatible")
+        self.assertEquals(value, b'test_tree1\0')
+        self.assertEquals(-libfdt.NOTFOUND, self.fdt.getprop(root, 'missing',
+                                                             QUIET_NOTFOUND))
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.getprop(root, 'missing')
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node = self.fdt.path_offset('/subnode@1/subsubnode')
+        value = self.fdt.getprop(node, "compatible")
+        self.assertEquals(value, b'subsubnode1\0subsubnode\0')
+
+    def testStrError(self):
+        """Check that we can get an error string"""
+        self.assertEquals(libfdt.strerror(-libfdt.NOTFOUND),
+                          'FDT_ERR_NOTFOUND')
+
+    def testNextNodeOffset(self):
+        """Check that we can walk through nodes"""
+        node_list = []
+        node = 0
+        depth = 0
+        while depth >= 0:
+            node_list.append([depth, self.fdt.get_name(node)])
+            node, depth = self.fdt.next_node(node, depth, (libfdt.BADOFFSET,))
+        self.assertEquals(node_list, [
+            [0, ''],
+            [1, 'subnode@1'],
+            [2, 'subsubnode'],
+            [2, 'ss1'],
+            [1, 'subnode@2'],
+            [2, 'subsubnode@0'],
+            [2, 'ss2'],
+            ])
+
+    def testFirstNextSubnodeOffset(self):
+        """Check that we can walk through subnodes"""
+        node_list = []
+        node = self.fdt.first_subnode(0, QUIET_NOTFOUND)
+        while node >= 0:
+            node_list.append(self.fdt.get_name(node))
+            node = self.fdt.next_subnode(node, QUIET_NOTFOUND)
+        self.assertEquals(node_list, ['subnode@1', 'subnode@2'])
+
+    def testFirstNextSubnodeOffsetExceptions(self):
+        """Check except handling for first/next subnode functions"""
+        node = self.fdt.path_offset('/subnode@1/subsubnode', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.first_subnode(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_subnode(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node = self.fdt.path_offset('/subnode@1/ss1', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.next_subnode(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_subnode(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+    def testDeleteProperty(self):
+        """Test that we can delete a property"""
+        node_name = '/subnode@1'
+        self.assertEquals(self.GetPropList(node_name),
+                          ['compatible', 'reg', 'prop-int'])
+        node = self.fdt.path_offset('/%s' % node_name)
+        self.assertEquals(self.fdt.delprop(node, 'reg'), 0)
+        self.assertEquals(self.GetPropList(node_name),
+                          ['compatible', 'prop-int'])
+
+    def testHeader(self):
+        """Test that we can access the header values"""
+        self.assertEquals(self.fdt.magic(), 0xd00dfeed)
+        self.assertEquals(self.fdt.totalsize(), len(self.fdt._fdt))
+        self.assertEquals(self.fdt.off_dt_struct(), 88)
+        self.assertEquals(self.fdt.off_dt_strings(), 652)
+        self.assertEquals(self.fdt.off_mem_rsvmap(), 40)
+        self.assertEquals(self.fdt.version(), 17)
+        self.assertEquals(self.fdt.last_comp_version(), 16)
+        self.assertEquals(self.fdt.boot_cpuid_phys(), 0)
+        self.assertEquals(self.fdt.size_dt_strings(), 105)
+        self.assertEquals(self.fdt.size_dt_struct(), 564)
+
+    def testPack(self):
+        """Test that we can pack the tree after deleting something"""
+        orig_size = self.fdt.totalsize()
+        node = self.fdt.path_offset('/subnode@2', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.delprop(node, 'prop-int'), 0)
+        self.assertEquals(orig_size, self.fdt.totalsize())
+        self.assertEquals(self.fdt.pack(), 0)
+        self.assertTrue(self.fdt.totalsize() < orig_size)
+        self.assertEquals(self.fdt.totalsize(), len(self.fdt.as_bytearray()))
+
+    def testBadPropertyOffset(self):
+        """Test that bad property offsets are detected"""
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_property_by_offset(13)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(3)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_property_offset(3)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+    def testBadPathOffset(self):
+        """Test that bad path names are detected"""
+        with self.assertRaisesRegexp(FdtException, get_err(libfdt.BADPATH)):
+            self.fdt.path_offset('not-present')
+
+    def testQuietAll(self):
+        """Check that exceptions can be masked by QUIET_ALL"""
+        self.assertEquals(-libfdt.NOTFOUND,
+                          self.fdt.path_offset('/missing', QUIET_ALL))
+        self.assertEquals(-libfdt.BADOFFSET,
+                          self.fdt.get_property_by_offset(13, QUIET_ALL))
+        self.assertEquals(-libfdt.BADPATH,
+                          self.fdt.path_offset('missing', QUIET_ALL))
+
+    def testIntegers(self):
+        """Check that integers can be passed and returned"""
+        self.assertEquals(0, libfdt.fdt_get_phandle(self.fdt._fdt, 0))
+        node2 = self.fdt.path_offset('/subnode@2')
+        self.assertEquals(0x2000, libfdt.fdt_get_phandle(self.fdt._fdt, node2))
+
+    def testGetPhandle(self):
+        """Test for the get_phandle() method"""
+        self.assertEquals(0, self.fdt.get_phandle(0))
+        node2 = self.fdt.path_offset('/subnode@2')
+        self.assertEquals(0x2000, self.fdt.get_phandle(node2))
+
+    def testGetAlias(self):
+        """Test for the get_alias() method"""
+        self.assertEquals("/subnode@1", self.fdt3.get_alias('s1'))
+        self.assertEquals("/subnode@1/subsubnode", self.fdt3.get_alias('ss1'))
+        self.assertEquals("/subnode@1/subsubnode/subsubsubnode", self.fdt3.get_alias('sss1'))
+
+    def testParentOffset(self):
+        """Test for the parent_offset() method"""
+        self.assertEquals(-libfdt.NOTFOUND,
+                          self.fdt.parent_offset(0, QUIET_NOTFOUND))
+        with self.assertRaises(FdtException) as e:
+            self.fdt.parent_offset(0)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node1 = self.fdt.path_offset('/subnode@2')
+        self.assertEquals(0, self.fdt.parent_offset(node1))
+        node2 = self.fdt.path_offset('/subnode@2/subsubnode@0')
+        self.assertEquals(node1, self.fdt.parent_offset(node2))
+
+    def testNodeOffsetByPhandle(self):
+        """Test for the node_offset_by_phandle() method"""
+        self.assertEquals(-libfdt.NOTFOUND,
+                          self.fdt.node_offset_by_phandle(1, QUIET_NOTFOUND))
+        node1 = self.fdt.path_offset('/subnode@2')
+        self.assertEquals(node1, self.fdt.node_offset_by_phandle(0x2000))
+        node2 = self.fdt.path_offset('/subnode@2/subsubnode@0')
+        self.assertEquals(node2, self.fdt.node_offset_by_phandle(0x2001))
+
+    def get_prop(self, name):
+        return self.fdt2.getprop(0, name)
+
+    def testGetIntProperties(self):
+        """Test that we can access properties as integers"""
+        self.assertEquals(0xdeadbeef, self.get_prop("prop-hex32").as_uint32())
+        self.assertEquals(123, self.get_prop("prop-uint32").as_uint32())
+        self.assertEquals(-2, self.get_prop("prop-int32").as_int32())
+        self.assertEquals(9223372036854775807,
+                          self.get_prop("prop-uint64").as_uint64())
+        self.assertEquals(-2, self.get_prop("prop-int64").as_int64())
+
+    def testReserveMap(self):
+        """Test that we can access the memory reserve map"""
+        self.assertEquals(2, self.fdt.num_mem_rsv())
+        self.assertEquals([ 0xdeadbeef00000000, 0x100000],
+                          self.fdt.get_mem_rsv(0))
+        self.assertEquals([123456789, 0o10000], self.fdt.get_mem_rsv(1))
+
+    def testEmpty(self):
+        """Test that we can create an empty tree"""
+        self.assertEquals(-libfdt.NOSPACE,
+                          Fdt.create_empty_tree(1, (libfdt.NOSPACE,)))
+        fdt = Fdt.create_empty_tree(128)
+        self.assertEquals(128, fdt.totalsize())
+
+    def testOpenInto(self):
+        """Test that we can resize a tree"""
+        fdt = Fdt.create_empty_tree(128)
+        self.assertEquals(128, fdt.totalsize())
+        fdt.resize(256)
+        self.assertEquals(256, fdt.totalsize())
+        fdt.pack()
+        self.assertTrue(fdt.totalsize() < 128)
+
+    def testSetProp(self):
+        """Test that we can update and create properties"""
+        node = self.fdt.path_offset('/subnode@1')
+        self.fdt.setprop(node, 'compatible', TEST_BYTES_1)
+        self.assertEquals(TEST_BYTES_1, self.fdt.getprop(node, 'compatible'))
+
+        # Check that this property is missing, and that we don't have space to
+        # add it
+        self.assertEquals(-libfdt.NOTFOUND,
+                          self.fdt.getprop(node, 'missing', QUIET_NOTFOUND))
+        self.assertEquals(-libfdt.NOSPACE,
+                          self.fdt.setprop(node, 'missing', TEST_BYTES_1,
+                                           quiet=(libfdt.NOSPACE,)))
+
+        # Expand the device tree so we now have room
+        self.fdt.resize(self.fdt.totalsize() + 50)
+        self.fdt.setprop(node, 'missing', TEST_BYTES_1)
+        self.assertEquals(TEST_BYTES_1, self.fdt.getprop(node, 'missing'))
+
+    def testSetPropU32(self):
+        """Test that we can update and create integer properties"""
+        node = 0
+        prop = 'prop-int'
+        self.fdt.setprop_u32(node, prop, TEST_VALUE_1)
+        self.assertEquals(struct.pack('>I', TEST_VALUE_1),
+                          self.fdt.getprop(node, prop))
+
+    def testSetPropU64(self):
+        """Test that we can update and create integer properties"""
+        node = 0
+        prop = 'prop-int64'
+        self.fdt.setprop_u64(node, prop, TEST_VALUE64_1)
+        self.assertEquals(struct.pack('>Q', TEST_VALUE64_1),
+                          self.fdt.getprop(node, prop))
+
+    def testSetPropStr(self):
+        """Test that we can set a property to a particular string"""
+        node = 0
+        prop = 'prop-str'
+        self.assertEquals(TEST_STRING_1, self.fdt.getprop(node, prop).as_str())
+        self.fdt.setprop_str(node, prop, TEST_STRING_2)
+        self.assertEquals(TEST_STRING_2, self.fdt.getprop(node, prop).as_str())
+        with self.assertRaises(ValueError) as e:
+            self.fdt.getprop(node, 'prop-int').as_str()
+        self.assertIn('lacks nul termination', str(e.exception))
+
+        node2 = self.fdt.path_offset('/subnode@1/subsubnode')
+        with self.assertRaises(ValueError) as e:
+            self.fdt.getprop(node2, 'compatible').as_str()
+        self.assertIn('embedded nul', str(e.exception))
+
+        # Expand the device tree so we now have room
+        self.fdt.resize(self.fdt.totalsize() + 50)
+        prop = 'prop-unicode'
+        self.fdt.setprop_str(node, prop, TEST_STRING_3)
+        self.assertEquals(TEST_STRING_3,
+                          self.fdt.getprop(node, prop).as_str())
+
+    def testSetName(self):
+        """Test that we can update a node name"""
+        node = self.fdt.path_offset('/subnode@1')
+        old_val = self.fdt.get_name(node)
+        self.fdt.set_name(node, 'test')
+        self.assertEquals('test', self.fdt.get_name(node))
+
+        with self.assertRaises(ValueError) as e:
+            self.fdt.set_name(node, 'some\0name')
+        self.assertIn('embedded nul', str(e.exception))
+
+        with self.assertRaises(ValueError) as e:
+            self.fdt.set_name(node, 'name\0')
+        self.assertIn('embedded nul', str(e.exception))
+
+    def testAddDeleteNodes(self):
+        """Test that we can add and delete nodes"""
+        node_name = '/subnode@1'
+        self.assertEquals(self.GetSubnodes(node_name), ['subsubnode', 'ss1'])
+        node = self.fdt.path_offset('%s/subsubnode' % node_name)
+        self.assertEquals(self.fdt.del_node(node, 'subsubnode'), 0)
+        self.assertEquals(self.GetSubnodes(node_name), ['ss1'])
+
+        node = self.fdt.path_offset(node_name)
+        offset = self.fdt.add_subnode(node, 'more')
+        self.assertTrue(offset > 0)
+        self.assertEquals(self.GetSubnodes(node_name), ['more', 'ss1'])
+
+
+class PyLibfdtSwTests(unittest.TestCase):
+    """Test class for pylibfdt sequential-write DT creation
+    """
+    def assertOk(self, err_code):
+        self.assertEquals(0, err_code)
+
+    def testCreate(self):
+        # First check the minimum size and also the FdtSw() constructor
+        with self.assertRaisesRegexp(FdtException, get_err(libfdt.NOSPACE)):
+            self.assertEquals(-libfdt.NOSPACE, FdtSw(3))
+
+        sw = FdtSw()
+        sw.add_reservemap_entry(TEST_ADDR_1, TEST_SIZE_1)
+        sw.add_reservemap_entry(TEST_ADDR_2, TEST_SIZE_2)
+        sw.finish_reservemap()
+
+        sw.begin_node('')
+        sw.property_string('compatible', 'test_tree1')
+        sw.property_u32('prop-int', TEST_VALUE_1)
+
+        sw.property_u32('prop-int', TEST_VALUE_1)
+        sw.property_u64('prop-int64', TEST_VALUE64_1)
+        sw.property_string('prop-str', TEST_STRING_1)
+        sw.property_u32('#address-cells', 1)
+        sw.property_u32('#size-cells', 0)
+
+        sw.begin_node('subnode@1')
+        sw.property_string('compatible', 'subnode1')
+        sw.property_u32('reg', 1)
+        sw.property_cell('prop-int', TEST_VALUE_1)
+        sw.begin_node('subsubnode')
+        sw.property('compatible', 'subsubnode1\0subsubnode')
+        sw.property_cell('prop-int', TEST_VALUE_1)
+        sw.end_node()
+        sw.begin_node('ss1')
+        sw.end_node()
+        sw.end_node()
+
+        for i in range(2, 11):
+            with sw.add_node('subnode@%d' % i):
+                sw.property_u32('reg', 2)
+                sw.property_cell('linux,phandle', PHANDLE_1)
+                sw.property_cell('prop-int', TEST_VALUE_2)
+                sw.property_u32('#address-cells', 1)
+                sw.property_u32('#size-cells', 0)
+                with sw.add_node('subsubnode@0'):
+                    sw.property_u32('reg', 0)
+                    sw.property_cell('phandle', PHANDLE_2)
+                    sw.property('compatible', 'subsubnode2\0subsubnode')
+                    sw.property_cell('prop-int', TEST_VALUE_2)
+                with sw.add_node('ss2'):
+                    pass
+        sw.end_node()
+
+        fdt = sw.as_fdt()
+        self.assertEqual(2, fdt.num_mem_rsv())
+        self.assertEqual([TEST_ADDR_1, TEST_SIZE_1], fdt.get_mem_rsv(0))
+
+        # Make sure we can add a few more things
+        with sw.add_node('another'):
+            sw.property_u32('reg', 3)
+
+        # Make sure we can read from the tree too
+        node = sw.path_offset('/subnode@1')
+        self.assertEqual(b'subnode1\0', sw.getprop(node, 'compatible'))
+
+        # Make sure we did at least two resizes
+        self.assertTrue(len(fdt.as_bytearray()) > FdtSw.INC_SIZE * 2)
+
+
+class PyLibfdtRoTests(unittest.TestCase):
+    """Test class for read-only pylibfdt access functions
+
+    This just tests a few simple cases. Most of the tests are in
+    PyLibfdtBasicTests.
+
+    Properties:
+        fdt: Device tree file used for testing
+    """
+
+    def setUp(self):
+        """Read in the device tree we use for testing"""
+        self.fdt = libfdt.FdtRo(open('test_tree1.dtb', mode='rb').read())
+
+    def testAccess(self):
+        """Basic sanity check for the FdtRo class"""
+        node = self.fdt.path_offset('/subnode@1')
+        self.assertEqual(b'subnode1\0',
+                         self.fdt.getprop(node, 'compatible'))
+        node = self.fdt.first_subnode(node)
+        self.assertEqual(b'this is a placeholder string\0string2\0',
+                         self.fdt.getprop(node, 'placeholder'))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/references.c b/tests/references.c
new file mode 100644
index 0000000..d18e722
--- /dev/null
+++ b/tests/references.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for phandle references in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_ref(const void *fdt, int node, uint32_t checkref)
+{
+	const fdt32_t *p;
+	uint32_t ref;
+	int len;
+
+	p = fdt_getprop(fdt, node, "ref", &len);
+	if (!p)
+		FAIL("fdt_getprop(%d, \"ref\"): %s", node, fdt_strerror(len));
+	if (len != sizeof(*p))
+		FAIL("'ref' in node at %d has wrong size (%d instead of %zd)",
+		     node, len, sizeof(*p));
+	ref = fdt32_to_cpu(*p);
+	if (ref != checkref)
+		FAIL("'ref' in node at %d has value 0x%x instead of 0x%x",
+		     node, ref, checkref);
+
+	p = fdt_getprop(fdt, node, "lref", &len);
+	if (!p)
+		FAIL("fdt_getprop(%d, \"lref\"): %s", node, fdt_strerror(len));
+	if (len != sizeof(*p))
+		FAIL("'lref' in node at %d has wrong size (%d instead of %zd)",
+		     node, len, sizeof(*p));
+	ref = fdt32_to_cpu(*p);
+	if (ref != checkref)
+		FAIL("'lref' in node at %d has value 0x%x instead of 0x%x",
+		     node, ref, checkref);
+}
+
+static void check_rref(const void *fdt)
+{
+	const fdt32_t *p;
+	uint32_t ref;
+	int len;
+
+	p = fdt_getprop(fdt, 0, "rref", &len);
+	if (!p)
+		FAIL("fdt_getprop(0, \"rref\"): %s", fdt_strerror(len));
+	if (len != sizeof(*p))
+		FAIL("'rref' in root node has wrong size (%d instead of %zd)",
+		     len, sizeof(*p));
+	ref = fdt32_to_cpu(*p);
+	if (ref != fdt_get_phandle(fdt, 0))
+		FAIL("'rref' in root node has value 0x%x instead of 0x0", ref);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int n1, n2, n3, n4, n5, n6, err;
+	uint32_t h1, h2, h4, h5, h6, hn;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	n1 = fdt_path_offset(fdt, "/node1");
+	if (n1 < 0)
+		FAIL("fdt_path_offset(/node1): %s", fdt_strerror(n1));
+	n2 = fdt_path_offset(fdt, "/node2");
+	if (n2 < 0)
+		FAIL("fdt_path_offset(/node2): %s", fdt_strerror(n2));
+	n3 = fdt_path_offset(fdt, "/node3");
+	if (n3 < 0)
+		FAIL("fdt_path_offset(/node3): %s", fdt_strerror(n3));
+	n4 = fdt_path_offset(fdt, "/node4");
+	if (n4 < 0)
+		FAIL("fdt_path_offset(/node4): %s", fdt_strerror(n4));
+	n5 = fdt_path_offset(fdt, "/node5");
+	if (n5 < 0)
+		FAIL("fdt_path_offset(/node5): %s", fdt_strerror(n5));
+	n6 = fdt_path_offset(fdt, "/node6");
+	if (n6 < 0)
+		FAIL("fdt_path_offset(/node6): %s", fdt_strerror(n6));
+
+	h1 = fdt_get_phandle(fdt, n1);
+	h2 = fdt_get_phandle(fdt, n2);
+	h4 = fdt_get_phandle(fdt, n4);
+	h5 = fdt_get_phandle(fdt, n5);
+	h6 = fdt_get_phandle(fdt, n6);
+
+	if (h1 != 0x2000)
+		FAIL("/node1 has wrong phandle, 0x%x instead of 0x%x",
+		     h1, 0x2000);
+	if (h2 != 0x1)
+		FAIL("/node2 has wrong phandle, 0x%x instead of 0x%x",
+		     h2, 0x1);
+	if (h6 != FDT_MAX_PHANDLE)
+		FAIL("/node6 has wrong phandle, 0x%x instead of 0x%x",
+		     h6, FDT_MAX_PHANDLE);
+	if ((h4 == 0x2000) || (h4 == 0x1) || (h4 == 0))
+		FAIL("/node4 has bad phandle, 0x%x", h4);
+
+	if ((h5 == 0) || (h5 == -1))
+		FAIL("/node5 has bad phandle, 0x%x", h5);
+	if ((h5 == h4) || (h5 == h2) || (h5 == h1))
+		FAIL("/node5 has duplicate phandle, 0x%x", h5);
+
+	/*
+	 * /node6 has phandle FDT_MAX_PHANDLE, so fdt_generate_phandle() is
+	 * expected to fail.
+	 */
+	err = fdt_generate_phandle(fdt, &hn);
+	if (err != -FDT_ERR_NOPHANDLES)
+		FAIL("generated invalid phandle 0x%x\n", hn);
+
+	check_ref(fdt, n1, h2);
+	check_ref(fdt, n2, h1);
+	check_ref(fdt, n3, h4);
+
+	check_rref(fdt);
+
+	PASS();
+}
diff --git a/tests/references.dts b/tests/references.dts
new file mode 100644
index 0000000..b390639
--- /dev/null
+++ b/tests/references.dts
@@ -0,0 +1,41 @@
+/dts-v1/;
+
+/ {
+	rref = <&{/}>;
+
+	/* Explicit phandles */
+	n1: node1 {
+		linux,phandle = <0x2000>;
+		ref = <&{/node2}>; /* reference precedes target */
+		lref = <&n2>;
+	};
+	n2: node2 {
+		phandle = <0x1>;
+		ref = <&{/node1}>; /* reference after target */
+		lref = <&n1>;
+	};
+
+	/* Implicit phandles */
+	n3: node3 {
+		ref = <&{/node4}>;
+		lref = <&n4>;
+	};
+	n4: node4 {
+	};
+
+	/* Explicit phandle with implicit value */
+	/* This self-reference is the standard way to tag a node as requiring
+	 * a phandle (perhaps for reference by nodes that will be dynamically
+	 * added) without explicitly allocating it a phandle.
+	 * The self-reference requires some special internal handling, though
+	 * so check it actually works */
+	n5: node5 {
+		linux,phandle = <&n5>;
+		phandle = <&n5>;
+	};
+
+	node6 {
+		linux,phandle = <0xfffffffe>;
+		phandle = <0xfffffffe>;
+	};
+};
diff --git a/tests/reg-ranges-root.dts b/tests/reg-ranges-root.dts
new file mode 100644
index 0000000..9935b41
--- /dev/null
+++ b/tests/reg-ranges-root.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0x1000 0x10>;
+	ranges = <0x1000 0x2000 0x1000>;
+};
diff --git a/tests/reg-without-unit-addr.dts b/tests/reg-without-unit-addr.dts
new file mode 100644
index 0000000..aaf8af7
--- /dev/null
+++ b/tests/reg-without-unit-addr.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	node {
+		reg = <0 1>;
+	};
+};
diff --git a/tests/reuse-label.dts b/tests/reuse-label.dts
new file mode 100644
index 0000000..98b5ca9
--- /dev/null
+++ b/tests/reuse-label.dts
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+	label: property1 = "foo";
+	label: property2 = "bar";
+
+	test1 = &label;
+
+	label: node1 {
+		prop = "foo";
+	};
+	label: node2 {
+		prop = "bar";
+	};
+};
diff --git a/tests/reuse-label1.dts b/tests/reuse-label1.dts
new file mode 100644
index 0000000..f229569
--- /dev/null
+++ b/tests/reuse-label1.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+	label: node1 {
+		prop = "foo";
+	};
+	label: node2 {
+		prop = "bar";
+	};
+};
diff --git a/tests/reuse-label2.dts b/tests/reuse-label2.dts
new file mode 100644
index 0000000..01ea6b2
--- /dev/null
+++ b/tests/reuse-label2.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	label: property1 = "foo";
+	label: property2 = "bar";
+};
diff --git a/tests/reuse-label3.dts b/tests/reuse-label3.dts
new file mode 100644
index 0000000..fa3d2c7
--- /dev/null
+++ b/tests/reuse-label3.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+	label: property = "foo";
+
+	label: node {
+		property = "foo";
+	};
+};
diff --git a/tests/reuse-label4.dts b/tests/reuse-label4.dts
new file mode 100644
index 0000000..6805de3
--- /dev/null
+++ b/tests/reuse-label4.dts
@@ -0,0 +1,5 @@
+/dts-v1/;
+
+/ {
+	property = label: "foo" label:;
+};
diff --git a/tests/reuse-label5.dts b/tests/reuse-label5.dts
new file mode 100644
index 0000000..b7238e6
--- /dev/null
+++ b/tests/reuse-label5.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	prop1 = label: "foo";
+	prop2 = "bar" label:;
+};
diff --git a/tests/reuse-label6.dts b/tests/reuse-label6.dts
new file mode 100644
index 0000000..f5d507c
--- /dev/null
+++ b/tests/reuse-label6.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	label: prop1 = "foo";
+	prop2 = "bar" label:;
+};
diff --git a/tests/root_node.c b/tests/root_node.c
new file mode 100644
index 0000000..37e6f05
--- /dev/null
+++ b/tests/root_node.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Basic testcase for read-only access
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const struct fdt_node_header *nh;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	nh = fdt_offset_ptr(fdt, 0, sizeof(*nh));
+
+	if (! nh)
+		FAIL("NULL retrieving root node");
+
+	if (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE)
+		FAIL("Wrong tag on root node");
+
+	if (strlen(nh->name) != 0)
+		FAIL("Wrong name for root node, \"%s\" instead of empty",
+		     nh->name);
+
+	PASS();
+}
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
new file mode 100755
index 0000000..2620332
--- /dev/null
+++ b/tests/run_tests.sh
@@ -0,0 +1,1059 @@
+#! /bin/sh
+
+. ./testutils.sh
+
+if [ -z "$CC" ]; then
+    CC=gcc
+fi
+
+# stat differs between platforms
+if [ -z "$STATSZ" ]; then
+	stat --version 2>/dev/null | grep -q 'GNU'
+	GNUSTAT=$?
+	if [ "$GNUSTAT" -ne 0 ]; then
+		# Assume BSD stat if we can't detect as GNU stat
+		STATSZ="stat -f %Uz"
+	else
+		STATSZ="stat -c %s"
+	fi
+fi
+
+# Help things find the libfdt shared object
+export LD_LIBRARY_PATH=../libfdt
+
+export QUIET_TEST=1
+STOP_ON_FAIL=0
+
+export VALGRIND=
+VGCODE=126
+
+tot_tests=0
+tot_pass=0
+tot_fail=0
+tot_config=0
+tot_vg=0
+tot_strange=0
+
+base_run_test() {
+    tot_tests=$((tot_tests + 1))
+    if VALGRIND="$VALGRIND" "$@"; then
+	tot_pass=$((tot_pass + 1))
+    else
+	ret="$?"
+	if [ "$STOP_ON_FAIL" -eq 1 ]; then
+	    exit 1
+	fi
+	if [ "$ret" -eq 1 ]; then
+	    tot_config=$((tot_config + 1))
+	elif [ "$ret" -eq 2 ]; then
+	    tot_fail=$((tot_fail + 1))
+	elif [ "$ret" -eq $VGCODE ]; then
+	    tot_vg=$((tot_vg + 1))
+	else
+	    tot_strange=$((tot_strange + 1))
+	fi
+    fi
+}
+
+shorten_echo () {
+    limit=32
+    printf "$1"
+    shift
+    for x; do
+	if [ ${#x} -le $limit ]; then
+	    printf " $x"
+	else
+	    short=$(echo "$x" | head -c$limit)
+	    printf " \"$short\"...<${#x} bytes>"
+	fi
+    done
+}
+
+run_test () {
+    printf "$*:	"
+    if [ -n "$VALGRIND" -a -f $1.supp ]; then
+	VGSUPP="--suppressions=$1.supp"
+    fi
+    base_run_test $VALGRIND $VGSUPP "./$@"
+}
+
+run_sh_test () {
+    printf "$*:	"
+    base_run_test sh "$@"
+}
+
+wrap_test () {
+    (
+	if verbose_run "$@"; then
+	    PASS
+	else
+	    ret="$?"
+	    if [ "$ret" -gt 127 ]; then
+		signame=$(kill -l $((ret - 128)))
+		FAIL "Killed by SIG$signame"
+	    elif [ "$ret" -eq $VGCODE ]; then
+		echo "VALGRIND ERROR"
+		exit $VGCODE
+	    else
+		FAIL "Returned error code $ret"
+	    fi
+	fi
+    )
+}
+
+run_wrap_test () {
+    shorten_echo "$@:	"
+    base_run_test wrap_test "$@"
+}
+
+wrap_error () {
+    (
+	if verbose_run "$@"; then
+	    FAIL "Expected non-zero return code"
+	else
+	    ret="$?"
+	    if [ "$ret" -gt 127 ]; then
+		signame=$(kill -l $((ret - 128)))
+		FAIL "Killed by SIG$signame"
+	    else
+		PASS
+	    fi
+	fi
+    )
+}
+
+run_wrap_error_test () {
+    shorten_echo "$@"
+    printf " {!= 0}:	"
+    base_run_test wrap_error "$@"
+}
+
+# $1: dtb file
+# $2: align base
+check_align () {
+    shorten_echo "check_align $@:	"
+    local size=$($STATSZ "$1")
+    local align="$2"
+    (
+	if [ $(($size % $align)) -eq 0 ] ;then
+	    PASS
+	else
+	    FAIL "Output size $size is not $align-byte aligned"
+	fi
+    )
+}
+
+run_dtc_test () {
+    printf "dtc $*:	"
+    base_run_test wrap_test $VALGRIND $DTC "$@"
+}
+
+asm_to_so () {
+    $CC -shared -o $1.test.so data.S $1.test.s
+}
+
+asm_to_so_test () {
+    run_wrap_test asm_to_so "$@"
+}
+
+run_fdtget_test () {
+    expect="$1"
+    shift
+    printf "fdtget-runtest.sh %s $*:	" "$(echo $expect)"
+    base_run_test sh fdtget-runtest.sh "$expect" "$@"
+}
+
+run_fdtput_test () {
+    expect="$1"
+    shift
+    shorten_echo fdtput-runtest.sh "$expect" "$@"
+    printf ":	"
+    base_run_test sh fdtput-runtest.sh "$expect" "$@"
+}
+
+run_fdtdump_test() {
+    file="$1"
+    shorten_echo fdtdump-runtest.sh "$file"
+    printf ":	"
+    base_run_test sh fdtdump-runtest.sh "$file" 2>/dev/null
+}
+
+run_fdtoverlay_test() {
+    expect="$1"
+    shift
+    shorten_echo fdtoverlay-runtest.sh "$expect" "$@"
+    printf ":	"
+    base_run_test sh fdtoverlay-runtest.sh "$expect" "$@"
+}
+
+BAD_FIXUP_TREES="bad_index \
+		empty \
+		empty_index \
+		index_trailing \
+		path_empty_prop \
+		path_only \
+		path_only_sep \
+		path_prop"
+
+# Test to exercise libfdt overlay application without dtc's overlay support
+libfdt_overlay_tests () {
+    # First test a doctored overlay which requires only local fixups
+    run_dtc_test -I dts -O dtb -o overlay_base_no_symbols.test.dtb overlay_base.dts
+    run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__fixups__"
+    run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__local_fixups__"
+
+    run_dtc_test -I dts -O dtb -o overlay_overlay_no_fixups.test.dtb overlay_overlay_no_fixups.dts
+    run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__fixups__"
+    run_test check_path overlay_overlay_no_fixups.test.dtb exists "/__local_fixups__"
+
+    run_test overlay overlay_base_no_symbols.test.dtb overlay_overlay_no_fixups.test.dtb
+
+    # Then test with manually constructed fixups
+    run_dtc_test -I dts -O dtb -o overlay_base_manual_symbols.test.dtb overlay_base_manual_symbols.dts
+    run_test check_path overlay_base_manual_symbols.test.dtb exists "/__symbols__"
+    run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__fixups__"
+    run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__local_fixups__"
+
+    run_dtc_test -I dts -O dtb -o overlay_overlay_manual_fixups.test.dtb overlay_overlay_manual_fixups.dts
+    run_test check_path overlay_overlay_manual_fixups.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__fixups__"
+    run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__local_fixups__"
+
+    run_test overlay overlay_base_manual_symbols.test.dtb overlay_overlay_manual_fixups.test.dtb
+
+    # test simplified plugin syntax
+    run_dtc_test -@ -I dts -O dtb -o overlay_overlay_simple.dtb overlay_overlay_simple.dts
+
+    # verify non-generation of local fixups
+    run_test check_path overlay_overlay_simple.dtb not-exists "/__local_fixups__"
+
+    # Bad fixup tests
+    for test in $BAD_FIXUP_TREES; do
+	tree="overlay_bad_fixup_$test"
+	run_dtc_test -I dts -O dtb -o $tree.test.dtb $tree.dts
+	run_test overlay_bad_fixup overlay_base_no_symbols.test.dtb $tree.test.dtb
+    done
+}
+
+# Tests to exercise dtc's overlay generation support
+dtc_overlay_tests () {
+    # Overlay tests for dtc
+    run_dtc_test -@ -I dts -O dtb -o overlay_base.test.dtb overlay_base.dts
+    run_test check_path overlay_base.test.dtb exists "/__symbols__"
+    run_test check_path overlay_base.test.dtb not-exists "/__fixups__"
+    run_test check_path overlay_base.test.dtb not-exists "/__local_fixups__"
+
+    # With syntactic sugar
+    run_dtc_test -I dts -O dtb -o overlay_overlay.test.dtb overlay_overlay.dts
+    run_test check_path overlay_overlay.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_overlay.test.dtb exists "/__fixups__"
+    run_test check_path overlay_overlay.test.dtb exists "/__local_fixups__"
+
+    # Without syntactic sugar
+    run_dtc_test -I dts -O dtb -o overlay_overlay_nosugar.test.dtb overlay_overlay.dts
+    run_test check_path overlay_overlay_nosugar.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_overlay_nosugar.test.dtb exists "/__fixups__"
+    run_test check_path overlay_overlay_nosugar.test.dtb exists "/__local_fixups__"
+
+    # Using target-path
+    run_dtc_test -I dts -O dtb -o overlay_overlay_bypath.test.dtb overlay_overlay_bypath.dts
+    run_test check_path overlay_overlay_bypath.test.dtb not-exists "/__symbols__"
+    run_test check_path overlay_overlay_bypath.test.dtb not-exists "/__fixups__"
+    run_test check_path overlay_overlay_bypath.test.dtb exists "/__local_fixups__"
+
+    # Make sure local target references are resolved and nodes are merged and that path references are not
+    run_dtc_test -I dts -O dtb -o overlay_overlay_local_merge.test.dtb overlay_overlay_local_merge.dts
+    run_test check_path overlay_overlay_local_merge.test.dtb exists "/fragment@0/__overlay__/new-node/new-merged-node"
+    run_test check_path overlay_overlay_local_merge.test.dtb exists "/fragment@1/__overlay__/new-root-node"
+
+    # Check building works the same as manual constructions
+    run_test dtbs_equal_ordered overlay_overlay.test.dtb overlay_overlay_nosugar.test.dtb
+
+    run_dtc_test -I dts -O dtb -o overlay_overlay_manual_fixups.test.dtb overlay_overlay_manual_fixups.dts
+    run_test dtbs_equal_ordered overlay_overlay.test.dtb overlay_overlay_manual_fixups.test.dtb
+
+    run_dtc_test -I dts -O dtb -o overlay_overlay_no_fixups.test.dtb overlay_overlay_no_fixups.dts
+    run_test dtbs_equal_ordered overlay_overlay_bypath.test.dtb overlay_overlay_no_fixups.test.dtb
+
+    # Check we can actually apply the result
+    run_dtc_test -I dts -O dtb -o overlay_base_no_symbols.test.dtb overlay_base.dts
+    run_test overlay overlay_base.test.dtb overlay_overlay.test.dtb
+    run_test overlay overlay_base_no_symbols.test.dtb overlay_overlay_bypath.test.dtb
+
+    # test plugin source to dtb and back
+    run_dtc_test -I dtb -O dts -o overlay_overlay_decompile.test.dts overlay_overlay.test.dtb
+    run_dtc_test -I dts -O dtb -o overlay_overlay_decompile.test.dtb overlay_overlay_decompile.test.dts
+    run_test dtbs_equal_ordered overlay_overlay.test.dtb overlay_overlay_decompile.test.dtb
+
+    # Test generation of aliases instead of symbols
+    run_dtc_test -A -I dts -O dtb -o overlay_base_with_aliases.dtb overlay_base.dts
+    run_test check_path overlay_base_with_aliases.dtb exists "/aliases"
+    run_test check_path overlay_base_with_aliases.dtb not-exists "/__symbols__"
+    run_test check_path overlay_base_with_aliases.dtb not-exists "/__fixups__"
+    run_test check_path overlay_base_with_aliases.dtb not-exists "/__local_fixups__"
+}
+
+tree1_tests () {
+    TREE=$1
+
+    # Read-only tests
+    run_test get_mem_rsv $TREE
+    run_test root_node $TREE
+    run_test find_property $TREE
+    run_test subnode_offset $TREE
+    run_test path_offset $TREE
+    run_test get_name $TREE
+    run_test getprop $TREE
+    run_test get_prop_offset $TREE
+    run_test get_phandle $TREE
+    run_test get_path $TREE
+    run_test supernode_atdepth_offset $TREE
+    run_test parent_offset $TREE
+    run_test node_offset_by_prop_value $TREE
+    run_test node_offset_by_phandle $TREE
+    run_test node_check_compatible $TREE
+    run_test node_offset_by_compatible $TREE
+    run_test notfound $TREE
+
+    # Write-in-place tests
+    run_test setprop_inplace $TREE
+    run_test nop_property $TREE
+    run_test nop_node $TREE
+}
+
+tree1_tests_rw () {
+    TREE=$1
+
+    # Read-write tests
+    run_test set_name $TREE
+    run_test setprop $TREE
+    run_test del_property $TREE
+    run_test del_node $TREE
+}
+
+check_tests () {
+    tree="$1"
+    shift
+    run_sh_test dtc-checkfails.sh "$@" -- -I dts -O dtb $tree
+    run_dtc_test -I dts -O dtb -o $tree.test.dtb -f $tree
+    run_sh_test dtc-checkfails.sh "$@" -- -I dtb -O dtb $tree.test.dtb
+}
+
+ALL_LAYOUTS="mts mst tms tsm smt stm"
+
+libfdt_tests () {
+    tree1_tests test_tree1.dtb
+
+    run_dtc_test -I dts -O dtb -o addresses.test.dtb addresses.dts
+    run_test addr_size_cells addresses.test.dtb
+    run_dtc_test -I dts -O dtb -o addresses2.test.dtb empty.dts
+    run_test addr_size_cells2 addresses2.test.dtb
+
+    run_dtc_test -I dts -O dtb -o stringlist.test.dtb stringlist.dts
+    run_test stringlist stringlist.test.dtb
+
+    for flags in default no_name_dedup; do
+        # Sequential write tests
+        run_test sw_tree1 fixed $flags
+        tree1_tests sw_tree1.test.dtb
+        tree1_tests unfinished_tree1.test.dtb
+        run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb
+        run_test sw_states
+
+        # Resizing tests
+        for mode in resize realloc newalloc; do
+            run_test sw_tree1 $mode $flags
+            tree1_tests sw_tree1.test.dtb
+            tree1_tests unfinished_tree1.test.dtb
+            run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb
+        done
+    done
+
+    # fdt_move tests
+    for tree in test_tree1.dtb sw_tree1.test.dtb unfinished_tree1.test.dtb; do
+	rm -f moved.$tree shunted.$tree deshunted.$tree
+	run_test move_and_save $tree
+	run_test dtbs_equal_ordered $tree moved.$tree
+	run_test dtbs_equal_ordered $tree shunted.$tree
+	run_test dtbs_equal_ordered $tree deshunted.$tree
+    done
+
+    # v16 and alternate layout tests
+    for tree in test_tree1.dtb; do
+	for version in 17 16; do
+	    for layout in $ALL_LAYOUTS; do
+		run_test mangle-layout $tree $version $layout
+		tree1_tests v$version.$layout.$tree
+		run_test dtbs_equal_ordered $tree v$version.$layout.$tree
+	    done
+	done
+    done
+
+    # Read-write tests
+    for basetree in test_tree1.dtb; do
+	for version in 17 16; do
+	    for layout in $ALL_LAYOUTS; do
+		tree=v$version.$layout.$basetree
+		rm -f opened.$tree repacked.$tree
+		run_test open_pack $tree
+		tree1_tests opened.$tree
+		tree1_tests repacked.$tree
+
+		tree1_tests_rw $tree
+		tree1_tests_rw opened.$tree
+		tree1_tests_rw repacked.$tree
+	    done
+	done
+    done
+    run_test rw_tree1
+    tree1_tests rw_tree1.test.dtb
+    tree1_tests_rw rw_tree1.test.dtb
+    run_test appendprop1
+    run_test appendprop2 appendprop1.test.dtb
+    run_dtc_test -I dts -O dtb -o appendprop.test.dtb appendprop.dts
+    run_test dtbs_equal_ordered appendprop2.test.dtb appendprop.test.dtb
+    libfdt_overlay_tests
+
+    for basetree in test_tree1.dtb sw_tree1.test.dtb rw_tree1.test.dtb; do
+	run_test nopulate $basetree
+	run_test dtbs_equal_ordered $basetree noppy.$basetree
+	tree1_tests noppy.$basetree
+	tree1_tests_rw noppy.$basetree
+    done
+
+    run_test rw_oom
+
+    run_dtc_test -I dts -O dtb -o subnode_iterate.dtb subnode_iterate.dts
+    run_test subnode_iterate subnode_iterate.dtb
+
+    run_dtc_test -I dts -O dtb -o property_iterate.dtb property_iterate.dts
+    run_test property_iterate property_iterate.dtb
+
+    run_dtc_test -I dts -O dtb -o unit-addr-without-reg.dtb unit-addr-without-reg.dts
+    run_test appendprop_addrrange unit-addr-without-reg.dtb 1 1 1
+    run_test appendprop_addrrange unit-addr-without-reg.dtb 2 2 2
+    run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3
+
+    # Tests for behaviour on various sorts of corrupted trees
+    run_test truncated_property
+    run_test truncated_string
+    run_test truncated_memrsv
+
+    # Check aliases support in fdt_path_offset
+    run_dtc_test -I dts -O dtb -o aliases.dtb aliases.dts
+    run_test get_alias aliases.dtb
+    run_test path_offset_aliases aliases.dtb
+
+    # Specific bug tests
+    run_test add_subnode_with_nops
+    run_dtc_test -I dts -O dts -o sourceoutput.test.dts sourceoutput.dts
+    run_dtc_test -I dts -O dtb -o sourceoutput.test.dtb sourceoutput.dts
+    run_dtc_test -I dts -O dtb -o sourceoutput.test.dts.test.dtb sourceoutput.test.dts
+    run_test dtbs_equal_ordered sourceoutput.test.dtb sourceoutput.test.dts.test.dtb
+
+    run_dtc_test -I dts -O dtb -o embedded_nul.test.dtb embedded_nul.dts
+    run_dtc_test -I dts -O dtb -o embedded_nul_equiv.test.dtb embedded_nul_equiv.dts
+    run_test dtbs_equal_ordered embedded_nul.test.dtb embedded_nul_equiv.test.dtb
+
+    run_dtc_test -I dts -O dtb bad-size-cells.dts
+
+    run_wrap_error_test $DTC division-by-zero.dts
+    run_wrap_error_test $DTC bad-octal-literal.dts
+    run_dtc_test -I dts -O dtb nul-in-escape.dts
+    run_wrap_error_test $DTC nul-in-line-info1.dts
+    run_wrap_error_test $DTC nul-in-line-info2.dts
+
+    run_wrap_error_test $DTC -I dtb -O dts -o /dev/null ovf_size_strings.dtb
+
+    run_test check_header test_tree1.dtb
+
+    FSBASE=fs
+    rm -rf $FSBASE
+    mkdir -p $FSBASE
+    run_test fs_tree1 $FSBASE/test_tree1
+    run_dtc_test -I fs -O dts -o fs.test_tree1.test.dts $FSBASE/test_tree1
+    run_dtc_test -I fs -O dtb -o fs.test_tree1.test.dtb $FSBASE/test_tree1
+    run_test dtbs_equal_unordered -m fs.test_tree1.test.dtb test_tree1.dtb
+
+    # check full tests
+    for good in test_tree1.dtb; do
+	run_test check_full $good
+    done
+    for bad in truncated_property.dtb truncated_string.dtb \
+				      truncated_memrsv.dtb; do
+	run_test check_full -n $bad
+    done
+}
+
+dtc_tests () {
+    run_dtc_test -I dts -O dtb -o dtc_tree1.test.dtb test_tree1.dts
+    tree1_tests dtc_tree1.test.dtb
+    tree1_tests_rw dtc_tree1.test.dtb
+    run_test dtbs_equal_ordered dtc_tree1.test.dtb test_tree1.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_escapes.test.dtb propname_escapes.dts
+    run_test propname_escapes dtc_escapes.test.dtb
+
+    run_dtc_test -I dts -O dtb -o line_directives.test.dtb line_directives.dts
+
+    run_dtc_test -I dts -O dtb -o dtc_escapes.test.dtb escapes.dts
+    run_test string_escapes dtc_escapes.test.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_char_literal.test.dtb char_literal.dts
+    run_test char_literal dtc_char_literal.test.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_sized_cells.test.dtb sized_cells.dts
+    run_test sized_cells dtc_sized_cells.test.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_extra-terminating-null.test.dtb extra-terminating-null.dts
+    run_test extra-terminating-null dtc_extra-terminating-null.test.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_references.test.dtb references.dts
+    run_test references dtc_references.test.dtb
+
+    run_dtc_test -I dts -O dtb -o dtc_path-references.test.dtb path-references.dts
+    run_test path-references dtc_path-references.test.dtb
+
+    run_test phandle_format dtc_references.test.dtb epapr
+    for f in legacy epapr both; do
+	run_dtc_test -I dts -O dtb -H $f -o dtc_references.test.$f.dtb references.dts
+	run_test phandle_format dtc_references.test.$f.dtb $f
+    done
+
+    run_dtc_test -I dts -O dtb -o multilabel.test.dtb multilabel.dts
+    run_test references multilabel.test.dtb
+
+    run_dtc_test -I dts -O dtb -o label_repeated.test.dtb label_repeated.dts
+
+    run_dtc_test -I dts -O dtb -o dtc_comments.test.dtb comments.dts
+    run_dtc_test -I dts -O dtb -o dtc_comments-cmp.test.dtb comments-cmp.dts
+    run_test dtbs_equal_ordered dtc_comments.test.dtb dtc_comments-cmp.test.dtb
+
+    # Check /include/ directive
+    run_dtc_test -I dts -O dtb -o includes.test.dtb include0.dts
+    run_test dtbs_equal_ordered includes.test.dtb test_tree1.dtb
+
+    # Check /incbin/ directive
+    run_dtc_test -I dts -O dtb -o incbin.test.dtb incbin.dts
+    run_test incbin incbin.test.dtb
+
+    # Check boot_cpuid_phys handling
+    run_dtc_test -I dts -O dtb -o boot_cpuid.test.dtb boot-cpuid.dts
+    run_test boot-cpuid boot_cpuid.test.dtb 16
+
+    run_dtc_test -I dts -O dtb -b 17 -o boot_cpuid_17.test.dtb boot-cpuid.dts
+    run_test boot-cpuid boot_cpuid_17.test.dtb 17
+
+    run_dtc_test -I dtb -O dtb -o preserve_boot_cpuid.test.dtb boot_cpuid.test.dtb
+    run_test boot-cpuid preserve_boot_cpuid.test.dtb 16
+    run_test dtbs_equal_ordered preserve_boot_cpuid.test.dtb boot_cpuid.test.dtb
+
+    run_dtc_test -I dtb -O dtb -o preserve_boot_cpuid_17.test.dtb boot_cpuid_17.test.dtb
+    run_test boot-cpuid preserve_boot_cpuid_17.test.dtb 17
+    run_test dtbs_equal_ordered preserve_boot_cpuid_17.test.dtb boot_cpuid_17.test.dtb
+
+    run_dtc_test -I dtb -O dtb -b17 -o override17_boot_cpuid.test.dtb boot_cpuid.test.dtb
+    run_test boot-cpuid override17_boot_cpuid.test.dtb 17
+
+    run_dtc_test -I dtb -O dtb -b0 -o override0_boot_cpuid_17.test.dtb boot_cpuid_17.test.dtb
+    run_test boot-cpuid override0_boot_cpuid_17.test.dtb 0
+
+
+    # Check -Oasm mode
+    for tree in test_tree1.dts escapes.dts references.dts path-references.dts \
+	comments.dts aliases.dts include0.dts incbin.dts \
+	value-labels.dts ; do
+	run_dtc_test -I dts -O asm -o oasm_$tree.test.s $tree
+	asm_to_so_test oasm_$tree
+	run_dtc_test -I dts -O dtb -o $tree.test.dtb $tree
+	run_test asm_tree_dump ./oasm_$tree.test.so oasm_$tree.test.dtb
+	run_wrap_test cmp oasm_$tree.test.dtb $tree.test.dtb
+    done
+
+    run_test value-labels ./oasm_value-labels.dts.test.so
+
+    # Check -Odts mode preserve all dtb information
+    for tree in test_tree1.dtb dtc_tree1.test.dtb dtc_escapes.test.dtb \
+	dtc_extra-terminating-null.test.dtb dtc_references.test.dtb; do
+	run_dtc_test -I dtb -O dts -o odts_$tree.test.dts $tree
+	run_dtc_test -I dts -O dtb -o odts_$tree.test.dtb odts_$tree.test.dts
+	run_test dtbs_equal_ordered $tree odts_$tree.test.dtb
+    done
+
+    # Check -Odts preserving type information
+    for tree in type-preservation.dts; do
+        run_dtc_test -I dts -O dts -o $tree.test.dts $tree
+        run_dtc_test -I dts -O dts $tree.test.dts
+        run_wrap_test cmp $tree $tree.test.dts
+    done
+    for tree in path-references; do
+        run_dtc_test -I dts -O dtb -o $tree.test.dtb $tree.dts
+        run_dtc_test -I dts -O dts -o $tree.test.dts $tree.dts
+        run_dtc_test -I dts -O dtb -o $tree.test.dts.test.dtb $tree.test.dts
+        run_test dtbs_equal_ordered $tree.test.dtb $tree.test.dts.test.dtb
+    done
+
+    # Check -Oyaml output
+    if pkg-config --exists yaml-0.1; then
+            for tree in type-preservation; do
+                run_dtc_test -I dts -O yaml -o $tree.test.dt.yaml $tree.dts
+                run_wrap_test cmp $tree.dt.yaml $tree.test.dt.yaml
+            done
+    fi
+
+    # Check version conversions
+    for tree in test_tree1.dtb ; do
+	 for aver in 1 2 3 16 17; do
+	     atree="ov${aver}_$tree.test.dtb"
+	     run_dtc_test -I dtb -O dtb -V$aver -o $atree $tree
+	     for bver in 16 17; do
+		 btree="ov${bver}_$atree"
+		 run_dtc_test -I dtb -O dtb -V$bver -o $btree $atree
+		 run_test dtbs_equal_ordered $btree $tree
+	     done
+	 done
+    done
+
+    # Check merge/overlay functionality
+    run_dtc_test -I dts -O dtb -o dtc_tree1_merge.test.dtb test_tree1_merge.dts
+    tree1_tests dtc_tree1_merge.test.dtb test_tree1.dtb
+    run_dtc_test -I dts -O dtb -o dtc_tree1_merge_labelled.test.dtb test_tree1_merge_labelled.dts
+    tree1_tests dtc_tree1_merge_labelled.test.dtb test_tree1.dtb
+    run_dtc_test -I dts -O dtb -o dtc_tree1_label_noderef.test.dtb test_tree1_label_noderef.dts
+    run_test dtbs_equal_unordered dtc_tree1_label_noderef.test.dtb test_tree1.dtb
+    run_dtc_test -I dts -O dtb -o multilabel_merge.test.dtb multilabel_merge.dts
+    run_test references multilabel.test.dtb
+    run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb
+    run_dtc_test -I dts -O dtb -o dtc_tree1_merge_path.test.dtb test_tree1_merge_path.dts
+    tree1_tests dtc_tree1_merge_path.test.dtb test_tree1.dtb
+    run_wrap_error_test $DTC -I dts -O dtb -o /dev/null test_label_ref.dts
+
+    # Check prop/node delete functionality
+    run_dtc_test -I dts -O dtb -o dtc_tree1_delete.test.dtb test_tree1_delete.dts
+    tree1_tests dtc_tree1_delete.test.dtb
+
+    # Check omit-if-no-ref functionality
+    run_dtc_test -I dts -O dtb -o omit-no-ref.test.dtb omit-no-ref.dts
+    run_test check_path omit-no-ref.test.dtb not-exists "/node1"
+    run_test check_path omit-no-ref.test.dtb not-exists "/node2"
+    run_test check_path omit-no-ref.test.dtb exists "/node3"
+    run_test check_path omit-no-ref.test.dtb exists "/node4"
+
+    run_dtc_test -I dts -O dts -o delete_reinstate_multilabel.dts.test.dts delete_reinstate_multilabel.dts
+    run_wrap_test cmp delete_reinstate_multilabel.dts.test.dts delete_reinstate_multilabel_ref.dts
+
+    # Check some checks
+    check_tests dup-nodename.dts duplicate_node_names
+    check_tests dup-propname.dts duplicate_property_names
+    check_tests dup-phandle.dts explicit_phandles
+    check_tests zero-phandle.dts explicit_phandles
+    check_tests minusone-phandle.dts explicit_phandles
+    run_sh_test dtc-checkfails.sh phandle_references -- -I dts -O dtb nonexist-node-ref.dts
+    run_sh_test dtc-checkfails.sh phandle_references -- -I dts -O dtb nonexist-label-ref.dts
+    run_sh_test dtc-fatal.sh -I dts -O dtb nonexist-node-ref2.dts
+    check_tests bad-name-property.dts name_properties
+
+    check_tests bad-ncells.dts address_cells_is_cell size_cells_is_cell interrupt_cells_is_cell
+    check_tests bad-string-props.dts device_type_is_string model_is_string status_is_string label_is_string compatible_is_string_list names_is_string_list
+    check_tests bad-chosen.dts chosen_node_is_root
+    check_tests bad-chosen.dts chosen_node_bootargs
+    check_tests bad-chosen.dts chosen_node_stdout_path
+    check_tests bad-reg-ranges.dts reg_format ranges_format
+    check_tests bad-empty-ranges.dts ranges_format
+    check_tests reg-ranges-root.dts reg_format ranges_format
+    check_tests default-addr-size.dts avoid_default_addr_size
+    check_tests obsolete-chosen-interrupt-controller.dts obsolete_chosen_interrupt_controller
+    check_tests reg-without-unit-addr.dts unit_address_vs_reg
+    check_tests unit-addr-without-reg.dts unit_address_vs_reg
+    check_tests unit-addr-leading-0x.dts unit_address_format
+    check_tests unit-addr-leading-0s.dts unit_address_format
+    check_tests unit-addr-unique.dts unique_unit_address
+    check_tests bad-phandle-cells.dts interrupts_extended_property
+    check_tests bad-gpio.dts gpios_property
+    check_tests bad-graph.dts graph_child_address
+    check_tests bad-graph.dts graph_port
+    check_tests bad-graph.dts graph_endpoint
+    run_sh_test dtc-checkfails.sh deprecated_gpio_property -- -Wdeprecated_gpio_property -I dts -O dtb bad-gpio.dts
+    check_tests bad-interrupt-cells.dts interrupts_property
+    run_sh_test dtc-checkfails.sh node_name_chars -- -I dtb -O dtb bad_node_char.dtb
+    run_sh_test dtc-checkfails.sh node_name_format -- -I dtb -O dtb bad_node_format.dtb
+    run_sh_test dtc-checkfails.sh property_name_chars -- -I dtb -O dtb bad_prop_char.dtb
+
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label1.dts
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label2.dts
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label3.dts
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label4.dts
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label5.dts
+    run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label6.dts
+
+    run_test check_path test_tree1.dtb exists "/subnode@1"
+    run_test check_path test_tree1.dtb not-exists "/subnode@10"
+
+    check_tests pci-bridge-ok.dts -n pci_bridge
+    check_tests pci-bridge-bad1.dts pci_bridge
+    check_tests pci-bridge-bad2.dts pci_bridge
+
+    check_tests unit-addr-simple-bus-reg-mismatch.dts simple_bus_reg
+    check_tests unit-addr-simple-bus-compatible.dts simple_bus_reg
+
+
+    # Check warning options
+    run_sh_test dtc-checkfails.sh address_cells_is_cell interrupt_cells_is_cell -n size_cells_is_cell -- -Wno_size_cells_is_cell -I dts -O dtb bad-ncells.dts
+    run_sh_test dtc-fails.sh -n test-warn-output.test.dtb -I dts -O dtb bad-ncells.dts
+    run_sh_test dtc-fails.sh test-error-output.test.dtb -I dts -O dtb bad-ncells.dts -Esize_cells_is_cell
+    run_sh_test dtc-checkfails.sh always_fail -- -Walways_fail -I dts -O dtb test_tree1.dts
+    run_sh_test dtc-checkfails.sh -n always_fail -- -Walways_fail -Wno_always_fail -I dts -O dtb test_tree1.dts
+    run_sh_test dtc-fails.sh test-negation-1.test.dtb -Ealways_fail -I dts -O dtb test_tree1.dts
+    run_sh_test dtc-fails.sh -n test-negation-2.test.dtb -Ealways_fail -Eno_always_fail -I dts -O dtb test_tree1.dts
+    run_sh_test dtc-fails.sh test-negation-3.test.dtb -Ealways_fail -Wno_always_fail -I dts -O dtb test_tree1.dts
+    run_sh_test dtc-fails.sh -n test-negation-4.test.dtb -Esize_cells_is_cell -Eno_size_cells_is_cell -I dts -O dtb bad-ncells.dts
+    run_sh_test dtc-checkfails.sh size_cells_is_cell -- -Esize_cells_is_cell -Eno_size_cells_is_cell -I dts -O dtb bad-ncells.dts
+
+    # Check for proper behaviour reading from stdin
+    run_dtc_test -I dts -O dtb -o stdin_dtc_tree1.test.dtb - < test_tree1.dts
+    run_wrap_test cmp stdin_dtc_tree1.test.dtb dtc_tree1.test.dtb
+    run_dtc_test -I dtb -O dts -o stdin_odts_test_tree1.dtb.test.dts - < test_tree1.dtb
+    run_wrap_test cmp stdin_odts_test_tree1.dtb.test.dts odts_test_tree1.dtb.test.dts
+
+    # Check integer expresisons
+    run_test integer-expressions -g integer-expressions.test.dts
+    run_dtc_test -I dts -O dtb -o integer-expressions.test.dtb integer-expressions.test.dts
+    run_test integer-expressions integer-expressions.test.dtb
+
+    # Check for graceful failure in some error conditions
+    run_sh_test dtc-fatal.sh -I dts -O dtb nosuchfile.dts
+    run_sh_test dtc-fatal.sh -I dtb -O dtb nosuchfile.dtb
+    run_sh_test dtc-fatal.sh -I fs -O dtb nosuchfile
+
+    # Dependencies
+    run_dtc_test -I dts -O dtb -o dependencies.test.dtb -d dependencies.test.d dependencies.dts
+    run_wrap_test cmp dependencies.test.d dependencies.cmp
+
+    # Search paths
+    run_wrap_error_test $DTC -I dts -O dtb -o search_paths.dtb search_paths.dts
+    run_dtc_test -i search_dir -I dts -O dtb -o search_paths.dtb \
+	search_paths.dts
+    run_wrap_error_test $DTC -i search_dir_b -I dts -O dtb \
+	-o search_paths_b.dtb search_paths_b.dts
+    run_dtc_test -i search_dir_b -i search_dir -I dts -O dtb \
+	-o search_paths_b.dtb search_paths_b.dts
+    run_dtc_test -I dts -O dtb -o search_paths_subdir.dtb \
+	search_dir_b/search_paths_subdir.dts
+
+    # Check -a option
+    for align in 2 4 8 16 32 64; do
+	# -p -a
+	run_dtc_test -O dtb -p 1000 -a $align -o align0.dtb subnode_iterate.dts
+	base_run_test check_align align0.dtb $align
+	# -S -a
+	run_dtc_test -O dtb -S 1999 -a $align -o align1.dtb subnode_iterate.dts
+	base_run_test check_align align1.dtb $align
+    done
+
+    # Tests for overlay/plugin generation
+    dtc_overlay_tests
+}
+
+cmp_tests () {
+    basetree="$1"
+    shift
+    wrongtrees="$@"
+
+    run_test dtb_reverse $basetree
+
+    # First dtbs_equal_ordered
+    run_test dtbs_equal_ordered $basetree $basetree
+    run_test dtbs_equal_ordered -n $basetree $basetree.reversed.test.dtb
+    for tree in $wrongtrees; do
+	run_test dtbs_equal_ordered -n $basetree $tree
+    done
+
+    # now unordered
+    run_test dtbs_equal_unordered $basetree $basetree
+    run_test dtbs_equal_unordered $basetree $basetree.reversed.test.dtb
+    run_test dtbs_equal_unordered $basetree.reversed.test.dtb $basetree
+    for tree in $wrongtrees; do
+	run_test dtbs_equal_unordered -n $basetree $tree
+    done
+
+    # now dtc --sort
+    run_dtc_test -I dtb -O dtb -s -o $basetree.sorted.test.dtb $basetree
+    run_test dtbs_equal_unordered $basetree $basetree.sorted.test.dtb
+    run_dtc_test -I dtb -O dtb -s -o $basetree.reversed.sorted.test.dtb $basetree.reversed.test.dtb
+    run_test dtbs_equal_unordered $basetree.reversed.test.dtb $basetree.reversed.sorted.test.dtb
+    run_test dtbs_equal_ordered $basetree.sorted.test.dtb $basetree.reversed.sorted.test.dtb
+}
+
+dtbs_equal_tests () {
+    WRONG_TREE1=""
+    for x in 1 2 3 4 5 6 7 8 9; do
+	run_dtc_test -I dts -O dtb -o test_tree1_wrong$x.test.dtb test_tree1_wrong$x.dts
+	WRONG_TREE1="$WRONG_TREE1 test_tree1_wrong$x.test.dtb"
+    done
+    cmp_tests test_tree1.dtb $WRONG_TREE1
+}
+
+fdtget_tests () {
+    dts=label01.dts
+    dtb=$dts.fdtget.test.dtb
+    run_dtc_test -O dtb -o $dtb $dts
+
+    # run_fdtget_test <expected-result> [<flags>] <file> <node> <property>
+    run_fdtget_test "MyBoardName" $dtb / model
+    run_fdtget_test "MyBoardName MyBoardFamilyName" $dtb / compatible
+    run_fdtget_test "77 121 66 111 \
+97 114 100 78 97 109 101 0 77 121 66 111 97 114 100 70 97 109 105 \
+108 121 78 97 109 101 0" -t bu $dtb / compatible
+    run_fdtget_test "MyBoardName MyBoardFamilyName" -t s $dtb / compatible
+    run_fdtget_test 32768 $dtb /cpus/PowerPC,970@1 d-cache-size
+    run_fdtget_test 8000 -tx $dtb /cpus/PowerPC,970@1 d-cache-size
+    run_fdtget_test "61 62 63 0" -tbx $dtb /randomnode tricky1
+    run_fdtget_test "a b c d de ea ad be ef" -tbx $dtb /randomnode blob
+
+    # Here the property size is not a multiple of 4 bytes, so it should fail
+    run_wrap_error_test $DTGET -tlx $dtb /randomnode mixed
+    run_fdtget_test "6162 6300 1234 0 a 0 b 0 c" -thx $dtb /randomnode mixed
+    run_fdtget_test "61 62 63 0 12 34 0 0 0 a 0 0 0 b 0 0 0 c" \
+	-thhx $dtb /randomnode mixed
+    run_wrap_error_test $DTGET -ts $dtb /randomnode doctor-who
+
+    # Test multiple arguments
+    run_fdtget_test "MyBoardName\nmemory" -ts $dtb / model /memory device_type
+
+    # Test defaults
+    run_wrap_error_test $DTGET -tx $dtb /randomnode doctor-who
+    run_fdtget_test "<the dead silence>" -tx \
+	-d "<the dead silence>" $dtb /randomnode doctor-who
+    run_fdtget_test "<blink>" -tx -d "<blink>" $dtb /memory doctor-who
+}
+
+fdtput_tests () {
+    dts=label01.dts
+    dtb=$dts.fdtput.test.dtb
+    text=lorem.txt
+
+    # Allow just enough space for $text
+    run_dtc_test -O dtb -p $($STATSZ $text) -o $dtb $dts
+
+    # run_fdtput_test <expected-result> <file> <node> <property> <flags> <value>
+    run_fdtput_test "a_model" $dtb / model -ts "a_model"
+    run_fdtput_test "board1 board2" $dtb / compatible -ts board1 board2
+    run_fdtput_test "board1 board2" $dtb / compatible -ts "board1 board2"
+    run_fdtput_test "32768" $dtb /cpus/PowerPC,970@1 d-cache-size "" "32768"
+    run_fdtput_test "8001" $dtb /cpus/PowerPC,970@1 d-cache-size -tx 0x8001
+    run_fdtput_test "2 3 12" $dtb /randomnode tricky1 -tbi "02 003 12"
+    run_fdtput_test "a b c ea ad be ef" $dtb /randomnode blob \
+	-tbx "a b c ea ad be ef"
+    run_fdtput_test "a0b0c0d deeaae ef000000" $dtb /randomnode blob \
+	-tx "a0b0c0d deeaae ef000000"
+    run_fdtput_test "$(cat $text)" $dtb /randomnode blob -ts "$(cat $text)"
+
+    # Test expansion of the blob when insufficient room for property
+    run_fdtput_test "$(cat $text $text)" $dtb /randomnode blob -ts "$(cat $text $text)"
+
+    # Start again with a fresh dtb
+    run_dtc_test -O dtb -p $($STATSZ $text) -o $dtb $dts
+
+    # Node creation
+    run_wrap_error_test $DTPUT $dtb -c /baldrick sod
+    run_wrap_test $DTPUT $dtb -c /chosen/son /chosen/daughter
+    run_fdtput_test "eva" $dtb /chosen/daughter name "" -ts "eva"
+    run_fdtput_test "adam" $dtb /chosen/son name "" -ts "adam"
+
+    # Not allowed to create an existing node
+    run_wrap_error_test $DTPUT $dtb -c /chosen
+    run_wrap_error_test $DTPUT $dtb -c /chosen/son
+
+    # Automatic node creation
+    run_wrap_test $DTPUT $dtb -cp /blackadder/the-second/turnip \
+	/blackadder/the-second/potato
+    run_fdtput_test 1000 $dtb /blackadder/the-second/turnip cost "" 1000
+    run_fdtput_test "fine wine" $dtb /blackadder/the-second/potato drink \
+	"-ts" "fine wine"
+    run_wrap_test $DTPUT $dtb -p /you/are/drunk/sir/winston slurp -ts twice
+
+    # Test expansion of the blob when insufficient room for a new node
+    run_wrap_test $DTPUT $dtb -cp "$(cat $text $text)/longish"
+
+    # Allowed to create an existing node with -p
+    run_wrap_test $DTPUT $dtb -cp /chosen
+    run_wrap_test $DTPUT $dtb -cp /chosen/son
+
+    # Start again with a fresh dtb
+    run_dtc_test -O dtb -p $($STATSZ $text) -o $dtb $dts
+
+    # Node delete
+    run_wrap_test $DTPUT $dtb -c /chosen/node1 /chosen/node2 /chosen/node3
+    run_fdtget_test "node3\nnode2\nnode1" $dtb -l  /chosen
+    run_wrap_test $DTPUT $dtb -r /chosen/node1 /chosen/node2
+    run_fdtget_test "node3" $dtb -l  /chosen
+
+    # Delete the non-existent node
+    run_wrap_error_test $DTPUT $dtb -r /non-existent/node
+
+    # Property delete
+    run_fdtput_test "eva" $dtb /chosen/ name "" -ts "eva"
+    run_fdtput_test "016" $dtb /chosen/ age  "" -ts "016"
+    run_fdtget_test "age\nname\nbootargs\nlinux,platform" $dtb -p  /chosen
+    run_wrap_test $DTPUT $dtb -d /chosen/ name age
+    run_fdtget_test "bootargs\nlinux,platform" $dtb -p  /chosen
+
+    # Delete the non-existent property
+    run_wrap_error_test $DTPUT $dtb -d /chosen   non-existent-prop
+
+    # TODO: Add tests for verbose mode?
+}
+
+utilfdt_tests () {
+    run_test utilfdt_test
+}
+
+fdtdump_tests () {
+    run_fdtdump_test fdtdump.dts
+}
+
+fdtoverlay_tests() {
+    base=overlay_base.dts
+    basedtb=overlay_base.fdoverlay.test.dtb
+    overlay=overlay_overlay_manual_fixups.dts
+    overlaydtb=overlay_overlay_manual_fixups.fdoverlay.test.dtb
+    targetdtb=target.fdoverlay.test.dtb
+
+    run_dtc_test -@ -I dts -O dtb -o $basedtb $base
+    run_dtc_test -@ -I dts -O dtb -o $overlaydtb $overlay
+
+    # test that the new property is installed
+    run_fdtoverlay_test foobar "/test-node" "test-str-property" "-ts" ${basedtb} ${targetdtb} ${overlaydtb}
+
+    stacked_base=stacked_overlay_base.dts
+    stacked_basedtb=stacked_overlay_base.fdtoverlay.test.dtb
+    stacked_bar=stacked_overlay_bar.dts
+    stacked_bardtb=stacked_overlay_bar.fdtoverlay.test.dtb
+    stacked_baz=stacked_overlay_baz.dts
+    stacked_bazdtb=stacked_overlay_baz.fdtoverlay.test.dtb
+    stacked_targetdtb=stacked_overlay_target.fdtoverlay.test.dtb
+
+    run_dtc_test -@ -I dts -O dtb -o $stacked_basedtb $stacked_base
+    run_dtc_test -@ -I dts -O dtb -o $stacked_bardtb $stacked_bar
+    run_dtc_test -@ -I dts -O dtb -o $stacked_bazdtb $stacked_baz
+
+    # test that baz correctly inserted the property
+    run_fdtoverlay_test baz "/foonode/barnode/baznode" "baz-property" "-ts" ${stacked_basedtb} ${stacked_targetdtb} ${stacked_bardtb} ${stacked_bazdtb}
+
+    overlay_long_path=overlay_overlay_long_path.dts
+    overlay_long_pathdtb=overlay_overlay_long_path.fdoverlay.test.dtb
+    target_long_pathdtb=overlay_overlay_long_path_target.fdoverlay.test.dtb
+    run_dtc_test -@ -I dts -O dtb -o $overlay_long_pathdtb $overlay_long_path
+
+    # test that fdtoverlay manages to apply overlays with long target path
+    run_fdtoverlay_test lpath "/test-node/sub-test-node/sub-test-node-with-very-long-target-path/test-0" "prop" "-ts" ${basedtb} ${target_long_pathdtb} ${overlay_long_pathdtb}
+
+    # test adding a label to the root of a fragment
+    stacked_base_nolabel=stacked_overlay_base_nolabel.dts
+    stacked_base_nolabeldtb=stacked_overlay_base_nolabel.test.dtb
+    stacked_addlabel=stacked_overlay_addlabel.dts
+    stacked_addlabeldtb=stacked_overlay_addlabel.test.dtb
+    stacked_addlabel_targetdtb=stacked_overlay_target_nolabel.fdtoverlay.test.dtb
+
+    run_dtc_test -@ -I dts -O dtb -o $stacked_base_nolabeldtb $stacked_base_nolabel
+    run_dtc_test -@ -I dts -O dtb -o $stacked_addlabeldtb $stacked_addlabel
+
+    run_fdtoverlay_test baz "/foonode/barnode/baznode" "baz-property" "-ts" ${stacked_base_nolabeldtb} ${stacked_addlabel_targetdtb} ${stacked_addlabeldtb} ${stacked_bardtb} ${stacked_bazdtb}
+}
+
+pylibfdt_tests () {
+    run_dtc_test -I dts -O dtb -o test_props.dtb test_props.dts
+    TMP=/tmp/tests.stderr.$$
+    $PYTHON pylibfdt_tests.py -v 2> $TMP
+
+    # Use the 'ok' message meaning the test passed, 'ERROR' meaning it failed
+    # and the summary line for total tests (e.g. 'Ran 17 tests in 0.002s').
+    # We could add pass + fail to get total tests, but this provides a useful
+    # sanity check.
+    pass_count=$(grep "ok$" $TMP | wc -l)
+    fail_count=$(grep "^ERROR: " $TMP | wc -l)
+    total_tests=$(sed -n 's/^Ran \([0-9]*\) tests.*$/\1/p' $TMP)
+    cat $TMP
+    rm $TMP
+
+    # Extract the test results and add them to our totals
+    tot_fail=$((tot_fail + $fail_count))
+    tot_pass=$((tot_pass + $pass_count))
+    tot_tests=$((tot_tests + $total_tests))
+}
+
+while getopts "vt:me" ARG ; do
+    case $ARG in
+	"v")
+	    unset QUIET_TEST
+	    ;;
+	"t")
+	    TESTSETS=$OPTARG
+	    ;;
+	"m")
+	    VALGRIND="valgrind --tool=memcheck -q --error-exitcode=$VGCODE"
+	    ;;
+	"e")
+	    STOP_ON_FAIL=1
+	    ;;
+    esac
+done
+
+if [ -z "$TESTSETS" ]; then
+    TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay"
+
+    # Test pylibfdt if the libfdt Python module is available.
+    if [ -f ../pylibfdt/_libfdt.so ] || [ -f ../pylibfdt/_libfdt.cpython-3*.so ]; then
+        TESTSETS="$TESTSETS pylibfdt"
+    fi
+fi
+
+# Make sure we don't have stale blobs lying around
+rm -f *.test.dtb *.test.dts
+
+for set in $TESTSETS; do
+    case $set in
+	"libfdt")
+	    libfdt_tests
+	    ;;
+	"utilfdt")
+	    utilfdt_tests
+	    ;;
+	"dtc")
+	    dtc_tests
+	    ;;
+	"dtbs_equal")
+	    dtbs_equal_tests
+	    ;;
+	"fdtget")
+	    fdtget_tests
+	    ;;
+	"fdtput")
+	    fdtput_tests
+	    ;;
+	"fdtdump")
+	    fdtdump_tests
+	    ;;
+	"pylibfdt")
+	    pylibfdt_tests
+	    ;;
+        "fdtoverlay")
+	    fdtoverlay_tests
+	    ;;
+    esac
+done
+
+echo "********** TEST SUMMARY"
+echo "*     Total testcases:	$tot_tests"
+echo "*                PASS:	$tot_pass"
+echo "*                FAIL:	$tot_fail"
+echo "*   Bad configuration:	$tot_config"
+if [ -n "$VALGRIND" ]; then
+    echo "*    valgrind errors:	$tot_vg"
+fi
+echo "* Strange test result:	$tot_strange"
+echo "**********"
+
+[ "$tot_tests" -eq "$tot_pass" ] || exit 1
diff --git a/tests/rw_oom.c b/tests/rw_oom.c
new file mode 100644
index 0000000..39fc312
--- /dev/null
+++ b/tests/rw_oom.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+/* This is not derived programmatically. May require adjustment to changes. */
+#define SPACE	285
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+#define OFF_CHECK(off, code) \
+	{ \
+		(off) = (code); \
+		if (off < 0) \
+			FAIL(#code ": %s", fdt_strerror(off)); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int err;
+	int offset, s1;
+	int strsize1, strsize2;
+
+	/*
+	 * Check OOM path, and check that property is cleaned up if it fails
+	 * with OOM, rather than adding an orphan name.
+	 *
+	 * SW OOM is tested with the realloc/resize strategies.
+	 */
+	test_init(argc, argv);
+
+	fdt = xmalloc(SPACE);
+
+	/* First create empty tree with SW */
+	CHECK(fdt_create_empty_tree(fdt, SPACE));
+
+	CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_1, TEST_SIZE_1));
+	CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_2, TEST_SIZE_2));
+
+	CHECK(fdt_setprop_string(fdt, 0, "compatible", "test_oom"));
+	CHECK(fdt_setprop_u32(fdt, 0, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_setprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
+	CHECK(fdt_setprop_string(fdt, 0, "prop-str", TEST_STRING_1));
+
+	OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@1"));
+	s1 = offset;
+
+	strsize1 = fdt_size_dt_strings(fdt);
+	err = fdt_setprop_string(fdt, s1, "unique", "subnode1");
+	if (err != -FDT_ERR_NOSPACE)
+		FAIL("fdt_setprop_string(fdt, s1, \"compatible\", \"subnode1\"): %s", fdt_strerror(err));
+	strsize2 = fdt_size_dt_strings(fdt);
+
+	if (strsize1 != strsize2)
+		FAIL("fdt_setprop NOSPACE error failed to clean up allocated string\n");
+	err = 0;
+
+	/* Ensure we failed in the right place */
+	CHECK(fdt_setprop_string(fdt, s1, "unique", ""));
+
+	CHECK(fdt_pack(fdt));
+
+	PASS();
+}
diff --git a/tests/rw_tree1.c b/tests/rw_tree1.c
new file mode 100644
index 0000000..1fe2351
--- /dev/null
+++ b/tests/rw_tree1.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE	65536
+
+#define CHECK(code) \
+	{ \
+		err = (code); \
+		if (err) \
+			FAIL(#code ": %s", fdt_strerror(err)); \
+	}
+
+#define OFF_CHECK(off, code) \
+	{ \
+		(off) = (code); \
+		if (off < 0) \
+			FAIL(#code ": %s", fdt_strerror(off)); \
+	}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int err;
+	int offset, s1, s2;
+
+	test_init(argc, argv);
+
+	fdt = xmalloc(SPACE);
+
+	/* First create empty tree with SW */
+	CHECK(fdt_create_empty_tree(fdt, SPACE));
+
+	CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_1, TEST_SIZE_1));
+	CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_2, TEST_SIZE_2));
+
+	CHECK(fdt_setprop_string(fdt, 0, "compatible", "test_tree1"));
+	CHECK(fdt_setprop_u32(fdt, 0, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_setprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
+	CHECK(fdt_setprop_string(fdt, 0, "prop-str", TEST_STRING_1));
+
+	OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@1"));
+	s1 = offset;
+	CHECK(fdt_setprop_string(fdt, s1, "compatible", "subnode1"));
+	CHECK(fdt_setprop_cell(fdt, s1, "prop-int", TEST_VALUE_1));
+	OFF_CHECK(offset, fdt_add_subnode(fdt, s1, "subsubnode"));
+	CHECK(fdt_setprop(fdt, offset, "compatible",
+			  "subsubnode1\0subsubnode", 23));
+	CHECK(fdt_setprop_cell(fdt, offset, "prop-int", TEST_VALUE_1));
+	OFF_CHECK(offset, fdt_add_subnode(fdt, s1, "ss1"));
+
+	OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@2"));
+	s2 = offset;
+	CHECK(fdt_setprop_cell(fdt, s2, "linux,phandle", PHANDLE_1));
+	CHECK(fdt_setprop_cell(fdt, s2, "prop-int", TEST_VALUE_2));
+	OFF_CHECK(offset, fdt_add_subnode(fdt, s2, "subsubnode@0"));
+	CHECK(fdt_setprop_cell(fdt, offset, "linux,phandle", PHANDLE_2));
+	CHECK(fdt_setprop(fdt, offset, "compatible",
+			  "subsubnode2\0subsubnode", 23));
+	CHECK(fdt_setprop_cell(fdt, offset, "prop-int", TEST_VALUE_2));
+	OFF_CHECK(offset, fdt_add_subnode(fdt, s2, "ss2"));
+
+	CHECK(fdt_pack(fdt));
+
+	save_blob("rw_tree1.test.dtb", fdt);
+
+	PASS();
+}
diff --git a/tests/search_dir/search_test.dtsi b/tests/search_dir/search_test.dtsi
new file mode 100644
index 0000000..217fb80
--- /dev/null
+++ b/tests/search_dir/search_test.dtsi
@@ -0,0 +1,4 @@
+/include/ "search_test2.dtsi"
+
+/ {
+};
diff --git a/tests/search_dir/search_test2.dtsi b/tests/search_dir/search_test2.dtsi
new file mode 100644
index 0000000..7b9099e
--- /dev/null
+++ b/tests/search_dir/search_test2.dtsi
@@ -0,0 +1,3 @@
+
+/ {
+};
diff --git a/tests/search_dir_b/search_paths_subdir.dts b/tests/search_dir_b/search_paths_subdir.dts
new file mode 100644
index 0000000..5c5c962
--- /dev/null
+++ b/tests/search_dir_b/search_paths_subdir.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/include/ "search_test_c.dtsi"
+
+/ {
+};
diff --git a/tests/search_dir_b/search_test_b.dtsi b/tests/search_dir_b/search_test_b.dtsi
new file mode 100644
index 0000000..b06a7d6
--- /dev/null
+++ b/tests/search_dir_b/search_test_b.dtsi
@@ -0,0 +1,4 @@
+/include/ "search_test_b2.dtsi"
+
+/ {
+};
diff --git a/tests/search_dir_b/search_test_b2.dtsi b/tests/search_dir_b/search_test_b2.dtsi
new file mode 100644
index 0000000..2526b43
--- /dev/null
+++ b/tests/search_dir_b/search_test_b2.dtsi
@@ -0,0 +1,5 @@
+
+/include/ "search_test.dtsi"
+
+/ {
+};
diff --git a/tests/search_dir_b/search_test_c.dtsi b/tests/search_dir_b/search_test_c.dtsi
new file mode 100644
index 0000000..336d7a2
--- /dev/null
+++ b/tests/search_dir_b/search_test_c.dtsi
@@ -0,0 +1,2 @@
+/ {
+};
diff --git a/tests/search_paths.dts b/tests/search_paths.dts
new file mode 100644
index 0000000..a2bf179
--- /dev/null
+++ b/tests/search_paths.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/include/ "search_test.dtsi"
+
+/ {
+};
diff --git a/tests/search_paths_b.dts b/tests/search_paths_b.dts
new file mode 100644
index 0000000..6ace6e2
--- /dev/null
+++ b/tests/search_paths_b.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/include/ "search_test_b.dtsi"
+
+/ {
+};
diff --git a/tests/set_name.c b/tests/set_name.c
new file mode 100644
index 0000000..a62cb58
--- /dev/null
+++ b/tests/set_name.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_set_name()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_set_name(void *fdt, const char *path, const char *newname)
+{
+	int offset;
+	const char *getname, *oldname;
+	int len, err;
+
+	oldname = strrchr(path, '/');
+	if (!oldname)
+		TEST_BUG();
+	oldname += 1;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find %s", path);
+
+	getname = fdt_get_name(fdt, offset, &len);
+	verbose_printf("fdt_get_name(%d) returns \"%s\" (len=%d)\n",
+		       offset, getname, len);
+	if (!getname)
+		FAIL("fdt_get_name(%d): %s", offset, fdt_strerror(len));
+
+	if (strcmp(getname, oldname) != 0)
+		FAIL("fdt_get_name(%s) returned \"%s\" instead of \"%s\"",
+		     path, getname, oldname);
+
+	if (len != strlen(getname))
+		FAIL("fdt_get_name(%s) returned length %d instead of %zd",
+		     path, len, strlen(getname));
+
+	err = fdt_set_name(fdt, offset, newname);
+	if (err)
+		FAIL("fdt_set_name(%d, \"%s\"): %s", offset, newname,
+		     fdt_strerror(err));
+
+	getname = fdt_get_name(fdt, offset, &len);
+	if (!getname)
+		FAIL("fdt_get_name(%d): %s", offset, fdt_strerror(len));
+
+	if (strcmp(getname, newname) != 0)
+		FAIL("fdt_get_name(%s) returned \"%s\" instead of \"%s\"",
+		     path, getname, newname);
+
+	if (len != strlen(getname))
+		FAIL("fdt_get_name(%s) returned length %d instead of %zd",
+		     path, len, strlen(getname));
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+	fdt = open_blob_rw(fdt);
+
+	check_set_name(fdt, "/subnode@1", "subnode@17");
+	check_set_name(fdt, "/subnode@2/subsubnode@0", "fred@0");
+	check_set_name(fdt, "/subnode@17/subsubnode", "something@0");
+
+	PASS();
+}
diff --git a/tests/setprop.c b/tests/setprop.c
new file mode 100644
index 0000000..fa3938d
--- /dev/null
+++ b/tests/setprop.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_setprop()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE		65536
+#define NEW_STRING	"here is quite a long test string, blah blah blah"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	void *buf;
+	const uint32_t *intp;
+	const char *strp;
+	int err;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	buf = xmalloc(SPACE);
+
+	err = fdt_open_into(fdt, buf, SPACE);
+	if (err)
+		FAIL("fdt_open_into(): %s", fdt_strerror(err));
+
+	fdt = buf;
+
+	intp = check_getprop_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+
+	verbose_printf("Old int value was 0x%08x\n", *intp);
+	err = fdt_setprop_string(fdt, 0, "prop-int", NEW_STRING);
+	if (err)
+		FAIL("Failed to set \"prop-int\" to \"%s\": %s",
+		     NEW_STRING, fdt_strerror(err));
+
+	strp = check_getprop_string(fdt, 0, "prop-int", NEW_STRING);
+	verbose_printf("New value is \"%s\"\n", strp);
+
+	strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1,
+			     TEST_STRING_1);
+
+	verbose_printf("Old string value was \"%s\"\n", strp);
+	err = fdt_setprop_empty(fdt, 0, "prop-str");
+	if (err)
+		FAIL("Failed to empty \"prop-str\": %s",
+		     fdt_strerror(err));
+
+	check_getprop(fdt, 0, "prop-str", 0, NULL);
+
+	err = fdt_setprop_u32(fdt, 0, "prop-u32", TEST_VALUE_2);
+	if (err)
+		FAIL("Failed to set \"prop-u32\" to 0x%08x: %s",
+		     TEST_VALUE_2, fdt_strerror(err));
+	check_getprop_cell(fdt, 0, "prop-u32", TEST_VALUE_2);
+
+	err = fdt_setprop_cell(fdt, 0, "prop-cell", TEST_VALUE_2);
+	if (err)
+		FAIL("Failed to set \"prop-cell\" to 0x%08x: %s",
+		     TEST_VALUE_2, fdt_strerror(err));
+	check_getprop_cell(fdt, 0, "prop-cell", TEST_VALUE_2);
+
+	err = fdt_setprop_u64(fdt, 0, "prop-u64", TEST_VALUE64_1);
+	if (err)
+		FAIL("Failed to set \"prop-u64\" to 0x%016llx: %s",
+		     TEST_VALUE64_1, fdt_strerror(err));
+	check_getprop_64(fdt, 0, "prop-u64", TEST_VALUE64_1);
+	
+	PASS();
+}
diff --git a/tests/setprop_inplace.c b/tests/setprop_inplace.c
new file mode 100644
index 0000000..7e1198d
--- /dev/null
+++ b/tests/setprop_inplace.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_setprop_inplace()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	const uint32_t *intp;
+	const uint64_t *int64p;
+	const char *strp;
+	char *xstr;
+	int xlen, i;
+	int err;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	intp = check_getprop_cell(fdt, 0, "prop-int", TEST_VALUE_1);
+
+	verbose_printf("Old int value was 0x%08x\n", *intp);
+	err = fdt_setprop_inplace_cell(fdt, 0, "prop-int", ~TEST_VALUE_1);
+	if (err)
+		FAIL("Failed to set \"prop-int\" to 0x%08x: %s",
+		     ~TEST_VALUE_1, fdt_strerror(err));
+	intp = check_getprop_cell(fdt, 0, "prop-int", ~TEST_VALUE_1);
+	verbose_printf("New int value is 0x%08x\n", *intp);
+
+	strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1,
+			     TEST_STRING_1);
+
+
+	int64p = check_getprop_64(fdt, 0, "prop-int64", TEST_VALUE64_1);
+
+	verbose_printf("Old int64 value was 0x%016" PRIx64 "\n", *int64p);
+	err = fdt_setprop_inplace_u64(fdt, 0, "prop-int64", ~TEST_VALUE64_1);
+	if (err)
+		FAIL("Failed to set \"prop-int64\" to 0x%016llx: %s",
+		     ~TEST_VALUE64_1, fdt_strerror(err));
+	int64p = check_getprop_64(fdt, 0, "prop-int64", ~TEST_VALUE64_1);
+	verbose_printf("New int64 value is 0x%016" PRIx64 "\n", *int64p);
+
+	strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1,
+			     TEST_STRING_1);
+
+	verbose_printf("Old string value was \"%s\"\n", strp);
+	xstr = strdup(strp);
+	xlen = strlen(xstr);
+	for (i = 0; i < xlen; i++)
+		xstr[i] = toupper(xstr[i]);
+	err = fdt_setprop_inplace(fdt, 0, "prop-str", xstr, xlen+1);
+	if (err)
+		FAIL("Failed to set \"prop-str\" to \"%s\": %s",
+		     xstr, fdt_strerror(err));
+
+	strp = check_getprop(fdt, 0, "prop-str", xlen+1, xstr);
+	verbose_printf("New string value is \"%s\"\n", strp);
+
+	err = fdt_setprop_inplace_namelen_partial(fdt, 0, "compatible",
+						  strlen("compatible"), 4,
+						  TEST_STRING_4_PARTIAL,
+						  strlen(TEST_STRING_4_PARTIAL));
+	if (err)
+		FAIL("Failed to set \"compatible\": %s\n", fdt_strerror(err));
+
+	check_getprop(fdt, 0, "compatible", strlen(TEST_STRING_4_RESULT) + 1,
+		      TEST_STRING_4_RESULT);
+
+	PASS();
+}
diff --git a/tests/sized_cells.c b/tests/sized_cells.c
new file mode 100644
index 0000000..9ca5a59
--- /dev/null
+++ b/tests/sized_cells.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for variable sized cells in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright (C) 2011 The Chromium Authors. All rights reserved.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_compare_properties(void *fdt,
+				     char const *name_one,
+				     char const *name_two)
+{
+	const void *propval;
+	int proplen;
+
+	propval = fdt_getprop(fdt, 0, name_one, &proplen);
+
+	if (!propval)
+		FAIL("fdt_getprop(\"%s\"): %s",
+		     name_one,
+		     fdt_strerror(proplen));
+
+	check_getprop(fdt, 0, name_two, proplen, propval);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	uint8_t expected_8[6] = {TEST_CHAR1,
+				 TEST_CHAR2,
+				 TEST_CHAR3,
+				 TEST_CHAR4,
+				 TEST_CHAR5,
+				 TEST_VALUE_1 >> 24};
+	fdt16_t expected_16[6];
+	fdt32_t expected_32[6];
+	fdt64_t expected_64[6];
+	int i;
+
+	for (i = 0; i < 5; ++i) {
+		expected_16[i] = cpu_to_fdt16(expected_8[i]);
+		expected_32[i] = cpu_to_fdt32(expected_8[i]);
+		expected_64[i] = cpu_to_fdt64(expected_8[i]);
+	}
+
+	expected_16[5] = cpu_to_fdt16(TEST_VALUE_1 >> 16);
+	expected_32[5] = cpu_to_fdt32(TEST_VALUE_1);
+	expected_64[5] = cpu_to_fdt64(TEST_ADDR_1);
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop(fdt, 0, "cells-8b", sizeof(expected_8), expected_8);
+	check_getprop(fdt, 0, "cells-16b", sizeof(expected_16), expected_16);
+	check_getprop(fdt, 0, "cells-32b", sizeof(expected_32), expected_32);
+	check_getprop(fdt, 0, "cells-64b", sizeof(expected_64), expected_64);
+
+	check_compare_properties(fdt, "cells-one-16b", "cells-one-32b");
+
+	PASS();
+}
diff --git a/tests/sized_cells.dts b/tests/sized_cells.dts
new file mode 100644
index 0000000..efea9f5
--- /dev/null
+++ b/tests/sized_cells.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	cells-8b = /bits/ 8 <'\r' 'b' '\0' '\'' '\xff' 0xde>;
+	cells-16b = /bits/ 16 <'\r' 'b' '\0' '\'' '\xff' 0xdead>;
+	cells-32b = /bits/ 32 <'\r' 'b' '\0' '\'' '\xff' 0xdeadbeef>;
+	cells-64b = /bits/ 64 <'\r' 'b' '\0' '\'' '\xff' 0xdeadbeef00000000>;
+
+	cells-one-16b = /bits/ 16 <0x1234 0x5678 0x0 0xffff>;
+	cells-one-32b = <0x12345678 0x0000ffff>;
+};
diff --git a/tests/sourceoutput.dts b/tests/sourceoutput.dts
new file mode 100644
index 0000000..5a7459b
--- /dev/null
+++ b/tests/sourceoutput.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	/* Some versions had an off-by-2 bug which caused an abort
+	 * when outputting labels within strings like this in source
+	 * format */
+	prop1: prop1 = start1: "foo", mid1: "bar" end1: ;
+
+	/* Make sure that we correctly handle source output of things
+	 * which could almost be expressed as strings, except for the
+	 * embedded labels */
+	prop2 = start2: [66 6f 6f], mid2: "bar" end2: ;
+};
+
diff --git a/tests/stacked_overlay_addlabel.dts b/tests/stacked_overlay_addlabel.dts
new file mode 100644
index 0000000..e7187a3
--- /dev/null
+++ b/tests/stacked_overlay_addlabel.dts
@@ -0,0 +1,15 @@
+/dts-v1/;
+/plugin/;
+/ {
+	frag1: fragment@1 {
+		target-path = "/foonode";
+		local: localinfo {
+		};
+		foo: __overlay__ {
+			overlay-1-property;
+			bar: barnode {
+				bar-property = "bar";
+			};
+		};
+	};
+};
diff --git a/tests/stacked_overlay_bar.dts b/tests/stacked_overlay_bar.dts
new file mode 100644
index 0000000..c646399
--- /dev/null
+++ b/tests/stacked_overlay_bar.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+/plugin/;
+/ {
+	fragment@1 {
+		target = <&foo>;
+		__overlay__ {
+			overlay-1-property;
+			bar: barnode {
+				bar-property = "bar";
+			};
+		};
+	};
+};
diff --git a/tests/stacked_overlay_base.dts b/tests/stacked_overlay_base.dts
new file mode 100644
index 0000000..2916423
--- /dev/null
+++ b/tests/stacked_overlay_base.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+/ {
+	foo: foonode {
+		foo-property = "foo";
+	};
+};
diff --git a/tests/stacked_overlay_base_nolabel.dts b/tests/stacked_overlay_base_nolabel.dts
new file mode 100644
index 0000000..0c47f0d
--- /dev/null
+++ b/tests/stacked_overlay_base_nolabel.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+/ {
+	foonode {
+		foo-property = "foo";
+	};
+};
diff --git a/tests/stacked_overlay_baz.dts b/tests/stacked_overlay_baz.dts
new file mode 100644
index 0000000..a52f0cc
--- /dev/null
+++ b/tests/stacked_overlay_baz.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+/plugin/;
+/ {
+	fragment@1 {
+		target = <&bar>;
+		__overlay__ {
+			overlay-2-property;
+			baz: baznode {
+				baz-property = "baz";
+			};
+		};
+	};
+};
diff --git a/tests/string_escapes.c b/tests/string_escapes.c
new file mode 100644
index 0000000..53c9dfc
--- /dev/null
+++ b/tests/string_escapes.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for string escapes in dtc
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_getprop(fdt, 0, "escape-str",
+		      strlen(TEST_STRING_2)+1, TEST_STRING_2);
+	check_getprop(fdt, 0, "escape-str-2",
+		      strlen(TEST_STRING_3)+1, TEST_STRING_3);
+
+	PASS();
+}
diff --git a/tests/stringlist.c b/tests/stringlist.c
new file mode 100644
index 0000000..bbc3020
--- /dev/null
+++ b/tests/stringlist.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for string handling
+ * Copyright (C) 2015 NVIDIA Corporation
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check_expected_failure(const void *fdt, const char *path,
+				   const char *property)
+{
+	int offset, err;
+
+	offset = fdt_path_offset(fdt, "/");
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	err = fdt_stringlist_count(fdt, offset, "#address-cells");
+	if (err != -FDT_ERR_BADVALUE)
+		FAIL("unexpectedly succeeded in parsing #address-cells\n");
+
+	err = fdt_stringlist_search(fdt, offset, "#address-cells", "foo");
+	if (err != -FDT_ERR_BADVALUE)
+		FAIL("found string in #address-cells: %d\n", err);
+
+	/*
+	 * Note that the #address-cells property contains a small 32-bit
+	 * unsigned integer, hence some bytes will be zero, and searching for
+	 * the empty string will succeed.
+	 *
+	 * The reason for this oddity is that the function will exit when the
+	 * first occurrence of the string is found, but in order to determine
+	 * that the property does not contain a valid string list it would
+	 * need to process the whole value.
+	 */
+	err = fdt_stringlist_search(fdt, offset, "#address-cells", "");
+	if (err != 0)
+		FAIL("empty string not found in #address-cells: %d\n", err);
+
+	/*
+	 * fdt_getprop_string() can successfully extract strings from
+	 * non-string properties. This is because it doesn't
+	 * necessarily parse the whole property value, which would be
+	 * necessary for it to determine if a valid string or string
+	 * list is present.
+	 */
+}
+
+static void check_string_count(const void *fdt, const char *path,
+			       const char *property, int count)
+{
+	int offset, err;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	err = fdt_stringlist_count(fdt, offset, property);
+	if (err < 0)
+		FAIL("Couldn't count strings in property %s of node %s: %d\n",
+		     property, path, err);
+
+	if (err != count)
+		FAIL("String count for property %s of node %s is %d instead of %d\n",
+		     path, property, err, count);
+}
+
+static void check_string_index(const void *fdt, const char *path,
+			       const char *property, const char *string,
+			       int idx)
+{
+	int offset, err;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	err = fdt_stringlist_search(fdt, offset, property, string);
+
+	if (err != idx)
+		FAIL("Index of %s in property %s of node %s is %d, expected %d\n",
+		     string, property, path, err, idx);
+}
+
+static void check_string(const void *fdt, const char *path,
+			 const char *property, int idx,
+			 const char *string)
+{
+	const char *result;
+	int offset, len;
+
+	offset = fdt_path_offset(fdt, path);
+	if (offset < 0)
+		FAIL("Couldn't find path %s", path);
+
+	result = fdt_stringlist_get(fdt, offset, property, idx, &len);
+	if (!result)
+		FAIL("Couldn't extract string %d from property %s of node %s: %d\n",
+		     idx, property, path, len);
+
+	if (strcmp(string, result) != 0)
+		FAIL("String %d in property %s of node %s is %s, expected %s\n",
+		     idx, property, path, result, string);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>\n", argv[0]);
+
+	test_init(argc, argv);
+	fdt = load_blob(argv[1]);
+
+	check_expected_failure(fdt, "/", "#address-cells");
+	check_expected_failure(fdt, "/", "#size-cells");
+
+	check_string_count(fdt, "/", "compatible", 1);
+	check_string_count(fdt, "/device", "compatible", 2);
+	check_string_count(fdt, "/device", "big-endian", 0);
+
+	check_string_index(fdt, "/", "compatible", "test-strings", 0);
+	check_string_index(fdt, "/device", "compatible", "foo", 0);
+	check_string_index(fdt, "/device", "compatible", "bar", 1);
+	check_string_index(fdt, "/device", "big-endian", "baz", -1);
+
+	check_string(fdt, "/", "compatible", 0, "test-strings");
+	check_string(fdt, "/device", "compatible", 0, "foo");
+	check_string(fdt, "/device", "compatible", 1, "bar");
+
+	PASS();
+}
diff --git a/tests/stringlist.dts b/tests/stringlist.dts
new file mode 100644
index 0000000..1e4d314
--- /dev/null
+++ b/tests/stringlist.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	compatible = "test-strings";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	device {
+		compatible = "foo", "bar";
+		big-endian;
+	};
+};
diff --git a/tests/subnode_iterate.c b/tests/subnode_iterate.c
new file mode 100644
index 0000000..2dc9b2d
--- /dev/null
+++ b/tests/subnode_iterate.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Tests that fdt_next_subnode() works as expected
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void test_node(void *fdt, int parent_offset)
+{
+	uint32_t subnodes;
+	const fdt32_t *prop;
+	int offset;
+	int count;
+	int len;
+
+	/* This property indicates the number of subnodes to expect */
+	prop = fdt_getprop(fdt, parent_offset, "subnodes", &len);
+	if (!prop || len != sizeof(fdt32_t)) {
+		FAIL("Missing/invalid subnodes property at '%s'",
+		     fdt_get_name(fdt, parent_offset, NULL));
+	}
+	subnodes = fdt32_to_cpu(*prop);
+
+	count = 0;
+	fdt_for_each_subnode(offset, fdt, parent_offset)
+		count++;
+
+	if (count != subnodes) {
+		FAIL("Node '%s': Expected %d subnodes, got %d\n",
+		     fdt_get_name(fdt, parent_offset, NULL), subnodes,
+		     count);
+	}
+}
+
+static void check_fdt_next_subnode(void *fdt)
+{
+	int offset;
+	int count = 0;
+
+	fdt_for_each_subnode(offset, fdt, 0) {
+		test_node(fdt, offset);
+		count++;
+	}
+
+	if (count != 2)
+		FAIL("Expected %d tests, got %d\n", 2, count);
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>", argv[0]);
+
+	fdt = load_blob(argv[1]);
+	if (!fdt)
+		FAIL("No device tree available");
+
+	check_fdt_next_subnode(fdt);
+
+	PASS();
+}
diff --git a/tests/subnode_iterate.dts b/tests/subnode_iterate.dts
new file mode 100644
index 0000000..14a0d3a
--- /dev/null
+++ b/tests/subnode_iterate.dts
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	test1 {
+		subnodes = <2>;
+		linux,phandle = <0x1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		PowerPC,970@0 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000000>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			linux,boot-cpu;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+			another-sub-node {
+				should-be-ignored;
+				yet-another {
+					should-also-be-ignored;
+				};
+			};
+		};
+
+		PowerPC,970@1 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000001>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+		};
+	};
+
+	test2 {
+		subnodes = <0>;
+	};
+};
+
diff --git a/tests/subnode_offset.c b/tests/subnode_offset.c
new file mode 100644
index 0000000..1362f99
--- /dev/null
+++ b/tests/subnode_offset.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_subnode_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int check_subnode(struct fdt_header *fdt, int parent, const char *name)
+{
+	int offset;
+	const struct fdt_node_header *nh;
+	uint32_t tag;
+
+	verbose_printf("Checking subnode \"%s\" of %d...", name, parent);
+	offset = fdt_subnode_offset(fdt, parent, name);
+	verbose_printf("offset %d...", offset);
+	if (offset < 0)
+		FAIL("fdt_subnode_offset(\"%s\"): %s", name, fdt_strerror(offset));
+	nh = fdt_offset_ptr(fdt, offset, sizeof(*nh));
+	verbose_printf("pointer %p\n", nh);
+	if (! nh)
+		FAIL("NULL retrieving subnode \"%s\"", name);
+
+	tag = fdt32_to_cpu(nh->tag);
+
+	if (tag != FDT_BEGIN_NODE)
+		FAIL("Incorrect tag 0x%08x on property \"%s\"", tag, name);
+	if (!nodename_eq(nh->name, name))
+		FAIL("Subnode name mismatch \"%s\" instead of \"%s\"",
+		     nh->name, name);
+
+	return offset;
+}
+
+int main(int argc, char *argv[])
+{
+	void *fdt;
+	int subnode1_offset, subnode2_offset;
+	int subsubnode1_offset, subsubnode2_offset, subsubnode2_offset2;
+	int ss12_off, ss21_off;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	subnode1_offset = check_subnode(fdt, 0, "subnode@1");
+	subnode2_offset = check_subnode(fdt, 0, "subnode@2");
+
+	if (subnode1_offset == subnode2_offset)
+		FAIL("Different subnodes have same offset");
+
+	check_property_cell(fdt, subnode1_offset, "prop-int", TEST_VALUE_1);
+	check_property_cell(fdt, subnode2_offset, "prop-int", TEST_VALUE_2);
+
+	subsubnode1_offset = check_subnode(fdt, subnode1_offset, "subsubnode");
+	subsubnode2_offset = check_subnode(fdt, subnode2_offset, "subsubnode@0");
+	subsubnode2_offset2 = check_subnode(fdt, subnode2_offset, "subsubnode");
+
+	check_property_cell(fdt, subsubnode1_offset, "prop-int", TEST_VALUE_1);
+	check_property_cell(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2);
+	check_property_cell(fdt, subsubnode2_offset2, "prop-int", TEST_VALUE_2);
+
+	if (subsubnode2_offset != subsubnode2_offset2)
+		FAIL("Different offsets with and without unit address");
+
+	check_subnode(fdt, subnode1_offset, "ss1");
+	ss21_off = fdt_subnode_offset(fdt, subnode2_offset, "ss1");
+	if (ss21_off != -FDT_ERR_NOTFOUND)
+		FAIL("Incorrectly found ss1 in subnode2");
+
+	ss12_off = fdt_subnode_offset(fdt, subnode1_offset, "ss2");
+	if (ss12_off != -FDT_ERR_NOTFOUND)
+		FAIL("Incorrectly found ss2 in subnode1");
+	check_subnode(fdt, subnode2_offset, "ss2");
+
+	PASS();
+}
diff --git a/tests/supernode_atdepth_offset.c b/tests/supernode_atdepth_offset.c
new file mode 100644
index 0000000..4435b49
--- /dev/null
+++ b/tests/supernode_atdepth_offset.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_supernode_atdepth_offset()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static int path_depth(const char *path)
+{
+	const char *p;
+	int depth = 0;
+
+	if (path[0] != '/')
+		TEST_BUG();
+
+	if (strcmp(path, "/") == 0)
+		return 0;
+	for (p = path; *p; p++)
+		if (*p == '/')
+			depth++;
+
+	/* Special case for path == "/" */
+	if (p == (path + 1))
+		return 0;
+	else
+		return depth;
+}
+
+static int path_prefix(const char *path, int depth)
+{
+	const char *p;
+	int i;
+
+	if (path[0] != '/')
+		TEST_BUG();
+
+	if (depth == 0)
+		return 1;
+
+	p = path;
+	for (i = 0; i < depth; i++)
+		p = p+1 + strcspn(p+1, "/");
+
+	return p - path;
+}
+
+static void check_supernode_atdepth(struct fdt_header *fdt, const char *path,
+			     int depth)
+{
+	int pdepth = path_depth(path);
+	char *superpath;
+	int nodeoffset, supernodeoffset, superpathoffset, pathprefixlen;
+	int nodedepth;
+
+	pathprefixlen = path_prefix(path, depth);
+	superpath = alloca(pathprefixlen + 1);
+	strncpy(superpath, path, pathprefixlen);
+	superpath[pathprefixlen] = '\0';
+
+	verbose_printf("Path %s (%d), depth %d, supernode is %s\n",
+		       path, pdepth, depth, superpath);
+
+	nodeoffset = fdt_path_offset(fdt, path);
+	if (nodeoffset < 0)
+		FAIL("fdt_path_offset(%s): %s", path, fdt_strerror(nodeoffset));
+	superpathoffset = fdt_path_offset(fdt, superpath);
+	if (superpathoffset < 0)
+		FAIL("fdt_path_offset(%s): %s", superpath,
+		     fdt_strerror(superpathoffset));
+
+	supernodeoffset = fdt_supernode_atdepth_offset(fdt, nodeoffset,
+						       depth, &nodedepth);
+	if (supernodeoffset < 0)
+		FAIL("fdt_supernode_atdepth_offset(): %s",
+		     fdt_strerror(supernodeoffset));
+
+	if (supernodeoffset != superpathoffset)
+		FAIL("fdt_supernode_atdepth_offset() returns %d instead of %d",
+		     supernodeoffset, superpathoffset);
+
+	if (nodedepth != pdepth)
+		FAIL("fdt_supernode_atdept_offset() returns node depth %d "
+		     "instead of %d", nodedepth, pdepth);
+}
+
+static void check_supernode_overdepth(struct fdt_header *fdt, const char *path)
+{
+	int pdepth = path_depth(path);
+	int nodeoffset, err;
+
+	nodeoffset = fdt_path_offset(fdt, path);
+	if (nodeoffset < 0)
+		FAIL("fdt_path_offset(%s): %s", path, fdt_strerror(nodeoffset));
+
+	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, pdepth + 1, NULL);
+	if (err != -FDT_ERR_NOTFOUND)
+		FAIL("fdt_supernode_atdept_offset(%s, %d) returns %d instead "
+		     "of FDT_ERR_NOTFOUND", path, pdepth+1, err);
+}
+
+static void check_path(struct fdt_header *fdt, const char *path)
+{
+	int i;
+
+	for (i = 0; i <= path_depth(path); i++)
+		check_supernode_atdepth(fdt, path, i);
+	check_supernode_overdepth(fdt, path);
+}
+int main(int argc, char *argv[])
+{
+	void *fdt;
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+
+	check_path(fdt, "/");
+	check_path(fdt, "/subnode@1");
+	check_path(fdt, "/subnode@2");
+	check_path(fdt, "/subnode@1/subsubnode");
+	check_path(fdt, "/subnode@2/subsubnode@0");
+
+	PASS();
+}
diff --git a/tests/sw_states.c b/tests/sw_states.c
new file mode 100644
index 0000000..42d57ae
--- /dev/null
+++ b/tests/sw_states.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for error handling with sequential write states
+ * Copyright (C) 2018 David Gibson, Red Hat Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE	65536
+
+#define CHECK_OK(code)							\
+	do {								\
+		verbose_printf(" OK: %s\n", #code);			\
+		err = (code);						\
+		if (err)						\
+			FAIL(#code ": %s", fdt_strerror(err));		\
+	} while (0)
+
+#define CHECK_BADSTATE(code)						\
+	do {								\
+		verbose_printf("BAD: %s\n", #code);			\
+		err = (code);						\
+		if (err == 0)						\
+			FAIL(#code ": succeeded in bad state");		\
+		else if (err != -FDT_ERR_BADSTATE)			\
+			FAIL(#code ": %s", fdt_strerror(err));		\
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	void *fdt = NULL;
+	int err;
+
+	test_init(argc, argv);
+
+	fdt = xmalloc(SPACE);
+
+	err = fdt_create(fdt, SPACE);
+	if (err)
+		FAIL("fdt_create(): %s", fdt_strerror(err));
+
+	/* Memory reserve state */
+
+	CHECK_BADSTATE(fdt_begin_node(fdt, ""));
+	CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1"));
+	CHECK_BADSTATE(fdt_end_node(fdt));
+	CHECK_BADSTATE(fdt_finish(fdt));
+
+	CHECK_OK(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1));
+	CHECK_OK(fdt_add_reservemap_entry(fdt, TEST_ADDR_2, TEST_SIZE_2));
+
+	CHECK_BADSTATE(fdt_begin_node(fdt, ""));
+	CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1"));
+	CHECK_BADSTATE(fdt_end_node(fdt));
+	CHECK_BADSTATE(fdt_finish(fdt));
+
+	CHECK_OK(fdt_finish_reservemap(fdt));
+
+	/* Structure state */
+
+	CHECK_BADSTATE(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1));
+	CHECK_BADSTATE(fdt_finish_reservemap(fdt));
+
+	CHECK_OK(fdt_begin_node(fdt, ""));
+	CHECK_OK(fdt_property_string(fdt, "compatible", "test_tree1"));
+	CHECK_OK(fdt_property_u32(fdt, "prop-int", TEST_VALUE_1));
+	CHECK_OK(fdt_property_u64(fdt, "prop-int64", TEST_VALUE64_1));
+	CHECK_OK(fdt_property_string(fdt, "prop-str", TEST_STRING_1));
+	CHECK_OK(fdt_property_u32(fdt, "#address-cells", 1));
+	CHECK_OK(fdt_property_u32(fdt, "#size-cells", 0));
+
+	CHECK_OK(fdt_begin_node(fdt, "subnode@1"));
+	CHECK_OK(fdt_property_string(fdt, "compatible", "subnode1"));
+	CHECK_OK(fdt_property_u32(fdt, "reg", 1));
+	CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1));
+	CHECK_OK(fdt_begin_node(fdt, "subsubnode"));
+	CHECK_OK(fdt_property(fdt, "compatible", "subsubnode1\0subsubnode",
+			   23));
+	CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1));
+	CHECK_OK(fdt_end_node(fdt));
+	CHECK_OK(fdt_begin_node(fdt, "ss1"));
+	CHECK_OK(fdt_end_node(fdt));
+	CHECK_OK(fdt_end_node(fdt));
+
+	CHECK_OK(fdt_begin_node(fdt, "subnode@2"));
+	CHECK_OK(fdt_property_u32(fdt, "reg", 2));
+	CHECK_OK(fdt_property_cell(fdt, "linux,phandle", PHANDLE_1));
+	CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2));
+	CHECK_OK(fdt_property_u32(fdt, "#address-cells", 1));
+	CHECK_OK(fdt_property_u32(fdt, "#size-cells", 0));
+	CHECK_OK(fdt_begin_node(fdt, "subsubnode@0"));
+	CHECK_OK(fdt_property_u32(fdt, "reg", 0));
+	CHECK_OK(fdt_property_cell(fdt, "phandle", PHANDLE_2));
+	CHECK_OK(fdt_property(fdt, "compatible", "subsubnode2\0subsubnode",
+			   23));
+	CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2));
+	CHECK_OK(fdt_end_node(fdt));
+	CHECK_OK(fdt_begin_node(fdt, "ss2"));
+	CHECK_OK(fdt_end_node(fdt));
+
+	CHECK_OK(fdt_end_node(fdt));
+
+	CHECK_OK(fdt_end_node(fdt));
+
+	CHECK_OK(fdt_finish(fdt));
+
+	/* Completed state */
+
+	CHECK_BADSTATE(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1));
+	CHECK_BADSTATE(fdt_finish_reservemap(fdt));
+	CHECK_BADSTATE(fdt_begin_node(fdt, ""));
+	CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1"));
+	CHECK_BADSTATE(fdt_end_node(fdt));
+	CHECK_BADSTATE(fdt_finish(fdt));
+
+	PASS();
+}
diff --git a/tests/sw_tree1.c b/tests/sw_tree1.c
new file mode 100644
index 0000000..7069ace
--- /dev/null
+++ b/tests/sw_tree1.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for fdt_nop_node()
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+#define SPACE	65536
+
+static enum {
+	FIXED = 0,
+	RESIZE,
+	REALLOC,
+	NEWALLOC,
+} alloc_mode;
+
+static void realloc_fdt(void **fdt, size_t *size, bool created)
+{
+	int err;
+
+	switch (alloc_mode) {
+	case FIXED:
+		if (!(*fdt))
+			*fdt = xmalloc(*size);
+		else
+			FAIL("Ran out of space");
+		return;
+
+	case RESIZE:
+		if (!(*fdt)) {
+			*fdt = xmalloc(SPACE);
+		} else if (*size < SPACE) {
+			*size += 1;
+			err = fdt_resize(*fdt, *fdt, *size);
+			if (err < 0)
+				FAIL("fdt_resize() failed: %s",
+				     fdt_strerror(err));
+		} else {
+			FAIL("Ran out of space");
+		}		
+		return;
+
+	case REALLOC:
+		*size += 1;
+		*fdt = xrealloc(*fdt, *size);
+		if (created) {
+			err = fdt_resize(*fdt, *fdt, *size);
+			if (err < 0)
+				FAIL("fdt_resize() failed: %s",
+				     fdt_strerror(err));
+		}
+		return;
+
+	case NEWALLOC: {
+		void *buf;
+
+		*size += 1;
+		buf = xmalloc(*size);
+		if (created) {
+			err = fdt_resize(*fdt, buf, *size);
+			if (err < 0)
+				FAIL("fdt_resize() failed: %s",
+				     fdt_strerror(err));
+		}
+		free(*fdt);
+		*fdt = buf;
+		return;
+	}
+
+	default:
+		CONFIG("Bad allocation mode");
+	}
+}
+
+#define CHECK(code) \
+	do {			      \
+		err = (code);			     \
+		if (err == -FDT_ERR_NOSPACE)			\
+			realloc_fdt(&fdt, &size, created);		\
+		else if (err)						\
+			FAIL(#code ": %s", fdt_strerror(err));		\
+	} while (err != 0)
+
+int main(int argc, char *argv[])
+{
+	void *fdt = NULL;
+	size_t size;
+	int err;
+	bool created = false;
+	void *place;
+	const char place_str[] = "this is a placeholder string\0string2";
+	int place_len = sizeof(place_str);
+	int create_flags;
+
+	test_init(argc, argv);
+
+	alloc_mode = FIXED;
+	size = SPACE;
+	create_flags = 0;
+
+	if (argc == 2 || argc == 3) {
+		if (streq(argv[1], "fixed")) {
+			alloc_mode = FIXED;
+			size = SPACE;
+		} else if (streq(argv[1], "resize")) {
+			alloc_mode = REALLOC;
+			size = 0;
+		} else if (streq(argv[1], "realloc")) {
+			alloc_mode = REALLOC;
+			size = 0;
+		} else if (streq(argv[1], "newalloc")) {
+			alloc_mode = NEWALLOC;
+			size = 0;
+		} else {
+			char *endp;
+
+			size = strtoul(argv[1], &endp, 0);
+			if (*endp == '\0')
+				alloc_mode = FIXED;
+			else 
+				CONFIG("Bad allocation mode \"%s\" specified",
+				       argv[1]);
+		}
+	}
+	if (argc == 3) {
+		char *str = argv[2], *saveptr, *tok;
+		bool default_flag = false;
+
+		while ((tok = strtok_r(str, ",", &saveptr)) != NULL) {
+			str = NULL;
+			if (streq(tok, "default")) {
+				default_flag = true;
+			} else if (streq(tok, "no_name_dedup")) {
+				create_flags |= FDT_CREATE_FLAG_NO_NAME_DEDUP;
+			} else if (streq(tok, "bad")) {
+				create_flags |= 0xffffffff;
+			} else {
+				CONFIG("Bad creation flags \"%s\" specified",
+				       argv[2]);
+			}
+		}
+
+		if (default_flag && create_flags != 0)
+			CONFIG("Bad creation flags \"%s\" specified",
+			       argv[2]);
+	}
+
+	if (argc > 3) {
+		CONFIG("sw_tree1 [<allocation mode>] [<create flags>]");
+	}
+
+	fdt = xmalloc(size);
+	CHECK(fdt_create_with_flags(fdt, size, create_flags));
+
+	created = true;
+
+	CHECK(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1));
+
+	CHECK(fdt_add_reservemap_entry(fdt, TEST_ADDR_2, TEST_SIZE_2));
+	CHECK(fdt_finish_reservemap(fdt));
+
+	CHECK(fdt_begin_node(fdt, ""));
+	CHECK(fdt_property_string(fdt, "compatible", "test_tree1"));
+	CHECK(fdt_property_u32(fdt, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_property_u64(fdt, "prop-int64", TEST_VALUE64_1));
+	CHECK(fdt_property_string(fdt, "prop-str", TEST_STRING_1));
+	CHECK(fdt_property_u32(fdt, "#address-cells", 1));
+	CHECK(fdt_property_u32(fdt, "#size-cells", 0));
+
+	CHECK(fdt_begin_node(fdt, "subnode@1"));
+	CHECK(fdt_property_string(fdt, "compatible", "subnode1"));
+	CHECK(fdt_property_u32(fdt, "reg", 1));
+	CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_begin_node(fdt, "subsubnode"));
+	CHECK(fdt_property(fdt, "compatible", "subsubnode1\0subsubnode",
+			   23));
+	CHECK(fdt_property_placeholder(fdt, "placeholder", place_len, &place));
+	memcpy(place, place_str, place_len);
+	CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1));
+	CHECK(fdt_end_node(fdt));
+	CHECK(fdt_begin_node(fdt, "ss1"));
+	CHECK(fdt_end_node(fdt));
+	CHECK(fdt_end_node(fdt));
+
+	CHECK(fdt_begin_node(fdt, "subnode@2"));
+	CHECK(fdt_property_u32(fdt, "reg", 2));
+	CHECK(fdt_property_cell(fdt, "linux,phandle", PHANDLE_1));
+	CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2));
+	CHECK(fdt_property_u32(fdt, "#address-cells", 1));
+	CHECK(fdt_property_u32(fdt, "#size-cells", 0));
+	CHECK(fdt_begin_node(fdt, "subsubnode@0"));
+	CHECK(fdt_property_u32(fdt, "reg", 0));
+	CHECK(fdt_property_cell(fdt, "phandle", PHANDLE_2));
+	CHECK(fdt_property(fdt, "compatible", "subsubnode2\0subsubnode",
+			   23));
+	CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2));
+	CHECK(fdt_end_node(fdt));
+	CHECK(fdt_begin_node(fdt, "ss2"));
+	CHECK(fdt_end_node(fdt));
+
+	CHECK(fdt_end_node(fdt));
+
+	CHECK(fdt_end_node(fdt));
+
+	save_blob("unfinished_tree1.test.dtb", fdt);
+
+	CHECK(fdt_finish(fdt));
+
+	verbose_printf("Completed tree, totalsize = %d\n",
+		       fdt_totalsize(fdt));
+
+	save_blob("sw_tree1.test.dtb", fdt);
+
+	PASS();
+}
diff --git a/tests/test01.asm b/tests/test01.asm
new file mode 100644
index 0000000..bbf66c7
--- /dev/null
+++ b/tests/test01.asm
@@ -0,0 +1,294 @@
+/* autogenerated by dtc, do not edit */
+
+#define OF_DT_HEADER 0xd00dfeed
+#define OF_DT_BEGIN_NODE 0x1
+#define OF_DT_END_NODE 0x2
+#define OF_DT_PROP 0x3
+#define OF_DT_END 0x9
+
+	.globl	dt_blob_start
+dt_blob_start:
+_dt_blob_start:
+	.globl	dt_header
+dt_header:
+_dt_header:
+	.long	OF_DT_HEADER /* magic */
+	.long	_dt_blob_end - _dt_blob_start /* totalsize */
+	.long	_dt_struct_start - _dt_blob_start /* off_dt_struct */
+	.long	_dt_strings_start - _dt_blob_start /* off_dt_strings */
+	.long	_dt_reserve_map - _dt_blob_start /* off_dt_strings */
+	.long	16 /* version */
+	.long	16 /* last_comp_version */
+	.long	0	/*boot_cpuid_phys*/
+	.long	_dt_strings_end - _dt_strings_start	/* size_dt_strings */
+	.balign	8
+	.globl	dt_reserve_map
+dt_reserve_map:
+_dt_reserve_map:
+/* Memory reserve map from source file */
+	.long	0x10000000
+	.long	0x00000000
+	.long	0x00000000
+	.long	0x02000000
+	.long	0x20000000
+	.long	0x00000000
+	.long	0x01000000
+	.long	0x00000000
+	.long	0x00000000
+	.long	0x00000000
+	.long	0x00000000
+	.long	0x00000014
+	.long	0, 0
+	.long	0, 0
+	.globl	dt_struct_start
+dt_struct_start:
+_dt_struct_start:
+	.long	OF_DT_BEGIN_NODE
+	.string	""
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0xc
+	.long	0x0
+	.long	0x4d79426f
+	.long	0x6172644e
+	.long	0x616d6500
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x1e
+	.long	0x6
+	.long	0x4d79426f
+	.long	0x6172644e
+	.long	0x616d6500
+	.long	0x4d79426f
+	.long	0x61726446
+	.long	0x616d696c
+	.long	0x794e616d
+	.short	0x6500
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x11
+	.long	0x2
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x20
+	.long	0x2
+	.balign	4
+	.long	OF_DT_BEGIN_NODE
+	.string	"cpus"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x2c
+	.long	0x1
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x11
+	.long	0x1
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x20
+	.long	0x0
+	.balign	4
+	.long	OF_DT_BEGIN_NODE
+	.string	"PowerPC,970@0"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0xc
+	.long	0x3a
+	.long	0x506f7765
+	.long	0x7250432c
+	.long	0x39373000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x3f
+	.long	0x63707500
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x4b
+	.long	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x4f
+	.long	0x5f5e1000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x5f
+	.long	0x1fca055
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x0
+	.long	0x72
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x81
+	.long	0x10000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x8e
+	.long	0x8000
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_BEGIN_NODE
+	.string	"PowerPC,970@1"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0xc
+	.long	0x3a
+	.long	0x506f7765
+	.long	0x7250432c
+	.long	0x39373000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x3f
+	.long	0x63707500
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x4b
+	.long	0x1
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x4f
+	.long	0x5f5e1000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x5f
+	.long	0x1fca055
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x81
+	.long	0x10000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x8e
+	.long	0x8000
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_END_NODE
+	.long	OF_DT_BEGIN_NODE
+	.string	"randomnode"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x13
+	.long	0x9b
+	.long	0xff007374
+	.long	0x75666673
+	.long	0x74756666
+	.long	0x909090a
+	.short	0xa0a
+	.byte	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x9
+	.long	0xa2
+	.long	0xa0b0c0d
+	.long	0xdeeaadbe
+	.byte	0xef
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0xa7
+	.long	0x2
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x14
+	.long	0xab
+	.long	0x61626300
+	.long	0x12340000
+	.long	0xa
+	.long	0xb
+	.long	0xc
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_BEGIN_NODE
+	.string	"memory@0"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x7
+	.long	0x3f
+	.long	0x6d656d6f
+	.short	0x7279
+	.byte	0x0
+	.balign	4
+	.globl	memreg
+memreg:
+	.long	OF_DT_PROP
+	.long	0x10
+	.long	0x4b
+	.long	0x0
+	.long	0x0
+	.long	0x0
+	.long	0x20000000
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0x2c
+	.long	0x2
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_BEGIN_NODE
+	.string	"chosen"
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0xf
+	.long	0xb1
+	.long	0x726f6f74
+	.long	0x3d2f6465
+	.long	0x762f7364
+	.short	0x6132
+	.byte	0x0
+	.balign	4
+	.long	OF_DT_PROP
+	.long	0x4
+	.long	0xba
+	.long	0x600
+	.balign	4
+	.long	OF_DT_END_NODE
+	.long	OF_DT_END_NODE
+	.long	OF_DT_END
+	.globl	dt_struct_end
+dt_struct_end:
+_dt_struct_end:
+	.globl	dt_strings_start
+dt_strings_start:
+_dt_strings_start:
+	.string "model"
+	.string "compatible"
+	.string "#address-cells"
+	.string "#size-cells"
+	.string "linux,phandle"
+	.string "name"
+	.string "device_type"
+	.string "reg"
+	.string "clock-frequency"
+	.string "timebase-frequency"
+	.string "linux,boot-cpu"
+	.string "i-cache-size"
+	.string "d-cache-size"
+	.string "string"
+	.string "blob"
+	.string "ref"
+	.string "mixed"
+	.string "bootargs"
+	.string "linux,platform"
+	.globl	dt_strings_end
+dt_strings_end:
+_dt_strings_end:
+	.globl	dt_blob_end
+dt_blob_end:
+_dt_blob_end:
diff --git a/tests/test01.dts b/tests/test01.dts
new file mode 100644
index 0000000..f9fd165
--- /dev/null
+++ b/tests/test01.dts
@@ -0,0 +1,57 @@
+/dts-v1/;
+
+/memreserve/ 0x1000000000000000 0x0000000002000000;
+/memreserve/ 0x2000000000000000 0x0100000000000000;
+/memreserve/ 0x0000000000000000 0x0000000000000014;
+
+/ {
+	model = "MyBoardName";
+	compatible = "MyBoardName", "MyBoardFamilyName";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	cpus {
+		linux,phandle = <0x1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		PowerPC,970@0 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000000>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			linux,boot-cpu;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+		};
+
+		PowerPC,970@1 {
+			name = "PowerPC,970";
+			device_type = "cpu";
+			reg = <0x00000001>;
+			clock-frequency = <1600000000>;
+			timebase-frequency = <33333333>;
+			i-cache-size = <65536>;
+			d-cache-size = <32768>;
+		};
+
+	};
+
+	randomnode {
+		string = "\xff\0stuffstuff\t\t\t\n\n\n";
+		blob = [0a 0b 0c 0d de ea ad be ef];
+		ref = < &{/memory@0} >;
+		mixed = "abc", [1234], <0xa 0xb 0xc>;
+	};
+
+	memory@0 {
+		device_type = "memory";
+		memreg: reg = <0x00000000 0x00000000 0x00000000 0x20000000>;
+	};
+
+	chosen {
+		bootargs = "root=/dev/sda2";
+		linux,platform = <0x600>;
+	};
+
+};
diff --git a/tests/test01.stderr b/tests/test01.stderr
new file mode 100644
index 0000000..82ea3f6
--- /dev/null
+++ b/tests/test01.stderr
@@ -0,0 +1,4 @@
+DTC: dts->asm  on file "test.dts"
+Warning: "linux,boot-cpu" property is deprecated in blob version 2 or higher
+Warning: /chosen has no "linux,stdout-path" property
+Warning: /chosen has no "interrupt-controller" property
diff --git a/tests/test_kernel_dts b/tests/test_kernel_dts
new file mode 100755
index 0000000..238f3f7
--- /dev/null
+++ b/tests/test_kernel_dts
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+my $dtc_old = "/home/jdl/FSL/dtc/dtc-old";
+my $dtc_new = "/home/jdl/FSL/dtc/dtc-new";
+
+my $basic_options = "-b 0 -f -I dts -O dtb";
+
+my $linux_dts_dir = "/usr/src/linux-2.6/arch/powerpc/boot/dts";
+
+# Yeah, sure, we could, like, readdir() this instead...
+my @boards = (
+	      "bamboo",
+	      "ebony",
+	      "ep88xc",
+	      "holly",
+	      "kilauea",
+	      "kuroboxHD",
+	      "kuroboxHG",
+	      "lite5200",
+	      "lite5200b",
+	      "mpc7448hpc2",
+	      "mpc8272ads",
+	      "mpc8313erdb",
+	      "mpc832x_mds",
+	      "mpc832x_rdb",
+	      "mpc8349emitx",
+	      "mpc8349emitxgp",
+	      "mpc834x_mds",
+	      "mpc836x_mds",
+	      "mpc8540ads",
+	      "mpc8541cds",
+	      "mpc8544ds",
+	      "mpc8548cds",
+	      "mpc8555cds",
+	      "mpc8560ads",
+	      "mpc8568mds",
+	      "mpc8572ds",
+	      "mpc8610_hpcd",
+	      "mpc8641_hpcn",
+	      "mpc866ads",	# Feh.  Bad node references...
+	      "mpc885ads",
+	      "pq2fads",
+	      "prpmc2800",
+	      "ps3",
+	      "sequoia",
+	      "walnut",
+);
+
+foreach my $board (@boards) {
+	my $dts_file = "$linux_dts_dir/$board.dts";
+
+	my $old_dtb_file = "/tmp/$board.dtb.old";
+	my $new_dtb_file = "/tmp/$board.dtb.new";
+
+	my $cmd_old = "$dtc_old $basic_options -o $old_dtb_file $dts_file";
+	my $cmd_new = "$dtc_new $basic_options -o $new_dtb_file $dts_file";
+	my $cmd_cmp = "cmp $old_dtb_file $new_dtb_file";
+
+	print "------------------------------------------------\n";
+	print "OLD: $cmd_old\n";
+	unlink($old_dtb_file) if (-f $old_dtb_file);
+	system("$cmd_old >& /dev/null");
+	my $status = $?;
+	if ($status) {
+		print "    FAILED to run old DTC on $board\n";
+	}
+
+	print "NEW: $cmd_new\n";
+	unlink($new_dtb_file) if (-f $new_dtb_file);
+	system("$cmd_new >& /dev/null");
+	$status = $?;
+	if ($status) {
+		print "    FAILED to run new DTC on $board\n";
+	}
+
+	if (-f $old_dtb_file && -f $new_dtb_file) {
+	    print "CMP: $cmd_cmp\n";
+	    system($cmd_cmp);
+	    $status = $?;
+	    if ($status) {
+		print "    FAILED $board\n";
+	    }
+	} else {
+	    printf "    FAILED: Missing dtb file\n";
+	}
+}
diff --git a/tests/test_label_ref.dts b/tests/test_label_ref.dts
new file mode 100644
index 0000000..7009c79
--- /dev/null
+++ b/tests/test_label_ref.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+
+};
+
+label: &handle {
+
+};
diff --git a/tests/test_props.dts b/tests/test_props.dts
new file mode 100644
index 0000000..7e59bd1
--- /dev/null
+++ b/tests/test_props.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	compatible = "test_props";
+	prop-hex32 = <0xdeadbeef>;
+	prop-uint32 = <123>;
+	prop-int32 = <0xfffffffe>;
+	prop-hex64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	prop-uint64 = /bits/ 64 <9223372036854775807>;
+	prop-int64 = /bits/ 64 <0xfffffffffffffffe>;
+};
diff --git a/tests/test_tree1.dts b/tests/test_tree1.dts
new file mode 100644
index 0000000..77ea325
--- /dev/null
+++ b/tests/test_tree1.dts
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ssn0: subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_delete.dts b/tests/test_tree1_delete.dts
new file mode 100644
index 0000000..b95ef1e
--- /dev/null
+++ b/tests/test_tree1_delete.dts
@@ -0,0 +1,68 @@
+/dts-v1/;
+
+/include/ "test_tree1.dts"
+
+/ {
+	nonexistant-property = <0xdeadbeef>;
+
+	nonexistant-subnode {
+		prop-int = <1>;
+	};
+
+	dellabel: deleted-by-label {
+		prop-int = <1>;
+	};
+
+	subnode@1 {
+		delete-this-str = "deadbeef";
+	};
+
+};
+
+/ {
+	/delete-property/ nonexistant-property;
+
+	/delete-node/ nonexistant-subnode;
+
+	subnode@1 {
+		/delete-property/ delete-this-str;
+	};
+};
+
+/delete-node/ &dellabel;
+
+/ {
+	/delete-property/ prop-str;
+};
+
+/ {
+	prop-str = "hello world";
+};
+
+/ {
+	subnode@1 {
+		/delete-node/ ss1;
+	};
+};
+
+/ {
+	subnode@1 {
+		ss1 {
+		};
+	};
+};
+
+/{
+	duplabel1: foo1 = "bar";
+	duplabel2: foo2 = "bar";
+};
+
+/{
+	duplabel1: baz1 = "qux";
+	duplabel2: baz2 = "qux";
+};
+
+/{
+	/delete-property/ foo1;
+	/delete-property/ baz2;
+};
diff --git a/tests/test_tree1_label_noderef.dts b/tests/test_tree1_label_noderef.dts
new file mode 100644
index 0000000..cfe5946
--- /dev/null
+++ b/tests/test_tree1_label_noderef.dts
@@ -0,0 +1,56 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ssn0: subsubnode@0 {
+			phandle = <0x2001>;
+			prop-int = <0xbad>;
+		};
+
+		ss2 {
+		};
+	};
+};
+
+/* Add label to a noderef */
+ssn1: &ssn0 {
+	reg = <0>;
+	prop-int = <123456789>;
+};
+
+/* Use the new label for merging */
+&ssn1 {
+	prop-int = <0726746425>;
+	compatible = "subsubnode2", "subsubnode";
+};
diff --git a/tests/test_tree1_merge.dts b/tests/test_tree1_merge.dts
new file mode 100644
index 0000000..b100c12
--- /dev/null
+++ b/tests/test_tree1_merge.dts
@@ -0,0 +1,51 @@
+/dts-v1/;
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = "wrong!";
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ss2 {
+		};
+	};
+};
+
+/ {
+	prop-int = <0xdeadbeef>;
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	subnode@1 {
+		prop-int = [deadbeef];
+	};
+	subnode@2 {
+		ssn0: subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+	};
+};
diff --git a/tests/test_tree1_merge_labelled.dts b/tests/test_tree1_merge_labelled.dts
new file mode 100644
index 0000000..fcf5dc4
--- /dev/null
+++ b/tests/test_tree1_merge_labelled.dts
@@ -0,0 +1,49 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ssn0: subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			prop-int = <0xbad>;
+		};
+
+		ss2 {
+		};
+	};
+};
+
+&ssn0 {
+	compatible = "subsubnode2", "subsubnode";
+	prop-int = <0726746425>;
+};
diff --git a/tests/test_tree1_merge_path.dts b/tests/test_tree1_merge_path.dts
new file mode 100644
index 0000000..c2ad829
--- /dev/null
+++ b/tests/test_tree1_merge_path.dts
@@ -0,0 +1,49 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ssn0: subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			prop-int = <0xbad>;
+		};
+
+		ss2 {
+		};
+	};
+};
+
+&{/subnode@2/subsubnode@0} {
+	compatible = "subsubnode2", "subsubnode";
+	prop-int = <0726746425>;
+};
diff --git a/tests/test_tree1_wrong1.dts b/tests/test_tree1_wrong1.dts
new file mode 100644
index 0000000..900d385
--- /dev/null
+++ b/tests/test_tree1_wrong1.dts
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong2.dts b/tests/test_tree1_wrong2.dts
new file mode 100644
index 0000000..099752b
--- /dev/null
+++ b/tests/test_tree1_wrong2.dts
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong3.dts b/tests/test_tree1_wrong3.dts
new file mode 100644
index 0000000..069353a
--- /dev/null
+++ b/tests/test_tree1_wrong3.dts
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong4.dts b/tests/test_tree1_wrong4.dts
new file mode 100644
index 0000000..2c56416
--- /dev/null
+++ b/tests/test_tree1_wrong4.dts
@@ -0,0 +1,41 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong5.dts b/tests/test_tree1_wrong5.dts
new file mode 100644
index 0000000..6ddd72d
--- /dev/null
+++ b/tests/test_tree1_wrong5.dts
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbefe>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong6.dts b/tests/test_tree1_wrong6.dts
new file mode 100644
index 0000000..36b4e1f
--- /dev/null
+++ b/tests/test_tree1_wrong6.dts
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+			extra-prop;
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong7.dts b/tests/test_tree1_wrong7.dts
new file mode 100644
index 0000000..54150e6
--- /dev/null
+++ b/tests/test_tree1_wrong7.dts
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+			extranode {
+			};
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong8.dts b/tests/test_tree1_wrong8.dts
new file mode 100644
index 0000000..7a28a9f
--- /dev/null
+++ b/tests/test_tree1_wrong8.dts
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010001;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <2>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/test_tree1_wrong9.dts b/tests/test_tree1_wrong9.dts
new file mode 100644
index 0000000..f6486fa
--- /dev/null
+++ b/tests/test_tree1_wrong9.dts
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/memreserve/ 0xdeadbeef00000000 0x100000;
+/memreserve/ 123456789 010000;
+/memreserve/ 0 1;
+
+/ {
+	compatible = "test_tree1";
+	prop-int = <0xdeadbeef>;
+	prop-str = "hello world";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	subnode@1 {
+		compatible = "subnode1";
+		reg = <1>;
+		prop-int = [deadbeef];
+
+		subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+			prop-int = <0xdeadbeef>;
+		};
+
+		ss1 {
+		};
+	};
+
+	subnode@2 {
+		reg = <2>;
+		linux,phandle = <0x2000>;
+		prop-int = <123456789>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		subsubnode@0 {
+			reg = <0>;
+			phandle = <0x2001>;
+			compatible = "subsubnode2", "subsubnode";
+			prop-int = <0726746425>;
+		};
+
+		ss2 {
+		};
+	};
+};
diff --git a/tests/testdata.h b/tests/testdata.h
new file mode 100644
index 0000000..0d08efb
--- /dev/null
+++ b/tests/testdata.h
@@ -0,0 +1,58 @@
+#ifdef __ASSEMBLY__
+#define ASM_CONST_LL(x)	(x)
+#else
+#define ASM_CONST_LL(x)	(x##ULL)
+#endif
+
+#define TEST_ADDR_1H	ASM_CONST_LL(0xdeadbeef)
+#define TEST_ADDR_1L	ASM_CONST_LL(0x00000000)
+#define TEST_ADDR_1	((TEST_ADDR_1H << 32) | TEST_ADDR_1L)
+#define TEST_SIZE_1H	ASM_CONST_LL(0x00000000)
+#define TEST_SIZE_1L	ASM_CONST_LL(0x00100000)
+#define TEST_SIZE_1	((TEST_SIZE_1H << 32) | TEST_SIZE_1L)
+#define TEST_ADDR_2H	ASM_CONST_LL(0)
+#define TEST_ADDR_2L	ASM_CONST_LL(123456789)
+#define TEST_ADDR_2	((TEST_ADDR_2H << 32) | TEST_ADDR_2L)
+#define TEST_SIZE_2H	ASM_CONST_LL(0)
+#define TEST_SIZE_2L	ASM_CONST_LL(010000)
+#define TEST_SIZE_2	((TEST_SIZE_2H << 32) | TEST_SIZE_2L)
+
+#define TEST_VALUE_1	0xdeadbeef
+#define TEST_VALUE_2	123456789
+
+#define TEST_VALUE64_1H	ASM_CONST_LL(0xdeadbeef)
+#define TEST_VALUE64_1L	ASM_CONST_LL(0x01abcdef)
+#define TEST_VALUE64_1	((TEST_VALUE64_1H << 32) | TEST_VALUE64_1L)
+
+#define PHANDLE_1	0x2000
+#define PHANDLE_2	0x2001
+
+#define TEST_STRING_1	"hello world"
+#define TEST_STRING_2	"nastystring: \a\b\t\n\v\f\r\\\""
+#define TEST_STRING_3	"\xde\xad\xbe\xef"
+
+#define TEST_STRING_4_PARTIAL	"foobar"
+#define TEST_STRING_4_RESULT	"testfoobar"
+
+#define TEST_CHAR1	'\r'
+#define TEST_CHAR2	'b'
+#define TEST_CHAR3	'\0'
+#define TEST_CHAR4	'\''
+#define TEST_CHAR5	'\xff'
+
+#define TEST_MEMREGION_ADDR	0x12345678
+#define TEST_MEMREGION_ADDR_HI	0x8765432100000000
+#define TEST_MEMREGION_SIZE	0x9abcdef0
+#define TEST_MEMREGION_SIZE_HI	0x0fedcba900000000
+#define TEST_MEMREGION_SIZE_INC	0x1000
+
+#ifndef __ASSEMBLY__
+extern struct fdt_header test_tree1;
+extern struct fdt_header truncated_property;
+extern struct fdt_header bad_node_char;
+extern struct fdt_header bad_node_format;
+extern struct fdt_header bad_prop_char;
+extern struct fdt_header ovf_size_strings;
+extern struct fdt_header truncated_string;
+extern struct fdt_header truncated_memrsv;
+#endif /* ! __ASSEMBLY */
diff --git a/tests/tests.h b/tests/tests.h
new file mode 100644
index 0000000..1017366
--- /dev/null
+++ b/tests/tests.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef TESTS_H
+#define TESTS_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase definitions
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#define DEBUG
+
+/* Test return codes */
+#define RC_PASS 	0
+#define RC_CONFIG 	1
+#define RC_FAIL		2
+#define RC_BUG		99
+
+extern int verbose_test;
+extern char *test_name;
+void test_init(int argc, char *argv[]);
+
+#define ALIGN(x, a)	(((x) + (a) - 1) & ~((a) - 1))
+#define PALIGN(p, a)	((void *)ALIGN((unsigned long)(p), (a)))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define streq(s1, s2)	(strcmp((s1),(s2)) == 0)
+
+/* Each test case must define this function */
+void cleanup(void);
+
+#define verbose_printf(...) \
+	if (verbose_test) { \
+		printf(__VA_ARGS__); \
+		fflush(stdout); \
+	}
+#define ERR	"ERR: "
+#define ERROR(fmt, args...)	fprintf(stderr, ERR fmt, ## args)
+
+
+#define	PASS()						\
+	do {						\
+		cleanup();				\
+		printf("PASS\n");			\
+		exit(RC_PASS);				\
+	} while (0)
+
+#define	PASS_INCONCLUSIVE()				\
+	do {						\
+		cleanup();				\
+		printf("PASS (inconclusive)\n");	\
+		exit(RC_PASS);				\
+	} while (0)
+
+#define IRRELEVANT()					\
+	do {						\
+		cleanup();				\
+		printf("PASS (irrelevant)\n");		\
+		exit(RC_PASS);				\
+	} while (0)
+
+/* Look out, gcc extension below... */
+#define FAIL(fmt, ...)					\
+	do {						\
+		cleanup();				\
+		printf("FAIL\t" fmt "\n", ##__VA_ARGS__);	\
+		exit(RC_FAIL);				\
+	} while (0)
+
+#define CONFIG(fmt, ...)				\
+	do {						\
+		cleanup();				\
+		printf("Bad configuration: " fmt "\n", ##__VA_ARGS__);	\
+		exit(RC_CONFIG);			\
+	} while (0)
+
+#define TEST_BUG(fmt, ...)				\
+	do {						\
+		cleanup();				\
+		printf("BUG in testsuite: " fmt "\n", ##__VA_ARGS__);	\
+		exit(RC_BUG);				\
+	} while (0)
+
+void check_mem_rsv(void *fdt, int n, uint64_t addr, uint64_t size);
+
+void check_property(void *fdt, int nodeoffset, const char *name,
+		    int len, const void *val);
+#define check_property_cell(fdt, nodeoffset, name, val) \
+	({ \
+		fdt32_t x = cpu_to_fdt32(val);			      \
+		check_property(fdt, nodeoffset, name, sizeof(x), &x); \
+	})
+
+
+const void *check_getprop(void *fdt, int nodeoffset, const char *name,
+			  int len, const void *val);
+#define check_getprop_cell(fdt, nodeoffset, name, val) \
+	({ \
+		fdt32_t x = cpu_to_fdt32(val);			     \
+		check_getprop(fdt, nodeoffset, name, sizeof(x), &x); \
+	})
+#define check_getprop_64(fdt, nodeoffset, name, val) \
+	({ \
+		fdt64_t x = cpu_to_fdt64(val);			     \
+		check_getprop(fdt, nodeoffset, name, sizeof(x), &x); \
+	})
+#define check_getprop_string(fdt, nodeoffset, name, s) \
+	check_getprop((fdt), (nodeoffset), (name), strlen(s)+1, (s))
+
+/* Returns non-NULL if the property at poffset has the name in_name */
+const void *check_get_prop_offset(void *fdt, int poffset, const char *in_name,
+				  int in_len, const void *in_val);
+#define check_get_prop_offset_cell(fdt, poffset, name, val) \
+	({ \
+		fdt32_t x = cpu_to_fdt32(val);			     \
+		check_get_prop_offset(fdt, poffset, name, sizeof(x), &x); \
+	})
+
+const void *check_getprop_addrrange(void *fdt, int parent, int nodeoffset,
+				    const char *name, int num);
+
+int nodename_eq(const char *s1, const char *s2);
+void vg_prepare_blob(void *fdt, size_t bufsize);
+void *load_blob(const char *filename);
+void *load_blob_arg(int argc, char *argv[]);
+void save_blob(const char *filename, void *blob);
+void *open_blob_rw(void *blob);
+
+#include "util.h"
+
+#endif /* TESTS_H */
diff --git a/tests/testutils.c b/tests/testutils.c
new file mode 100644
index 0000000..5e494c5
--- /dev/null
+++ b/tests/testutils.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase common utility functions
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#define _GNU_SOURCE /* for strsignal() in glibc.  FreeBSD has it either way */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#if NO_VALGRIND
+static inline void VALGRIND_MAKE_MEM_UNDEFINED(void *p, size_t len)
+{
+}
+
+static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len)
+{
+}
+#else
+#include <valgrind/memcheck.h>
+#endif
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+/* For FDT_SW_MAGIC */
+#include "libfdt_internal.h"
+
+int verbose_test = 1;
+char *test_name;
+
+void  __attribute__((weak)) cleanup(void)
+{
+}
+
+static void sigint_handler(int signum, siginfo_t *si, void *uc)
+{
+	cleanup();
+	fprintf(stderr, "%s: %s (pid=%d)\n", test_name,
+		strsignal(signum), getpid());
+	exit(RC_BUG);
+}
+
+void test_init(int argc, char *argv[])
+{
+	int err;
+	struct sigaction sa_int = {
+		.sa_sigaction = sigint_handler,
+	};
+
+	test_name = argv[0];
+
+	err = sigaction(SIGINT, &sa_int, NULL);
+	if (err)
+		FAIL("Can't install SIGINT handler");
+
+	if (getenv("QUIET_TEST"))
+		verbose_test = 0;
+
+	verbose_printf("Starting testcase \"%s\", pid %d\n",
+		       test_name, getpid());
+}
+
+void check_mem_rsv(void *fdt, int n, uint64_t addr, uint64_t size)
+{
+	int err;
+	uint64_t addr_v, size_v;
+
+	err = fdt_get_mem_rsv(fdt, n, &addr_v, &size_v);
+	if (err < 0)
+		FAIL("fdt_get_mem_rsv(%d): %s", n, fdt_strerror(err));
+	if ((addr_v != addr) || (size_v != size))
+		FAIL("fdt_get_mem_rsv() returned (0x%llx,0x%llx) "
+		     "instead of (0x%llx,0x%llx)",
+		     (unsigned long long)addr_v, (unsigned long long)size_v,
+		     (unsigned long long)addr, (unsigned long long)size);
+}
+
+void check_property(void *fdt, int nodeoffset, const char *name,
+		    int len, const void *val)
+{
+	const struct fdt_property *prop;
+	int retlen, namelen;
+	uint32_t tag, nameoff, proplen;
+	const char *propname;
+
+	verbose_printf("Checking property \"%s\"...", name);
+	prop = fdt_get_property(fdt, nodeoffset, name, &retlen);
+	verbose_printf("pointer %p\n", prop);
+	if (! prop)
+		FAIL("Error retrieving \"%s\" pointer: %s", name,
+		     fdt_strerror(retlen));
+
+	tag = fdt32_to_cpu(prop->tag);
+	nameoff = fdt32_to_cpu(prop->nameoff);
+	proplen = fdt32_to_cpu(prop->len);
+
+	if (tag != FDT_PROP)
+		FAIL("Incorrect tag 0x%08x on property \"%s\"", tag, name);
+
+	propname = fdt_get_string(fdt, nameoff, &namelen);
+	if (!propname)
+		FAIL("Couldn't get property name: %s", fdt_strerror(namelen));
+	if (namelen != strlen(propname))
+		FAIL("Incorrect prop name length: %d instead of %zd",
+		     namelen, strlen(propname));
+	if (!streq(propname, name))
+		FAIL("Property name mismatch \"%s\" instead of \"%s\"",
+		     propname, name);
+	if (proplen != retlen)
+		FAIL("Length retrieved for \"%s\" by fdt_get_property()"
+		     " differs from stored length (%d != %d)",
+		     name, retlen, proplen);
+	if (proplen != len)
+		FAIL("Size mismatch on property \"%s\": %d insead of %d",
+		     name, proplen, len);
+	if (len && memcmp(val, prop->data, len) != 0)
+		FAIL("Data mismatch on property \"%s\"", name);
+}
+
+const void *check_getprop(void *fdt, int nodeoffset, const char *name,
+			  int len, const void *val)
+{
+	const void *propval;
+	int proplen;
+
+	propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
+	if (! propval)
+		FAIL("fdt_getprop(\"%s\"): %s", name, fdt_strerror(proplen));
+
+	if (proplen != len)
+		FAIL("Size mismatch on property \"%s\": %d insead of %d",
+		     name, proplen, len);
+	if (len && memcmp(val, propval, len) != 0)
+		FAIL("Data mismatch on property \"%s\"", name);
+
+	return propval;
+}
+
+const void *check_get_prop_offset(void *fdt, int poffset, const char *exp_name,
+				  int exp_len, const void *exp_val)
+{
+	const void *propval;
+	const char *name;
+	int proplen;
+
+	propval = fdt_getprop_by_offset(fdt, poffset, &name, &proplen);
+	if (!propval)
+		FAIL("fdt_getprop(\"%s\"): %s", name, fdt_strerror(proplen));
+
+	/* Not testing for this field, so ignore */
+	if (strcmp(name, exp_name))
+		return NULL;
+
+	if (proplen != exp_len)
+		FAIL("Size mismatch on property \"%s\": %d insead of %d",
+		     name, proplen, exp_len);
+	if (exp_len && memcmp(exp_val, propval, exp_len))
+		FAIL("Data mismatch on property \"%s\"", name);
+
+	return propval;
+}
+
+const void *check_getprop_addrrange(void *fdt, int parent, int nodeoffset,
+				    const char *name, int num)
+{
+	const void *propval;
+	int xac, xsc, buf_size, cells, i;
+	char *buf, *p;
+	uint64_t addr, size;
+	fdt32_t val;
+
+	xac = fdt_address_cells(fdt, parent);
+	xsc = fdt_size_cells(fdt, parent);
+
+	if (xac <= 0)
+		FAIL("Couldn't identify #address-cells: %s",
+		     fdt_strerror(xac));
+	if (xsc <= 0)
+		FAIL("Couldn't identify #size-cells: %s",
+		     fdt_strerror(xsc));
+
+	buf_size = (xac + xsc) * sizeof(fdt32_t) * num;
+	buf = malloc(buf_size);
+	if (!buf)
+		FAIL("Couldn't allocate temporary buffer");
+
+	/* expected value */
+	addr = TEST_MEMREGION_ADDR;
+	if (xac > 1)
+		addr += TEST_MEMREGION_ADDR_HI;
+	size = TEST_MEMREGION_SIZE;
+	if (xsc > 1)
+		size += TEST_MEMREGION_SIZE_HI;
+	for (p = buf, i = 0; i < num; i++) {
+		cells = xac;
+		while (cells) {
+			val = cpu_to_fdt32(addr >> (32 * (--cells)));
+			memcpy(p, &val, sizeof(val));
+			p += sizeof(val);
+		}
+		cells = xsc;
+		while (cells) {
+			val = cpu_to_fdt32(size >> (32 * (--cells)));
+			memcpy(p, &val, sizeof(val));
+			p += sizeof(val);
+		}
+
+		addr += size;
+		size += TEST_MEMREGION_SIZE_INC;
+	}
+
+	/* check */
+	propval = check_getprop(fdt, nodeoffset, name, buf_size,
+				(const void *)buf);
+
+	free(buf);
+
+	return propval;
+}
+
+int nodename_eq(const char *s1, const char *s2)
+{
+	int len = strlen(s2);
+
+	if (strncmp(s1, s2, len) != 0)
+		return 0;
+	if (s1[len] == '\0')
+		return 1;
+	else if (!memchr(s2, '@', len) && (s1[len] == '@'))
+		return 1;
+	else
+		return 0;
+}
+
+void vg_prepare_blob(void *fdt, size_t bufsize)
+{
+	char *blob = fdt;
+	int off_memrsv, off_strings, off_struct;
+	int num_memrsv;
+	size_t size_memrsv, size_strings, size_struct;
+
+	off_memrsv = fdt_off_mem_rsvmap(fdt);
+	num_memrsv = fdt_num_mem_rsv(fdt);
+	if (num_memrsv < 0)
+		size_memrsv = fdt_totalsize(fdt) - off_memrsv;
+	else
+		size_memrsv = (num_memrsv + 1)
+			* sizeof(struct fdt_reserve_entry);
+
+	VALGRIND_MAKE_MEM_UNDEFINED(blob, bufsize);
+	VALGRIND_MAKE_MEM_DEFINED(blob, FDT_V1_SIZE);
+	VALGRIND_MAKE_MEM_DEFINED(blob, fdt_header_size(fdt));
+
+	if (fdt_magic(fdt) == FDT_MAGIC) {
+		off_strings = fdt_off_dt_strings(fdt);
+		if (fdt_version(fdt) >= 3)
+			size_strings = fdt_size_dt_strings(fdt);
+		else
+			size_strings = fdt_totalsize(fdt) - off_strings;
+
+		off_struct = fdt_off_dt_struct(fdt);
+		if (fdt_version(fdt) >= 17)
+			size_struct = fdt_size_dt_struct(fdt);
+		else
+			size_struct = fdt_totalsize(fdt) - off_struct;
+	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+		size_strings = fdt_size_dt_strings(fdt);
+		off_strings = fdt_off_dt_strings(fdt) - size_strings;
+
+		off_struct = fdt_off_dt_struct(fdt);
+		size_struct = fdt_size_dt_struct(fdt);
+		size_struct = fdt_totalsize(fdt) - off_struct;
+
+	} else {
+		CONFIG("Bad magic on vg_prepare_blob()");
+	}
+
+	VALGRIND_MAKE_MEM_DEFINED(blob + off_memrsv, size_memrsv);
+	VALGRIND_MAKE_MEM_DEFINED(blob + off_strings, size_strings);
+	VALGRIND_MAKE_MEM_DEFINED(blob + off_struct, size_struct);
+}
+
+void *load_blob(const char *filename)
+{
+	char *blob;
+	size_t len;
+	int ret = utilfdt_read_err(filename, &blob, &len);
+
+	if (ret)
+		CONFIG("Couldn't open blob from \"%s\": %s", filename,
+		       strerror(ret));
+
+	vg_prepare_blob(blob, len);
+
+	return blob;
+}
+
+void *load_blob_arg(int argc, char *argv[])
+{
+	if (argc != 2)
+		CONFIG("Usage: %s <dtb file>", argv[0]);
+	return load_blob(argv[1]);
+}
+
+void save_blob(const char *filename, void *fdt)
+{
+	size_t size = fdt_totalsize(fdt);
+	void *tmp;
+	int ret;
+
+	/* Make a temp copy of the blob so that valgrind won't check
+	 * about uninitialized bits in the pieces between blocks */
+	tmp = xmalloc(size);
+	fdt_move(fdt, tmp, size);
+	VALGRIND_MAKE_MEM_DEFINED(tmp, size);
+	ret = utilfdt_write_err(filename, tmp);
+	if (ret)
+		CONFIG("Couldn't write blob to \"%s\": %s", filename,
+		       strerror(ret));
+	free(tmp);
+}
+
+void *open_blob_rw(void *blob)
+{
+	int err;
+	void *buf = blob;
+
+	err = fdt_open_into(blob, buf, fdt_totalsize(blob));
+	if (err == -FDT_ERR_NOSPACE) {
+		/* Ran out of space converting to v17 */
+		int newsize = fdt_totalsize(blob) + 8;
+
+		buf = xmalloc(newsize);
+		err = fdt_open_into(blob, buf, newsize);
+	}
+	if (err)
+		FAIL("fdt_open_into(): %s", fdt_strerror(err));
+	return buf;
+}
diff --git a/tests/testutils.sh b/tests/testutils.sh
new file mode 100644
index 0000000..8dda6e1
--- /dev/null
+++ b/tests/testutils.sh
@@ -0,0 +1,63 @@
+# Common functions for shell testcases
+
+PASS () {
+    echo "PASS"
+    exit 0
+}
+
+FAIL () {
+    echo "FAIL" "$@"
+    exit 2
+}
+
+FAIL_IF_SIGNAL () {
+    ret="$1"
+    if [ "$ret" -gt 127 ]; then
+	signame=$(kill -l $((ret - 128)))
+	FAIL "Killed by SIG$signame"
+    fi
+}
+
+DTC=../dtc
+DTGET=../fdtget
+DTPUT=../fdtput
+FDTDUMP=../fdtdump
+FDTOVERLAY=../fdtoverlay
+
+verbose_run () {
+    if [ -z "$QUIET_TEST" ]; then
+	"$@"
+    else
+	"$@" > /dev/null 2> /dev/null
+    fi
+}
+
+verbose_run_check () {
+    verbose_run "$@"
+    ret="$?"
+    FAIL_IF_SIGNAL $ret
+    if [ $ret != 0 ]; then
+	FAIL "Returned error code $ret"
+    fi
+}
+
+verbose_run_log () {
+    LOG="$1"
+    shift
+    "$@" > "$LOG" 2>&1
+    ret=$?
+    if [ -z "$QUIET_TEST" ]; then
+	cat "$LOG" >&2
+    fi
+    return $ret
+}
+
+verbose_run_log_check () {
+    verbose_run_log "$@"
+    ret="$?"
+    FAIL_IF_SIGNAL $ret
+    if [ $ret != 0 ]; then
+	FAIL "Returned error code $ret"
+    fi
+}
+
diff --git a/tests/trees.S b/tests/trees.S
new file mode 100644
index 0000000..efab287
--- /dev/null
+++ b/tests/trees.S
@@ -0,0 +1,281 @@
+#include <fdt.h>
+#include "testdata.h"
+
+#define FDTLONG(val) \
+	.byte	((val) >> 24) & 0xff ; \
+	.byte	((val) >> 16) & 0xff ; \
+	.byte	((val) >> 8) & 0xff ; \
+	.byte	(val) & 0xff	;
+
+#define TREE_HDR(tree) \
+	.balign	8		; \
+	.globl	tree		; \
+tree:	\
+	FDTLONG(FDT_MAGIC)	; \
+	FDTLONG(tree##_end - tree) ; \
+	FDTLONG(tree##_struct - tree) ; \
+	FDTLONG(tree##_strings - tree) ; \
+	FDTLONG(tree##_rsvmap - tree) ; \
+	FDTLONG(0x11)		; \
+	FDTLONG(0x10)		; \
+	FDTLONG(0)		; \
+	FDTLONG(tree##_strings_end - tree##_strings) ; \
+	FDTLONG(tree##_struct_end - tree##_struct) ;
+
+#define RSVMAP_ENTRY(addrh, addrl, lenh, lenl) \
+	FDTLONG(addrh)		; \
+	FDTLONG(addrl)		; \
+	FDTLONG(lenh)		; \
+	FDTLONG(lenl)
+
+#define EMPTY_RSVMAP(tree) \
+	.balign	8		; \
+tree##_rsvmap:			; \
+	RSVMAP_ENTRY(0, 0, 0, 0) \
+tree##_rsvmap_end:		;
+
+#define PROPHDR(tree, name, len) \
+	FDTLONG(FDT_PROP)	; \
+	FDTLONG(len)		; \
+	FDTLONG(tree##_##name - tree##_strings) ;
+
+#define PROP_EMPTY(tree, name) \
+	PROPHDR(tree, name, 0)	;
+
+#define PROP_INT(tree, name, val) \
+	PROPHDR(tree, name, 4) \
+	FDTLONG(val)		;
+
+#define PROP_INT64(tree, name, valh, vall) \
+	PROPHDR(tree, name, 8) \
+	FDTLONG(valh)		; \
+	FDTLONG(vall)		;
+
+#define PROP_STR(tree, name, str) \
+	PROPHDR(tree, name, 55f - 54f) \
+54:	\
+	.string	str		; \
+55:	\
+	.balign	4		;
+
+#define BEGIN_NODE(name) \
+	FDTLONG(FDT_BEGIN_NODE)	; \
+	.string	name		; \
+	.balign 4		;
+
+#define END_NODE \
+	FDTLONG(FDT_END_NODE)	;
+
+#define STRING(tree, name, str) \
+tree##_##name:			; \
+	.string	str		;
+
+	.data
+
+	TREE_HDR(test_tree1)
+
+	.balign	8
+test_tree1_rsvmap:
+	RSVMAP_ENTRY(TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L)
+	RSVMAP_ENTRY(TEST_ADDR_2H, TEST_ADDR_2L, TEST_SIZE_2H, TEST_SIZE_2L)
+	RSVMAP_ENTRY(0, 0, 0, 0)
+test_tree1_rsvmap_end:
+
+test_tree1_struct:
+	BEGIN_NODE("")
+	PROP_STR(test_tree1, compatible, "test_tree1")
+	PROP_INT(test_tree1, prop_int, TEST_VALUE_1)
+	PROP_INT64(test_tree1, prop_int64, TEST_VALUE64_1H, TEST_VALUE64_1L)
+	PROP_STR(test_tree1, prop_str, TEST_STRING_1)
+	PROP_INT(test_tree1, address_cells, 1)
+	PROP_INT(test_tree1, size_cells, 0)
+
+	BEGIN_NODE("subnode@1")
+	PROP_STR(test_tree1, compatible, "subnode1")
+	PROP_INT(test_tree1, reg, 1)
+	PROP_INT(test_tree1, prop_int, TEST_VALUE_1)
+
+	BEGIN_NODE("subsubnode")
+	PROP_STR(test_tree1, compatible, "subsubnode1\0subsubnode")
+	PROP_STR(test_tree1, placeholder, "this is a placeholder string\0string2")
+	PROP_INT(test_tree1, prop_int, TEST_VALUE_1)
+	END_NODE
+
+	BEGIN_NODE("ss1")
+	END_NODE
+
+	END_NODE
+
+	BEGIN_NODE("subnode@2")
+	PROP_INT(test_tree1, reg, 2)
+	PROP_INT(test_tree1, linux_phandle, PHANDLE_1)
+	PROP_INT(test_tree1, prop_int, TEST_VALUE_2)
+	PROP_INT(test_tree1, address_cells, 1)
+	PROP_INT(test_tree1, size_cells, 0)
+
+	BEGIN_NODE("subsubnode@0")
+	PROP_INT(test_tree1, reg, 0)
+	PROP_INT(test_tree1, phandle, PHANDLE_2)
+	PROP_STR(test_tree1, compatible, "subsubnode2\0subsubnode")
+	PROP_INT(test_tree1, prop_int, TEST_VALUE_2)
+	END_NODE
+
+	BEGIN_NODE("ss2")
+	END_NODE
+
+	END_NODE
+
+	END_NODE
+	FDTLONG(FDT_END)
+test_tree1_struct_end:
+
+test_tree1_strings:
+	STRING(test_tree1, compatible, "compatible")
+	STRING(test_tree1, prop_int, "prop-int")
+	STRING(test_tree1, prop_int64, "prop-int64")
+	STRING(test_tree1, prop_str, "prop-str")
+	STRING(test_tree1, linux_phandle, "linux,phandle")
+	STRING(test_tree1, phandle, "phandle")
+	STRING(test_tree1, reg, "reg")
+	STRING(test_tree1, placeholder, "placeholder")
+	STRING(test_tree1, address_cells, "#address-cells")
+	STRING(test_tree1, size_cells, "#size-cells")
+test_tree1_strings_end:
+test_tree1_end:
+
+
+	TREE_HDR(truncated_property)
+	EMPTY_RSVMAP(truncated_property)
+
+truncated_property_struct:
+	BEGIN_NODE("")
+	PROPHDR(truncated_property, prop_truncated, 4)
+	/* Oops, no actual property data here */
+truncated_property_struct_end:
+
+truncated_property_strings:
+	STRING(truncated_property, prop_truncated, "truncated")
+truncated_property_strings_end:
+
+truncated_property_end:
+
+
+	TREE_HDR(bad_node_char)
+	EMPTY_RSVMAP(bad_node_char)
+
+bad_node_char_struct:
+	BEGIN_NODE("")
+	BEGIN_NODE("sub$node")
+	END_NODE
+	END_NODE
+	FDTLONG(FDT_END)
+bad_node_char_struct_end:
+
+bad_node_char_strings:
+bad_node_char_strings_end:
+bad_node_char_end:
+
+
+	TREE_HDR(bad_node_format)
+	EMPTY_RSVMAP(bad_node_format)
+
+bad_node_format_struct:
+	BEGIN_NODE("")
+	BEGIN_NODE("subnode@1@2")
+	END_NODE
+	END_NODE
+	FDTLONG(FDT_END)
+bad_node_format_struct_end:
+
+bad_node_format_strings:
+bad_node_format_strings_end:
+bad_node_format_end:
+
+
+	TREE_HDR(bad_prop_char)
+	EMPTY_RSVMAP(bad_prop_char)
+
+bad_prop_char_struct:
+	BEGIN_NODE("")
+	PROP_INT(bad_prop_char, prop, TEST_VALUE_1)
+	END_NODE
+	FDTLONG(FDT_END)
+bad_prop_char_struct_end:
+
+bad_prop_char_strings:
+	STRING(bad_prop_char, prop, "prop$erty")
+bad_prop_char_strings_end:
+bad_prop_char_end:
+
+
+	/* overflow_size_strings */
+	.balign	8
+	.globl	ovf_size_strings
+ovf_size_strings:
+	FDTLONG(FDT_MAGIC)
+	FDTLONG(ovf_size_strings_end - ovf_size_strings)
+	FDTLONG(ovf_size_strings_struct - ovf_size_strings) 
+	FDTLONG(ovf_size_strings_strings - ovf_size_strings)
+	FDTLONG(ovf_size_strings_rsvmap - ovf_size_strings)
+	FDTLONG(0x11)
+	FDTLONG(0x10)
+	FDTLONG(0)
+	FDTLONG(0xffffffff)
+	FDTLONG(ovf_size_strings_struct_end - ovf_size_strings_struct)
+	EMPTY_RSVMAP(ovf_size_strings)
+
+ovf_size_strings_struct:
+	BEGIN_NODE("")
+	PROP_INT(ovf_size_strings, bad_string, 0)
+	END_NODE
+	FDTLONG(FDT_END)
+ovf_size_strings_struct_end:
+
+ovf_size_strings_strings:
+	STRING(ovf_size_strings, x, "x")
+	ovf_size_strings_bad_string = ovf_size_strings_strings + 0x10000000
+ovf_size_strings_strings_end:
+ovf_size_strings_end:
+
+
+	/* truncated_string */
+	TREE_HDR(truncated_string)
+	EMPTY_RSVMAP(truncated_string)
+
+truncated_string_struct:
+	BEGIN_NODE("")
+	PROP_EMPTY(truncated_string, good_string)
+	PROP_EMPTY(truncated_string, bad_string)
+	END_NODE
+	FDTLONG(FDT_END)
+truncated_string_struct_end:
+
+truncated_string_strings:
+	STRING(truncated_string, good_string, "good")
+truncated_string_bad_string:
+	.byte	'b'
+	.byte	'a'
+	.byte	'd'
+	/* NOTE: terminating \0 deliberately missing */
+truncated_string_strings_end:
+truncated_string_end:
+
+
+	/* truncated_memrsv */
+	TREE_HDR(truncated_memrsv)
+
+truncated_memrsv_struct:
+	BEGIN_NODE("")
+	END_NODE
+	FDTLONG(FDT_END)
+truncated_memrsv_struct_end:
+
+truncated_memrsv_strings:
+truncated_memrsv_strings_end:
+
+	.balign	8
+truncated_memrsv_rsvmap:
+	RSVMAP_ENTRY(TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L)
+truncated_memrsv_rsvmap_end:
+
+truncated_memrsv_end:
diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c
new file mode 100644
index 0000000..d78036c
--- /dev/null
+++ b/tests/truncated_memrsv.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for misbehaviour on a truncated string
+ * Copyright (C) 2018 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt = &truncated_memrsv;
+	int err;
+	uint64_t addr, size;
+
+	test_init(argc, argv);
+
+	err = fdt_check_header(fdt);
+	if (err != 0)
+		FAIL("Bad header: %s", fdt_strerror(err));
+
+	err = fdt_num_mem_rsv(fdt);
+	if (err != -FDT_ERR_TRUNCATED)
+		FAIL("fdt_num_mem_rsv() returned %d instead of -FDT_ERR_TRUNCATED",
+		     err);
+
+	err = fdt_get_mem_rsv(fdt, 0, &addr, &size);
+	if (err != 0)
+		FAIL("fdt_get_mem_rsv() failed on first entry: %s",
+		     fdt_strerror(err));
+	if ((addr != TEST_ADDR_1) || (size != TEST_SIZE_1))
+		FAIL("Entry doesn't match: (0x%llx, 0x%llx) != (0x%llx, 0x%llx)",
+		     (unsigned long long)addr, (unsigned long long)size,
+		     TEST_ADDR_1, TEST_SIZE_1);
+
+	err = fdt_get_mem_rsv(fdt, 1, &addr, &size);
+	if (err != -FDT_ERR_BADOFFSET)
+		FAIL("fdt_get_mem_rsv(1) returned %d instead of -FDT_ERR_BADOFFSET",
+		     err);
+
+	PASS();
+}
diff --git a/tests/truncated_property.c b/tests/truncated_property.c
new file mode 100644
index 0000000..d9d52b2
--- /dev/null
+++ b/tests/truncated_property.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for misbehaviour on a truncated property
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt = &truncated_property;
+	const void *prop;
+	int len;
+
+	test_init(argc, argv);
+
+	vg_prepare_blob(fdt, fdt_totalsize(fdt));
+
+	prop = fdt_getprop(fdt, 0, "truncated", &len);
+	if (prop)
+		FAIL("fdt_getprop() succeeded on truncated property");
+	if (len != -FDT_ERR_BADSTRUCTURE)
+		FAIL("fdt_getprop() failed with \"%s\" instead of \"%s\"",
+		     fdt_strerror(len), fdt_strerror(-FDT_ERR_BADSTRUCTURE));
+
+	PASS();
+}
diff --git a/tests/truncated_string.c b/tests/truncated_string.c
new file mode 100644
index 0000000..d745414
--- /dev/null
+++ b/tests/truncated_string.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Testcase for misbehaviour on a truncated string
+ * Copyright (C) 2018 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	void *fdt = &truncated_string;
+	const struct fdt_property *good, *bad;
+	int off, len;
+	const char *name;
+
+	test_init(argc, argv);
+
+	vg_prepare_blob(fdt, fdt_totalsize(fdt));
+
+	off = fdt_first_property_offset(fdt, 0);
+	good = fdt_get_property_by_offset(fdt, off, NULL);
+
+	off = fdt_next_property_offset(fdt, off);
+	bad = fdt_get_property_by_offset(fdt, off, NULL);
+
+	if (fdt32_to_cpu(good->len) != 0)
+		FAIL("Unexpected length for good property");
+	name = fdt_get_string(fdt, fdt32_to_cpu(good->nameoff), &len);
+	if (!name)
+		FAIL("fdt_get_string() failed on good property: %s",
+		     fdt_strerror(len));
+	if (len != 4)
+		FAIL("fdt_get_string() returned length %d (not 4) on good property",
+		     len);
+	if (!streq(name, "good"))
+		FAIL("fdt_get_string() returned \"%s\" (not \"good\") on good property",
+		     name);
+
+	if (fdt32_to_cpu(bad->len) != 0)
+		FAIL("Unexpected length for bad property\n");
+	name = fdt_get_string(fdt, fdt32_to_cpu(bad->nameoff), &len);
+	if (name)
+		FAIL("fdt_get_string() succeeded incorrectly on bad property");
+	else if (len != -FDT_ERR_TRUNCATED)
+		FAIL("fdt_get_string() gave unexpected error on bad property: %s",
+		     fdt_strerror(len));
+
+	/* Make sure the 'good' property breaks correctly if we
+	 * truncate the strings section */
+	fdt_set_size_dt_strings(fdt, fdt32_to_cpu(good->nameoff) + 4);
+	name = fdt_get_string(fdt, fdt32_to_cpu(good->nameoff), &len);
+	if (name)
+		FAIL("fdt_get_string() succeeded incorrectly on mangled property");
+	else if (len != -FDT_ERR_TRUNCATED)
+		FAIL("fdt_get_string() gave unexpected error on mangled property: %s",
+		     fdt_strerror(len));
+
+	PASS();
+}
diff --git a/tests/type-preservation.dt.yaml b/tests/type-preservation.dt.yaml
new file mode 100644
index 0000000..ee8cfde
--- /dev/null
+++ b/tests/type-preservation.dt.yaml
@@ -0,0 +1,20 @@
+---
+- '#address-cells': [[0x1]]
+  '#size-cells': [[0x0]]
+  subnode@1:
+    compatible: ["subnode1"]
+    reg: [[0x1]]
+    int-array: [[0x0, 0x1], [0x2, 0x3]]
+    int8: [!u8 [0x56]]
+    int8-array: [!u8 [0x0, 0x12, 0x34, 0x56]]
+    int16: [!u16 [0x3210]]
+    int16-array: [!u16 [0x1234, 0x5678, 0x90ab, 0xcdef]]
+    int16-matrix: [!u16 [0x1234, 0x5678], [0x90ab, 0xcdef]]
+    int64: [!u64 [0x200000000]]
+    int64-array: [!u64 [0x100000000, 0x0]]
+    a-string-with-nulls: ["foo\0bar", "baz"]
+    subsubnode:
+      compatible: ["subsubnode1", "subsubnode"]
+      subsubsubnode:
+        compatible: ["subsubsubnode1", [0x1234], "subsubsubnode"]
+...
diff --git a/tests/type-preservation.dts b/tests/type-preservation.dts
new file mode 100644
index 0000000..3e380ba
--- /dev/null
+++ b/tests/type-preservation.dts
@@ -0,0 +1,28 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <0x01>;
+	#size-cells = <0x00>;
+
+	sub1: subnode@1 {
+		prop_label: compatible = value_label: "subnode1";
+		reg = <0x01>;
+		int-array = <0x00 0x01>, int_value_label: <0x02 0x03>;
+		int8 = [56];
+		int8-array = [00 12 34 56] label:;
+		int16 = /bits/ 16 <0x3210>;
+		int16-array = /bits/ 16 <0x1234 0x5678 0x90ab 0xcdef>;
+		int16-matrix = /bits/ 16 <0x1234 0x5678>, <0x90ab 0xcdef>;
+		int64 = /bits/ 64 <0x200000000>;
+		int64-array = /bits/ 64 <0x100000000 0x00> int64_array_label_end:;
+		a-string-with-nulls = "foo\0bar", "baz";
+
+		subsub1: subsubnode {
+			compatible = "subsubnode1", "subsubnode";
+
+			subsubsub1: subsubsubnode {
+				compatible = "subsubsubnode1", <0x1234>, valuea: valueb: "subsubsubnode";
+			};
+		};
+	};
+};
diff --git a/tests/unit-addr-leading-0s.dts b/tests/unit-addr-leading-0s.dts
new file mode 100644
index 0000000..cc017e9
--- /dev/null
+++ b/tests/unit-addr-leading-0s.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	bus {
+		node@001 {
+			reg = <1 0>;
+		};
+	};
+};
diff --git a/tests/unit-addr-leading-0x.dts b/tests/unit-addr-leading-0x.dts
new file mode 100644
index 0000000..74f1967
--- /dev/null
+++ b/tests/unit-addr-leading-0x.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	bus {
+		node@0x1 {
+			reg = <1 0>;
+		};
+	};
+};
diff --git a/tests/unit-addr-simple-bus-compatible.dts b/tests/unit-addr-simple-bus-compatible.dts
new file mode 100644
index 0000000..c8f9341
--- /dev/null
+++ b/tests/unit-addr-simple-bus-compatible.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	bus@10000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "foo-bus", "simple-bus";
+		ranges = <0x0 0x10000000 0x10000>;
+
+		node@100 {
+			reg = <0x1000 1>;
+		};
+	};
+
+};
diff --git a/tests/unit-addr-simple-bus-reg-mismatch.dts b/tests/unit-addr-simple-bus-reg-mismatch.dts
new file mode 100644
index 0000000..2823377
--- /dev/null
+++ b/tests/unit-addr-simple-bus-reg-mismatch.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	bus@10000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "simple-bus";
+		ranges = <0x0 0x10000000 0x10000>;
+
+		node@100 {
+			reg = <0x1000 1>;
+		};
+	};
+
+};
diff --git a/tests/unit-addr-unique.dts b/tests/unit-addr-unique.dts
new file mode 100644
index 0000000..7cc650b
--- /dev/null
+++ b/tests/unit-addr-unique.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	foo@1 {
+		reg = <1>;
+	};
+
+	bar@1 {
+		reg = <1>;
+	};
+};
diff --git a/tests/unit-addr-without-reg.dts b/tests/unit-addr-without-reg.dts
new file mode 100644
index 0000000..ac786eb
--- /dev/null
+++ b/tests/unit-addr-without-reg.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	node@1 {
+	};
+};
diff --git a/tests/utilfdt_test.c b/tests/utilfdt_test.c
new file mode 100644
index 0000000..c621759
--- /dev/null
+++ b/tests/utilfdt_test.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
+ *
+ * utilfdt_test - Tests for utilfdt library
+ */
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <libfdt.h>
+#include <util.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check(const char *fmt, int expect_type, int expect_size)
+{
+	int type;
+	int size;
+
+	if (utilfdt_decode_type(fmt, &type, &size))
+		FAIL("format '%s': valid format string returned failure", fmt);
+	if (expect_type != type)
+		FAIL("format '%s': expected type='%c', got type='%c'", fmt,
+		     expect_type, type);
+	if (expect_size != size)
+		FAIL("format '%s': expected size=%d, got size=%d", fmt,
+		     expect_size, size);
+}
+
+static void checkfail(const char *fmt)
+{
+	int type;
+	int size;
+
+	if (!utilfdt_decode_type(fmt, &type, &size))
+		FAIL("format '%s': invalid format string returned success",
+		     fmt);
+}
+
+/**
+ * Add the given modifier to each of the valid sizes, and check that we get
+ * correct values.
+ *
+ * \param modifier	Modifer string to use as a prefix
+ * \param expected_size	The size (in bytes) that we expect (ignored for
+ *			strings)
+ */
+static void check_sizes(char *modifier, int expected_size)
+{
+	char fmt[10], *ptr;
+
+	/* set up a string with a hole in it for the format character */
+	if (strlen(modifier) + 2 >= sizeof(fmt))
+		FAIL("modifier string '%s' too long", modifier);
+	strcpy(fmt, modifier);
+	ptr = fmt + strlen(fmt);
+	ptr[1] = '\0';
+
+	/* now try each format character in turn */
+	*ptr = 'i';
+	check(fmt, 'i', expected_size);
+
+	*ptr = 'u';
+	check(fmt, 'u', expected_size);
+
+	*ptr = 'x';
+	check(fmt, 'x', expected_size);
+
+	*ptr = 's';
+	check(fmt, 's', -1);
+}
+
+static void test_utilfdt_decode_type(void)
+{
+	char fmt[10];
+	int ch;
+
+	/* check all the valid modifiers and sizes */
+	check_sizes("", -1);
+	check_sizes("b", 1);
+	check_sizes("hh", 1);
+	check_sizes("h", 2);
+	check_sizes("l", 4);
+
+	/* try every other character */
+	checkfail("");
+	for (ch = ' '; ch < 127; ch++) {
+		if (!strchr("iuxs", ch)) {
+			*fmt = ch;
+			fmt[1] = '\0';
+			checkfail(fmt);
+		}
+	}
+
+	/* try a few modifiers at the end */
+	checkfail("sx");
+	checkfail("ihh");
+	checkfail("xb");
+
+	/* and one for the doomsday archives */
+	checkfail("He has all the virtues I dislike and none of the vices "
+			"I admire.");
+}
+
+int main(int argc, char *argv[])
+{
+	test_utilfdt_decode_type();
+	PASS();
+}
diff --git a/tests/value-labels.c b/tests/value-labels.c
new file mode 100644
index 0000000..e318357
--- /dev/null
+++ b/tests/value-labels.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Test labels within values
+ * Copyright (C) 2008 David Gibson, IBM Corporation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <dlfcn.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+struct val_label {
+	const char *labelname;
+	int propoff;
+};
+
+static struct val_label labels1[] = {
+	{ "start1", 0 },
+	{ "mid1", 2 },
+	{ "end1", -1 },
+};
+
+static struct val_label labels2[] = {
+	{ "start2", 0 },
+	{ "innerstart2", 0 },
+	{ "innermid2", 4 },
+	{ "innerend2", -1 },
+	{ "end2", -1 },
+};
+
+static struct val_label labels3[] = {
+	{ "start3", 0 },
+	{ "innerstart3", 0 },
+	{ "innermid3", 1 },
+	{ "innerend3", -1 },
+	{ "end3", -1 },
+};
+
+static void check_prop_labels(void *sohandle, void *fdt, const char *name,
+			      const struct val_label* labels, int n)
+{
+	const struct fdt_property *prop;
+	const char *p;
+	int len;
+	int i;
+
+	prop = fdt_get_property(fdt, 0, name, &len);
+	if (!prop)
+		FAIL("Couldn't locate property \"%s\"", name);
+
+	p = dlsym(sohandle, name);
+	if (!p)
+		FAIL("Couldn't locate label symbol \"%s\"", name);
+
+	if (p != (const char *)prop)
+		FAIL("Label \"%s\" does not point to correct property", name);
+
+	for (i = 0; i < n; i++) {
+		int off = labels[i].propoff;
+
+		if (off == -1)
+			off = len;
+
+		p = dlsym(sohandle, labels[i].labelname);
+		if (!p)
+			FAIL("Couldn't locate label symbol \"%s\"", name);
+
+		if ((p - prop->data) != off)
+			FAIL("Label \"%s\" points to offset %ld instead of %d"
+			     "in property \"%s\"", labels[i].labelname,
+			     (long)(p - prop->data), off, name);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	void *sohandle;
+	void *fdt;
+	int err;
+
+	test_init(argc, argv);
+	if (argc != 2)
+		CONFIG("Usage: %s <so file>", argv[0]);
+
+	sohandle = dlopen(argv[1], RTLD_NOW);
+	if (!sohandle)
+		FAIL("Couldn't dlopen() %s", argv[1]);
+
+	fdt = dlsym(sohandle, "dt_blob_start");
+	if (!fdt)
+		FAIL("Couldn't locate \"dt_blob_start\" symbol in %s",
+		     argv[1]);
+
+	err = fdt_check_header(fdt);
+	if (err != 0)
+		FAIL("%s contains invalid tree: %s", argv[1],
+		     fdt_strerror(err));
+
+
+	check_prop_labels(sohandle, fdt, "prop1", labels1, ARRAY_SIZE(labels1));
+	check_prop_labels(sohandle, fdt, "prop2", labels2, ARRAY_SIZE(labels2));
+	check_prop_labels(sohandle, fdt, "prop3", labels3, ARRAY_SIZE(labels3));
+
+	PASS();
+}
diff --git a/tests/value-labels.dts b/tests/value-labels.dts
new file mode 100644
index 0000000..490c609
--- /dev/null
+++ b/tests/value-labels.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+/ {
+	prop1: prop1 = start1: "a", mid1: "b" end1:;
+	prop2: prop2 = start2: < innerstart2: 0xdeadbeef innermid2: 0xabcd1234 innerend2: > end2:;
+	prop3: prop3 = start3: [ innerstart3: ab innermid3: cd innerend3: ] end3:;
+};
+
diff --git a/tests/zero-phandle.dts b/tests/zero-phandle.dts
new file mode 100644
index 0000000..7997d98
--- /dev/null
+++ b/tests/zero-phandle.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+	node {
+		linux,phandle = <0>;
+	};
+};
diff --git a/treesource.c b/treesource.c
new file mode 100644
index 0000000..c9d980c
--- /dev/null
+++ b/treesource.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include "dtc.h"
+#include "srcpos.h"
+
+extern FILE *yyin;
+extern int yyparse(void);
+extern YYLTYPE yylloc;
+
+struct dt_info *parser_output;
+bool treesource_error;
+
+struct dt_info *dt_from_source(const char *fname)
+{
+	parser_output = NULL;
+	treesource_error = false;
+
+	srcfile_push(fname);
+	yyin = current_srcfile->f;
+	yylloc.file = current_srcfile;
+
+	if (yyparse() != 0)
+		die("Unable to parse input tree\n");
+
+	if (treesource_error)
+		die("Syntax error parsing input tree\n");
+
+	return parser_output;
+}
+
+static void write_prefix(FILE *f, int level)
+{
+	int i;
+
+	for (i = 0; i < level; i++)
+		fputc('\t', f);
+}
+
+static bool isstring(char c)
+{
+	return (isprint((unsigned char)c)
+		|| (c == '\0')
+		|| strchr("\a\b\t\n\v\f\r", c));
+}
+
+static void write_propval_string(FILE *f, const char *s, size_t len)
+{
+	const char *end = s + len - 1;
+
+	if (!len)
+		return;
+
+	assert(*end == '\0');
+
+	fprintf(f, "\"");
+	while (s < end) {
+		char c = *s++;
+		switch (c) {
+		case '\a':
+			fprintf(f, "\\a");
+			break;
+		case '\b':
+			fprintf(f, "\\b");
+			break;
+		case '\t':
+			fprintf(f, "\\t");
+			break;
+		case '\n':
+			fprintf(f, "\\n");
+			break;
+		case '\v':
+			fprintf(f, "\\v");
+			break;
+		case '\f':
+			fprintf(f, "\\f");
+			break;
+		case '\r':
+			fprintf(f, "\\r");
+			break;
+		case '\\':
+			fprintf(f, "\\\\");
+			break;
+		case '\"':
+			fprintf(f, "\\\"");
+			break;
+		case '\0':
+			fprintf(f, "\\0");
+			break;
+		default:
+			if (isprint((unsigned char)c))
+				fprintf(f, "%c", c);
+			else
+				fprintf(f, "\\x%02"PRIx8, c);
+		}
+	}
+	fprintf(f, "\"");
+}
+
+static void write_propval_int(FILE *f, const char *p, size_t len, size_t width)
+{
+	const char *end = p + len;
+	assert(len % width == 0);
+
+	for (; p < end; p += width) {
+		switch (width) {
+		case 1:
+			fprintf(f, "%02"PRIx8, *(const uint8_t*)p);
+			break;
+		case 2:
+			fprintf(f, "0x%02"PRIx16, fdt16_to_cpu(*(const fdt16_t*)p));
+			break;
+		case 4:
+			fprintf(f, "0x%02"PRIx32, fdt32_to_cpu(*(const fdt32_t*)p));
+			break;
+		case 8:
+			fprintf(f, "0x%02"PRIx64, fdt64_to_cpu(*(const fdt64_t*)p));
+			break;
+		}
+		if (p + width < end)
+			fputc(' ', f);
+	}
+}
+
+static bool has_data_type_information(struct marker *m)
+{
+	return m->type >= TYPE_UINT8;
+}
+
+static struct marker *next_type_marker(struct marker *m)
+{
+	while (m && !has_data_type_information(m))
+		m = m->next;
+	return m;
+}
+
+size_t type_marker_length(struct marker *m)
+{
+	struct marker *next = next_type_marker(m->next);
+
+	if (next)
+		return next->offset - m->offset;
+	return 0;
+}
+
+static const char *delim_start[] = {
+	[TYPE_UINT8] = "[",
+	[TYPE_UINT16] = "/bits/ 16 <",
+	[TYPE_UINT32] = "<",
+	[TYPE_UINT64] = "/bits/ 64 <",
+	[TYPE_STRING] = "",
+};
+static const char *delim_end[] = {
+	[TYPE_UINT8] = "]",
+	[TYPE_UINT16] = ">",
+	[TYPE_UINT32] = ">",
+	[TYPE_UINT64] = ">",
+	[TYPE_STRING] = "",
+};
+
+static enum markertype guess_value_type(struct property *prop)
+{
+	int len = prop->val.len;
+	const char *p = prop->val.val;
+	struct marker *m = prop->val.markers;
+	int nnotstring = 0, nnul = 0;
+	int nnotstringlbl = 0, nnotcelllbl = 0;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (! isstring(p[i]))
+			nnotstring++;
+		if (p[i] == '\0')
+			nnul++;
+	}
+
+	for_each_marker_of_type(m, LABEL) {
+		if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0'))
+			nnotstringlbl++;
+		if ((m->offset % sizeof(cell_t)) != 0)
+			nnotcelllbl++;
+	}
+
+	if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul < (len-nnul))
+	    && (nnotstringlbl == 0)) {
+		return TYPE_STRING;
+	} else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) {
+		return TYPE_UINT32;
+	}
+
+	return TYPE_UINT8;
+}
+
+static void write_propval(FILE *f, struct property *prop)
+{
+	size_t len = prop->val.len;
+	struct marker *m = prop->val.markers;
+	struct marker dummy_marker;
+	enum markertype emit_type = TYPE_NONE;
+	char *srcstr;
+
+	if (len == 0) {
+		fprintf(f, ";");
+		if (annotate) {
+			srcstr = srcpos_string_first(prop->srcpos, annotate);
+			if (srcstr) {
+				fprintf(f, " /* %s */", srcstr);
+				free(srcstr);
+			}
+		}
+		fprintf(f, "\n");
+		return;
+	}
+
+	fprintf(f, " =");
+
+	if (!next_type_marker(m)) {
+		/* data type information missing, need to guess */
+		dummy_marker.type = guess_value_type(prop);
+		dummy_marker.next = prop->val.markers;
+		dummy_marker.offset = 0;
+		dummy_marker.ref = NULL;
+		m = &dummy_marker;
+	}
+
+	for_each_marker(m) {
+		size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
+		size_t data_len = type_marker_length(m) ? : len - m->offset;
+		const char *p = &prop->val.val[m->offset];
+
+		if (has_data_type_information(m)) {
+			emit_type = m->type;
+			fprintf(f, " %s", delim_start[emit_type]);
+		} else if (m->type == LABEL)
+			fprintf(f, " %s:", m->ref);
+		else if (m->offset)
+			fputc(' ', f);
+
+		if (emit_type == TYPE_NONE) {
+			assert(chunk_len == 0);
+			continue;
+		}
+
+		switch(emit_type) {
+		case TYPE_UINT16:
+			write_propval_int(f, p, chunk_len, 2);
+			break;
+		case TYPE_UINT32:
+			write_propval_int(f, p, chunk_len, 4);
+			break;
+		case TYPE_UINT64:
+			write_propval_int(f, p, chunk_len, 8);
+			break;
+		case TYPE_STRING:
+			write_propval_string(f, p, chunk_len);
+			break;
+		default:
+			write_propval_int(f, p, chunk_len, 1);
+		}
+
+		if (chunk_len == data_len) {
+			size_t pos = m->offset + chunk_len;
+			fprintf(f, pos == len ? "%s" : "%s,",
+			        delim_end[emit_type] ? : "");
+			emit_type = TYPE_NONE;
+		}
+	}
+	fprintf(f, ";");
+	if (annotate) {
+		srcstr = srcpos_string_first(prop->srcpos, annotate);
+		if (srcstr) {
+			fprintf(f, " /* %s */", srcstr);
+			free(srcstr);
+		}
+	}
+	fprintf(f, "\n");
+}
+
+static void write_tree_source_node(FILE *f, struct node *tree, int level)
+{
+	struct property *prop;
+	struct node *child;
+	struct label *l;
+	char *srcstr;
+
+	write_prefix(f, level);
+	for_each_label(tree->labels, l)
+		fprintf(f, "%s: ", l->label);
+	if (tree->name && (*tree->name))
+		fprintf(f, "%s {", tree->name);
+	else
+		fprintf(f, "/ {");
+
+	if (annotate) {
+		srcstr = srcpos_string_first(tree->srcpos, annotate);
+		if (srcstr) {
+			fprintf(f, " /* %s */", srcstr);
+			free(srcstr);
+		}
+	}
+	fprintf(f, "\n");
+
+	for_each_property(tree, prop) {
+		write_prefix(f, level+1);
+		for_each_label(prop->labels, l)
+			fprintf(f, "%s: ", l->label);
+		fprintf(f, "%s", prop->name);
+		write_propval(f, prop);
+	}
+	for_each_child(tree, child) {
+		fprintf(f, "\n");
+		write_tree_source_node(f, child, level+1);
+	}
+	write_prefix(f, level);
+	fprintf(f, "};");
+	if (annotate) {
+		srcstr = srcpos_string_last(tree->srcpos, annotate);
+		if (srcstr) {
+			fprintf(f, " /* %s */", srcstr);
+			free(srcstr);
+		}
+	}
+	fprintf(f, "\n");
+}
+
+void dt_to_source(FILE *f, struct dt_info *dti)
+{
+	struct reserve_info *re;
+
+	fprintf(f, "/dts-v1/;\n\n");
+
+	for (re = dti->reservelist; re; re = re->next) {
+		struct label *l;
+
+		for_each_label(re->labels, l)
+			fprintf(f, "%s: ", l->label);
+		fprintf(f, "/memreserve/\t0x%016llx 0x%016llx;\n",
+			(unsigned long long)re->address,
+			(unsigned long long)re->size);
+	}
+
+	write_tree_source_node(f, dti->dt, 0);
+}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..48af961
--- /dev/null
+++ b/util.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
+ * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
+ *
+ * util_is_printable_string contributed by
+ *	Pantelis Antoniou <pantelis.antoniou AT gmail.com>
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libfdt.h"
+#include "util.h"
+#include "version_gen.h"
+
+char *xstrdup(const char *s)
+{
+	int len = strlen(s) + 1;
+	char *d = xmalloc(len);
+
+	memcpy(d, s, len);
+
+	return d;
+}
+
+int xavsprintf_append(char **strp, const char *fmt, va_list ap)
+{
+	int n, size = 0;	/* start with 128 bytes */
+	char *p;
+	va_list ap_copy;
+
+	p = *strp;
+	if (p)
+		size = strlen(p);
+
+	va_copy(ap_copy, ap);
+	n = vsnprintf(NULL, 0, fmt, ap_copy) + 1;
+	va_end(ap_copy);
+
+	p = xrealloc(p, size + n);
+
+	n = vsnprintf(p + size, n, fmt, ap);
+
+	*strp = p;
+	return strlen(p);
+}
+
+int xasprintf_append(char **strp, const char *fmt, ...)
+{
+	int n;
+	va_list ap;
+
+	va_start(ap, fmt);
+	n = xavsprintf_append(strp, fmt, ap);
+	va_end(ap);
+
+	return n;
+}
+
+int xasprintf(char **strp, const char *fmt, ...)
+{
+	int n;
+	va_list ap;
+
+	*strp = NULL;
+
+	va_start(ap, fmt);
+	n = xavsprintf_append(strp, fmt, ap);
+	va_end(ap);
+
+	return n;
+}
+
+char *join_path(const char *path, const char *name)
+{
+	int lenp = strlen(path);
+	int lenn = strlen(name);
+	int len;
+	int needslash = 1;
+	char *str;
+
+	len = lenp + lenn + 2;
+	if ((lenp > 0) && (path[lenp-1] == '/')) {
+		needslash = 0;
+		len--;
+	}
+
+	str = xmalloc(len);
+	memcpy(str, path, lenp);
+	if (needslash) {
+		str[lenp] = '/';
+		lenp++;
+	}
+	memcpy(str+lenp, name, lenn+1);
+	return str;
+}
+
+bool util_is_printable_string(const void *data, int len)
+{
+	const char *s = data;
+	const char *ss, *se;
+
+	/* zero length is not */
+	if (len == 0)
+		return 0;
+
+	/* must terminate with zero */
+	if (s[len - 1] != '\0')
+		return 0;
+
+	se = s + len;
+
+	while (s < se) {
+		ss = s;
+		while (s < se && *s && isprint((unsigned char)*s))
+			s++;
+
+		/* not zero, or not done yet */
+		if (*s != '\0' || s == ss)
+			return 0;
+
+		s++;
+	}
+
+	return 1;
+}
+
+/*
+ * Parse a octal encoded character starting at index i in string s.  The
+ * resulting character will be returned and the index i will be updated to
+ * point at the character directly after the end of the encoding, this may be
+ * the '\0' terminator of the string.
+ */
+static char get_oct_char(const char *s, int *i)
+{
+	char x[4];
+	char *endx;
+	long val;
+
+	x[3] = '\0';
+	strncpy(x, s + *i, 3);
+
+	val = strtol(x, &endx, 8);
+
+	assert(endx > x);
+
+	(*i) += endx - x;
+	return val;
+}
+
+/*
+ * Parse a hexadecimal encoded character starting at index i in string s.  The
+ * resulting character will be returned and the index i will be updated to
+ * point at the character directly after the end of the encoding, this may be
+ * the '\0' terminator of the string.
+ */
+static char get_hex_char(const char *s, int *i)
+{
+	char x[3];
+	char *endx;
+	long val;
+
+	x[2] = '\0';
+	strncpy(x, s + *i, 2);
+
+	val = strtol(x, &endx, 16);
+	if (!(endx  > x))
+		die("\\x used with no following hex digits\n");
+
+	(*i) += endx - x;
+	return val;
+}
+
+char get_escape_char(const char *s, int *i)
+{
+	char	c = s[*i];
+	int	j = *i + 1;
+	char	val;
+
+	switch (c) {
+	case 'a':
+		val = '\a';
+		break;
+	case 'b':
+		val = '\b';
+		break;
+	case 't':
+		val = '\t';
+		break;
+	case 'n':
+		val = '\n';
+		break;
+	case 'v':
+		val = '\v';
+		break;
+	case 'f':
+		val = '\f';
+		break;
+	case 'r':
+		val = '\r';
+		break;
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+		j--; /* need to re-read the first digit as
+		      * part of the octal value */
+		val = get_oct_char(s, &j);
+		break;
+	case 'x':
+		val = get_hex_char(s, &j);
+		break;
+	default:
+		val = c;
+	}
+
+	(*i) = j;
+	return val;
+}
+
+int utilfdt_read_err(const char *filename, char **buffp, size_t *len)
+{
+	int fd = 0;	/* assume stdin */
+	char *buf = NULL;
+	size_t bufsize = 1024, offset = 0;
+	int ret = 0;
+
+	*buffp = NULL;
+	if (strcmp(filename, "-") != 0) {
+		fd = open(filename, O_RDONLY);
+		if (fd < 0)
+			return errno;
+	}
+
+	/* Loop until we have read everything */
+	buf = xmalloc(bufsize);
+	do {
+		/* Expand the buffer to hold the next chunk */
+		if (offset == bufsize) {
+			bufsize *= 2;
+			buf = xrealloc(buf, bufsize);
+		}
+
+		ret = read(fd, &buf[offset], bufsize - offset);
+		if (ret < 0) {
+			ret = errno;
+			break;
+		}
+		offset += ret;
+	} while (ret != 0);
+
+	/* Clean up, including closing stdin; return errno on error */
+	close(fd);
+	if (ret)
+		free(buf);
+	else
+		*buffp = buf;
+	if (len)
+		*len = bufsize;
+	return ret;
+}
+
+char *utilfdt_read(const char *filename, size_t *len)
+{
+	char *buff;
+	int ret = utilfdt_read_err(filename, &buff, len);
+
+	if (ret) {
+		fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
+			strerror(ret));
+		return NULL;
+	}
+	/* Successful read */
+	return buff;
+}
+
+int utilfdt_write_err(const char *filename, const void *blob)
+{
+	int fd = 1;	/* assume stdout */
+	int totalsize;
+	int offset;
+	int ret = 0;
+	const char *ptr = blob;
+
+	if (strcmp(filename, "-") != 0) {
+		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (fd < 0)
+			return errno;
+	}
+
+	totalsize = fdt_totalsize(blob);
+	offset = 0;
+
+	while (offset < totalsize) {
+		ret = write(fd, ptr + offset, totalsize - offset);
+		if (ret < 0) {
+			ret = -errno;
+			break;
+		}
+		offset += ret;
+	}
+	/* Close the file/stdin; return errno on error */
+	if (fd != 1)
+		close(fd);
+	return ret < 0 ? -ret : 0;
+}
+
+
+int utilfdt_write(const char *filename, const void *blob)
+{
+	int ret = utilfdt_write_err(filename, blob);
+
+	if (ret) {
+		fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename,
+			strerror(ret));
+	}
+	return ret ? -1 : 0;
+}
+
+int utilfdt_decode_type(const char *fmt, int *type, int *size)
+{
+	int qualifier = 0;
+
+	if (!*fmt)
+		return -1;
+
+	/* get the conversion qualifier */
+	*size = -1;
+	if (strchr("hlLb", *fmt)) {
+		qualifier = *fmt++;
+		if (qualifier == *fmt) {
+			switch (*fmt++) {
+/* TODO:		case 'l': qualifier = 'L'; break;*/
+			case 'h':
+				qualifier = 'b';
+				break;
+			}
+		}
+	}
+
+	/* we should now have a type */
+	if ((*fmt == '\0') || !strchr("iuxs", *fmt))
+		return -1;
+
+	/* convert qualifier (bhL) to byte size */
+	if (*fmt != 's')
+		*size = qualifier == 'b' ? 1 :
+				qualifier == 'h' ? 2 :
+				qualifier == 'l' ? 4 : -1;
+	*type = *fmt++;
+
+	/* that should be it! */
+	if (*fmt)
+		return -1;
+	return 0;
+}
+
+void utilfdt_print_data(const char *data, int len)
+{
+	int i;
+	const char *s;
+
+	/* no data, don't print */
+	if (len == 0)
+		return;
+
+	if (util_is_printable_string(data, len)) {
+		printf(" = ");
+
+		s = data;
+		do {
+			printf("\"%s\"", s);
+			s += strlen(s) + 1;
+			if (s < data + len)
+				printf(", ");
+		} while (s < data + len);
+
+	} else if ((len % 4) == 0) {
+		const fdt32_t *cell = (const fdt32_t *)data;
+
+		printf(" = <");
+		for (i = 0, len /= 4; i < len; i++)
+			printf("0x%08x%s", fdt32_to_cpu(cell[i]),
+			       i < (len - 1) ? " " : "");
+		printf(">");
+	} else {
+		const unsigned char *p = (const unsigned char *)data;
+		printf(" = [");
+		for (i = 0; i < len; i++)
+			printf("%02x%s", *p++, i < len - 1 ? " " : "");
+		printf("]");
+	}
+}
+
+void NORETURN util_version(void)
+{
+	printf("Version: %s\n", DTC_VERSION);
+	exit(0);
+}
+
+void NORETURN util_usage(const char *errmsg, const char *synopsis,
+			 const char *short_opts,
+			 struct option const long_opts[],
+			 const char * const opts_help[])
+{
+	FILE *fp = errmsg ? stderr : stdout;
+	const char a_arg[] = "<arg>";
+	size_t a_arg_len = strlen(a_arg) + 1;
+	size_t i;
+	int optlen;
+
+	fprintf(fp,
+		"Usage: %s\n"
+		"\n"
+		"Options: -[%s]\n", synopsis, short_opts);
+
+	/* prescan the --long opt length to auto-align */
+	optlen = 0;
+	for (i = 0; long_opts[i].name; ++i) {
+		/* +1 is for space between --opt and help text */
+		int l = strlen(long_opts[i].name) + 1;
+		if (long_opts[i].has_arg == a_argument)
+			l += a_arg_len;
+		if (optlen < l)
+			optlen = l;
+	}
+
+	for (i = 0; long_opts[i].name; ++i) {
+		/* helps when adding new applets or options */
+		assert(opts_help[i] != NULL);
+
+		/* first output the short flag if it has one */
+		if (long_opts[i].val > '~')
+			fprintf(fp, "      ");
+		else
+			fprintf(fp, "  -%c, ", long_opts[i].val);
+
+		/* then the long flag */
+		if (long_opts[i].has_arg == no_argument)
+			fprintf(fp, "--%-*s", optlen, long_opts[i].name);
+		else
+			fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg,
+				(int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
+
+		/* finally the help text */
+		fprintf(fp, "%s\n", opts_help[i]);
+	}
+
+	if (errmsg) {
+		fprintf(fp, "\nError: %s\n", errmsg);
+		exit(EXIT_FAILURE);
+	} else
+		exit(EXIT_SUCCESS);
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..ca5cb52
--- /dev/null
+++ b/util.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
+ * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
+ */
+
+#ifdef __GNUC__
+#define PRINTF(i, j)	__attribute__((format (printf, i, j)))
+#define NORETURN	__attribute__((noreturn))
+#else
+#define PRINTF(i, j)
+#define NORETURN
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define stringify(s)	stringify_(s)
+#define stringify_(s)	#s
+
+static inline void NORETURN PRINTF(1, 2) die(const char *str, ...)
+{
+	va_list ap;
+
+	va_start(ap, str);
+	fprintf(stderr, "FATAL ERROR: ");
+	vfprintf(stderr, str, ap);
+	va_end(ap);
+	exit(1);
+}
+
+static inline void *xmalloc(size_t len)
+{
+	void *new = malloc(len);
+
+	if (!new)
+		die("malloc() failed\n");
+
+	return new;
+}
+
+static inline void *xrealloc(void *p, size_t len)
+{
+	void *new = realloc(p, len);
+
+	if (!new)
+		die("realloc() failed (len=%zd)\n", len);
+
+	return new;
+}
+
+extern char *xstrdup(const char *s);
+
+extern int PRINTF(2, 3) xasprintf(char **strp, const char *fmt, ...);
+extern int PRINTF(2, 3) xasprintf_append(char **strp, const char *fmt, ...);
+extern int xavsprintf_append(char **strp, const char *fmt, va_list ap);
+extern char *join_path(const char *path, const char *name);
+
+/**
+ * Check a property of a given length to see if it is all printable and
+ * has a valid terminator. The property can contain either a single string,
+ * or multiple strings each of non-zero length.
+ *
+ * @param data	The string to check
+ * @param len	The string length including terminator
+ * @return 1 if a valid printable string, 0 if not
+ */
+bool util_is_printable_string(const void *data, int len);
+
+/*
+ * Parse an escaped character starting at index i in string s.  The resulting
+ * character will be returned and the index i will be updated to point at the
+ * character directly after the end of the encoding, this may be the '\0'
+ * terminator of the string.
+ */
+char get_escape_char(const char *s, int *i);
+
+/**
+ * Read a device tree file into a buffer. This will report any errors on
+ * stderr.
+ *
+ * @param filename	The filename to read, or - for stdin
+ * @param len		If non-NULL, the amount of data we managed to read
+ * @return Pointer to allocated buffer containing fdt, or NULL on error
+ */
+char *utilfdt_read(const char *filename, size_t *len);
+
+/**
+ * Read a device tree file into a buffer. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename	The filename to read, or - for stdin
+ * @param buffp		Returns pointer to buffer containing fdt
+ * @param len		If non-NULL, the amount of data we managed to read
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_read_err(const char *filename, char **buffp, size_t *len);
+
+/**
+ * Write a device tree buffer to a file. This will report any errors on
+ * stderr.
+ *
+ * @param filename	The filename to write, or - for stdout
+ * @param blob		Pointer to buffer containing fdt
+ * @return 0 if ok, -1 on error
+ */
+int utilfdt_write(const char *filename, const void *blob);
+
+/**
+ * Write a device tree buffer to a file. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename	The filename to write, or - for stdout
+ * @param blob		Pointer to buffer containing fdt
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_write_err(const char *filename, const void *blob);
+
+/**
+ * Decode a data type string. The purpose of this string
+ *
+ * The string consists of an optional character followed by the type:
+ *	Modifier characters:
+ *		hh or b	1 byte
+ *		h	2 byte
+ *		l	4 byte, default
+ *
+ *	Type character:
+ *		s	string
+ *		i	signed integer
+ *		u	unsigned integer
+ *		x	hex
+ *
+ * TODO: Implement ll modifier (8 bytes)
+ * TODO: Implement o type (octal)
+ *
+ * @param fmt		Format string to process
+ * @param type		Returns type found(s/d/u/x), or 0 if none
+ * @param size		Returns size found(1,2,4,8) or 4 if none
+ * @return 0 if ok, -1 on error (no type given, or other invalid format)
+ */
+int utilfdt_decode_type(const char *fmt, int *type, int *size);
+
+/*
+ * This is a usage message fragment for the -t option. It is the format
+ * supported by utilfdt_decode_type.
+ */
+
+#define USAGE_TYPE_MSG \
+	"<type>\ts=string, i=int, u=unsigned, x=hex\n" \
+	"\tOptional modifier prefix:\n" \
+	"\t\thh or b=byte, h=2 byte, l=4 byte (default)";
+
+/**
+ * Print property data in a readable format to stdout
+ *
+ * Properties that look like strings will be printed as strings. Otherwise
+ * the data will be displayed either as cells (if len is a multiple of 4
+ * bytes) or bytes.
+ *
+ * If len is 0 then this function does nothing.
+ *
+ * @param data	Pointers to property data
+ * @param len	Length of property data
+ */
+void utilfdt_print_data(const char *data, int len);
+
+/**
+ * Show source version and exit
+ */
+void NORETURN util_version(void);
+
+/**
+ * Show usage and exit
+ *
+ * This helps standardize the output of various utils.  You most likely want
+ * to use the usage() helper below rather than call this.
+ *
+ * @param errmsg	If non-NULL, an error message to display
+ * @param synopsis	The initial example usage text (and possible examples)
+ * @param short_opts	The string of short options
+ * @param long_opts	The structure of long options
+ * @param opts_help	An array of help strings (should align with long_opts)
+ */
+void NORETURN util_usage(const char *errmsg, const char *synopsis,
+			 const char *short_opts,
+			 struct option const long_opts[],
+			 const char * const opts_help[]);
+
+/**
+ * Show usage and exit
+ *
+ * If you name all your usage variables with usage_xxx, then you can call this
+ * help macro rather than expanding all arguments yourself.
+ *
+ * @param errmsg	If non-NULL, an error message to display
+ */
+#define usage(errmsg) \
+	util_usage(errmsg, usage_synopsis, usage_short_opts, \
+		   usage_long_opts, usage_opts_help)
+
+/**
+ * Call getopt_long() with standard options
+ *
+ * Since all util code runs getopt in the same way, provide a helper.
+ */
+#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \
+				       usage_long_opts, NULL)
+
+/* Helper for aligning long_opts array */
+#define a_argument required_argument
+
+/* Helper for usage_short_opts string constant */
+#define USAGE_COMMON_SHORT_OPTS "hV"
+
+/* Helper for usage_long_opts option array */
+#define USAGE_COMMON_LONG_OPTS \
+	{"help",      no_argument, NULL, 'h'}, \
+	{"version",   no_argument, NULL, 'V'}, \
+	{NULL,        no_argument, NULL, 0x0}
+
+/* Helper for usage_opts_help array */
+#define USAGE_COMMON_OPTS_HELP \
+	"Print this help and exit", \
+	"Print version and exit", \
+	NULL
+
+/* Helper for getopt case statements */
+#define case_USAGE_COMMON_FLAGS \
+	case 'h': usage(NULL); \
+	case 'V': util_version(); \
+	case '?': usage("unknown option");
+
+#endif /* UTIL_H */
diff --git a/yamltree.c b/yamltree.c
new file mode 100644
index 0000000..5b6ea8e
--- /dev/null
+++ b/yamltree.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright Linaro, Ltd. 2018
+ * (C) Copyright Arm Holdings.  2017
+ * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
+ */
+
+#include <stdlib.h>
+#include <yaml.h>
+#include "dtc.h"
+#include "srcpos.h"
+
+char *yaml_error_name[] = {
+	[YAML_NO_ERROR] = "no error",
+	[YAML_MEMORY_ERROR] = "memory error",
+	[YAML_READER_ERROR] = "reader error",
+	[YAML_SCANNER_ERROR] = "scanner error",
+	[YAML_PARSER_ERROR] = "parser error",
+	[YAML_COMPOSER_ERROR] = "composer error",
+	[YAML_WRITER_ERROR] = "writer error",
+	[YAML_EMITTER_ERROR] = "emitter error",
+};
+
+#define yaml_emitter_emit_or_die(emitter, event) (			\
+{									\
+	if (!yaml_emitter_emit(emitter, event))				\
+		die("yaml '%s': %s in %s, line %i\n",			\
+		    yaml_error_name[(emitter)->error], 			\
+		    (emitter)->problem, __func__, __LINE__);		\
+})
+
+static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, int len, int width)
+{
+	yaml_event_t event;
+	void *tag;
+	int off, start_offset = markers->offset;
+
+	switch(width) {
+		case 1: tag = "!u8"; break;
+		case 2: tag = "!u16"; break;
+		case 4: tag = "!u32"; break;
+		case 8: tag = "!u64"; break;
+		default:
+			die("Invalid width %i", width);
+	}
+	assert(len % width == 0);
+
+	yaml_sequence_start_event_initialize(&event, NULL,
+		(yaml_char_t *)tag, width == 4, YAML_FLOW_SEQUENCE_STYLE);
+	yaml_emitter_emit_or_die(emitter, &event);
+
+	for (off = 0; off < len; off += width) {
+		char buf[32];
+		struct marker *m;
+		bool is_phandle = false;
+
+		switch(width) {
+		case 1:
+			sprintf(buf, "0x%"PRIx8, *(uint8_t*)(data + off));
+			break;
+		case 2:
+			sprintf(buf, "0x%"PRIx16, fdt16_to_cpu(*(fdt16_t*)(data + off)));
+			break;
+		case 4:
+			sprintf(buf, "0x%"PRIx32, fdt32_to_cpu(*(fdt32_t*)(data + off)));
+			m = markers;
+			is_phandle = false;
+			for_each_marker_of_type(m, REF_PHANDLE) {
+				if (m->offset == (start_offset + off)) {
+					is_phandle = true;
+					break;
+				}
+			}
+			break;
+		case 8:
+			sprintf(buf, "0x%"PRIx64, fdt64_to_cpu(*(fdt64_t*)(data + off)));
+			break;
+		}
+
+		if (is_phandle)
+			yaml_scalar_event_initialize(&event, NULL,
+				(yaml_char_t*)"!phandle", (yaml_char_t *)buf,
+				strlen(buf), 0, 0, YAML_PLAIN_SCALAR_STYLE);
+		else
+			yaml_scalar_event_initialize(&event, NULL,
+				(yaml_char_t*)YAML_INT_TAG, (yaml_char_t *)buf,
+				strlen(buf), 1, 1, YAML_PLAIN_SCALAR_STYLE);
+		yaml_emitter_emit_or_die(emitter, &event);
+	}
+
+	yaml_sequence_end_event_initialize(&event);
+	yaml_emitter_emit_or_die(emitter, &event);
+}
+
+static void yaml_propval_string(yaml_emitter_t *emitter, char *str, int len)
+{
+	yaml_event_t event;
+	int i;
+
+	assert(str[len-1] == '\0');
+
+	/* Make sure the entire string is in the lower 7-bit ascii range */
+	for (i = 0; i < len; i++)
+		assert(isascii(str[i]));
+
+	yaml_scalar_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)str,
+		len-1, 0, 1, YAML_DOUBLE_QUOTED_SCALAR_STYLE);
+	yaml_emitter_emit_or_die(emitter, &event);
+}
+
+static void yaml_propval(yaml_emitter_t *emitter, struct property *prop)
+{
+	yaml_event_t event;
+	int len = prop->val.len;
+	struct marker *m = prop->val.markers;
+
+	/* Emit the property name */
+	yaml_scalar_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)prop->name,
+		strlen(prop->name), 1, 1, YAML_PLAIN_SCALAR_STYLE);
+	yaml_emitter_emit_or_die(emitter, &event);
+
+	/* Boolean properties are easiest to deal with. Length is zero, so just emit 'true' */
+	if (len == 0) {
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t *)YAML_BOOL_TAG,
+			(yaml_char_t*)"true",
+			strlen("true"), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+		yaml_emitter_emit_or_die(emitter, &event);
+		return;
+	}
+
+	if (!m)
+		die("No markers present in property '%s' value\n", prop->name);
+
+	yaml_sequence_start_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_SEQ_TAG, 1, YAML_FLOW_SEQUENCE_STYLE);
+	yaml_emitter_emit_or_die(emitter, &event);
+
+	for_each_marker(m) {
+		int chunk_len;
+		char *data = &prop->val.val[m->offset];
+
+		if (m->type < TYPE_UINT8)
+			continue;
+
+		chunk_len = type_marker_length(m) ? : len;
+		assert(chunk_len > 0);
+		len -= chunk_len;
+
+		switch(m->type) {
+		case TYPE_UINT16:
+			yaml_propval_int(emitter, m, data, chunk_len, 2);
+			break;
+		case TYPE_UINT32:
+			yaml_propval_int(emitter, m, data, chunk_len, 4);
+			break;
+		case TYPE_UINT64:
+			yaml_propval_int(emitter, m, data, chunk_len, 8);
+			break;
+		case TYPE_STRING:
+			yaml_propval_string(emitter, data, chunk_len);
+			break;
+		default:
+			yaml_propval_int(emitter, m, data, chunk_len, 1);
+			break;
+		}
+	}
+
+	yaml_sequence_end_event_initialize(&event);
+	yaml_emitter_emit_or_die(emitter, &event);
+}
+
+
+static void yaml_tree(struct node *tree, yaml_emitter_t *emitter)
+{
+	struct property *prop;
+	struct node *child;
+	yaml_event_t event;
+
+	if (tree->deleted)
+		return;
+
+	yaml_mapping_start_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_MAP_TAG, 1, YAML_ANY_MAPPING_STYLE);
+	yaml_emitter_emit_or_die(emitter, &event);
+
+	for_each_property(tree, prop)
+		yaml_propval(emitter, prop);
+
+	/* Loop over all the children, emitting them into the map */
+	for_each_child(tree, child) {
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)child->name,
+			strlen(child->name), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+		yaml_emitter_emit_or_die(emitter, &event);
+		yaml_tree(child, emitter);
+	}
+
+	yaml_mapping_end_event_initialize(&event);
+	yaml_emitter_emit_or_die(emitter, &event);
+}
+
+void dt_to_yaml(FILE *f, struct dt_info *dti)
+{
+	yaml_emitter_t emitter;
+	yaml_event_t event;
+
+	yaml_emitter_initialize(&emitter);
+	yaml_emitter_set_output_file(&emitter, f);
+	yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_ANY_SEQUENCE_STYLE);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_tree(dti->dt, &emitter);
+
+	yaml_sequence_end_event_initialize(&event);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_document_end_event_initialize(&event, 0);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_stream_end_event_initialize(&event);
+	yaml_emitter_emit_or_die(&emitter, &event);
+
+	yaml_emitter_delete(&emitter);
+}