diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
commit | 9e80202352dd49bdd9e67b8b906d86f058431505 (patch) | |
tree | 5673c17aad6e3833da8c4ff21b5a11f666ec9fbe /src/dtc | |
download | hqemu-9e80202352dd49bdd9e67b8b906d86f058431505.zip hqemu-9e80202352dd49bdd9e67b8b906d86f058431505.tar.gz |
Diffstat (limited to 'src/dtc')
210 files changed, 21086 insertions, 0 deletions
diff --git a/src/dtc/.gitignore b/src/dtc/.gitignore new file mode 100644 index 0000000..545b899 --- /dev/null +++ b/src/dtc/.gitignore @@ -0,0 +1,17 @@ +*.o +*.d +*.a +*.patch +*.so +*~ +*.tab.[ch] +lex.yy.c +*.lex.c +/dtc +/fdtdump +/convert-dtsv0 +/version_gen.h +/fdtget +/fdtput +/patches +/.pc diff --git a/src/dtc/Documentation/dtc-paper.bib b/src/dtc/Documentation/dtc-paper.bib new file mode 100644 index 0000000..d01e2ff --- /dev/null +++ b/src/dtc/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/src/dtc/Documentation/dtc-paper.tex b/src/dtc/Documentation/dtc-paper.tex new file mode 100644 index 0000000..4494226 --- /dev/null +++ b/src/dtc/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/src/dtc/Documentation/dts-format.txt b/src/dtc/Documentation/dts-format.txt new file mode 100644 index 0000000..41741df --- /dev/null +++ b/src/dtc/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/src/dtc/Documentation/manual.txt b/src/dtc/Documentation/manual.txt new file mode 100644 index 0000000..65c8540 --- /dev/null +++ b/src/dtc/Documentation/manual.txt @@ -0,0 +1,660 @@ +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 jdl.com. +The gitweb interface is: + + http://git.jdl.com/gitweb/ + +The repository is here: + + git://www.jdl.com/software/dtc.git + http://www.jdl.com/software/dtc.git + +Tarballs of the 1.0.0 and latest releases are here: + + http://www.jdl.com/software/dtc-v1.2.0.tgz + http://www.jdl.com/software/dtc-latest.tgz + +1.1) Submitting Patches + +Patches should be sent to jdl@jdl.com, and CC'ed to +devicetree-discuss@lists.ozlabs.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. + + +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. + + -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: list_of_memreserve devicetree + + memreserve: label 'memreserve' ADDR ADDR ';' + | label 'memreserve' ADDR '-' ADDR ';' + + devicetree: '/' nodedef + + 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 it turn has a string property + called "childprop". + + childnode@addresss { + 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 exmaple, a node is labled "mpic" and then referenced: + + mpic: interrupt-controller@40000 { + ... + }; + + ethernet-phy@3 { + interrupt-parent = <&mpic>; + ... + }; + +And used in properties, lables 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 informations 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 informations 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 <DTB-file-name> diff --git a/src/dtc/GPL b/src/dtc/GPL new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/src/dtc/GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/src/dtc/Makefile b/src/dtc/Makefile new file mode 100644 index 0000000..962f94e --- /dev/null +++ b/src/dtc/Makefile @@ -0,0 +1,263 @@ +# +# 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 = 4 +SUBLEVEL = 0 +EXTRAVERSION = +LOCAL_VERSION = +CONFIG_LOCALVERSION = + +CPPFLAGS = -I libfdt -I . +WARNINGS = -Werror -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \ + -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls +CFLAGS = -g -Os -fPIC -Werror $(WARNINGS) + +BISON = bison +LEX = flex + +INSTALL = /usr/bin/install +DESTDIR = +PREFIX = $(HOME) +BINDIR = $(PREFIX)/bin +LIBDIR = $(PREFIX)/lib +INCLUDEDIR = $(PREFIX)/include + +HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ + sed -e 's/\(cygwin\).*/cygwin/') + +ifeq ($(HOSTOS),darwin) +SHAREDLIB_EXT=dylib +SHAREDLIB_LINK_OPTIONS=-dynamiclib -Wl,-install_name -Wl, +else +SHAREDLIB_EXT=so +SHAREDLIB_LINK_OPTIONS=-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 + +SCRIPTS = dtdiff + +all: $(BIN) libfdt + + +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) +endif + + + +# +# Rules for libfdt +# +LIBFDT_objdir = libfdt +LIBFDT_srcdir = libfdt +LIBFDT_archive = $(LIBFDT_objdir)/libfdt.a +LIBFDT_lib = $(LIBFDT_objdir)/libfdt-$(DTC_VERSION).$(SHAREDLIB_EXT) +LIBFDT_include = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_INCLUDES)) +LIBFDT_version = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_VERSION)) + +include $(LIBFDT_srcdir)/Makefile.libfdt + +.PHONY: libfdt +libfdt: $(LIBFDT_archive) $(LIBFDT_lib) + +$(LIBFDT_archive): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS)) +$(LIBFDT_lib): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS)) + +libfdt_clean: + @$(VECHO) CLEAN "(libfdt)" + rm -f $(addprefix $(LIBFDT_objdir)/,$(STD_CLEANFILES)) + rm -f $(LIBFDT_objdir)/*.so + +ifneq ($(DEPTARGETS),) +-include $(LIBFDT_OBJS:%.o=$(LIBFDT_objdir)/%.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: all $(SCRIPTS) + @$(VECHO) INSTALL + $(INSTALL) -d $(DESTDIR)$(BINDIR) + $(INSTALL) $(BIN) $(SCRIPTS) $(DESTDIR)$(BINDIR) + $(INSTALL) -d $(DESTDIR)$(LIBDIR) + $(INSTALL) $(LIBFDT_lib) $(DESTDIR)$(LIBDIR) + ln -sf $(notdir $(LIBFDT_lib)) $(DESTDIR)$(LIBDIR)/$(LIBFDT_soname) + ln -sf $(LIBFDT_soname) $(DESTDIR)$(LIBDIR)/libfdt.$(SHAREDLIB_EXT) + $(INSTALL) -m 644 $(LIBFDT_archive) $(DESTDIR)$(LIBDIR) + $(INSTALL) -d $(DESTDIR)$(INCLUDEDIR) + $(INSTALL) -m 644 $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR) + +$(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_archive) + +fdtput: $(FDTPUT_OBJS) $(LIBFDT_archive) + + +# +# Testsuite rules +# +TESTS_PREFIX=tests/ + +TESTS_BIN += dtc +TESTS_BIN += convert-dtsv0 +TESTS_BIN += fdtput +TESTS_BIN += fdtget + +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 tests_clean + @$(VECHO) CLEAN + rm -f $(STD_CLEANFILES) + rm -f $(VERSION_FILE) + rm -f $(BIN) + +# +# Generic compile rules +# +%: %.o + @$(VECHO) LD $@ + $(LINK.c) -o $@ $^ + +%.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) $@ $^ + +$(LIBFDT_lib): + @$(VECHO) LD $@ + $(CC) $(LDFLAGS) -fPIC $(SHAREDLIB_LINK_OPTIONS)$(LIBFDT_soname) -o $(LIBFDT_lib) $^ + +%.lex.c: %.l + @$(VECHO) LEX $@ + $(LEX) -o$@ $< + +%.tab.c %.tab.h %.output: %.y + @$(VECHO) BISON $@ + $(BISON) -d $< + +FORCE: diff --git a/src/dtc/Makefile.convert-dtsv0 b/src/dtc/Makefile.convert-dtsv0 new file mode 100644 index 0000000..08ea40a --- /dev/null +++ b/src/dtc/Makefile.convert-dtsv0 @@ -0,0 +1,13 @@ +# +# 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/src/dtc/Makefile.dtc b/src/dtc/Makefile.dtc new file mode 100644 index 0000000..bece49b --- /dev/null +++ b/src/dtc/Makefile.dtc @@ -0,0 +1,18 @@ +# 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 + +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/src/dtc/Makefile.utils b/src/dtc/Makefile.utils new file mode 100644 index 0000000..48ece49 --- /dev/null +++ b/src/dtc/Makefile.utils @@ -0,0 +1,24 @@ +# +# 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) diff --git a/src/dtc/README.license b/src/dtc/README.license new file mode 100644 index 0000000..d56c88f --- /dev/null +++ b/src/dtc/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 incoporate or rely on libfdt for its operation, nor vice +versa. It is important that these two pieces of software have +different license conditions. + +As the copyright banners 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 are +given in the copyright banners of each of the libfdt source files. +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 therby 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/src/dtc/TODO b/src/dtc/TODO new file mode 100644 index 0000000..a3e7182 --- /dev/null +++ b/src/dtc/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/src/dtc/checks.c b/src/dtc/checks.c new file mode 100644 index 0000000..ee96a25 --- /dev/null +++ b/src/dtc/checks.c @@ -0,0 +1,759 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2007. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "dtc.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 (*tree_check_fn)(struct check *c, struct node *dt); +typedef void (*node_check_fn)(struct check *c, struct node *dt, struct node *node); +typedef void (*prop_check_fn)(struct check *c, struct node *dt, + struct node *node, struct property *prop); + +struct check { + const char *name; + tree_check_fn tree_fn; + node_check_fn node_fn; + prop_check_fn prop_fn; + void *data; + bool warn, error; + enum checkstatus status; + int inprogress; + int num_prereqs; + struct check **prereq; +}; + +#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \ + static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \ + static struct check nm = { \ + .name = #nm, \ + .tree_fn = (tfn), \ + .node_fn = (nfn), \ + .prop_fn = (pfn), \ + .data = (d), \ + .warn = (w), \ + .error = (e), \ + .status = UNCHECKED, \ + .num_prereqs = ARRAY_SIZE(nm##_prereqs), \ + .prereq = nm##_prereqs, \ + }; +#define WARNING(nm, tfn, nfn, pfn, d, ...) \ + CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__) +#define ERROR(nm, tfn, nfn, pfn, d, ...) \ + CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__) +#define CHECK(nm, tfn, nfn, pfn, d, ...) \ + CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__) + +#define TREE_WARNING(nm, d, ...) \ + WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) +#define TREE_ERROR(nm, d, ...) \ + ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) +#define TREE_CHECK(nm, d, ...) \ + CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) +#define NODE_WARNING(nm, d, ...) \ + WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) +#define NODE_ERROR(nm, d, ...) \ + ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) +#define NODE_CHECK(nm, d, ...) \ + CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) +#define PROP_WARNING(nm, d, ...) \ + WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) +#define PROP_ERROR(nm, d, ...) \ + ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) +#define PROP_CHECK(nm, d, ...) \ + CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) + +#ifdef __GNUC__ +static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3))); +#endif +static inline void check_msg(struct check *c, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if ((c->warn && (quiet < 1)) + || (c->error && (quiet < 2))) { + fprintf(stderr, "%s (%s): ", + (c->error) ? "ERROR" : "Warning", c->name); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } +} + +#define FAIL(c, ...) \ + do { \ + TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \ + (c)->status = FAILED; \ + check_msg((c), __VA_ARGS__); \ + } while (0) + +static void check_nodes_props(struct check *c, struct node *dt, struct node *node) +{ + struct node *child; + struct property *prop; + + TRACE(c, "%s", node->fullpath); + if (c->node_fn) + c->node_fn(c, dt, node); + + if (c->prop_fn) + for_each_property(node, prop) { + TRACE(c, "%s\t'%s'", node->fullpath, prop->name); + c->prop_fn(c, dt, node, prop); + } + + for_each_child(node, child) + check_nodes_props(c, dt, child); +} + +static int run_check(struct check *c, struct node *dt) +{ + int error = 0; + int i; + + assert(!c->inprogress); + + if (c->status != UNCHECKED) + goto out; + + c->inprogress = 1; + + for (i = 0; i < c->num_prereqs; i++) { + struct check *prq = c->prereq[i]; + error |= run_check(prq, dt); + if (prq->status != PASSED) { + c->status = PREREQ; + check_msg(c, "Failed prerequisite '%s'", + c->prereq[i]->name); + } + } + + if (c->status != UNCHECKED) + goto out; + + if (c->node_fn || c->prop_fn) + check_nodes_props(c, dt, dt); + + if (c->tree_fn) + c->tree_fn(c, dt); + if (c->status == UNCHECKED) + c->status = PASSED; + + TRACE(c, "\tCompleted, status %d", c->status); + +out: + c->inprogress = 0; + if ((c->status != PASSED) && (c->error)) + error = 1; + return error; +} + +/* + * Utility check functions + */ + +/* A check which always fails, for testing purposes only */ +static inline void check_always_fail(struct check *c, struct node *dt) +{ + FAIL(c, "always_fail check"); +} +TREE_CHECK(always_fail, NULL); + +static void check_is_string(struct check *c, struct node *root, + 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(c, "\"%s\" property in %s is not a string", + propname, node->fullpath); +} +#define WARNING_IF_NOT_STRING(nm, propname) \ + WARNING(nm, NULL, check_is_string, NULL, (propname)) +#define ERROR_IF_NOT_STRING(nm, propname) \ + ERROR(nm, NULL, check_is_string, NULL, (propname)) + +static void check_is_cell(struct check *c, struct node *root, + 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(c, "\"%s\" property in %s is not a single cell", + propname, node->fullpath); +} +#define WARNING_IF_NOT_CELL(nm, propname) \ + WARNING(nm, NULL, check_is_cell, NULL, (propname)) +#define ERROR_IF_NOT_CELL(nm, propname) \ + ERROR(nm, NULL, check_is_cell, NULL, (propname)) + +/* + * Structural check functions + */ + +static void check_duplicate_node_names(struct check *c, struct node *dt, + 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, "Duplicate node name %s", + child->fullpath); +} +NODE_ERROR(duplicate_node_names, NULL); + +static void check_duplicate_property_names(struct check *c, struct node *dt, + 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(c, "Duplicate property name %s in %s", + prop->name, node->fullpath); + } + } +} +NODE_ERROR(duplicate_property_names, NULL); + +#define LOWERCASE "abcdefghijklmnopqrstuvwxyz" +#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define DIGITS "0123456789" +#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" + +static void check_node_name_chars(struct check *c, struct node *dt, + struct node *node) +{ + int n = strspn(node->name, c->data); + + if (n < strlen(node->name)) + FAIL(c, "Bad character '%c' in node %s", + node->name[n], node->fullpath); +} +NODE_ERROR(node_name_chars, PROPNODECHARS "@"); + +static void check_node_name_format(struct check *c, struct node *dt, + struct node *node) +{ + if (strchr(get_unitname(node), '@')) + FAIL(c, "Node %s has multiple '@' characters in name", + node->fullpath); +} +NODE_ERROR(node_name_format, NULL, &node_name_chars); + +static void check_property_name_chars(struct check *c, struct node *dt, + struct node *node, struct property *prop) +{ + int n = strspn(prop->name, c->data); + + if (n < strlen(prop->name)) + FAIL(c, "Bad character '%c' in property name \"%s\", node %s", + prop->name[n], prop->name, node->fullpath); +} +PROP_ERROR(property_name_chars, PROPNODECHARS); + +#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 node *dt, + const char *label, struct node *node, + struct property *prop, struct marker *mark) +{ + 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, "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 node *dt, + struct node *node) +{ + struct label *l; + + for_each_label(node->labels, l) + check_duplicate_label(c, dt, l->label, node, NULL, NULL); +} +static void check_duplicate_label_prop(struct check *c, struct node *dt, + struct node *node, struct property *prop) +{ + struct marker *m = prop->val.markers; + struct label *l; + + for_each_label(prop->labels, l) + check_duplicate_label(c, dt, l->label, node, prop, NULL); + + for_each_marker_of_type(m, LABEL) + check_duplicate_label(c, dt, m->ref, node, prop, m); +} +ERROR(duplicate_label, NULL, check_duplicate_label_node, + check_duplicate_label_prop, NULL); + +static void check_explicit_phandles(struct check *c, struct node *root, + struct node *node, struct property *prop) +{ + struct marker *m; + struct node *other; + cell_t phandle; + + if (!streq(prop->name, "phandle") + && !streq(prop->name, "linux,phandle")) + return; + + if (prop->val.len != sizeof(cell_t)) { + FAIL(c, "%s has bad length (%d) %s property", + node->fullpath, prop->val.len, prop->name); + return; + } + + 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, "%s in %s is a reference to another node", + prop->name, node->fullpath); + return; + } + /* 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 + * no further checking for now. */ + return; + } + + phandle = propval_cell(prop); + + if ((phandle == 0) || (phandle == -1)) { + FAIL(c, "%s has bad value (0x%x) in %s property", + node->fullpath, phandle, prop->name); + return; + } + + if (node->phandle && (node->phandle != phandle)) + FAIL(c, "%s has %s property which replaces existing phandle information", + node->fullpath, prop->name); + + other = get_node_by_phandle(root, phandle); + if (other && (other != node)) { + FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)", + node->fullpath, phandle, other->fullpath); + return; + } + + node->phandle = phandle; +} +PROP_ERROR(explicit_phandles, NULL); + +static void check_name_properties(struct check *c, struct node *root, + 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, "\"name\" property in %s is incorrect (\"%s\" instead" + " of base node name)", node->fullpath, 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"); +NODE_ERROR(name_properties, NULL, &name_is_string); + +/* + * Reference fixup functions + */ + +static void fixup_phandle_references(struct check *c, struct node *dt, + struct node *node, struct property *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) { + FAIL(c, "Reference to non-existent node or label \"%s\"\n", + m->ref); + continue; + } + + phandle = get_node_phandle(dt, refnode); + *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); + } +} +ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, + &duplicate_node_names, &explicit_phandles); + +static void fixup_path_references(struct check *c, struct node *dt, + struct node *node, struct property *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, "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); + } +} +ERROR(path_references, NULL, NULL, fixup_path_references, NULL, + &duplicate_node_names); + +/* + * 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"); + +static void fixup_addr_size_cells(struct check *c, struct node *dt, + 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, NULL, fixup_addr_size_cells, NULL, 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 node *dt, + 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, "Root node has a \"reg\" property"); + return; + } + + if (prop->val.len == 0) + FAIL(c, "\"reg\" property in %s is empty", node->fullpath); + + addr_cells = node_addr_cells(node->parent); + size_cells = node_size_cells(node->parent); + entrylen = (addr_cells + size_cells) * sizeof(cell_t); + + if ((prop->val.len % entrylen) != 0) + FAIL(c, "\"reg\" property in %s has invalid length (%d bytes) " + "(#address-cells == %d, #size-cells == %d)", + node->fullpath, prop->val.len, addr_cells, size_cells); +} +NODE_WARNING(reg_format, NULL, &addr_size_cells); + +static void check_ranges_format(struct check *c, struct node *dt, + 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(c, "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(c, "%s has empty \"ranges\" property but its " + "#address-cells (%d) differs from %s (%d)", + node->fullpath, c_addr_cells, node->parent->fullpath, + p_addr_cells); + if (p_size_cells != c_size_cells) + FAIL(c, "%s has empty \"ranges\" property but its " + "#size-cells (%d) differs from %s (%d)", + node->fullpath, c_size_cells, node->parent->fullpath, + p_size_cells); + } else if ((prop->val.len % entrylen) != 0) { + FAIL(c, "\"ranges\" property in %s has invalid length (%d bytes) " + "(parent #address-cells == %d, child #address-cells == %d, " + "#size-cells == %d)", node->fullpath, prop->val.len, + p_addr_cells, c_addr_cells, c_size_cells); + } +} +NODE_WARNING(ranges_format, NULL, &addr_size_cells); + +/* + * Style checks + */ +static void check_avoid_default_addr_size(struct check *c, struct node *dt, + 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, "Relying on default #address-cells value for %s", + node->fullpath); + + if ((node->parent->size_cells == -1)) + FAIL(c, "Relying on default #size-cells value for %s", + node->fullpath); +} +NODE_WARNING(avoid_default_addr_size, NULL, &addr_size_cells); + +static void check_obsolete_chosen_interrupt_controller(struct check *c, + struct node *dt) +{ + struct node *chosen; + struct property *prop; + + chosen = get_node_by_path(dt, "/chosen"); + if (!chosen) + return; + + prop = get_property(chosen, "interrupt-controller"); + if (prop) + FAIL(c, "/chosen has obsolete \"interrupt-controller\" " + "property"); +} +TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); + +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, + + &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, + &device_type_is_string, &model_is_string, &status_is_string, + + &addr_size_cells, ®_format, &ranges_format, + + &avoid_default_addr_size, + &obsolete_chosen_interrupt_controller, + + &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 *optarg) +{ + int i; + const char *name = optarg; + bool enable = true; + + if ((strncmp(optarg, "no-", 3) == 0) + || (strncmp(optarg, "no_", 3) == 0)) { + name = optarg + 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(int force, struct boot_info *bi) +{ + struct node *dt = bi->dt; + 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, dt); + } + + 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/src/dtc/convert-dtsv0-lexer.l b/src/dtc/convert-dtsv0-lexer.l new file mode 100644 index 0000000..548e719 --- /dev/null +++ b/src/dtc/convert-dtsv0-lexer.l @@ -0,0 +1,248 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005, 2008. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +%option noyywrap nounput noinput never-interactive + +%x INCLUDE +%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 */ + +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()) + ; +} + +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/src/dtc/data.c b/src/dtc/data.c new file mode 100644 index 0000000..4a40c5b --- /dev/null +++ b/src/dtc/data.c @@ -0,0 +1,269 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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_grow_for(empty_data, strlen(s)+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; + + 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; + uint16_t value_16; + uint32_t value_32; + uint64_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, const struct fdt_reserve_entry *re) +{ + struct fdt_reserve_entry bere; + + bere.address = cpu_to_fdt64(re->address); + bere.size = cpu_to_fdt64(re->size); + + return data_append_data(d, &bere, sizeof(bere)); +} + +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); +} + +int data_is_one_string(struct data d) +{ + int i; + int len = d.len; + + if (len == 0) + return 0; + + for (i = 0; i < len-1; i++) + if (d.val[i] == '\0') + return 0; + + if (d.val[len-1] != '\0') + return 0; + + return 1; +} diff --git a/src/dtc/dtc-lexer.l b/src/dtc/dtc-lexer.l new file mode 100644 index 0000000..3b41bfc --- /dev/null +++ b/src/dtc/dtc-lexer.l @@ -0,0 +1,250 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +%option noyywrap nounput noinput never-interactive + +%x INCLUDE +%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; + +/* 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 int pop_input_file(void); +%} + +%% +<*>"/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, *tmp, *fn; + /* skip text before line # */ + line = yytext; + while (!isdigit(*line)) + line++; + /* skip digits in line # */ + tmp = line; + while (!isspace(*tmp)) + tmp++; + /* "NULL"-terminate line # */ + *tmp = '\0'; + /* start of filename */ + fn = strchr(tmp + 1, '"') + 1; + /* strip trailing " from filename */ + tmp = strchr(fn, '"'); + *tmp = 0; + /* -1 since #line is the number of the next line */ + srcpos_set_line(xstrdup(fn), atoi(line) - 1); + } + +<*><<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; + } + +<*>"/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; + } + +<*>{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)? { + yylval.literal = xstrdup(yytext); + DPRINT("Literal: '%s'\n", yylval.literal); + return DT_LITERAL; + } + +<*>{CHAR_LITERAL} { + yytext[yyleng-1] = '\0'; + yylval.literal = xstrdup(yytext+1); + DPRINT("Character literal: %s\n", yylval.literal); + return DT_CHAR_LITERAL; + } + +<*>\&{LABEL} { /* label reference */ + DPRINT("Ref: %s\n", yytext+1); + yylval.labelref = xstrdup(yytext+1); + return DT_REF; + } + +<*>"&{/"{PATHCHAR}+\} { /* new-style path reference */ + yytext[yyleng-1] = '\0'; + DPRINT("Ref: %s\n", yytext+2); + yylval.labelref = xstrdup(yytext+2); + return DT_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 int pop_input_file(void) +{ + if (srcfile_pop() == 0) + return 0; + + yypop_buffer_state(); + yyin = current_srcfile->f; + + return 1; +} diff --git a/src/dtc/dtc-parser.y b/src/dtc/dtc-parser.y new file mode 100644 index 0000000..f412460 --- /dev/null +++ b/src/dtc/dtc-parser.y @@ -0,0 +1,532 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +%{ +#include <stdio.h> + +#include "dtc.h" +#include "srcpos.h" + +YYLTYPE yylloc; + +extern int yylex(void); +extern void print_error(char const *fmt, ...); +extern void yyerror(char const *s); + +extern struct boot_info *the_boot_info; +extern int treesource_error; + +static unsigned long long eval_literal(const char *s, int base, int bits); +static unsigned char eval_char_literal(const char *s); +%} + +%union { + char *propnodename; + char *literal; + char *labelref; + unsigned int cbase; + 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; +} + +%token DT_V1 +%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 <propnodename> DT_PROPNODENAME +%token <literal> DT_LITERAL +%token <literal> DT_CHAR_LITERAL +%token <cbase> DT_BASE +%token <byte> DT_BYTE +%token <data> DT_STRING +%token <labelref> DT_LABEL +%token <labelref> DT_REF +%token DT_INCBIN + +%type <data> propdata +%type <data> propdataprefix +%type <re> memreserve +%type <re> memreserves +%type <array> arrayprefix +%type <data> bytestring +%type <prop> propdef +%type <proplist> proplist + +%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: + DT_V1 ';' memreserves devicetree + { + the_boot_info = build_boot_info($3, $4, + guess_boot_cpuid($4)); + } + ; + +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; + } + ; + +devicetree: + '/' nodedef + { + $$ = name_node($2, ""); + } + | devicetree '/' nodedef + { + $$ = merge_nodes($1, $3); + } + | devicetree DT_REF nodedef + { + struct node *target = get_node_by_ref($1, $2); + + if (target) + merge_nodes(target, $3); + else + print_error("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) + print_error("label or path, '%s', not found", $3); + else + delete_node(target); + + $$ = $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 + { + $$ = 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) + print_error("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 '<' + { + $$.data = empty_data; + $$.bits = eval_literal($2, 0, 7); + + if (($$.bits != 8) && + ($$.bits != 16) && + ($$.bits != 32) && + ($$.bits != 64)) + { + print_error("Only 8, 16, 32 and 64-bit elements" + " are currently supported"); + $$.bits = 32; + } + } + | '<' + { + $$.data = empty_data; + $$.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)) + print_error( + "integer value out of range " + "%016lx (%d bits)", $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 + print_error("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 + { + $$ = eval_literal($1, 0, 64); + } + | DT_CHAR_LITERAL + { + $$ = eval_char_literal($1); + } + | '(' 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 { $$ = $1 / $3; } + | integer_mul '%' integer_unary { $$ = $1 % $3; } + | integer_unary + ; + +integer_unary: + integer_prim + | '-' integer_unary { $$ = -$2; } + | '~' integer_unary { $$ = ~$2; } + | '!' integer_unary { $$ = !$2; } + ; + +bytestring: + /* empty */ + { + $$ = empty_data; + } + | 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 + { + print_error("syntax error: 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_LABEL subnode + { + add_label(&$2->labels, $1); + $$ = $2; + } + ; + +%% + +void print_error(char const *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + srcpos_verror(&yylloc, fmt, va); + va_end(va); + + treesource_error = 1; +} + +void yyerror(char const *s) { + print_error("%s", s); +} + +static unsigned long long eval_literal(const char *s, int base, int bits) +{ + unsigned long long val; + char *e; + + errno = 0; + val = strtoull(s, &e, base); + if (*e) { + size_t uls = strspn(e, "UL"); + if (e[uls]) + print_error("bad characters in literal"); + } + if ((errno == ERANGE) + || ((bits < 64) && (val >= (1ULL << bits)))) + print_error("literal out of range"); + else if (errno != 0) + print_error("bad literal"); + return val; +} + +static unsigned char eval_char_literal(const char *s) +{ + int i = 1; + char c = s[0]; + + if (c == '\0') + { + print_error("empty character literal"); + return 0; + } + + /* + * If the first character in the character literal is a \ then process + * the remaining characters as an escape encoding. If the first + * character is neither an escape or a terminator it should be the only + * character in the literal and will be returned. + */ + if (c == '\\') + c = get_escape_char(s, &i); + + if (s[i] != '\0') + print_error("malformed character literal"); + + return c; +} diff --git a/src/dtc/dtc.c b/src/dtc/dtc.c new file mode 100644 index 0000000..e3c9653 --- /dev/null +++ b/src/dtc/dtc.c @@ -0,0 +1,259 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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 phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ + +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:fb:i:H:sW:E:hv"; +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'}, + {"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'}, + {"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" + "\t\tasm - assembler source", + "\n\tBlob version to produce, defaults to %d (for dtb and asm output)", //, DEFAULT_FDT_VERSION); + "\n\tOutput dependency file", + "\n\ttMake 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\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\tPrint this help and exit", + "\n\tPrint version and exit", + NULL, +}; + +int main(int argc, char *argv[]) +{ + struct boot_info *bi; + const char *inform = "dts"; + const char *outform = "dts"; + const char *outname = "-"; + const char *depname = NULL; + int force = 0, sort = 0; + 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; + + 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 'f': + force = 1; + 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 = 1; + break; + + case 'W': + parse_checks_option(true, false, optarg); + break; + + case 'E': + parse_checks_option(false, true, optarg); + 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 (streq(inform, "dts")) + bi = dt_from_source(arg); + else if (streq(inform, "fs")) + bi = dt_from_fs(arg); + else if(streq(inform, "dtb")) + bi = dt_from_blob(arg); + else + die("Unknown input format \"%s\"\n", inform); + + if (depfile) { + fputc('\n', depfile); + fclose(depfile); + } + + if (cmdline_boot_cpuid != -1) + bi->boot_cpuid_phys = cmdline_boot_cpuid; + + fill_fullpaths(bi->dt, ""); + process_checks(force, bi); + + if (sort) + sort_tree(bi); + + if (streq(outname, "-")) { + outf = stdout; + } else { + outf = fopen(outname, "w"); + if (! outf) + die("Couldn't open output file %s: %s\n", + outname, strerror(errno)); + } + + if (streq(outform, "dts")) { + dt_to_source(outf, bi); + } else if (streq(outform, "dtb")) { + dt_to_blob(outf, bi, outversion); + } else if (streq(outform, "asm")) { + dt_to_asm(outf, bi, outversion); + } else if (streq(outform, "null")) { + /* do nothing */ + } else { + die("Unknown output format \"%s\"\n", outform); + } + + exit(0); +} diff --git a/src/dtc/dtc.h b/src/dtc/dtc.h new file mode 100644 index 0000000..264a20c --- /dev/null +++ b/src/dtc/dtc.h @@ -0,0 +1,269 @@ +#ifndef _DTC_H +#define _DTC_H + +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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 <libfdt_env.h> +#include <fdt.h> + +#include "util.h" + +#ifdef DEBUG +#define debug(fmt,args...) printf(fmt, ##args) +#else +#define debug(fmt,args...) +#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 phandle_format; /* Use linux,phandle or phandle properties */ + +#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 strneq(a, b, n) (strncmp((a), (b), (n)) == 0) + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +/* Data blobs */ +enum markertype { + REF_PHANDLE, + REF_PATH, + LABEL, +}; + +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){ /* 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)) + +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, const struct fdt_reserve_entry *re); +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); + +int data_is_one_string(struct data d); + +/* DT constraints */ + +#define MAX_PROPNAME_LEN 31 +#define MAX_NODENAME_LEN 31 + +/* Live trees */ +struct label { + int deleted; + char *label; + struct label *next; +}; + +struct property { + int deleted; + char *name; + struct data val; + + struct property *next; + + struct label *labels; +}; + +struct node { + int 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; +}; + +#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 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 node *build_node_delete(void); +struct node *name_node(struct node *node, char *name); +struct node *chain_node(struct node *first, struct node *list); +struct node *merge_nodes(struct node *old_node, struct node *new_node); + +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); + +const char *get_unitname(struct node *node); +struct property *get_property(struct node *node, const char *propname); +cell_t propval_cell(struct property *prop); +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 { + struct fdt_reserve_entry re; + + 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 boot_info { + struct reserve_info *reservelist; + struct node *dt; /* the device tree */ + uint32_t boot_cpuid_phys; +}; + +struct boot_info *build_boot_info(struct reserve_info *reservelist, + struct node *tree, uint32_t boot_cpuid_phys); +void sort_tree(struct boot_info *bi); + +/* Checks */ + +void parse_checks_option(bool warn, bool error, const char *optarg); +void process_checks(int force, struct boot_info *bi); + +/* Flattened trees */ + +void dt_to_blob(FILE *f, struct boot_info *bi, int version); +void dt_to_asm(FILE *f, struct boot_info *bi, int version); + +struct boot_info *dt_from_blob(const char *fname); + +/* Tree source */ + +void dt_to_source(FILE *f, struct boot_info *bi); +struct boot_info *dt_from_source(const char *f); + +/* FS trees */ + +struct boot_info *dt_from_fs(const char *dirname); + +#endif /* _DTC_H */ diff --git a/src/dtc/dtdiff b/src/dtc/dtdiff new file mode 100644 index 0000000..5fa772b --- /dev/null +++ b/src/dtc/dtdiff @@ -0,0 +1,38 @@ +#! /bin/bash + +# 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/src/dtc/fdtdump.c b/src/dtc/fdtdump.c new file mode 100644 index 0000000..723770d --- /dev/null +++ b/src/dtc/fdtdump.c @@ -0,0 +1,227 @@ +/* + * 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 <libfdt.h> +#include <libfdt_env.h> +#include <fdt.h> + +#include "util.h" + +#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 uint32_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%x\n", fdt32_to_cpu(bph->magic)); + printf("// totalsize:\t\t0x%x (%d)\n", totalsize, totalsize); + printf("// off_dt_struct:\t0x%x\n", off_dt); + printf("// off_dt_strings:\t0x%x\n", off_str); + printf("// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap); + printf("// version:\t\t%d\n", version); + printf("// last_comp_version:\t%d\n", + fdt32_to_cpu(bph->last_comp_version)); + if (version >= 2) + printf("// boot_cpuid_phys:\t0x%x\n", + fdt32_to_cpu(bph->boot_cpuid_phys)); + + if (version >= 3) + printf("// size_dt_strings:\t0x%x\n", + fdt32_to_cpu(bph->size_dt_strings)); + if (version >= 17) + printf("// size_dt_struct:\t0x%x\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/ %llx %llx;\n", + (unsigned long long)addr, (unsigned long long)size); + } + + p = p_struct; + while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) { + + dumpf("%04zx: tag: 0x%08x (%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%08x\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("%04zx: string: %s\n", (uintptr_t)s - blob_off, s); + dumpf("%04zx: 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 +}; + +int main(int argc, char *argv[]) +{ + int opt; + const char *file; + char *buf; + bool debug = false; + bool scan = false; + off_t len; + + 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_len(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[4]; + char *p = buf; + char *endp = buf + len; + + fdt_set_magic(smagic, FDT_MAGIC); + + /* poor man's memmem */ + while (true) { + p = memchr(p, smagic[0], endp - p - 4); + if (!p) + break; + if (fdt_magic(p) == FDT_MAGIC) { + /* try and validate the main struct */ + off_t this_len = endp - p; + fdt32_t max_version = 17; + if (fdt_version(p) <= max_version && + fdt_last_comp_version(p) < max_version && + fdt_totalsize(p) < this_len && + fdt_off_dt_struct(p) < this_len && + fdt_off_dt_strings(p) < this_len) + break; + if (debug) + printf("%s: skipping fdt magic at offset %#zx\n", + file, p - buf); + } + ++p; + } + if (!p) + die("%s: could not locate fdt magic\n", file); + printf("%s: found fdt at offset %#zx\n", file, p - buf); + buf = p; + } + + dump_blob(buf, debug); + + return 0; +} diff --git a/src/dtc/fdtget.c b/src/dtc/fdtget.c new file mode 100644 index 0000000..4377419 --- /dev/null +++ b/src/dtc/fdtget.c @@ -0,0 +1,360 @@ +/* + * 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> + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#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)); +} + +/** + * 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 i, size; + const uint8_t *p = (const uint8_t *)data; + const char *s; + int value; + int is_string; + char fmt[3]; + + /* 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; + } + 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_to_cpu(*(const uint32_t *)p) : + size == 2 ? (*p << 8) | p[1] : *p; + printf(fmt, value); + } + return 0; +} + +/** + * 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 struct fdt_property *data; + 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; + data = fdt_get_property_by_offset(blob, prop, NULL); + name = fdt_string(blob, fdt32_to_cpu(data->nameoff)); + 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 + * @param 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); + 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); + return -1; + } + } + prop = args_per_step == 1 ? NULL : arg[i + 1]; + + if (show_data_for_item(blob, disp, node, prop)) + return -1; + } + 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/src/dtc/fdtput.c b/src/dtc/fdtput.c new file mode 100644 index 0000000..5226a4e --- /dev/null +++ b/src/dtc/fdtput.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#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 */ +}; + +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 = realloc(value, value_size); + if (!value) { + fprintf(stderr, "Out of mmory: cannot alloc " + "%d bytes\n", value_size); + return -1; + } + } + + ptr = value + upto; + if (disp->type == 's') { + memcpy(ptr, *arg, len); + if (disp->verbose) + fprintf(stderr, "\tstring: '%s'\n", ptr); + } else { + int *iptr = (int *)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; +} + +static int do_fdtput(struct display_info *disp, const char *filename, + char **arg, int arg_count) +{ + char *value; + char *blob; + int len, ret = 0; + + blob = utilfdt_read(filename); + 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; + } + if (ret >= 0) { + fdt_pack(blob); + ret = utilfdt_write(filename, blob); + } + + free(blob); + 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" + "\n" + "The command line arguments are joined together into a single value.\n" + USAGE_TYPE_MSG; +static const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS; +static struct option const usage_long_opts[] = { + {"create", no_argument, NULL, 'c'}, + {"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", + "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: + * - delete property + * - delete node (optionally recursively) + * - 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 '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 (do_fdtput(&disp, filename, argv, argc)) + return 1; + return 0; +} diff --git a/src/dtc/flattree.c b/src/dtc/flattree.c new file mode 100644 index 0000000..665dad7 --- /dev/null +++ b/src/dtc/flattree.c @@ -0,0 +1,933 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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 *, 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, 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, char *str, int len) +{ + FILE *f = e; + char c = 0; + + if (len != 0) { + /* XXX: ewww */ + c = str[len]; + str[len] = '\0'; + } + + fprintf(f, "\t.string\t\"%s\"\n", str); + + if (len != 0) { + str[len] = c; + } +} + +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(*((uint32_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; + int seen_name_prop = 0; + + 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 = 1; + + 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; + static struct fdt_reserve_entry null_re = {0,0}; + int j; + + for (re = reservelist; re; re = re->next) { + d = data_append_re(d, &re->re); + } + /* + * Add additional reserved slots if the user asked for them. + */ + for (j = 0; j < reservenum; j++) { + d = data_append_re(d, &null_re); + } + + 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 boot_info *bi, 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(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi); + bin_emit_cell(&dtbuf, FDT_END); + + reservebuf = flatten_reserve_list(bi->reservelist, vi); + + /* Make header */ + make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, + bi->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) && (quiet < 1)) + fprintf(stderr, + "Warning: blob size %d >= minimum size %d\n", + fdt32_to_cpu(fdt.totalsize), minsize); + } + + if (padsize > 0) + padlen = padsize; + + 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 boot_info *bi, 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, bi->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 halfs of u64s to avoid .quad + * as it appears .quad isn't available in some assemblers. + */ + for (re = bi->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->re.address >> 32)); + ASM_EMIT_BELONG(f, "0x%08x", + (unsigned int)(re->re.address & 0xffffffff)); + ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32)); + ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->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(bi->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); + } + 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) +{ + uint32_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); +} + + +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) { + flat_read_chunk(inb, &re, sizeof(re)); + re.address = fdt64_to_cpu(re.address); + re.size = fdt64_to_cpu(re.size); + if (re.size == 0) + break; + + new = build_reserve_entry(re.address, re.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 (!strneq(ppath, cpath, plen)) + 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); + + 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); + + return node; +} + + +struct boot_info *dt_from_blob(const char *fname) +{ + FILE *f; + 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, sizeof(magic), 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); + if (magic != FDT_MAGIC) + die("Blob has incorrect magic number\n"); + + rc = fread(&totalsize, sizeof(totalsize), 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); + 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 > 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 > 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_boot_info(reservelist, tree, boot_cpuid_phys); +} diff --git a/src/dtc/fstree.c b/src/dtc/fstree.c new file mode 100644 index 0000000..f377453 --- /dev/null +++ b/src/dtc/fstree.c @@ -0,0 +1,91 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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); + + while ((de = readdir(d)) != NULL) { + char *tmpnam; + + if (streq(de->d_name, ".") + || streq(de->d_name, "..")) + continue; + + tmpnam = join_path(dirname, de->d_name); + + if (lstat(tmpnam, &st) < 0) + die("stat(%s): %s\n", tmpnam, strerror(errno)); + + if (S_ISREG(st.st_mode)) { + struct property *prop; + FILE *pfile; + + pfile = fopen(tmpnam, "r"); + if (! pfile) { + fprintf(stderr, + "WARNING: Cannot open %s: %s\n", + tmpnam, strerror(errno)); + } else { + prop = build_property(xstrdup(de->d_name), + data_copy_file(pfile, + st.st_size)); + add_property(tree, prop); + fclose(pfile); + } + } else if (S_ISDIR(st.st_mode)) { + struct node *newchild; + + newchild = read_fstree(tmpnam); + newchild = name_node(newchild, xstrdup(de->d_name)); + add_child(tree, newchild); + } + + free(tmpnam); + } + + closedir(d); + return tree; +} + +struct boot_info *dt_from_fs(const char *dirname) +{ + struct node *tree; + + tree = read_fstree(dirname); + tree = name_node(tree, ""); + + return build_boot_info(NULL, tree, guess_boot_cpuid(tree)); +} + diff --git a/src/dtc/libfdt/Makefile.libfdt b/src/dtc/libfdt/Makefile.libfdt new file mode 100644 index 0000000..91126c0 --- /dev/null +++ b/src/dtc/libfdt/Makefile.libfdt @@ -0,0 +1,10 @@ +# 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 +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) diff --git a/src/dtc/libfdt/TODO b/src/dtc/libfdt/TODO new file mode 100644 index 0000000..288437e --- /dev/null +++ b/src/dtc/libfdt/TODO @@ -0,0 +1,3 @@ +- Tree traversal functions +- Graft function +- Complete libfdt.h documenting comments diff --git a/src/dtc/libfdt/fdt.c b/src/dtc/libfdt/fdt.c new file mode 100644 index 0000000..2ce6a44 --- /dev/null +++ b/src/dtc/libfdt/fdt.c @@ -0,0 +1,250 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_check_header(const void *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; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + const char *p; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + p = _fdt_offset_ptr(fdt, offset); + + if (p + len < p) + return NULL; + return p; +} + +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); + 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_CHECK_HEADER(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/src/dtc/libfdt/fdt.h b/src/dtc/libfdt/fdt.h new file mode 100644 index 0000000..526aedb --- /dev/null +++ b/src/dtc/libfdt/fdt.h @@ -0,0 +1,111 @@ +#ifndef _FDT_H +#define _FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ + +#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/src/dtc/libfdt/fdt_empty_tree.c b/src/dtc/libfdt/fdt_empty_tree.c new file mode 100644 index 0000000..f72d13b --- /dev/null +++ b/src/dtc/libfdt/fdt_empty_tree.c @@ -0,0 +1,84 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#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/src/dtc/libfdt/fdt_ro.c b/src/dtc/libfdt/fdt_ro.c new file mode 100644 index 0000000..50007f6 --- /dev/null +++ b/src/dtc/libfdt/fdt_ro.c @@ -0,0 +1,573 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#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) +{ + const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); + + if (! p) + /* 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_string(const void *fdt, int stroffset) +{ + return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; +} + +static int _fdt_string_eq(const void *fdt, int stroffset, + const char *s, int len) +{ + const char *p = fdt_string(fdt, stroffset); + + return (strlen(p) == len) && (memcmp(p, s, len) == 0); +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + FDT_CHECK_HEADER(fdt); + *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); + *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) + i++; + return i; +} + +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_CHECK_HEADER(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(const void *fdt, const char *path) +{ + const char *end = path + strlen(path); + const char *p = path; + int offset = 0; + + FDT_CHECK_HEADER(fdt); + + /* see if we have an alias */ + if (*path != '/') { + const char *q = strchr(path, '/'); + + 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) { + const char *q; + + while (*p == '/') + p++; + if (! *p) + return offset; + q = strchr(p, '/'); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); + int err; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) + goto fail; + + if (len) + *len = strlen(nh->name); + + return nh->name; + + 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); +} + +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_to_cpu(prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + 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_to_cpu(prop->nameoff), + name, namelen)) + return prop; + } + + if (lenp) + *lenp = offset; + return 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) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); + if (! prop) + return NULL; + + 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) + *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + 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_to_cpu(*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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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_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; + if (fdt_stringlist_contains(prop, len, compatible)) + return 0; + else + return 1; +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/src/dtc/libfdt/fdt_rw.c b/src/dtc/libfdt/fdt_rw.c new file mode 100644 index 0000000..fdba618 --- /dev/null +++ b/src/dtc/libfdt/fdt_rw.c @@ -0,0 +1,492 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#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_check_header(void *fdt) +{ + FDT_CHECK_HEADER(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_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_rw_check_header(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 ((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; +} + +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) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + 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; + + 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_CHECK_HEADER(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); + int err; + + FDT_RW_CHECK_HEADER(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + err = _fdt_splice_mem_rsv(fdt, re, 1, 0); + if (err) + return err; + return 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; + + if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = _fdt_find_add_string(fdt, name); + 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) + 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_CHECK_HEADER(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(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + FDT_RW_CHECK_HEADER(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; + + 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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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_CHECK_HEADER(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/src/dtc/libfdt/fdt_strerror.c b/src/dtc/libfdt/fdt_strerror.c new file mode 100644 index 0000000..e6c3cee --- /dev/null +++ b/src/dtc/libfdt/fdt_strerror.c @@ -0,0 +1,96 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#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_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), +}; +#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/src/dtc/libfdt/fdt_sw.c b/src/dtc/libfdt/fdt_sw.c new file mode 100644 index 0000000..f422754 --- /dev/null +++ b/src/dtc/libfdt/fdt_sw.c @@ -0,0 +1,256 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int _fdt_sw_check_header(void *fdt) +{ + if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + /* FIXME: should check more details about the header state */ + return 0; +} + +#define FDT_SW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_sw_check_header(fdt)) != 0) \ + return err; \ + } + +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(void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof(struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset(buf, 0, bufsize); + + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry))); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, 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_CHECK_HEADER(fdt); + + if (fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTATE; + + 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) +{ + return fdt_add_reservemap_entry(fdt, 0, 0); +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen = strlen(name) + 1; + + FDT_SW_CHECK_HEADER(fdt); + + 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_CHECK_HEADER(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_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + const char *p; + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + p = _fdt_find_string(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + 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; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int nameoff; + + FDT_SW_CHECK_HEADER(fdt); + + nameoff = _fdt_find_add_string(fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + memcpy(prop->data, 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_CHECK_HEADER(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)); + fdt_set_magic(fdt, FDT_MAGIC); + return 0; +} diff --git a/src/dtc/libfdt/fdt_wip.c b/src/dtc/libfdt/fdt_wip.c new file mode 100644 index 0000000..c5bbb68 --- /dev/null +++ b/src/dtc/libfdt/fdt_wip.c @@ -0,0 +1,118 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen); + if (! propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + memcpy(propval, val, len); + return 0; +} + +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/src/dtc/libfdt/libfdt.h b/src/dtc/libfdt/libfdt.h new file mode 100644 index 0000000..02baa84 --- /dev/null +++ b/src/dtc/libfdt/libfdt.h @@ -0,0 +1,1514 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ + +#include <libfdt_env.h> +#include <fdt.h> + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#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: Attemped 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 + * value. phandle values of 0 and -1 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: Structure block of the given device tree + * ends without an FDT_END tag. */ +#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. */ + +#define FDT_ERR_MAX 13 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +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); + +/**********************************************************************/ +/* 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); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((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_check_header - sanity check a device tree or possible device tree + * @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 with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, 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 */ +/**********************************************************************/ + +/** + * 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 + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * 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. + */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +/** + * 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 - 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_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. + * + * 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_namelen(), but only examine the first + * namelen characters of name for matching the property name. + */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); + +/** + * 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 + * @ffset: 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 + */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); + +/** + * 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. + */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); + +/** + * 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. + */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); + +/** + * fdt_get_alias - retreive 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); + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * 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 + */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * 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 */ +/**********************************************************************/ + +int fdt_create(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)); +} +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#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_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_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_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. + */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); + +/** + * 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); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* _LIBFDT_H */ diff --git a/src/dtc/libfdt/libfdt_env.h b/src/dtc/libfdt/libfdt_env.h new file mode 100644 index 0000000..9dea97d --- /dev/null +++ b/src/dtc/libfdt/libfdt_env.h @@ -0,0 +1,111 @@ +#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. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef uint16_t __bitwise fdt16_t; +typedef uint32_t __bitwise fdt32_t; +typedef uint64_t __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 (__force uint16_t)CPU_TO_FDT16(x); +} +static inline fdt16_t cpu_to_fdt16(uint16_t x) +{ + return (__force fdt16_t)CPU_TO_FDT16(x); +} + +static inline uint32_t fdt32_to_cpu(fdt32_t x) +{ + return (__force uint32_t)CPU_TO_FDT32(x); +} +static inline fdt32_t cpu_to_fdt32(uint32_t x) +{ + return (__force fdt32_t)CPU_TO_FDT32(x); +} + +static inline uint64_t fdt64_to_cpu(fdt64_t x) +{ + return (__force uint64_t)CPU_TO_FDT64(x); +} +static inline fdt64_t cpu_to_fdt64(uint64_t x) +{ + return (__force fdt64_t)CPU_TO_FDT64(x); +} +#undef CPU_TO_FDT64 +#undef CPU_TO_FDT32 +#undef CPU_TO_FDT16 +#undef EXTRACT_BYTE + +#endif /* _LIBFDT_ENV_H */ diff --git a/src/dtc/libfdt/libfdt_internal.h b/src/dtc/libfdt/libfdt_internal.h new file mode 100644 index 0000000..381133b --- /dev/null +++ b/src/dtc/libfdt/libfdt_internal.h @@ -0,0 +1,95 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 OWNER 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. + */ +#include <fdt.h> + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +#define FDT_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = fdt_check_header(fdt)) != 0) \ + return err; \ + } + +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/src/dtc/libfdt/version.lds b/src/dtc/libfdt/version.lds new file mode 100644 index 0000000..80b322b --- /dev/null +++ b/src/dtc/libfdt/version.lds @@ -0,0 +1,60 @@ +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; + 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; + + local: + *; +}; diff --git a/src/dtc/livetree.c b/src/dtc/livetree.c new file mode 100644 index 0000000..b61465f --- /dev/null +++ b/src/dtc/livetree.c @@ -0,0 +1,709 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "dtc.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 property *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + new->name = name; + new->val = val; + + 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 node *new = xmalloc(sizeof(*new)); + struct node *child; + + memset(new, 0, sizeof(*new)); + + new->proplist = reverse_properties(proplist); + new->children = children; + + for_each_child(new, child) { + child->parent = new; + } + + return new; +} + +struct node *build_node_delete(void) +{ + struct node *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + new->deleted = 1; + + return new; +} + +struct node *name_node(struct node *node, char *name) +{ + assert(node->name == NULL); + + node->name = name; + + 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(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 occured, add child to the old node. */ + if (new_child) + add_child(old_node, new_child); + } + + /* The new node contents are now merged into the old node. Free + * the new node. */ + free(new_node); + + return old_node; +} + +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 (!strcmp(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 (!strcmp(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); +} + +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->re.address = address; + new->re.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 boot_info *build_boot_info(struct reserve_info *reservelist, + struct node *tree, uint32_t boot_cpuid_phys) +{ + struct boot_info *bi; + + bi = xmalloc(sizeof(*bi)); + bi->reservelist = reservelist; + bi->dt = tree; + bi->boot_cpuid_phys = boot_cpuid_phys; + + return bi; +} + +/* + * 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(*((cell_t *)prop->val.val)); +} + +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 && strneq(path, child->name, p-path)) + 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; + + assert((phandle != 0) && (phandle != -1)); + + 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 (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 */ + + if ((node->phandle != 0) && (node->phandle != -1)) + return node->phandle; + + while (get_node_by_phandle(root, phandle)) + phandle++; + + node->phandle = phandle; + + if (!get_property(node, "linux,phandle") + && (phandle_format & PHANDLE_LEGACY)) + add_property(node, + build_property("linux,phandle", + data_append_cell(empty_data, phandle))); + + if (!get_property(node, "phandle") + && (phandle_format & PHANDLE_EPAPR)) + add_property(node, + build_property("phandle", + data_append_cell(empty_data, phandle))); + + /* 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->re.address < b->re.address) + return -1; + else if (a->re.address > b->re.address) + return 1; + else if (a->re.size < b->re.size) + return -1; + else if (a->re.size > b->re.size) + return 1; + else + return 0; +} + +static void sort_reserve_entries(struct boot_info *bi) +{ + struct reserve_info *ri, **tbl; + int n = 0, i = 0; + + for (ri = bi->reservelist; + ri; + ri = ri->next) + n++; + + if (n == 0) + return; + + tbl = xmalloc(n * sizeof(*tbl)); + + for (ri = bi->reservelist; + ri; + ri = ri->next) + tbl[i++] = ri; + + qsort(tbl, n, sizeof(*tbl), cmp_reserve_info); + + bi->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 boot_info *bi) +{ + sort_reserve_entries(bi); + sort_node(bi->dt); +} diff --git a/src/dtc/scripts/setlocalversion b/src/dtc/scripts/setlocalversion new file mode 100755 index 0000000..82e4993 --- /dev/null +++ b/src/dtc/scripts/setlocalversion @@ -0,0 +1,22 @@ +#!/bin/sh +# 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/src/dtc/srcpos.c b/src/dtc/srcpos.c new file mode 100644 index 0000000..c20bc53 --- /dev/null +++ b/src/dtc/srcpos.c @@ -0,0 +1,336 @@ +/* + * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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; + + +static char *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 */ + +/* Detect infinite include recursion. */ +#define MAX_SRCFILE_DEPTH (100) +static int srcfile_depth; /* = 0 */ + + +/** + * 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, "r"); + 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 = dirname(srcfile->name); + srcfile->prev = current_srcfile; + + srcfile->lineno = 1; + srcfile->colno = 1; + + current_srcfile = srcfile; +} + +int 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 ? 1 : 0; +} + +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; +} + +/* + * The empty source position. + */ + +struct srcpos srcpos_empty = { + .first_line = 0, + .first_column = 0, + .last_line = 0, + .last_column = 0, + .file = NULL, +}; + +#define TAB_SIZE 8 + +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 if (text[i] == '\t') { + current_srcfile->colno = + ALIGN(current_srcfile->colno, TAB_SIZE); + } 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; + + pos_new = xmalloc(sizeof(struct srcpos)); + memcpy(pos_new, pos, sizeof(struct srcpos)); + + return pos_new; +} + + + +void +srcpos_dump(struct srcpos *pos) +{ + printf("file : \"%s\"\n", + pos->file ? (char *) pos->file : "<no file>"); + printf("first_line : %d\n", pos->first_line); + printf("first_column: %d\n", pos->first_column); + printf("last_line : %d\n", pos->last_line); + printf("last_column : %d\n", pos->last_column); + printf("file : %s\n", pos->file->name); +} + + +char * +srcpos_string(struct srcpos *pos) +{ + const char *fname = "<no-file>"; + char *pos_str; + int rc; + + if (pos) + fname = pos->file->name; + + + if (pos->first_line != pos->last_line) + rc = asprintf(&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) + rc = asprintf(&pos_str, "%s:%d.%d-%d", fname, + pos->first_line, pos->first_column, + pos->last_column); + else + rc = asprintf(&pos_str, "%s:%d.%d", fname, + pos->first_line, pos->first_column); + + if (rc == -1) + die("Couldn't allocate in srcpos string"); + + return pos_str; +} + +void +srcpos_verror(struct srcpos *pos, char const *fmt, va_list va) +{ + const char *srcstr; + + srcstr = srcpos_string(pos); + + fprintf(stderr, "Error: %s ", srcstr); + vfprintf(stderr, fmt, va); + fprintf(stderr, "\n"); +} + +void +srcpos_error(struct srcpos *pos, char const *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + srcpos_verror(pos, fmt, va); + va_end(va); +} + + +void +srcpos_warn(struct srcpos *pos, char const *fmt, ...) +{ + const char *srcstr; + va_list va; + va_start(va, fmt); + + srcstr = srcpos_string(pos); + + fprintf(stderr, "Warning: %s ", srcstr); + vfprintf(stderr, fmt, va); + fprintf(stderr, "\n"); + + va_end(va); +} + +void srcpos_set_line(char *f, int l) +{ + current_srcfile->name = f; + current_srcfile->lineno = l; +} diff --git a/src/dtc/srcpos.h b/src/dtc/srcpos.h new file mode 100644 index 0000000..93a2712 --- /dev/null +++ b/src/dtc/srcpos.h @@ -0,0 +1,118 @@ +/* + * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _SRCPOS_H_ +#define _SRCPOS_H_ + +#include <stdio.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); +int 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; +}; + +#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; \ + } \ + } while (0) + + +/* + * Fictional source position used for IR nodes that are + * created without otherwise knowing a true source position. + * For example,constant definitions from the command line. + */ +extern struct srcpos srcpos_empty; + +extern void srcpos_update(struct srcpos *pos, const char *text, int len); +extern struct srcpos *srcpos_copy(struct srcpos *pos); +extern char *srcpos_string(struct srcpos *pos); +extern void srcpos_dump(struct srcpos *pos); + +extern void srcpos_verror(struct srcpos *pos, char const *, va_list va) + __attribute__((format(printf, 2, 0))); +extern void srcpos_error(struct srcpos *pos, char const *, ...) + __attribute__((format(printf, 2, 3))); +extern void srcpos_warn(struct srcpos *pos, char const *, ...) + __attribute__((format(printf, 2, 3))); + +extern void srcpos_set_line(char *f, int l); + +#endif /* _SRCPOS_H_ */ diff --git a/src/dtc/tests/.gitignore b/src/dtc/tests/.gitignore new file mode 100644 index 0000000..bb5e33a --- /dev/null +++ b/src/dtc/tests/.gitignore @@ -0,0 +1,57 @@ +*.dtb +*.dts.test.s +*.test.dts +tmp.* +/add_subnode_with_nops +/appendprop[12] +/asm_tree_dump +/boot-cpuid +/char_literal +/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 +/incbin +/integer-expressions +/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 +/parent_offset +/path-references +/path_offset +/path_offset_aliases +/phandle_format +/propname_escapes +/references +/root_node +/rw_tree1 +/set_name +/setprop +/setprop_inplace +/sized_cells +/string_escapes +/subnode_iterate +/subnode_offset +/supernode_atdepth_offset +/sw_tree1 +/truncated_property +/utilfdt_test +/value-labels diff --git a/src/dtc/tests/Makefile.tests b/src/dtc/tests/Makefile.tests new file mode 100644 index 0000000..dafb618 --- /dev/null +++ b/src/dtc/tests/Makefile.tests @@ -0,0 +1,82 @@ +LIB_TESTS_L = get_mem_rsv \ + root_node find_property subnode_offset path_offset \ + get_name getprop 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 \ + setprop_inplace nop_property nop_node \ + sw_tree1 \ + move_and_save mangle-layout nopulate \ + open_pack rw_tree1 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 \ + subnode_iterate +LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) + +LIBTREE_TESTS_L = truncated_property +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 = *.output vglog.* vgcore.* *.dtb *.test.dts *.dtsv1 tmp.* +TESTS_CLEANFILES_L += dumptrees +TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%) + +.PHONY: tests +tests: $(TESTS) $(TESTS_TREES) + +$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_archive) + +$(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_archive) + @$(VECHO) LD [libdl] $@ + $(LINK.c) -o $@ $^ -ldl + +$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o \ + util.o $(LIBFDT_archive) + +$(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 $(STD_CLEANFILES:%=$(TESTS_PREFIX)%) + rm -f $(TESTS_CLEANFILES) + +check: tests ${TESTS_BIN} + cd $(TESTS_PREFIX); ./run_tests.sh + +checkm: tests ${TESTS_BIN} + cd $(TESTS_PREFIX); ./run_tests.sh -m 2>&1 | tee vglog.$$$$ + +checkv: tests ${TESTS_BIN} + cd $(TESTS_PREFIX); ./run_tests.sh -v + +ifneq ($(DEPTARGETS),) +-include $(TESTS_DEPFILES) +endif + diff --git a/src/dtc/tests/add_subnode_with_nops.c b/src/dtc/tests/add_subnode_with_nops.c new file mode 100644 index 0000000..95ddf6a --- /dev/null +++ b/src/dtc/tests/add_subnode_with_nops.c @@ -0,0 +1,84 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/aliases.dts b/src/dtc/tests/aliases.dts new file mode 100644 index 0000000..39d88ff --- /dev/null +++ b/src/dtc/tests/aliases.dts @@ -0,0 +1,21 @@ +/dts-v1/; + +/ { + aliases { + s1 = &sub1; + ss1 = &subsub1; + sss1 = &subsubsub1; + }; + + sub1: subnode@1 { + compatible = "subnode1"; + + subsub1: subsubnode { + compatible = "subsubnode1", "subsubnode"; + + subsubsub1: subsubsubnode { + compatible = "subsubsubnode1", "subsubsubnode"; + }; + }; + }; +}; diff --git a/src/dtc/tests/appendprop.dts b/src/dtc/tests/appendprop.dts new file mode 100644 index 0000000..f4bc730 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/appendprop1.c b/src/dtc/tests/appendprop1.c new file mode 100644 index 0000000..9d6b3ad --- /dev/null +++ b/src/dtc/tests/appendprop1.c @@ -0,0 +1,70 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_appendprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/appendprop2.c b/src/dtc/tests/appendprop2.c new file mode 100644 index 0000000..ca1446c --- /dev/null +++ b/src/dtc/tests/appendprop2.c @@ -0,0 +1,64 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_appendprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/asm_tree_dump.c b/src/dtc/tests/asm_tree_dump.c new file mode 100644 index 0000000..bd12eda --- /dev/null +++ b/src/dtc/tests/asm_tree_dump.c @@ -0,0 +1,61 @@ +/* + * 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. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/bad-empty-ranges.dts b/src/dtc/tests/bad-empty-ranges.dts new file mode 100644 index 0000000..2be7bc8 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/bad-name-property.dts b/src/dtc/tests/bad-name-property.dts new file mode 100644 index 0000000..4fde4be --- /dev/null +++ b/src/dtc/tests/bad-name-property.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + node@0 { + name = "badthing"; + }; +}; diff --git a/src/dtc/tests/bad-ncells.dts b/src/dtc/tests/bad-ncells.dts new file mode 100644 index 0000000..636198c --- /dev/null +++ b/src/dtc/tests/bad-ncells.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + #address-cells = "badthing"; + #size-cells = "badthing"; + #interrupt-cells = "badthing"; +}; diff --git a/src/dtc/tests/bad-reg-ranges.dts b/src/dtc/tests/bad-reg-ranges.dts new file mode 100644 index 0000000..77419f5 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/bad-string-props.dts b/src/dtc/tests/bad-string-props.dts new file mode 100644 index 0000000..396f820 --- /dev/null +++ b/src/dtc/tests/bad-string-props.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + device_type = <0xdeadbeef>; + model = <0xdeadbeef>; + status = <0xdeadbeef>; +}; diff --git a/src/dtc/tests/base01.asm b/src/dtc/tests/base01.asm new file mode 100644 index 0000000..266e446 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/base01.cmd b/src/dtc/tests/base01.cmd new file mode 100644 index 0000000..e1fce6c --- /dev/null +++ b/src/dtc/tests/base01.cmd @@ -0,0 +1 @@ +dtc -f -b 0 -V 16 -I dts -O asm diff --git a/src/dtc/tests/base01.dts b/src/dtc/tests/base01.dts new file mode 100644 index 0000000..97a5dd5 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/base01.stderr b/src/dtc/tests/base01.stderr new file mode 100644 index 0000000..0510b0f --- /dev/null +++ b/src/dtc/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/src/dtc/tests/boot-cpuid.c b/src/dtc/tests/boot-cpuid.c new file mode 100644 index 0000000..ca39f4b --- /dev/null +++ b/src/dtc/tests/boot-cpuid.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/boot-cpuid.dts b/src/dtc/tests/boot-cpuid.dts new file mode 100644 index 0000000..7021a24 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/char_literal.c b/src/dtc/tests/char_literal.c new file mode 100644 index 0000000..d7a4773 --- /dev/null +++ b/src/dtc/tests/char_literal.c @@ -0,0 +1,49 @@ +/* + * 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. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 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/src/dtc/tests/char_literal.dts b/src/dtc/tests/char_literal.dts new file mode 100644 index 0000000..22e17ed --- /dev/null +++ b/src/dtc/tests/char_literal.dts @@ -0,0 +1,5 @@ +/dts-v1/; + +/ { + char-literal-cells = <'\r' 'b' '\0' '\'' '\xff'>; +}; diff --git a/src/dtc/tests/comments-cmp.dts b/src/dtc/tests/comments-cmp.dts new file mode 100644 index 0000000..4ee9f52 --- /dev/null +++ b/src/dtc/tests/comments-cmp.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + prop1; + prop2; + prop3; + prop4; + prop5; + prop6; + prop7; + prop8; + prop9; + prop10; + child { + }; +}; diff --git a/src/dtc/tests/comments.dts b/src/dtc/tests/comments.dts new file mode 100644 index 0000000..0b04b6b --- /dev/null +++ b/src/dtc/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/src/dtc/tests/data.S b/src/dtc/tests/data.S new file mode 100644 index 0000000..86ad539 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/default-addr-size.dts b/src/dtc/tests/default-addr-size.dts new file mode 100644 index 0000000..e964a55 --- /dev/null +++ b/src/dtc/tests/default-addr-size.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + node { + reg = <0 0 0>; + }; +}; diff --git a/src/dtc/tests/del_node.c b/src/dtc/tests/del_node.c new file mode 100644 index 0000000..45cb060 --- /dev/null +++ b/src/dtc/tests/del_node.c @@ -0,0 +1,123 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/del_property.c b/src/dtc/tests/del_property.c new file mode 100644 index 0000000..42fd7cb --- /dev/null +++ b/src/dtc/tests/del_property.c @@ -0,0 +1,90 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_delprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/delete_reinstate_multilabel.dts b/src/dtc/tests/delete_reinstate_multilabel.dts new file mode 100644 index 0000000..281a6b2 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/delete_reinstate_multilabel_ref.dts b/src/dtc/tests/delete_reinstate_multilabel_ref.dts new file mode 100644 index 0000000..28fa117 --- /dev/null +++ b/src/dtc/tests/delete_reinstate_multilabel_ref.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + prop = "value"; + + node { + prop = "value"; + }; +}; diff --git a/src/dtc/tests/dependencies.cmp b/src/dtc/tests/dependencies.cmp new file mode 100644 index 0000000..bcd9432 --- /dev/null +++ b/src/dtc/tests/dependencies.cmp @@ -0,0 +1 @@ +dependencies.test.dtb: dependencies.dts deps_inc1.dtsi deps_inc2.dtsi diff --git a/src/dtc/tests/dependencies.dts b/src/dtc/tests/dependencies.dts new file mode 100644 index 0000000..2cfe31b --- /dev/null +++ b/src/dtc/tests/dependencies.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/include/ "deps_inc1.dtsi" + +/ { +}; diff --git a/src/dtc/tests/deps_inc1.dtsi b/src/dtc/tests/deps_inc1.dtsi new file mode 100644 index 0000000..5c607dc --- /dev/null +++ b/src/dtc/tests/deps_inc1.dtsi @@ -0,0 +1 @@ +/include/ "deps_inc2.dtsi" diff --git a/src/dtc/tests/deps_inc2.dtsi b/src/dtc/tests/deps_inc2.dtsi new file mode 100644 index 0000000..710cecc --- /dev/null +++ b/src/dtc/tests/deps_inc2.dtsi @@ -0,0 +1 @@ +/* Empty */ diff --git a/src/dtc/tests/dtb_reverse.c b/src/dtc/tests/dtb_reverse.c new file mode 100644 index 0000000..527fd71 --- /dev/null +++ b/src/dtc/tests/dtb_reverse.c @@ -0,0 +1,163 @@ +/* + * libfdt - Flat Device Tree manipulation + * Tests if two given dtbs are structurally equal (including order) + * Copyright (C) 2010 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/dtbs_equal_ordered.c b/src/dtc/tests/dtbs_equal_ordered.c new file mode 100644 index 0000000..12495de --- /dev/null +++ b/src/dtc/tests/dtbs_equal_ordered.c @@ -0,0 +1,173 @@ +/* + * libfdt - Flat Device Tree manipulation + * Tests if two given dtbs are structurally equal (including order) + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +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/src/dtc/tests/dtbs_equal_unordered.c b/src/dtc/tests/dtbs_equal_unordered.c new file mode 100644 index 0000000..20b4356 --- /dev/null +++ b/src/dtc/tests/dtbs_equal_unordered.c @@ -0,0 +1,223 @@ +/* + * libfdt - Flat Device Tree manipulation + * Tests if two given dtbs are structurally equal (including order) + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <limits.h> + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +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 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 (re1->address < re2->address) + return -1; + else if (re1->address > re2->address) + return 1; + + if (re1->size < re2->size) + return -1; + else if (re1->size > 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); +} + +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_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/src/dtc/tests/dtc-checkfails.sh b/src/dtc/tests/dtc-checkfails.sh new file mode 100755 index 0000000..76ded15 --- /dev/null +++ b/src/dtc/tests/dtc-checkfails.sh @@ -0,0 +1,44 @@ +#! /bin/sh + +. ./tests.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/src/dtc/tests/dtc-fails.sh b/src/dtc/tests/dtc-fails.sh new file mode 100755 index 0000000..4ddcb27 --- /dev/null +++ b/src/dtc/tests/dtc-fails.sh @@ -0,0 +1,30 @@ +#! /bin/sh + +. ./tests.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/src/dtc/tests/dtc-fatal.sh b/src/dtc/tests/dtc-fatal.sh new file mode 100644 index 0000000..6781ced --- /dev/null +++ b/src/dtc/tests/dtc-fatal.sh @@ -0,0 +1,14 @@ +#! /bin/sh + +. ./tests.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/src/dtc/tests/dumptrees.c b/src/dtc/tests/dumptrees.c new file mode 100644 index 0000000..bebf553 --- /dev/null +++ b/src/dtc/tests/dumptrees.c @@ -0,0 +1,69 @@ +/* + * dumptrees - utility for libfdt testing + * + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2006. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdint.h> + +#include <libfdt.h> + +#include "testdata.h" + +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), +}; + +#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/src/dtc/tests/dup-nodename.dts b/src/dtc/tests/dup-nodename.dts new file mode 100644 index 0000000..2a3aa75 --- /dev/null +++ b/src/dtc/tests/dup-nodename.dts @@ -0,0 +1,8 @@ +/dts-v1/; + +/ { + node { + }; + node { + }; +}; diff --git a/src/dtc/tests/dup-phandle.dts b/src/dtc/tests/dup-phandle.dts new file mode 100644 index 0000000..c266c61 --- /dev/null +++ b/src/dtc/tests/dup-phandle.dts @@ -0,0 +1,10 @@ +/dts-v1/; + +/ { + node1 { + linux,phandle = <1>; + }; + node2 { + linux,phandle = <1>; + }; +}; diff --git a/src/dtc/tests/dup-propname.dts b/src/dtc/tests/dup-propname.dts new file mode 100644 index 0000000..8145f6e --- /dev/null +++ b/src/dtc/tests/dup-propname.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + prop; + prop; +}; diff --git a/src/dtc/tests/empty.dts b/src/dtc/tests/empty.dts new file mode 100644 index 0000000..e160dad --- /dev/null +++ b/src/dtc/tests/empty.dts @@ -0,0 +1,4 @@ +/dts-v1/; + +/ { +}; diff --git a/src/dtc/tests/escapes.dts b/src/dtc/tests/escapes.dts new file mode 100644 index 0000000..e05ab46 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/extra-terminating-null.c b/src/dtc/tests/extra-terminating-null.c new file mode 100644 index 0000000..dc1fe89 --- /dev/null +++ b/src/dtc/tests/extra-terminating-null.c @@ -0,0 +1,58 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for properties with more than one terminating null + * Copyright (C) 2009 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/extra-terminating-null.dts b/src/dtc/tests/extra-terminating-null.dts new file mode 100644 index 0000000..b6cc19c --- /dev/null +++ b/src/dtc/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/src/dtc/tests/fdtget-runtest.sh b/src/dtc/tests/fdtget-runtest.sh new file mode 100755 index 0000000..8d8b058 --- /dev/null +++ b/src/dtc/tests/fdtget-runtest.sh @@ -0,0 +1,24 @@ +#! /bin/sh + +. ./tests.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/src/dtc/tests/fdtput-runtest.sh b/src/dtc/tests/fdtput-runtest.sh new file mode 100644 index 0000000..527a968 --- /dev/null +++ b/src/dtc/tests/fdtput-runtest.sh @@ -0,0 +1,39 @@ +#! /bin/sh + +# Run script for fdtput tests +# We run fdtput to update the device tree, thn fdtget to check it + +# Usage +# fdtput-runtest.sh name expected_output dtb_file node property flags value + +. ./tests.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/src/dtc/tests/find_property.c b/src/dtc/tests/find_property.c new file mode 100644 index 0000000..4dc3030 --- /dev/null +++ b/src/dtc/tests/find_property.c @@ -0,0 +1,41 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_property_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/get_alias.c b/src/dtc/tests/get_alias.c new file mode 100644 index 0000000..5060795 --- /dev/null +++ b/src/dtc/tests/get_alias.c @@ -0,0 +1,57 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_get_alias() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/get_mem_rsv.c b/src/dtc/tests/get_mem_rsv.c new file mode 100644 index 0000000..1812639 --- /dev/null +++ b/src/dtc/tests/get_mem_rsv.c @@ -0,0 +1,48 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_get_mem_rsv() and fdt_num_mem_rsv() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/get_name.c b/src/dtc/tests/get_name.c new file mode 100644 index 0000000..c6ca9f9 --- /dev/null +++ b/src/dtc/tests/get_name.c @@ -0,0 +1,82 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_get_name() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/get_path.c b/src/dtc/tests/get_path.c new file mode 100644 index 0000000..7352976 --- /dev/null +++ b/src/dtc/tests/get_path.c @@ -0,0 +1,92 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_get_path() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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, buf); + + 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/src/dtc/tests/get_phandle.c b/src/dtc/tests/get_phandle.c new file mode 100644 index 0000000..2079591 --- /dev/null +++ b/src/dtc/tests/get_phandle.c @@ -0,0 +1,57 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_get_phandle() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + + 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); + + PASS(); +} diff --git a/src/dtc/tests/getprop.c b/src/dtc/tests/getprop.c new file mode 100644 index 0000000..6255bad --- /dev/null +++ b/src/dtc/tests/getprop.c @@ -0,0 +1,42 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_getprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/incbin.bin b/src/dtc/tests/incbin.bin new file mode 100644 index 0000000..e6e3e48 --- /dev/null +++ b/src/dtc/tests/incbin.bin @@ -0,0 +1 @@ +abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
\ No newline at end of file diff --git a/src/dtc/tests/incbin.c b/src/dtc/tests/incbin.c new file mode 100644 index 0000000..4100ba0 --- /dev/null +++ b/src/dtc/tests/incbin.c @@ -0,0 +1,75 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for string escapes in dtc + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/incbin.dts b/src/dtc/tests/incbin.dts new file mode 100644 index 0000000..7c30e0e --- /dev/null +++ b/src/dtc/tests/incbin.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + incbin = /incbin/("incbin.bin"); + incbin-partial = /incbin/("incbin.bin", 13, 17); +}; diff --git a/src/dtc/tests/include0.dts b/src/dtc/tests/include0.dts new file mode 100644 index 0000000..355ed6a --- /dev/null +++ b/src/dtc/tests/include0.dts @@ -0,0 +1 @@ +/include/ "include1.dts" diff --git a/src/dtc/tests/include1.dts b/src/dtc/tests/include1.dts new file mode 100644 index 0000000..893aaff --- /dev/null +++ b/src/dtc/tests/include1.dts @@ -0,0 +1,27 @@ +/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"; + + /include/ "include7.dts" + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + /include/ "include8.dts" + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/include2.dts b/src/dtc/tests/include2.dts new file mode 100644 index 0000000..7e189dd --- /dev/null +++ b/src/dtc/tests/include2.dts @@ -0,0 +1 @@ +/memreserve/ 0xdeadbeef00000000 0x100000; diff --git a/src/dtc/tests/include3.dts b/src/dtc/tests/include3.dts new file mode 100644 index 0000000..ee9d277 --- /dev/null +++ b/src/dtc/tests/include3.dts @@ -0,0 +1 @@ +123456789 010000 diff --git a/src/dtc/tests/include4.dts b/src/dtc/tests/include4.dts new file mode 100644 index 0000000..b2ddbe5 --- /dev/null +++ b/src/dtc/tests/include4.dts @@ -0,0 +1 @@ + compatible = "test_tree1"; diff --git a/src/dtc/tests/include5.dts b/src/dtc/tests/include5.dts new file mode 100644 index 0000000..9a35dc5 --- /dev/null +++ b/src/dtc/tests/include5.dts @@ -0,0 +1 @@ +prop-int diff --git a/src/dtc/tests/include5a.dts b/src/dtc/tests/include5a.dts new file mode 100644 index 0000000..39ddba4 --- /dev/null +++ b/src/dtc/tests/include5a.dts @@ -0,0 +1 @@ += /bits/ 64 <0xdeadbeef01abcdef>
\ No newline at end of file diff --git a/src/dtc/tests/include6.dts b/src/dtc/tests/include6.dts new file mode 100644 index 0000000..cd4bc1a --- /dev/null +++ b/src/dtc/tests/include6.dts @@ -0,0 +1 @@ +"hello world" diff --git a/src/dtc/tests/include7.dts b/src/dtc/tests/include7.dts new file mode 100644 index 0000000..dba5e47 --- /dev/null +++ b/src/dtc/tests/include7.dts @@ -0,0 +1,12 @@ + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; diff --git a/src/dtc/tests/include8.dts b/src/dtc/tests/include8.dts new file mode 100644 index 0000000..ec70064 --- /dev/null +++ b/src/dtc/tests/include8.dts @@ -0,0 +1 @@ +subsubnode@0 {
\ No newline at end of file diff --git a/src/dtc/tests/integer-expressions.c b/src/dtc/tests/integer-expressions.c new file mode 100644 index 0000000..57e2ff6 --- /dev/null +++ b/src/dtc/tests/integer-expressions.c @@ -0,0 +1,116 @@ +/* + * Testcase for dtc expression support + * + * Copyright (C) 2008 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +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 uint32_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 retreiving 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/src/dtc/tests/label01.dts b/src/dtc/tests/label01.dts new file mode 100644 index 0000000..a895803 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/label_repeated.dts b/src/dtc/tests/label_repeated.dts new file mode 100644 index 0000000..34225d3 --- /dev/null +++ b/src/dtc/tests/label_repeated.dts @@ -0,0 +1,15 @@ +/dts-v1/; + +/ { + l0: prop = "foo"; + + l1: node { + }; +}; + +/ { + l0: prop = "foo"; + + l1: node { + }; +}; diff --git a/src/dtc/tests/line_directives.dts b/src/dtc/tests/line_directives.dts new file mode 100644 index 0000000..046ef37 --- /dev/null +++ b/src/dtc/tests/line_directives.dts @@ -0,0 +1,21 @@ +/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 + >; +}; diff --git a/src/dtc/tests/lorem.txt b/src/dtc/tests/lorem.txt new file mode 100644 index 0000000..acff698 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/mangle-layout.c b/src/dtc/tests/mangle-layout.c new file mode 100644 index 0000000..a76e51e --- /dev/null +++ b/src/dtc/tests/mangle-layout.c @@ -0,0 +1,164 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase/tool for rearranging blocks of a dtb + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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; + + if (version == 16) + hdrsize = FDT_V16_SIZE; + else if (version == 17) + hdrsize = FDT_V17_SIZE; + else + 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/src/dtc/tests/mangle-layout.supp b/src/dtc/tests/mangle-layout.supp new file mode 100644 index 0000000..2890420 --- /dev/null +++ b/src/dtc/tests/mangle-layout.supp @@ -0,0 +1,7 @@ +{ + uninitialized alignment gaps can be dumped to output + Memcheck:Param + write(buf) + obj:/lib/ld-*.so + fun:main +} diff --git a/src/dtc/tests/minusone-phandle.dts b/src/dtc/tests/minusone-phandle.dts new file mode 100644 index 0000000..21d9986 --- /dev/null +++ b/src/dtc/tests/minusone-phandle.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + node { + linux,phandle = <0xffffffff>; + }; +}; diff --git a/src/dtc/tests/move_and_save.c b/src/dtc/tests/move_and_save.c new file mode 100644 index 0000000..393b60a --- /dev/null +++ b/src/dtc/tests/move_and_save.c @@ -0,0 +1,75 @@ +/* + * libfdt - Flat Device Tree manipulation + * Basic testcase for read-only access + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/multilabel.dts b/src/dtc/tests/multilabel.dts new file mode 100644 index 0000000..31116ce --- /dev/null +++ b/src/dtc/tests/multilabel.dts @@ -0,0 +1,42 @@ +/dts-v1/; + +m1: mq: /memreserve/ 0 0x1000; + +/ { + p0: pw: prop = "foo"; + + /* 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; + }; +}; diff --git a/src/dtc/tests/multilabel_merge.dts b/src/dtc/tests/multilabel_merge.dts new file mode 100644 index 0000000..1632300 --- /dev/null +++ b/src/dtc/tests/multilabel_merge.dts @@ -0,0 +1,66 @@ +/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; + }; +}; + +/ { + /* 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; + }; + +}; diff --git a/src/dtc/tests/node_check_compatible.c b/src/dtc/tests/node_check_compatible.c new file mode 100644 index 0000000..4bdf091 --- /dev/null +++ b/src/dtc/tests/node_check_compatible.c @@ -0,0 +1,62 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_node_check_compatible() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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); +} + +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_compatible(fdt, "/subnode@2/subsubnode", "subsubnode2"); + check_compatible(fdt, "/subnode@2/subsubnode", "subsubnode"); + + PASS(); +} diff --git a/src/dtc/tests/node_offset_by_compatible.c b/src/dtc/tests/node_offset_by_compatible.c new file mode 100644 index 0000000..f62b591 --- /dev/null +++ b/src/dtc/tests/node_offset_by_compatible.c @@ -0,0 +1,85 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_node_offset_by_compatible() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/node_offset_by_phandle.c b/src/dtc/tests/node_offset_by_phandle.c new file mode 100644 index 0000000..becff0f --- /dev/null +++ b/src/dtc/tests/node_offset_by_phandle.c @@ -0,0 +1,63 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_node_offset_by_phandle() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/node_offset_by_prop_value.c b/src/dtc/tests/node_offset_by_prop_value.c new file mode 100644 index 0000000..9212a4e --- /dev/null +++ b/src/dtc/tests/node_offset_by_prop_value.c @@ -0,0 +1,110 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_path_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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, ...) \ + { \ + uint32_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/src/dtc/tests/nonexist-label-ref.dts b/src/dtc/tests/nonexist-label-ref.dts new file mode 100644 index 0000000..25927a1 --- /dev/null +++ b/src/dtc/tests/nonexist-label-ref.dts @@ -0,0 +1,8 @@ +/dts-v1/; + +/ { + ref = <&label>; + badref = <&nosuchlabel>; + label: node { + }; +}; diff --git a/src/dtc/tests/nonexist-node-ref.dts b/src/dtc/tests/nonexist-node-ref.dts new file mode 100644 index 0000000..efd4140 --- /dev/null +++ b/src/dtc/tests/nonexist-node-ref.dts @@ -0,0 +1,8 @@ +/dts-v1/; + +/ { + ref = < &{/node} >; + badref = < &{/nosuchnode} >; + label: node { + }; +}; diff --git a/src/dtc/tests/nonexist-node-ref2.dts b/src/dtc/tests/nonexist-node-ref2.dts new file mode 100644 index 0000000..44b4ebe --- /dev/null +++ b/src/dtc/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/src/dtc/tests/nop_node.c b/src/dtc/tests/nop_node.c new file mode 100644 index 0000000..c316444 --- /dev/null +++ b/src/dtc/tests/nop_node.c @@ -0,0 +1,104 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/nop_property.c b/src/dtc/tests/nop_property.c new file mode 100644 index 0000000..644b0a6 --- /dev/null +++ b/src/dtc/tests/nop_property.c @@ -0,0 +1,70 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_property() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/nopulate.c b/src/dtc/tests/nopulate.c new file mode 100644 index 0000000..cd79872 --- /dev/null +++ b/src/dtc/tests/nopulate.c @@ -0,0 +1,105 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase/tool for rearranging blocks of a dtb + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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; + + *((uint32_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/src/dtc/tests/notfound.c b/src/dtc/tests/notfound.c new file mode 100644 index 0000000..dc623d6 --- /dev/null +++ b/src/dtc/tests/notfound.c @@ -0,0 +1,70 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for behaviour on searching for a non-existent node + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/obsolete-chosen-interrupt-controller.dts b/src/dtc/tests/obsolete-chosen-interrupt-controller.dts new file mode 100644 index 0000000..36dd6e8 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/open_pack.c b/src/dtc/tests/open_pack.c new file mode 100644 index 0000000..407ef6c --- /dev/null +++ b/src/dtc/tests/open_pack.c @@ -0,0 +1,71 @@ +/* + * libfdt - Flat Device Tree manipulation + * Basic testcase for read-only access + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/open_pack.supp b/src/dtc/tests/open_pack.supp new file mode 100644 index 0000000..c954fe7 --- /dev/null +++ b/src/dtc/tests/open_pack.supp @@ -0,0 +1,7 @@ +{ + opened blob dumps uninitialized data + Memcheck:Param + write(buf) + obj:/lib/ld-*.so + fun:main +} diff --git a/src/dtc/tests/parent_offset.c b/src/dtc/tests/parent_offset.c new file mode 100644 index 0000000..d4ab3cf --- /dev/null +++ b/src/dtc/tests/parent_offset.c @@ -0,0 +1,90 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_parent_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/path-references.c b/src/dtc/tests/path-references.c new file mode 100644 index 0000000..0746b3f --- /dev/null +++ b/src/dtc/tests/path-references.c @@ -0,0 +1,82 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for string references in dtc + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + const char *p; + int len, multilen; + int n1, n2; + + 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"); + + PASS(); +} diff --git a/src/dtc/tests/path-references.dts b/src/dtc/tests/path-references.dts new file mode 100644 index 0000000..91e7ef7 --- /dev/null +++ b/src/dtc/tests/path-references.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + /* 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; + }; +}; diff --git a/src/dtc/tests/path_offset.c b/src/dtc/tests/path_offset.c new file mode 100644 index 0000000..4e5b7a1 --- /dev/null +++ b/src/dtc/tests/path_offset.c @@ -0,0 +1,111 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_path_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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; +} + +int main(int argc, char *argv[]) +{ + void *fdt; + int root_offset; + int subnode1_offset, subnode2_offset; + int subnode1_offset_p, subnode2_offset_p; + int subsubnode1_offset, subsubnode2_offset, subsubnode2_offset2; + int subsubnode1_offset_p, subsubnode2_offset_p, subsubnode2_offset2_p; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + + root_offset = fdt_path_offset(fdt, "/"); + if (root_offset < 0) + FAIL("fdt_path_offset(\"/\") failed: %s", + fdt_strerror(root_offset)); + else if (root_offset != 0) + FAIL("fdt_path_offset(\"/\") returns incorrect offset %d", + root_offset); + subnode1_offset = check_subnode(fdt, 0, "subnode@1"); + subnode2_offset = check_subnode(fdt, 0, "subnode@2"); + + subnode1_offset_p = fdt_path_offset(fdt, "/subnode@1"); + subnode2_offset_p = fdt_path_offset(fdt, "/subnode@2"); + + if (subnode1_offset != subnode1_offset_p) + FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", + subnode1_offset, subnode1_offset_p); + + if (subnode2_offset != subnode2_offset_p) + FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", + subnode2_offset, subnode2_offset_p); + + 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"); + + subsubnode1_offset_p = fdt_path_offset(fdt, "/subnode@1/subsubnode"); + subsubnode2_offset_p = fdt_path_offset(fdt, "/subnode@2/subsubnode@0"); + subsubnode2_offset2_p = fdt_path_offset(fdt, "/subnode@2/subsubnode"); + + if (subsubnode1_offset != subsubnode1_offset_p) + FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", + subsubnode1_offset, subsubnode1_offset_p); + + if (subsubnode2_offset != subsubnode2_offset_p) + FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", + subsubnode2_offset, subsubnode2_offset_p); + + if (subsubnode2_offset2 != subsubnode2_offset2_p) + FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", + subsubnode2_offset2, subsubnode2_offset2_p); + + PASS(); +} diff --git a/src/dtc/tests/path_offset_aliases.c b/src/dtc/tests/path_offset_aliases.c new file mode 100644 index 0000000..78d5a46 --- /dev/null +++ b/src/dtc/tests/path_offset_aliases.c @@ -0,0 +1,58 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_path_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2008 Kumar Gala, Freescale Semiconductor, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/phandle_format.c b/src/dtc/tests/phandle_format.c new file mode 100644 index 0000000..5874ae7 --- /dev/null +++ b/src/dtc/tests/phandle_format.c @@ -0,0 +1,77 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for phandle format options + * Copyright (C) 2009 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/prop-after-subnode.dts b/src/dtc/tests/prop-after-subnode.dts new file mode 100644 index 0000000..6dd0b66 --- /dev/null +++ b/src/dtc/tests/prop-after-subnode.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + node1 { + }; + prop; + node2 { + }; +}; diff --git a/src/dtc/tests/propname_escapes.c b/src/dtc/tests/propname_escapes.c new file mode 100644 index 0000000..e91bd99 --- /dev/null +++ b/src/dtc/tests/propname_escapes.c @@ -0,0 +1,42 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_getprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/propname_escapes.dts b/src/dtc/tests/propname_escapes.dts new file mode 100644 index 0000000..9f70618 --- /dev/null +++ b/src/dtc/tests/propname_escapes.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + \#gpio-cells = <2>; +}; diff --git a/src/dtc/tests/references.c b/src/dtc/tests/references.c new file mode 100644 index 0000000..c9d05a2 --- /dev/null +++ b/src/dtc/tests/references.c @@ -0,0 +1,108 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for phandle references in dtc + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 uint32_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); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + int n1, n2, n3, n4, n5; + uint32_t h1, h2, h4, h5; + + 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)); + + h1 = fdt_get_phandle(fdt, n1); + h2 = fdt_get_phandle(fdt, n2); + h4 = fdt_get_phandle(fdt, n4); + h5 = fdt_get_phandle(fdt, n5); + + 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 ((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); + + check_ref(fdt, n1, h2); + check_ref(fdt, n2, h1); + check_ref(fdt, n3, h4); + + PASS(); +} diff --git a/src/dtc/tests/references.dts b/src/dtc/tests/references.dts new file mode 100644 index 0000000..640c931 --- /dev/null +++ b/src/dtc/tests/references.dts @@ -0,0 +1,34 @@ +/dts-v1/; + +/ { + /* 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>; + }; +}; diff --git a/src/dtc/tests/reg-ranges-root.dts b/src/dtc/tests/reg-ranges-root.dts new file mode 100644 index 0000000..9935b41 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/reuse-label.dts b/src/dtc/tests/reuse-label.dts new file mode 100644 index 0000000..98b5ca9 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/reuse-label1.dts b/src/dtc/tests/reuse-label1.dts new file mode 100644 index 0000000..f229569 --- /dev/null +++ b/src/dtc/tests/reuse-label1.dts @@ -0,0 +1,10 @@ +/dts-v1/; + +/ { + label: node1 { + prop = "foo"; + }; + label: node2 { + prop = "bar"; + }; +}; diff --git a/src/dtc/tests/reuse-label2.dts b/src/dtc/tests/reuse-label2.dts new file mode 100644 index 0000000..01ea6b2 --- /dev/null +++ b/src/dtc/tests/reuse-label2.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + label: property1 = "foo"; + label: property2 = "bar"; +}; diff --git a/src/dtc/tests/reuse-label3.dts b/src/dtc/tests/reuse-label3.dts new file mode 100644 index 0000000..fa3d2c7 --- /dev/null +++ b/src/dtc/tests/reuse-label3.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + label: property = "foo"; + + label: node { + property = "foo"; + }; +}; diff --git a/src/dtc/tests/reuse-label4.dts b/src/dtc/tests/reuse-label4.dts new file mode 100644 index 0000000..6805de3 --- /dev/null +++ b/src/dtc/tests/reuse-label4.dts @@ -0,0 +1,5 @@ +/dts-v1/; + +/ { + property = label: "foo" label:; +}; diff --git a/src/dtc/tests/reuse-label5.dts b/src/dtc/tests/reuse-label5.dts new file mode 100644 index 0000000..b7238e6 --- /dev/null +++ b/src/dtc/tests/reuse-label5.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + prop1 = label: "foo"; + prop2 = "bar" label:; +}; diff --git a/src/dtc/tests/reuse-label6.dts b/src/dtc/tests/reuse-label6.dts new file mode 100644 index 0000000..f5d507c --- /dev/null +++ b/src/dtc/tests/reuse-label6.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + label: prop1 = "foo"; + prop2 = "bar" label:; +}; diff --git a/src/dtc/tests/root_node.c b/src/dtc/tests/root_node.c new file mode 100644 index 0000000..58aebf6 --- /dev/null +++ b/src/dtc/tests/root_node.c @@ -0,0 +1,52 @@ +/* + * libfdt - Flat Device Tree manipulation + * Basic testcase for read-only access + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/run_tests.sh b/src/dtc/tests/run_tests.sh new file mode 100755 index 0000000..c0a136b --- /dev/null +++ b/src/dtc/tests/run_tests.sh @@ -0,0 +1,644 @@ +#! /bin/sh + +. ./tests.sh + +if [ -z "$CC" ]; then + CC=gcc +fi + +export QUIET_TEST=1 + +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 [ "$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 + echo -n "$1" + shift + for x; do + if [ ${#x} -le $limit ]; then + echo -n " $x" + else + short=$(echo "$x" | head -c$limit) + echo -n " \"$short\"...<${#x} bytes>" + fi + done +} + +run_test () { + echo -n "$@: " + if [ -n "$VALGRIND" -a -f $1.supp ]; then + VGSUPP="--suppressions=$1.supp" + fi + base_run_test $VALGRIND $VGSUPP "./$@" +} + +run_sh_test () { + echo -n "$@: " + 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" + 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 "$@" + echo -n " {!= 0}: " + base_run_test wrap_error "$@" +} + +run_dtc_test () { + echo -n "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 + echo -n "fdtget-runtest.sh "$expect" $@: " + base_run_test sh fdtget-runtest.sh "$expect" "$@" +} + +run_fdtput_test () { + expect="$1" + shift + shorten_echo fdtput-runtest.sh "$expect" "$@" + echo -n ": " + base_run_test sh fdtput-runtest.sh "$expect" "$@" +} + +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_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 + + # Sequential write tests + run_test sw_tree1 + tree1_tests sw_tree1.test.dtb + tree1_tests unfinished_tree1.test.dtb + run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb + + # 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 + + 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_dtc_test -I dts -O dtb -o subnode_iterate.dtb subnode_iterate.dts + run_test subnode_iterate subnode_iterate.dtb + + # Tests for behaviour on various sorts of corrupted trees + run_test truncated_property + + # Specific bug tests + run_test add_subnode_with_nops +} + +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 both + 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 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 + + # 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 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 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 + + # 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 + + 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 + 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 + 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 prop_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 + + # 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 +} + +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 $(stat -c %s $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 $(stat -c %s $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 insufficent 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 + + # TODO: Add tests for verbose mode? +} + +utilfdt_tests () { + run_test utilfdt_test +} + +while getopts "vt:m" ARG ; do + case $ARG in + "v") + unset QUIET_TEST + ;; + "t") + TESTSETS=$OPTARG + ;; + "m") + VALGRIND="valgrind --tool=memcheck -q --error-exitcode=$VGCODE" + ;; + esac +done + +if [ -z "$TESTSETS" ]; then + TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput" +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 + ;; + 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 "**********" + diff --git a/src/dtc/tests/rw_tree1.c b/src/dtc/tests/rw_tree1.c new file mode 100644 index 0000000..efd4718 --- /dev/null +++ b/src/dtc/tests/rw_tree1.c @@ -0,0 +1,95 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/src/dtc/tests/search_dir/search_test.dtsi b/src/dtc/tests/search_dir/search_test.dtsi new file mode 100644 index 0000000..217fb80 --- /dev/null +++ b/src/dtc/tests/search_dir/search_test.dtsi @@ -0,0 +1,4 @@ +/include/ "search_test2.dtsi" + +/ { +}; diff --git a/src/dtc/tests/search_dir/search_test2.dtsi b/src/dtc/tests/search_dir/search_test2.dtsi new file mode 100644 index 0000000..7b9099e --- /dev/null +++ b/src/dtc/tests/search_dir/search_test2.dtsi @@ -0,0 +1,3 @@ + +/ { +}; diff --git a/src/dtc/tests/search_dir_b/search_paths_subdir.dts b/src/dtc/tests/search_dir_b/search_paths_subdir.dts new file mode 100644 index 0000000..5c5c962 --- /dev/null +++ b/src/dtc/tests/search_dir_b/search_paths_subdir.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/include/ "search_test_c.dtsi" + +/ { +}; diff --git a/src/dtc/tests/search_dir_b/search_test_b.dtsi b/src/dtc/tests/search_dir_b/search_test_b.dtsi new file mode 100644 index 0000000..b06a7d6 --- /dev/null +++ b/src/dtc/tests/search_dir_b/search_test_b.dtsi @@ -0,0 +1,4 @@ +/include/ "search_test_b2.dtsi" + +/ { +}; diff --git a/src/dtc/tests/search_dir_b/search_test_b2.dtsi b/src/dtc/tests/search_dir_b/search_test_b2.dtsi new file mode 100644 index 0000000..2526b43 --- /dev/null +++ b/src/dtc/tests/search_dir_b/search_test_b2.dtsi @@ -0,0 +1,5 @@ + +/include/ "search_test.dtsi" + +/ { +}; diff --git a/src/dtc/tests/search_dir_b/search_test_c.dtsi b/src/dtc/tests/search_dir_b/search_test_c.dtsi new file mode 100644 index 0000000..336d7a2 --- /dev/null +++ b/src/dtc/tests/search_dir_b/search_test_c.dtsi @@ -0,0 +1,2 @@ +/ { +}; diff --git a/src/dtc/tests/search_paths.dts b/src/dtc/tests/search_paths.dts new file mode 100644 index 0000000..a2bf179 --- /dev/null +++ b/src/dtc/tests/search_paths.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/include/ "search_test.dtsi" + +/ { +}; diff --git a/src/dtc/tests/search_paths_b.dts b/src/dtc/tests/search_paths_b.dts new file mode 100644 index 0000000..6ace6e2 --- /dev/null +++ b/src/dtc/tests/search_paths_b.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/include/ "search_test_b.dtsi" + +/ { +}; diff --git a/src/dtc/tests/set_name.c b/src/dtc/tests/set_name.c new file mode 100644 index 0000000..9861587 --- /dev/null +++ b/src/dtc/tests/set_name.c @@ -0,0 +1,90 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_set_name() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/setprop.c b/src/dtc/tests/setprop.c new file mode 100644 index 0000000..d089f8d --- /dev/null +++ b/src/dtc/tests/setprop.c @@ -0,0 +1,95 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_setprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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(fdt, 0, "prop-str", NULL, 0); + 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/src/dtc/tests/setprop_inplace.c b/src/dtc/tests/setprop_inplace.c new file mode 100644 index 0000000..daef182 --- /dev/null +++ b/src/dtc/tests/setprop_inplace.c @@ -0,0 +1,87 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_setprop_inplace() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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); + + PASS(); +} diff --git a/src/dtc/tests/sized_cells.c b/src/dtc/tests/sized_cells.c new file mode 100644 index 0000000..94da03b --- /dev/null +++ b/src/dtc/tests/sized_cells.c @@ -0,0 +1,83 @@ +/* + * 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. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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}; + uint16_t expected_16[6]; + uint32_t expected_32[6]; + uint64_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/src/dtc/tests/sized_cells.dts b/src/dtc/tests/sized_cells.dts new file mode 100644 index 0000000..efea9f5 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/string_escapes.c b/src/dtc/tests/string_escapes.c new file mode 100644 index 0000000..8cdee4b --- /dev/null +++ b/src/dtc/tests/string_escapes.c @@ -0,0 +1,43 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for string escapes in dtc + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/subnode_iterate.c b/src/dtc/tests/subnode_iterate.c new file mode 100644 index 0000000..b9f379d --- /dev/null +++ b/src/dtc/tests/subnode_iterate.c @@ -0,0 +1,94 @@ +/* + * 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. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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) +{ + fdt32_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 = cpu_to_fdt32(*prop); + + count = 0; + for (offset = fdt_first_subnode(fdt, parent_offset); + offset >= 0; + offset = fdt_next_subnode(fdt, 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; + + for (offset = fdt_first_subnode(fdt, 0); + offset >= 0; + offset = fdt_next_subnode(fdt, offset)) { + 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/src/dtc/tests/subnode_iterate.dts b/src/dtc/tests/subnode_iterate.dts new file mode 100644 index 0000000..14a0d3a --- /dev/null +++ b/src/dtc/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/src/dtc/tests/subnode_offset.c b/src/dtc/tests/subnode_offset.c new file mode 100644 index 0000000..231fcb5 --- /dev/null +++ b/src/dtc/tests/subnode_offset.c @@ -0,0 +1,98 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_subnode_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/supernode_atdepth_offset.c b/src/dtc/tests/supernode_atdepth_offset.c new file mode 100644 index 0000000..43e120d --- /dev/null +++ b/src/dtc/tests/supernode_atdepth_offset.c @@ -0,0 +1,145 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_supernode_atdepth_offset() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/sw_tree1.c b/src/dtc/tests/sw_tree1.c new file mode 100644 index 0000000..8eb6e5f --- /dev/null +++ b/src/dtc/tests/sw_tree1.c @@ -0,0 +1,99 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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; + + test_init(argc, argv); + + fdt = xmalloc(SPACE); + CHECK(fdt_create(fdt, SPACE)); + + 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_begin_node(fdt, "subnode@1")); + CHECK(fdt_property_string(fdt, "compatible", "subnode1")); + 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_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_cell(fdt, "linux,phandle", PHANDLE_1)); + CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2)); + CHECK(fdt_begin_node(fdt, "subsubnode@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/src/dtc/tests/test01.asm b/src/dtc/tests/test01.asm new file mode 100644 index 0000000..bbf66c7 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/test01.dts b/src/dtc/tests/test01.dts new file mode 100644 index 0000000..f9fd165 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/test01.stderr b/src/dtc/tests/test01.stderr new file mode 100644 index 0000000..82ea3f6 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/test_kernel_dts b/src/dtc/tests/test_kernel_dts new file mode 100755 index 0000000..238f3f7 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/test_tree1.dts b/src/dtc/tests/test_tree1.dts new file mode 100644 index 0000000..c7b170c --- /dev/null +++ b/src/dtc/tests/test_tree1.dts @@ -0,0 +1,3 @@ +/dts-v1/; + +/include/ "test_tree1_body.dtsi" diff --git a/src/dtc/tests/test_tree1_body.dtsi b/src/dtc/tests/test_tree1_body.dtsi new file mode 100644 index 0000000..1446191 --- /dev/null +++ b/src/dtc/tests/test_tree1_body.dtsi @@ -0,0 +1,36 @@ +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + ssn0: subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_delete.dts b/src/dtc/tests/test_tree1_delete.dts new file mode 100644 index 0000000..a2f1bfd --- /dev/null +++ b/src/dtc/tests/test_tree1_delete.dts @@ -0,0 +1,68 @@ +/dts-v1/; + +/include/ "test_tree1_body.dtsi" + +/ { + 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/src/dtc/tests/test_tree1_merge.dts b/src/dtc/tests/test_tree1_merge.dts new file mode 100644 index 0000000..ded08d8 --- /dev/null +++ b/src/dtc/tests/test_tree1_merge.dts @@ -0,0 +1,44 @@ +/dts-v1/; +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = "wrong!"; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + ss2 { + }; + }; +}; + +/ { + prop-int = <0xdeadbeef>; + prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>; + subnode@1 { + prop-int = [deadbeef]; + }; + subnode@2 { + ssn0: subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_merge_labelled.dts b/src/dtc/tests/test_tree1_merge_labelled.dts new file mode 100644 index 0000000..29953b0 --- /dev/null +++ b/src/dtc/tests/test_tree1_merge_labelled.dts @@ -0,0 +1,42 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + ssn0: subsubnode@0 { + phandle = <0x2001>; + prop-int = <0xbad>; + }; + + ss2 { + }; + }; +}; + +&ssn0 { + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; +}; diff --git a/src/dtc/tests/test_tree1_merge_path.dts b/src/dtc/tests/test_tree1_merge_path.dts new file mode 100644 index 0000000..168d066 --- /dev/null +++ b/src/dtc/tests/test_tree1_merge_path.dts @@ -0,0 +1,42 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + ssn0: subsubnode@0 { + phandle = <0x2001>; + prop-int = <0xbad>; + }; + + ss2 { + }; + }; +}; + +&{/subnode@2/subsubnode@0} { + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; +}; diff --git a/src/dtc/tests/test_tree1_wrong1.dts b/src/dtc/tests/test_tree1_wrong1.dts new file mode 100644 index 0000000..d71820a --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong1.dts @@ -0,0 +1,36 @@ +/dts-v1/; + +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong2.dts b/src/dtc/tests/test_tree1_wrong2.dts new file mode 100644 index 0000000..ac27023 --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong2.dts @@ -0,0 +1,36 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong3.dts b/src/dtc/tests/test_tree1_wrong3.dts new file mode 100644 index 0000000..80be2fa --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong3.dts @@ -0,0 +1,36 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong4.dts b/src/dtc/tests/test_tree1_wrong4.dts new file mode 100644 index 0000000..09bb13b --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong4.dts @@ -0,0 +1,34 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong5.dts b/src/dtc/tests/test_tree1_wrong5.dts new file mode 100644 index 0000000..ef4c4f7 --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong5.dts @@ -0,0 +1,37 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbefe>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong6.dts b/src/dtc/tests/test_tree1_wrong6.dts new file mode 100644 index 0000000..98d6eda --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong6.dts @@ -0,0 +1,38 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + extra-prop; + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong7.dts b/src/dtc/tests/test_tree1_wrong7.dts new file mode 100644 index 0000000..f57ace0 --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong7.dts @@ -0,0 +1,39 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + extranode { + }; + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong8.dts b/src/dtc/tests/test_tree1_wrong8.dts new file mode 100644 index 0000000..811ce6c --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong8.dts @@ -0,0 +1,37 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010001; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/test_tree1_wrong9.dts b/src/dtc/tests/test_tree1_wrong9.dts new file mode 100644 index 0000000..6ff6fab --- /dev/null +++ b/src/dtc/tests/test_tree1_wrong9.dts @@ -0,0 +1,38 @@ +/dts-v1/; + +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; +/memreserve/ 0 1; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-str = "hello world"; + + subnode@1 { + compatible = "subnode1"; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + linux,phandle = <0x2000>; + prop-int = <123456789>; + + subsubnode@0 { + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/src/dtc/tests/testdata.h b/src/dtc/tests/testdata.h new file mode 100644 index 0000000..ce715e4 --- /dev/null +++ b/src/dtc/tests/testdata.h @@ -0,0 +1,36 @@ +#ifdef __ASSEMBLY__ +#define ASM_CONST_LL(x) (x) +#else +#define ASM_CONST_LL(x) (x##ULL) +#endif + +#define TEST_ADDR_1 ASM_CONST_LL(0xdeadbeef00000000) +#define TEST_SIZE_1 ASM_CONST_LL(0x100000) +#define TEST_ADDR_2 ASM_CONST_LL(123456789) +#define TEST_SIZE_2 ASM_CONST_LL(010000) + +#define TEST_VALUE_1 0xdeadbeef +#define TEST_VALUE_2 123456789 + +#define TEST_VALUE64_1 ASM_CONST_LL(0xdeadbeef01abcdef) + +#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_CHAR1 '\r' +#define TEST_CHAR2 'b' +#define TEST_CHAR3 '\0' +#define TEST_CHAR4 '\'' +#define TEST_CHAR5 '\xff' + +#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; +#endif /* ! __ASSEMBLY */ diff --git a/src/dtc/tests/tests.h b/src/dtc/tests/tests.h new file mode 100644 index 0000000..56a843c --- /dev/null +++ b/src/dtc/tests/tests.h @@ -0,0 +1,129 @@ +#ifndef _TESTS_H +#define _TESTS_H +/* + * libfdt - Flat Device Tree manipulation + * Testcase definitions + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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) \ + ({ \ + uint32_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) \ + ({ \ + uint32_t x = cpu_to_fdt32(val); \ + check_getprop(fdt, nodeoffset, name, sizeof(x), &x); \ + }) +#define check_getprop_64(fdt, nodeoffset, name, val) \ + ({ \ + uint64_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)) +int nodename_eq(const char *s1, const char *s2); +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/src/dtc/tests/tests.sh b/src/dtc/tests/tests.sh new file mode 100644 index 0000000..31530d5 --- /dev/null +++ b/src/dtc/tests/tests.sh @@ -0,0 +1,61 @@ +# 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 + +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/src/dtc/tests/testutils.c b/src/dtc/tests/testutils.c new file mode 100644 index 0000000..f185133 --- /dev/null +++ b/src/dtc/tests/testutils.c @@ -0,0 +1,203 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase common utility functions + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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> + +#include <libfdt.h> + +#include "tests.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; + 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 retreiving \"%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_string(fdt, nameoff); + if (!propname || !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 (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 (memcmp(val, propval, len) != 0) + FAIL("Data mismatch on property \"%s\"", name); + + return propval; +} + +int nodename_eq(const char *s1, const char *s2) +{ + int len = strlen(s2); + + 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; +} + +#define CHUNKSIZE 128 + +void *load_blob(const char *filename) +{ + char *blob; + int ret = utilfdt_read_err(filename, &blob); + + if (ret) + CONFIG("Couldn't open blob from \"%s\": %s", filename, + strerror(ret)); + 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) +{ + int ret = utilfdt_write_err(filename, fdt); + + if (ret) + CONFIG("Couldn't write blob to \"%s\": %s", filename, + strerror(ret)); +} + +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/src/dtc/tests/trees.S b/src/dtc/tests/trees.S new file mode 100644 index 0000000..cae0187 --- /dev/null +++ b/src/dtc/tests/trees.S @@ -0,0 +1,201 @@ +#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 FDTQUAD(val) \ + .byte ((val) >> 56) & 0xff ; \ + .byte ((val) >> 48) & 0xff ; \ + .byte ((val) >> 40) & 0xff ; \ + .byte ((val) >> 32) & 0xff ; \ + .byte ((val) >> 24) & 0xff ; \ + .byte ((val) >> 16) & 0xff ; \ + .byte ((val) >> 8) & 0xff ; \ + .byte (val) & 0xff ; + +#define TREE_HDR(tree) \ + .balign 8 ; \ + .globl _##tree ; \ +_##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(addr, len) \ + FDTQUAD(addr) ; \ + FDTQUAD(len) ; \ + +#define EMPTY_RSVMAP(tree) \ + .balign 8 ; \ +tree##_rsvmap: ; \ + RSVMAP_ENTRY(0, 0) \ +tree##_rsvmap_end: ; + +#define PROPHDR(tree, name, len) \ + FDTLONG(FDT_PROP) ; \ + FDTLONG(len) ; \ + FDTLONG(tree##_##name - tree##_strings) ; + +#define PROP_INT(tree, name, val) \ + PROPHDR(tree, name, 4) \ + FDTLONG(val) ; + +#define PROP_INT64(tree, name, val) \ + PROPHDR(tree, name, 8) \ + FDTQUAD(val) ; + +#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_1, TEST_SIZE_1) + RSVMAP_ENTRY(TEST_ADDR_2, TEST_SIZE_2) + RSVMAP_ENTRY(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_1) + PROP_STR(test_tree1, prop_str, TEST_STRING_1) + + BEGIN_NODE("subnode@1") + PROP_STR(test_tree1, compatible, "subnode1") + PROP_INT(test_tree1, prop_int, TEST_VALUE_1) + + BEGIN_NODE("subsubnode") + PROP_STR(test_tree1, compatible, "subsubnode1\0subsubnode") + 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, linux_phandle, PHANDLE_1) + PROP_INT(test_tree1, prop_int, TEST_VALUE_2) + + BEGIN_NODE("subsubnode@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") +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: diff --git a/src/dtc/tests/truncated_property.c b/src/dtc/tests/truncated_property.c new file mode 100644 index 0000000..f820d99 --- /dev/null +++ b/src/dtc/tests/truncated_property.c @@ -0,0 +1,47 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for misbehaviour on a truncated property + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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); + + 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/src/dtc/tests/utilfdt_test.c b/src/dtc/tests/utilfdt_test.c new file mode 100644 index 0000000..274c3d6 --- /dev/null +++ b/src/dtc/tests/utilfdt_test.c @@ -0,0 +1,127 @@ +/* + * Copyright 2011 The Chromium Authors, All Rights Reserved. + * + * utilfdt_test - Tests for utilfdt library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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/src/dtc/tests/value-labels.c b/src/dtc/tests/value-labels.c new file mode 100644 index 0000000..dcf2059 --- /dev/null +++ b/src/dtc/tests/value-labels.c @@ -0,0 +1,127 @@ +/* + * libfdt - Flat Device Tree manipulation + * Test labels within values + * Copyright (C) 2008 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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; +}; + +struct val_label labels1[] = { + { "start1", 0 }, + { "mid1", 2 }, + { "end1", -1 }, +}; + +struct val_label labels2[] = { + { "start2", 0 }, + { "innerstart2", 0 }, + { "innermid2", 4 }, + { "innerend2", -1 }, + { "end2", -1 }, +}; + +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/src/dtc/tests/value-labels.dts b/src/dtc/tests/value-labels.dts new file mode 100644 index 0000000..490c609 --- /dev/null +++ b/src/dtc/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/src/dtc/tests/zero-phandle.dts b/src/dtc/tests/zero-phandle.dts new file mode 100644 index 0000000..7997d98 --- /dev/null +++ b/src/dtc/tests/zero-phandle.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + node { + linux,phandle = <0>; + }; +}; diff --git a/src/dtc/treesource.c b/src/dtc/treesource.c new file mode 100644 index 0000000..33eeba5 --- /dev/null +++ b/src/dtc/treesource.c @@ -0,0 +1,284 @@ +/* + * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "dtc.h" +#include "srcpos.h" + +extern FILE *yyin; +extern int yyparse(void); +extern YYLTYPE yylloc; + +struct boot_info *the_boot_info; +int treesource_error; + +struct boot_info *dt_from_source(const char *fname) +{ + the_boot_info = NULL; + treesource_error = 0; + + 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 the_boot_info; +} + +static void write_prefix(FILE *f, int level) +{ + int i; + + for (i = 0; i < level; i++) + fputc('\t', f); +} + +static int isstring(char c) +{ + return (isprint(c) + || (c == '\0') + || strchr("\a\b\t\n\v\f\r", c)); +} + +static void write_propval_string(FILE *f, struct data val) +{ + const char *str = val.val; + int i; + struct marker *m = val.markers; + + assert(str[val.len-1] == '\0'); + + while (m && (m->offset == 0)) { + if (m->type == LABEL) + fprintf(f, "%s: ", m->ref); + m = m->next; + } + fprintf(f, "\""); + + for (i = 0; i < (val.len-1); i++) { + char c = str[i]; + + 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, "\", "); + while (m && (m->offset < i)) { + if (m->type == LABEL) { + assert(m->offset == (i+1)); + fprintf(f, "%s: ", m->ref); + } + m = m->next; + } + fprintf(f, "\""); + break; + default: + if (isprint(c)) + fprintf(f, "%c", c); + else + fprintf(f, "\\x%02hhx", c); + } + } + fprintf(f, "\""); + + /* Wrap up any labels at the end of the value */ + for_each_marker_of_type(m, LABEL) { + assert (m->offset == val.len); + fprintf(f, " %s:", m->ref); + } +} + +static void write_propval_cells(FILE *f, struct data val) +{ + void *propend = val.val + val.len; + cell_t *cp = (cell_t *)val.val; + struct marker *m = val.markers; + + fprintf(f, "<"); + for (;;) { + while (m && (m->offset <= ((char *)cp - val.val))) { + if (m->type == LABEL) { + assert(m->offset == ((char *)cp - val.val)); + fprintf(f, "%s: ", m->ref); + } + m = m->next; + } + + fprintf(f, "0x%x", fdt32_to_cpu(*cp++)); + if ((void *)cp >= propend) + break; + fprintf(f, " "); + } + + /* Wrap up any labels at the end of the value */ + for_each_marker_of_type(m, LABEL) { + assert (m->offset == val.len); + fprintf(f, " %s:", m->ref); + } + fprintf(f, ">"); +} + +static void write_propval_bytes(FILE *f, struct data val) +{ + void *propend = val.val + val.len; + const char *bp = val.val; + struct marker *m = val.markers; + + fprintf(f, "["); + for (;;) { + while (m && (m->offset == (bp-val.val))) { + if (m->type == LABEL) + fprintf(f, "%s: ", m->ref); + m = m->next; + } + + fprintf(f, "%02hhx", *bp++); + if ((const void *)bp >= propend) + break; + fprintf(f, " "); + } + + /* Wrap up any labels at the end of the value */ + for_each_marker_of_type(m, LABEL) { + assert (m->offset == val.len); + fprintf(f, " %s:", m->ref); + } + fprintf(f, "]"); +} + +static void write_propval(FILE *f, 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; + + if (len == 0) { + fprintf(f, ";\n"); + return; + } + + 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++; + } + + fprintf(f, " = "); + if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul < (len-nnul)) + && (nnotstringlbl == 0)) { + write_propval_string(f, prop->val); + } else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) { + write_propval_cells(f, prop->val); + } else { + write_propval_bytes(f, prop->val); + } + + 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; + + write_prefix(f, level); + for_each_label(tree->labels, l) + fprintf(f, "%s: ", l->label); + if (tree->name && (*tree->name)) + fprintf(f, "%s {\n", tree->name); + else + 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, "};\n"); +} + + +void dt_to_source(FILE *f, struct boot_info *bi) +{ + struct reserve_info *re; + + fprintf(f, "/dts-v1/;\n\n"); + + for (re = bi->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->re.address, + (unsigned long long)re->re.size); + } + + write_tree_source_node(f, bi->dt, 0); +} + diff --git a/src/dtc/util.c b/src/dtc/util.c new file mode 100644 index 0000000..3055c16 --- /dev/null +++ b/src/dtc/util.c @@ -0,0 +1,448 @@ +/* + * 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> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#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 *dup = xmalloc(len); + + memcpy(dup, s, len); + + return dup; +} + +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; +} + +int 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(*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; + + assert(c); + 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_len(const char *filename, char **buffp, off_t *len) +{ + int fd = 0; /* assume stdin */ + char *buf = NULL; + off_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); + if (!buf) { + ret = ENOMEM; + break; + } + } + + 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; + *len = bufsize; + return ret; +} + +int utilfdt_read_err(const char *filename, char **buffp) +{ + off_t len; + return utilfdt_read_err_len(filename, buffp, &len); +} + +char *utilfdt_read_len(const char *filename, off_t *len) +{ + char *buff; + int ret = utilfdt_read_err_len(filename, &buff, len); + + if (ret) { + fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, + strerror(ret)); + return NULL; + } + /* Successful read */ + return buff; +} + +char *utilfdt_read(const char *filename) +{ + off_t len; + return utilfdt_read_len(filename, &len); +} + +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 *p = data; + 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 uint32_t *cell = (const uint32_t *)data; + + printf(" = <"); + for (i = 0; i < len; i += 4) + printf("0x%08x%s", fdt32_to_cpu(cell[i]), + i < (len - 4) ? " " : ""); + printf(">"); + } else { + printf(" = ["); + for (i = 0; i < len; i++) + printf("%02x%s", *p++, i < len - 1 ? " " : ""); + printf("]"); + } +} + +void util_version(void) +{ + printf("Version: %s\n", DTC_VERSION); + exit(0); +} + +void 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/src/dtc/util.h b/src/dtc/util.h new file mode 100644 index 0000000..8f40b44 --- /dev/null +++ b/src/dtc/util.h @@ -0,0 +1,250 @@ +#ifndef _UTIL_H +#define _UTIL_H + +#include <stdarg.h> +#include <getopt.h> + +/* + * Copyright 2011 The Chromium Authors, All Rights Reserved. + * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static inline void __attribute__((noreturn)) die(const char *str, ...) +{ + va_list ap; + + va_start(ap, str); + fprintf(stderr, "FATAL ERROR: "); + vfprintf(stderr, str, 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=%d)\n", len); + + return new; +} + +extern char *xstrdup(const char *s); +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 + */ +int 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 + * @return Pointer to allocated buffer containing fdt, or NULL on error + */ +char *utilfdt_read(const char *filename); + +/** + * Like utilfdt_read(), but also passes back the size of the file read. + * + * @param len If non-NULL, the amount of data we managed to read + */ +char *utilfdt_read_len(const char *filename, off_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 + * @return 0 if ok, else an errno value representing the error + */ +int utilfdt_read_err(const char *filename, char **buffp); + +/** + * Like utilfdt_read_err(), but also passes back the size of the file read. + * + * @param len If non-NULL, the amount of data we managed to read + */ +int utilfdt_read_err_len(const char *filename, char **buffp, off_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 Poiner 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 Poiner 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 util_version(void) __attribute__((noreturn)); + +/** + * 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 util_usage(const char *errmsg, const char *synopsis, + const char *short_opts, struct option const long_opts[], + const char * const opts_help[]) __attribute__((noreturn)); + +/** + * 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 */ |