Initial import of the Device Tree Compiler (DTC) package v1.2.0.
Obtained from: git://git.jdl.com/software/dtc.git
This commit is contained in:
commit
cb591ed209
43
Documentation/dtc-paper.bib
Normal file
43
Documentation/dtc-paper.bib
Normal file
@ -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}",
|
||||
}
|
597
Documentation/dtc-paper.tex
Normal file
597
Documentation/dtc-paper.tex
Normal file
@ -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}
|
110
Documentation/dts-format.txt
Normal file
110
Documentation/dts-format.txt
Normal file
@ -0,0 +1,110 @@
|
||||
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 32-bit integer cells, as
|
||||
NUL-terminated strings, as bytestrings or a combination of these.
|
||||
|
||||
* Arrays of cells are represented by angle brackets surrounding a
|
||||
space separated list of C-style integers
|
||||
|
||||
e.g. interrupts = <17 0xc>;
|
||||
|
||||
* A 64-bit value is represented with two 32-bit cells.
|
||||
|
||||
e.g. clock-frequency = <0x00000001 0x00000000>;
|
||||
|
||||
* 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 a cell 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} >;
|
||||
|
||||
* Outside a cell 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 cells of a cell 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>
|
618
Documentation/manual.txt
Normal file
618
Documentation/manual.txt
Normal file
@ -0,0 +1,618 @@
|
||||
Device Tree Compiler Manual
|
||||
===========================
|
||||
|
||||
I - "dtc", the device tree compiler
|
||||
1) Obtaining Sources
|
||||
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
|
||||
|
||||
|
||||
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://www.jdl.com/git_repos/
|
||||
|
||||
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-1.0.0.tgz
|
||||
http://www.jdl.com/software/dtc-latest.tgz
|
||||
|
||||
|
||||
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.
|
||||
|
||||
-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.
|
340
GPL
Normal file
340
GPL
Normal file
@ -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.
|
221
Makefile
Normal file
221
Makefile
Normal file
@ -0,0 +1,221 @@
|
||||
#
|
||||
# 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 = 2
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION =
|
||||
LOCAL_VERSION =
|
||||
CONFIG_LOCALVERSION =
|
||||
|
||||
CPPFLAGS = -I libfdt
|
||||
CFLAGS = -Wall -g -Os -Wpointer-arith -Wcast-qual
|
||||
|
||||
BISON = bison
|
||||
LEX = flex
|
||||
|
||||
INSTALL = /usr/bin/install
|
||||
DESTDIR =
|
||||
PREFIX = $(HOME)
|
||||
BINDIR = $(PREFIX)/bin
|
||||
LIBDIR = $(PREFIX)/lib
|
||||
INCLUDEDIR = $(PREFIX)/include
|
||||
|
||||
#
|
||||
# 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
|
||||
|
||||
all: dtc ftdump convert-dtsv0 libfdt
|
||||
|
||||
install: all
|
||||
@$(VECHO) INSTALL
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 dtc $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL) -m 644 $(LIBFDT_lib) $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)
|
||||
$(INSTALL) -m 644 $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR)
|
||||
|
||||
#
|
||||
# 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
|
||||
|
||||
$(VERSION_FILE): Makefile FORCE
|
||||
$(call filechk,version)
|
||||
|
||||
#
|
||||
# Rules for dtc proper
|
||||
#
|
||||
include Makefile.dtc
|
||||
|
||||
BIN += dtc
|
||||
|
||||
# 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)
|
||||
|
||||
dtc: $(DTC_OBJS)
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include $(DTC_OBJS:%.o=%.d)
|
||||
endif
|
||||
#
|
||||
# Rules for ftdump & convert-dtsv0
|
||||
#
|
||||
BIN += ftdump convert-dtsv0
|
||||
|
||||
ftdump: ftdump.o
|
||||
|
||||
convert-dtsv0: convert-dtsv0-lexer.lex.o srcpos.o
|
||||
@$(VECHO) LD $@
|
||||
$(LINK.c) -o $@ $^
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include ftdump.d
|
||||
endif
|
||||
#
|
||||
# Rules for libfdt
|
||||
#
|
||||
LIBFDT_objdir = libfdt
|
||||
LIBFDT_srcdir = libfdt
|
||||
LIBFDT_lib = $(LIBFDT_objdir)/libfdt.a
|
||||
LIBFDT_include = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_INCLUDES))
|
||||
|
||||
include $(LIBFDT_srcdir)/Makefile.libfdt
|
||||
|
||||
.PHONY: libfdt
|
||||
libfdt: $(LIBFDT_lib)
|
||||
|
||||
$(LIBFDT_lib): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS))
|
||||
|
||||
libfdt_clean:
|
||||
@$(VECHO) CLEAN "(libfdt)"
|
||||
rm -f $(addprefix $(LIBFDT_objdir)/,$(STD_CLEANFILES))
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include $(LIBFDT_OBJS:%.o=$(LIBFDT_objdir)/%.d)
|
||||
endif
|
||||
|
||||
#
|
||||
# Testsuite rules
|
||||
#
|
||||
TESTS_PREFIX=tests/
|
||||
include tests/Makefile.tests
|
||||
|
||||
#
|
||||
# Clean rules
|
||||
#
|
||||
STD_CLEANFILES = *~ *.o *.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) $@ $^
|
||||
|
||||
%.lex.c: %.l
|
||||
@$(VECHO) LEX $@
|
||||
$(LEX) -o$@ $<
|
||||
|
||||
%.tab.c %.tab.h %.output: %.y
|
||||
@$(VECHO) BISON $@
|
||||
$(BISON) -d $<
|
||||
|
||||
FORCE:
|
9
Makefile.dtc
Normal file
9
Makefile.dtc
Normal file
@ -0,0 +1,9 @@
|
||||
# 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 = dtc.c flattree.c fstree.c data.c livetree.c treesource.c srcpos.c \
|
||||
checks.c
|
||||
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
||||
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
56
README.license
Normal file
56
README.license
Normal file
@ -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
|
8
TODO
Normal file
8
TODO
Normal file
@ -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
|
587
checks.c
Normal file
587
checks.c
Normal file
@ -0,0 +1,587 @@
|
||||
/*
|
||||
* (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 checklevel {
|
||||
IGNORE = 0,
|
||||
WARN = 1,
|
||||
ERROR = 2,
|
||||
};
|
||||
|
||||
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;
|
||||
enum checklevel level;
|
||||
enum checkstatus status;
|
||||
int inprogress;
|
||||
int num_prereqs;
|
||||
struct check **prereq;
|
||||
};
|
||||
|
||||
#define CHECK(nm, tfn, nfn, pfn, d, lvl, ...) \
|
||||
static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \
|
||||
static struct check nm = { \
|
||||
.name = #nm, \
|
||||
.tree_fn = (tfn), \
|
||||
.node_fn = (nfn), \
|
||||
.prop_fn = (pfn), \
|
||||
.data = (d), \
|
||||
.level = (lvl), \
|
||||
.status = UNCHECKED, \
|
||||
.num_prereqs = ARRAY_SIZE(nm##_prereqs), \
|
||||
.prereq = nm##_prereqs, \
|
||||
};
|
||||
|
||||
#define TREE_CHECK(nm, d, lvl, ...) \
|
||||
CHECK(nm, check_##nm, NULL, NULL, d, lvl, __VA_ARGS__)
|
||||
#define NODE_CHECK(nm, d, lvl, ...) \
|
||||
CHECK(nm, NULL, check_##nm, NULL, d, lvl, __VA_ARGS__)
|
||||
#define PROP_CHECK(nm, d, lvl, ...) \
|
||||
CHECK(nm, NULL, NULL, check_##nm, d, lvl, __VA_ARGS__)
|
||||
#define BATCH_CHECK(nm, lvl, ...) \
|
||||
CHECK(nm, NULL, NULL, NULL, NULL, lvl, __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->level < WARN) || (c->level <= quiet))
|
||||
return; /* Suppress message */
|
||||
|
||||
fprintf(stderr, "%s (%s): ",
|
||||
(c->level == 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->level == ERROR))
|
||||
error = 1;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility check functions
|
||||
*/
|
||||
|
||||
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 CHECK_IS_STRING(nm, propname, lvl) \
|
||||
CHECK(nm, NULL, check_is_string, NULL, (propname), (lvl))
|
||||
|
||||
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 CHECK_IS_CELL(nm, propname, lvl) \
|
||||
CHECK(nm, NULL, check_is_cell, NULL, (propname), (lvl))
|
||||
|
||||
/*
|
||||
* 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_CHECK(duplicate_node_names, NULL, ERROR);
|
||||
|
||||
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 (streq(prop->name, prop2->name))
|
||||
FAIL(c, "Duplicate property name %s in %s",
|
||||
prop->name, node->fullpath);
|
||||
}
|
||||
NODE_CHECK(duplicate_property_names, NULL, ERROR);
|
||||
|
||||
#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_CHECK(node_name_chars, PROPNODECHARS "@", ERROR);
|
||||
|
||||
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_CHECK(node_name_format, NULL, ERROR, &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_CHECK(property_name_chars, PROPNODECHARS, ERROR);
|
||||
|
||||
static void check_explicit_phandles(struct check *c, struct node *root,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
struct node *other;
|
||||
cell_t phandle;
|
||||
|
||||
prop = get_property(node, "linux,phandle");
|
||||
if (! prop)
|
||||
return; /* No phandle, that's fine */
|
||||
|
||||
if (prop->val.len != sizeof(cell_t)) {
|
||||
FAIL(c, "%s has bad length (%d) linux,phandle property",
|
||||
node->fullpath, prop->val.len);
|
||||
return;
|
||||
}
|
||||
|
||||
phandle = propval_cell(prop);
|
||||
if ((phandle == 0) || (phandle == -1)) {
|
||||
FAIL(c, "%s has invalid linux,phandle value 0x%x",
|
||||
node->fullpath, phandle);
|
||||
return;
|
||||
}
|
||||
|
||||
other = get_node_by_phandle(root, phandle);
|
||||
if (other) {
|
||||
FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)",
|
||||
node->fullpath, phandle, other->fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
node->phandle = phandle;
|
||||
}
|
||||
NODE_CHECK(explicit_phandles, NULL, ERROR);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
CHECK_IS_STRING(name_is_string, "name", ERROR);
|
||||
NODE_CHECK(name_properties, NULL, ERROR, &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);
|
||||
}
|
||||
}
|
||||
CHECK(phandle_references, NULL, NULL, fixup_phandle_references, NULL, ERROR,
|
||||
&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);
|
||||
}
|
||||
}
|
||||
CHECK(path_references, NULL, NULL, fixup_path_references, NULL, ERROR,
|
||||
&duplicate_node_names);
|
||||
|
||||
/*
|
||||
* Semantic checks
|
||||
*/
|
||||
CHECK_IS_CELL(address_cells_is_cell, "#address-cells", WARN);
|
||||
CHECK_IS_CELL(size_cells_is_cell, "#size-cells", WARN);
|
||||
CHECK_IS_CELL(interrupt_cells_is_cell, "#interrupt-cells", WARN);
|
||||
|
||||
CHECK_IS_STRING(device_type_is_string, "device_type", WARN);
|
||||
CHECK_IS_STRING(model_is_string, "model", WARN);
|
||||
CHECK_IS_STRING(status_is_string, "status", WARN);
|
||||
|
||||
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);
|
||||
}
|
||||
CHECK(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL, WARN,
|
||||
&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_CHECK(reg_format, NULL, WARN, &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_CHECK(ranges_format, NULL, WARN, &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_CHECK(avoid_default_addr_size, NULL, WARN, &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_CHECK(obsolete_chosen_interrupt_controller, NULL, WARN);
|
||||
|
||||
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,
|
||||
&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,
|
||||
};
|
||||
|
||||
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->level != IGNORE)
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
257
convert-dtsv0-lexer.l
Normal file
257
convert-dtsv0-lexer.l
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* (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
|
||||
|
||||
%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"
|
||||
|
||||
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 */
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
static inline void __attribute__((noreturn)) die(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;
|
||||
}
|
||||
|
||||
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 = strdup(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);
|
||||
}
|
||||
}
|
||||
|
||||
%%
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "convert-dtsv0 <v0 dts file>...\n");
|
||||
exit(3);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
srcpos_file = dtc_open_file(fname, NULL);
|
||||
yyin = srcpos_file->file;
|
||||
|
||||
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 i;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
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);
|
||||
}
|
321
data.c
Normal file
321
data.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* (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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 != '\\') {
|
||||
q[d.len++] = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = s[i++];
|
||||
assert(c);
|
||||
switch (c) {
|
||||
case 'a':
|
||||
q[d.len++] = '\a';
|
||||
break;
|
||||
case 'b':
|
||||
q[d.len++] = '\b';
|
||||
break;
|
||||
case 't':
|
||||
q[d.len++] = '\t';
|
||||
break;
|
||||
case 'n':
|
||||
q[d.len++] = '\n';
|
||||
break;
|
||||
case 'v':
|
||||
q[d.len++] = '\v';
|
||||
break;
|
||||
case 'f':
|
||||
q[d.len++] = '\f';
|
||||
break;
|
||||
case 'r':
|
||||
q[d.len++] = '\r';
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
i--; /* need to re-read the first digit as
|
||||
* part of the octal value */
|
||||
q[d.len++] = get_oct_char(s, &i);
|
||||
break;
|
||||
case 'x':
|
||||
q[d.len++] = get_hex_char(s, &i);
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
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_cell(struct data d, cell_t word)
|
||||
{
|
||||
cell_t beword = cpu_to_fdt32(word);
|
||||
|
||||
return data_append_data(d, &beword, sizeof(beword));
|
||||
}
|
||||
|
||||
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_addr(struct data d, uint64_t addr)
|
||||
{
|
||||
uint64_t beaddr = cpu_to_fdt64(addr);
|
||||
|
||||
return data_append_data(d, &beaddr, sizeof(beaddr));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
320
dtc-lexer.l
Normal file
320
dtc-lexer.l
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* (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 yylineno
|
||||
|
||||
%x INCLUDE
|
||||
%x BYTESTRING
|
||||
%x PROPNODENAME
|
||||
%s V1
|
||||
|
||||
PROPNODECHAR [a-zA-Z0-9,._+*#?@-]
|
||||
PATHCHAR ({PROPNODECHAR}|[/])
|
||||
LABEL [a-zA-Z_][a-zA-Z0-9_]*
|
||||
STRING \"([^\\"]|\\.)*\"
|
||||
WS [[:space:]]
|
||||
COMMENT "/*"([^*]|\*+[^*/])*\*+"/"
|
||||
LINECOMMENT "//".*\n
|
||||
|
||||
%{
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
#include "dtc-parser.tab.h"
|
||||
|
||||
|
||||
/*#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; /* = 0 */
|
||||
|
||||
#define BEGIN_DEFAULT() if (dts_version == 0) { \
|
||||
DPRINT("<INITIAL>\n"); \
|
||||
BEGIN(INITIAL); \
|
||||
} else { \
|
||||
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);
|
||||
}
|
||||
|
||||
<*><<EOF>> {
|
||||
if (!pop_input_file()) {
|
||||
yyterminate();
|
||||
}
|
||||
}
|
||||
|
||||
<*>{STRING} {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("String: %s\n", yytext);
|
||||
yylval.data = data_copy_escape_string(yytext+1,
|
||||
yyleng-2);
|
||||
yylloc.first_line = yylineno;
|
||||
return DT_STRING;
|
||||
}
|
||||
|
||||
<*>"/dts-v1/" {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Keyword: /dts-v1/\n");
|
||||
dts_version = 1;
|
||||
BEGIN_DEFAULT();
|
||||
return DT_V1;
|
||||
}
|
||||
|
||||
<*>"/memreserve/" {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Keyword: /memreserve/\n");
|
||||
BEGIN_DEFAULT();
|
||||
return DT_MEMRESERVE;
|
||||
}
|
||||
|
||||
<*>{LABEL}: {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Label: %s\n", yytext);
|
||||
yylval.labelref = strdup(yytext);
|
||||
yylval.labelref[yyleng-1] = '\0';
|
||||
return DT_LABEL;
|
||||
}
|
||||
|
||||
<INITIAL>[bodh]# {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
if (*yytext == 'b')
|
||||
yylval.cbase = 2;
|
||||
else if (*yytext == 'o')
|
||||
yylval.cbase = 8;
|
||||
else if (*yytext == 'd')
|
||||
yylval.cbase = 10;
|
||||
else
|
||||
yylval.cbase = 16;
|
||||
DPRINT("Base: %d\n", yylval.cbase);
|
||||
return DT_BASE;
|
||||
}
|
||||
|
||||
<INITIAL>[0-9a-fA-F]+ {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
yylval.literal = strdup(yytext);
|
||||
DPRINT("Literal: '%s'\n", yylval.literal);
|
||||
return DT_LEGACYLITERAL;
|
||||
}
|
||||
|
||||
<V1>[0-9]+|0[xX][0-9a-fA-F]+ {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
yylval.literal = strdup(yytext);
|
||||
DPRINT("Literal: '%s'\n", yylval.literal);
|
||||
return DT_LITERAL;
|
||||
}
|
||||
|
||||
\&{LABEL} { /* label reference */
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Ref: %s\n", yytext+1);
|
||||
yylval.labelref = strdup(yytext+1);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
"&{/"{PATHCHAR}+\} { /* new-style path reference */
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
yytext[yyleng-1] = '\0';
|
||||
DPRINT("Ref: %s\n", yytext+2);
|
||||
yylval.labelref = strdup(yytext+2);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<INITIAL>"&/"{PATHCHAR}+ { /* old-style path reference */
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Ref: %s\n", yytext+1);
|
||||
yylval.labelref = strdup(yytext+1);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<BYTESTRING>[0-9a-fA-F]{2} {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
yylval.byte = strtol(yytext, NULL, 16);
|
||||
DPRINT("Byte: %02x\n", (int)yylval.byte);
|
||||
return DT_BYTE;
|
||||
}
|
||||
|
||||
<BYTESTRING>"]" {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("/BYTESTRING\n");
|
||||
BEGIN_DEFAULT();
|
||||
return ']';
|
||||
}
|
||||
|
||||
<PROPNODENAME>{PROPNODECHAR}+ {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("PropNodeName: %s\n", yytext);
|
||||
yylval.propnodename = strdup(yytext);
|
||||
BEGIN_DEFAULT();
|
||||
return DT_PROPNODENAME;
|
||||
}
|
||||
|
||||
"/incbin/" {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Binary Include\n");
|
||||
return DT_INCBIN;
|
||||
}
|
||||
|
||||
<*>{WS}+ /* eat whitespace */
|
||||
<*>{COMMENT}+ /* eat C-style comments */
|
||||
<*>{LINECOMMENT}+ /* eat C++-style comments */
|
||||
|
||||
<*>. {
|
||||
yylloc.file = srcpos_file;
|
||||
yylloc.first_line = yylineno;
|
||||
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];
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
|
||||
/*
|
||||
* Stack of nested include file contexts.
|
||||
*/
|
||||
|
||||
struct incl_file {
|
||||
struct dtc_file *file;
|
||||
YY_BUFFER_STATE yy_prev_buf;
|
||||
int yy_prev_lineno;
|
||||
struct incl_file *prev;
|
||||
};
|
||||
|
||||
static struct incl_file *incl_file_stack;
|
||||
|
||||
|
||||
/*
|
||||
* Detect infinite include recursion.
|
||||
*/
|
||||
#define MAX_INCLUDE_DEPTH (100)
|
||||
|
||||
static int incl_depth = 0;
|
||||
|
||||
|
||||
static void push_input_file(const char *filename)
|
||||
{
|
||||
struct incl_file *incl_file;
|
||||
struct dtc_file *newfile;
|
||||
struct search_path search, *searchptr = NULL;
|
||||
|
||||
assert(filename);
|
||||
|
||||
if (incl_depth++ >= MAX_INCLUDE_DEPTH)
|
||||
die("Includes nested too deeply");
|
||||
|
||||
if (srcpos_file) {
|
||||
search.dir = srcpos_file->dir;
|
||||
search.next = NULL;
|
||||
search.prev = NULL;
|
||||
searchptr = &search;
|
||||
}
|
||||
|
||||
newfile = dtc_open_file(filename, searchptr);
|
||||
|
||||
incl_file = xmalloc(sizeof(struct incl_file));
|
||||
|
||||
/*
|
||||
* Save current context.
|
||||
*/
|
||||
incl_file->yy_prev_buf = YY_CURRENT_BUFFER;
|
||||
incl_file->yy_prev_lineno = yylineno;
|
||||
incl_file->file = srcpos_file;
|
||||
incl_file->prev = incl_file_stack;
|
||||
|
||||
incl_file_stack = incl_file;
|
||||
|
||||
/*
|
||||
* Establish new context.
|
||||
*/
|
||||
srcpos_file = newfile;
|
||||
yylineno = 1;
|
||||
yyin = newfile->file;
|
||||
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
|
||||
static int pop_input_file(void)
|
||||
{
|
||||
struct incl_file *incl_file;
|
||||
|
||||
if (incl_file_stack == 0)
|
||||
return 0;
|
||||
|
||||
dtc_close_file(srcpos_file);
|
||||
|
||||
/*
|
||||
* Pop.
|
||||
*/
|
||||
--incl_depth;
|
||||
incl_file = incl_file_stack;
|
||||
incl_file_stack = incl_file->prev;
|
||||
|
||||
/*
|
||||
* Recover old context.
|
||||
*/
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
||||
yy_switch_to_buffer(incl_file->yy_prev_buf);
|
||||
yylineno = incl_file->yy_prev_lineno;
|
||||
srcpos_file = incl_file->file;
|
||||
yyin = incl_file->file ? incl_file->file->file : NULL;
|
||||
|
||||
/*
|
||||
* Free old state.
|
||||
*/
|
||||
free(incl_file);
|
||||
|
||||
return 1;
|
||||
}
|
379
dtc-parser.y
Normal file
379
dtc-parser.y
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* (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
|
||||
*/
|
||||
|
||||
%locations
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
extern int yylex(void);
|
||||
|
||||
extern struct boot_info *the_boot_info;
|
||||
extern int treesource_error;
|
||||
|
||||
static unsigned long long eval_literal(const char *s, int base, int bits);
|
||||
%}
|
||||
|
||||
%union {
|
||||
char *propnodename;
|
||||
char *literal;
|
||||
char *labelref;
|
||||
unsigned int cbase;
|
||||
uint8_t byte;
|
||||
struct data data;
|
||||
|
||||
uint64_t addr;
|
||||
cell_t cell;
|
||||
struct property *prop;
|
||||
struct property *proplist;
|
||||
struct node *node;
|
||||
struct node *nodelist;
|
||||
struct reserve_info *re;
|
||||
}
|
||||
|
||||
%token DT_V1
|
||||
%token DT_MEMRESERVE
|
||||
%token <propnodename> DT_PROPNODENAME
|
||||
%token <literal> DT_LITERAL
|
||||
%token <literal> DT_LEGACYLITERAL
|
||||
%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 <re> v0_memreserve
|
||||
%type <re> v0_memreserves
|
||||
%type <addr> addr
|
||||
%type <data> celllist
|
||||
%type <cbase> cellbase
|
||||
%type <cell> cellval
|
||||
%type <data> bytestring
|
||||
%type <prop> propdef
|
||||
%type <proplist> proplist
|
||||
|
||||
%type <node> devicetree
|
||||
%type <node> nodedef
|
||||
%type <node> subnode
|
||||
%type <nodelist> subnodes
|
||||
%type <labelref> label
|
||||
|
||||
%%
|
||||
|
||||
sourcefile:
|
||||
DT_V1 ';' memreserves devicetree
|
||||
{
|
||||
the_boot_info = build_boot_info($3, $4, 0);
|
||||
}
|
||||
| v0_memreserves devicetree
|
||||
{
|
||||
the_boot_info = build_boot_info($1, $2, 0);
|
||||
}
|
||||
;
|
||||
|
||||
memreserves:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| memreserve memreserves
|
||||
{
|
||||
$$ = chain_reserve_entry($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
memreserve:
|
||||
label DT_MEMRESERVE addr addr ';'
|
||||
{
|
||||
$$ = build_reserve_entry($3, $4, $1);
|
||||
}
|
||||
;
|
||||
|
||||
v0_memreserves:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| v0_memreserve v0_memreserves
|
||||
{
|
||||
$$ = chain_reserve_entry($1, $2);
|
||||
};
|
||||
;
|
||||
|
||||
v0_memreserve:
|
||||
memreserve
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| label DT_MEMRESERVE addr '-' addr ';'
|
||||
{
|
||||
$$ = build_reserve_entry($3, $5 - $3 + 1, $1);
|
||||
}
|
||||
;
|
||||
|
||||
addr:
|
||||
DT_LITERAL
|
||||
{
|
||||
$$ = eval_literal($1, 0, 64);
|
||||
}
|
||||
| DT_LEGACYLITERAL
|
||||
{
|
||||
$$ = eval_literal($1, 16, 64);
|
||||
}
|
||||
;
|
||||
|
||||
devicetree:
|
||||
'/' nodedef
|
||||
{
|
||||
$$ = name_node($2, "", NULL);
|
||||
}
|
||||
;
|
||||
|
||||
nodedef:
|
||||
'{' proplist subnodes '}' ';'
|
||||
{
|
||||
$$ = build_node($2, $3);
|
||||
}
|
||||
;
|
||||
|
||||
proplist:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| proplist propdef
|
||||
{
|
||||
$$ = chain_property($2, $1);
|
||||
}
|
||||
;
|
||||
|
||||
propdef:
|
||||
label DT_PROPNODENAME '=' propdata ';'
|
||||
{
|
||||
$$ = build_property($2, $4, $1);
|
||||
}
|
||||
| label DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = build_property($2, empty_data, $1);
|
||||
}
|
||||
;
|
||||
|
||||
propdata:
|
||||
propdataprefix DT_STRING
|
||||
{
|
||||
$$ = data_merge($1, $2);
|
||||
}
|
||||
| propdataprefix '<' celllist '>'
|
||||
{
|
||||
$$ = data_merge($1, $3);
|
||||
}
|
||||
| propdataprefix '[' bytestring ']'
|
||||
{
|
||||
$$ = data_merge($1, $3);
|
||||
}
|
||||
| propdataprefix DT_REF
|
||||
{
|
||||
$$ = data_add_marker($1, REF_PATH, $2);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ',' addr ',' addr ')'
|
||||
{
|
||||
struct search_path path = { srcpos_file->dir, NULL, NULL };
|
||||
struct dtc_file *file = dtc_open_file($4.val, &path);
|
||||
struct data d = empty_data;
|
||||
|
||||
if ($6 != 0)
|
||||
if (fseek(file->file, $6, SEEK_SET) != 0)
|
||||
yyerrorf("Couldn't seek to offset %llu in \"%s\": %s",
|
||||
(unsigned long long)$6,
|
||||
$4.val, strerror(errno));
|
||||
|
||||
d = data_copy_file(file->file, $8);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
dtc_close_file(file);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ')'
|
||||
{
|
||||
struct search_path path = { srcpos_file->dir, NULL, NULL };
|
||||
struct dtc_file *file = dtc_open_file($4.val, &path);
|
||||
struct data d = empty_data;
|
||||
|
||||
d = data_copy_file(file->file, -1);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
dtc_close_file(file);
|
||||
}
|
||||
| propdata DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
propdataprefix:
|
||||
/* empty */
|
||||
{
|
||||
$$ = empty_data;
|
||||
}
|
||||
| propdata ','
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| propdataprefix DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
celllist:
|
||||
/* empty */
|
||||
{
|
||||
$$ = empty_data;
|
||||
}
|
||||
| celllist cellval
|
||||
{
|
||||
$$ = data_append_cell($1, $2);
|
||||
}
|
||||
| celllist DT_REF
|
||||
{
|
||||
$$ = data_append_cell(data_add_marker($1, REF_PHANDLE,
|
||||
$2), -1);
|
||||
}
|
||||
| celllist DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
cellbase:
|
||||
/* empty */
|
||||
{
|
||||
$$ = 16;
|
||||
}
|
||||
| DT_BASE
|
||||
;
|
||||
|
||||
cellval:
|
||||
DT_LITERAL
|
||||
{
|
||||
$$ = eval_literal($1, 0, 32);
|
||||
}
|
||||
| cellbase DT_LEGACYLITERAL
|
||||
{
|
||||
$$ = eval_literal($2, $1, 32);
|
||||
}
|
||||
;
|
||||
|
||||
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
|
||||
{
|
||||
yyerror("syntax error: properties must precede subnodes");
|
||||
YYERROR;
|
||||
}
|
||||
;
|
||||
|
||||
subnode:
|
||||
label DT_PROPNODENAME nodedef
|
||||
{
|
||||
$$ = name_node($3, $2, $1);
|
||||
}
|
||||
;
|
||||
|
||||
label:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| DT_LABEL
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerrorf(char const *s, ...)
|
||||
{
|
||||
const char *fname = srcpos_file ? srcpos_file->name : "<no-file>";
|
||||
va_list va;
|
||||
va_start(va, s);
|
||||
|
||||
if (strcmp(fname, "-") == 0)
|
||||
fname = "stdin";
|
||||
|
||||
fprintf(stderr, "%s:%d ", fname, yylloc.first_line);
|
||||
vfprintf(stderr, s, va);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
treesource_error = 1;
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void yyerror (char const *s)
|
||||
{
|
||||
yyerrorf("%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)
|
||||
yyerror("bad characters in literal");
|
||||
else if ((errno == ERANGE)
|
||||
|| ((bits < 64) && (val >= (1ULL << bits))))
|
||||
yyerror("literal out of range");
|
||||
else if (errno != 0)
|
||||
yyerror("bad literal");
|
||||
return val;
|
||||
}
|
226
dtc.c
Normal file
226
dtc.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* (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"
|
||||
|
||||
#include "version_gen.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 */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noreturn)) usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tdtc [options] <input file>\n");
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
fprintf(stderr, "\t-h\n");
|
||||
fprintf(stderr, "\t\tThis help text\n");
|
||||
fprintf(stderr, "\t-q\n");
|
||||
fprintf(stderr, "\t\tQuiet: -q suppress warnings, -qq errors, -qqq all\n");
|
||||
fprintf(stderr, "\t-I <input format>\n");
|
||||
fprintf(stderr, "\t\tInput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tfs - /proc/device-tree style directory\n");
|
||||
fprintf(stderr, "\t-o <output file>\n");
|
||||
fprintf(stderr, "\t-O <output format>\n");
|
||||
fprintf(stderr, "\t\tOutput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tasm - assembler source\n");
|
||||
fprintf(stderr, "\t-V <output version>\n");
|
||||
fprintf(stderr, "\t\tBlob version to produce, defaults to %d (relevant for dtb\n\t\tand asm output only)\n", DEFAULT_FDT_VERSION);
|
||||
fprintf(stderr, "\t-R <number>\n");
|
||||
fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n");
|
||||
fprintf(stderr, "\t-S <bytes>\n");
|
||||
fprintf(stderr, "\t\tMake the blob at least <bytes> long (extra space)\n");
|
||||
fprintf(stderr, "\t-p <bytes>\n");
|
||||
fprintf(stderr, "\t\tAdd padding to the blob of <bytes> long (extra space)\n");
|
||||
fprintf(stderr, "\t-b <number>\n");
|
||||
fprintf(stderr, "\t\tSet the physical boot cpu\n");
|
||||
fprintf(stderr, "\t-f\n");
|
||||
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
|
||||
fprintf(stderr, "\t-v\n");
|
||||
fprintf(stderr, "\t\tPrint DTC version and exit\n");
|
||||
exit(3);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct boot_info *bi;
|
||||
const char *inform = "dts";
|
||||
const char *outform = "dts";
|
||||
const char *outname = "-";
|
||||
int force = 0, check = 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 = getopt(argc, argv, "hI:O:o:V:R:S:p:fcqb:v")) != 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 '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 'c':
|
||||
check = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet++;
|
||||
break;
|
||||
case 'b':
|
||||
cmdline_boot_cpuid = strtoll(optarg, NULL, 0);
|
||||
break;
|
||||
case 'v':
|
||||
printf("Version: %s\n", DTC_VERSION);
|
||||
exit(0);
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > (optind+1))
|
||||
usage();
|
||||
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");
|
||||
|
||||
fprintf(stderr, "DTC: %s->%s on file \"%s\"\n",
|
||||
inform, outform, arg);
|
||||
|
||||
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 (cmdline_boot_cpuid != -1)
|
||||
bi->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
|
||||
fill_fullpaths(bi->dt, "");
|
||||
process_checks(force, 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);
|
||||
}
|
246
dtc.h
Normal file
246
dtc.h
Normal file
@ -0,0 +1,246 @@
|
||||
#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 <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfdt_env.h>
|
||||
#include <fdt.h>
|
||||
|
||||
#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 */
|
||||
|
||||
static inline void __attribute__((noreturn)) die(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;
|
||||
}
|
||||
|
||||
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))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/* 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_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 property {
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
struct property *next;
|
||||
|
||||
char *label;
|
||||
};
|
||||
|
||||
struct node {
|
||||
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;
|
||||
|
||||
char *label;
|
||||
};
|
||||
|
||||
#define for_each_property(n, p) \
|
||||
for ((p) = (n)->proplist; (p); (p) = (p)->next)
|
||||
|
||||
#define for_each_child(n, c) \
|
||||
for ((c) = (n)->children; (c); (c) = (c)->next_sibling)
|
||||
|
||||
struct property *build_property(char *name, struct data val, char *label);
|
||||
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 *name_node(struct node *node, char *name, char *label);
|
||||
struct node *chain_node(struct node *first, struct node *list);
|
||||
|
||||
void add_property(struct node *node, struct property *prop);
|
||||
void add_child(struct node *parent, struct node *child);
|
||||
|
||||
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 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);
|
||||
|
||||
/* Boot info (tree plus memreserve information */
|
||||
|
||||
struct reserve_info {
|
||||
struct fdt_reserve_entry re;
|
||||
|
||||
struct reserve_info *next;
|
||||
|
||||
char *label;
|
||||
};
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t start, uint64_t len, char *label);
|
||||
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);
|
||||
|
||||
/* Checks */
|
||||
|
||||
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);
|
||||
|
||||
/* misc */
|
||||
|
||||
char *join_path(const char *path, const char *name);
|
||||
|
||||
#endif /* _DTC_H */
|
906
flattree.c
Normal file
906
flattree.c
Normal file
@ -0,0 +1,906 @@
|
||||
/*
|
||||
* (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 *, const char *);
|
||||
void (*endnode)(void *, const char *);
|
||||
void (*property)(void *, const char *);
|
||||
};
|
||||
|
||||
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, const char *label)
|
||||
{
|
||||
bin_emit_cell(e, FDT_BEGIN_NODE);
|
||||
}
|
||||
|
||||
static void bin_emit_endnode(void *e, const char *label)
|
||||
{
|
||||
bin_emit_cell(e, FDT_END_NODE);
|
||||
}
|
||||
|
||||
static void bin_emit_property(void *e, const char *label)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void asm_emit_cell(void *e, cell_t val)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
fprintf(f, "\t.long\t0x%x\n", val);
|
||||
}
|
||||
|
||||
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\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)) {
|
||||
fprintf(f, "\t.long\t0x%x\n",
|
||||
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, const char *label)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
if (label) {
|
||||
fprintf(f, "\t.globl\t%s\n", label);
|
||||
fprintf(f, "%s:\n", label);
|
||||
}
|
||||
fprintf(f, "\t.long\tFDT_BEGIN_NODE\n");
|
||||
}
|
||||
|
||||
static void asm_emit_endnode(void *e, const char *label)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
fprintf(f, "\t.long\tFDT_END_NODE\n");
|
||||
if (label) {
|
||||
fprintf(f, "\t.globl\t%s_end\n", label);
|
||||
fprintf(f, "%s_end:\n", label);
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_emit_property(void *e, const char *label)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
if (label) {
|
||||
fprintf(f, "\t.globl\t%s\n", label);
|
||||
fprintf(f, "%s:\n", label);
|
||||
}
|
||||
fprintf(f, "\t.long\tFDT_PROP\n");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
emit->beginnode(etarget, tree->label);
|
||||
|
||||
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->label);
|
||||
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->label);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
fwrite(blob.val, blob.len, 1, f);
|
||||
|
||||
if (ferror(f))
|
||||
die("Error writing device tree blob: %s\n", strerror(errno));
|
||||
|
||||
/*
|
||||
* 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");
|
||||
fprintf(f, "#define FDT_MAGIC 0x%x\n", FDT_MAGIC);
|
||||
fprintf(f, "#define FDT_BEGIN_NODE 0x%x\n", FDT_BEGIN_NODE);
|
||||
fprintf(f, "#define FDT_END_NODE 0x%x\n", FDT_END_NODE);
|
||||
fprintf(f, "#define FDT_PROP 0x%x\n", FDT_PROP);
|
||||
fprintf(f, "#define FDT_END 0x%x\n", FDT_END);
|
||||
fprintf(f, "\n");
|
||||
|
||||
emit_label(f, symprefix, "blob_start");
|
||||
emit_label(f, symprefix, "header");
|
||||
fprintf(f, "\t.long\tFDT_MAGIC\t\t\t\t/* magic */\n");
|
||||
fprintf(f, "\t.long\t_%s_blob_abs_end - _%s_blob_start\t/* totalsize */\n",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t.long\t_%s_struct_start - _%s_blob_start\t/* off_dt_struct */\n",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t.long\t_%s_strings_start - _%s_blob_start\t/* off_dt_strings */\n",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t.long\t_%s_reserve_map - _%s_blob_start\t/* off_dt_strings */\n",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t.long\t%d\t\t\t\t\t/* version */\n", vi->version);
|
||||
fprintf(f, "\t.long\t%d\t\t\t\t\t/* last_comp_version */\n",
|
||||
vi->last_comp_version);
|
||||
|
||||
if (vi->flags & FTF_BOOTCPUID)
|
||||
fprintf(f, "\t.long\t%i\t\t\t\t\t/* boot_cpuid_phys */\n",
|
||||
bi->boot_cpuid_phys);
|
||||
|
||||
if (vi->flags & FTF_STRTABSIZE)
|
||||
fprintf(f, "\t.long\t_%s_strings_end - _%s_strings_start\t/* size_dt_strings */\n",
|
||||
symprefix, symprefix);
|
||||
|
||||
if (vi->flags & FTF_STRUCTSIZE)
|
||||
fprintf(f, "\t.long\t_%s_struct_end - _%s_struct_start\t/* size_dt_struct */\n",
|
||||
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) {
|
||||
if (re->label) {
|
||||
fprintf(f, "\t.globl\t%s\n", re->label);
|
||||
fprintf(f, "%s:\n", re->label);
|
||||
}
|
||||
fprintf(f, "\t.long\t0x%08x, 0x%08x\n",
|
||||
(unsigned int)(re->re.address >> 32),
|
||||
(unsigned int)(re->re.address & 0xffffffff));
|
||||
fprintf(f, "\t.long\t0x%08x, 0x%08x\n",
|
||||
(unsigned int)(re->re.size >> 32),
|
||||
(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.long\tFDT_END\n");
|
||||
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 = strdup(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 strdup(inb->base + offset);
|
||||
}
|
||||
|
||||
static struct property *flat_read_property(struct inbuf *dtbuf,
|
||||
struct inbuf *strbuf, int flags)
|
||||
{
|
||||
uint32_t proplen, stroff;
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
proplen = flat_read_word(dtbuf);
|
||||
stroff = flat_read_word(dtbuf);
|
||||
|
||||
name = flat_read_stringtable(strbuf, stroff);
|
||||
|
||||
if ((flags & FTF_VARALIGN) && (proplen >= 8))
|
||||
flat_realign(dtbuf, 8);
|
||||
|
||||
val = flat_read_data(dtbuf, proplen);
|
||||
|
||||
return build_property(name, val, NULL);
|
||||
}
|
||||
|
||||
|
||||
static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
|
||||
{
|
||||
struct reserve_info *reservelist = NULL;
|
||||
struct reserve_info *new;
|
||||
const char *p;
|
||||
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.
|
||||
*/
|
||||
p = inb->ptr;
|
||||
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, NULL);
|
||||
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 strdup(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)
|
||||
{
|
||||
struct dtc_file *dtcf;
|
||||
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;
|
||||
|
||||
dtcf = dtc_open_file(fname, NULL);
|
||||
|
||||
rc = fread(&magic, sizeof(magic), 1, dtcf->file);
|
||||
if (ferror(dtcf->file))
|
||||
die("Error reading DT blob magic number: %s\n",
|
||||
strerror(errno));
|
||||
if (rc < 1) {
|
||||
if (feof(dtcf->file))
|
||||
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, dtcf->file);
|
||||
if (ferror(dtcf->file))
|
||||
die("Error reading DT blob size: %s\n", strerror(errno));
|
||||
if (rc < 1) {
|
||||
if (feof(dtcf->file))
|
||||
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(dtcf->file))
|
||||
die("EOF before reading %d bytes of DT blob\n",
|
||||
totalsize);
|
||||
|
||||
rc = fread(p, 1, sizeleft, dtcf->file);
|
||||
if (ferror(dtcf->file))
|
||||
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);
|
||||
|
||||
dtc_close_file(dtcf);
|
||||
|
||||
return build_boot_info(reservelist, tree, boot_cpuid_phys);
|
||||
}
|
92
fstree.c
Normal file
92
fstree.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* (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(strdup(de->d_name),
|
||||
data_copy_file(pfile,
|
||||
st.st_size),
|
||||
NULL);
|
||||
add_property(tree, prop);
|
||||
fclose(pfile);
|
||||
}
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
struct node *newchild;
|
||||
|
||||
newchild = read_fstree(tmpnam);
|
||||
newchild = name_node(newchild, strdup(de->d_name),
|
||||
NULL);
|
||||
add_child(tree, newchild);
|
||||
}
|
||||
|
||||
free(tmpnam);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
struct boot_info *dt_from_fs(const char *dirname)
|
||||
{
|
||||
struct node *tree;
|
||||
|
||||
tree = read_fstree(dirname);
|
||||
tree = name_node(tree, "", NULL);
|
||||
|
||||
return build_boot_info(NULL, tree, 0);
|
||||
}
|
||||
|
195
ftdump.c
Normal file
195
ftdump.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* ftdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt_env.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, *((uint32_t *)(p-4)))
|
||||
|
||||
static int is_printable_string(const void *data, int len)
|
||||
{
|
||||
const char *s = data;
|
||||
const char *ss;
|
||||
|
||||
/* zero length is not */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
ss = s;
|
||||
while (*s && isprint(*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s != '\0' || (s + 1 - ss) < len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_data(const void *data, int len)
|
||||
{
|
||||
int i;
|
||||
const uint8_t *s;
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (is_printable_string(data, len)) {
|
||||
printf(" = \"%s\"", (const char *)data);
|
||||
} else if ((len % 4) == 0) {
|
||||
printf(" = <");
|
||||
for (i = 0; i < len; i += 4)
|
||||
printf("%08x%s", *((const uint32_t *)data + i),
|
||||
i < (len - 4) ? " " : "");
|
||||
printf(">");
|
||||
} else {
|
||||
printf(" = [");
|
||||
for (i = 0, s = data; i < len; i++)
|
||||
printf("%02x%s", s[i], i < len - 1 ? " " : "");
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_blob(void *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);
|
||||
char *p_struct = (char *)blob + off_dt;
|
||||
char *p_strings = (char *)blob + off_str;
|
||||
uint32_t version = fdt32_to_cpu(bph->version);
|
||||
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
|
||||
uint32_t tag;
|
||||
char *p;
|
||||
char *s, *t;
|
||||
int depth, sz, shift;
|
||||
int i;
|
||||
uint64_t addr, size;
|
||||
|
||||
depth = 0;
|
||||
shift = 4;
|
||||
|
||||
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) {
|
||||
|
||||
/* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */
|
||||
|
||||
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);
|
||||
|
||||
printf("%*s%s", depth * shift, "", s);
|
||||
print_data(t, sz);
|
||||
printf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[16384]; /* 16k max */
|
||||
int size;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "supply input filename\n");
|
||||
return 5;
|
||||
}
|
||||
|
||||
fp = fopen(argv[1], "rb");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "unable to open %s\n", argv[1]);
|
||||
return 10;
|
||||
}
|
||||
|
||||
size = fread(buf, 1, sizeof(buf), fp);
|
||||
if (size == sizeof(buf)) { /* too large */
|
||||
fprintf(stderr, "file too large\n");
|
||||
return 10;
|
||||
}
|
||||
|
||||
dump_blob(buf);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
8
libfdt/Makefile.libfdt
Normal file
8
libfdt/Makefile.libfdt
Normal file
@ -0,0 +1,8 @@
|
||||
# Makefile.libfdt
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
LIBFDT_INCLUDES = fdt.h libfdt.h
|
||||
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c
|
||||
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
|
3
libfdt/TODO
Normal file
3
libfdt/TODO
Normal file
@ -0,0 +1,3 @@
|
||||
- Tree traversal functions
|
||||
- Graft function
|
||||
- Complete libfdt.h documenting comments
|
201
libfdt/fdt.c
Normal file
201
libfdt/fdt.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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, 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 offset, int *nextoffset)
|
||||
{
|
||||
const uint32_t *tagp, *lenp;
|
||||
uint32_t tag;
|
||||
const char *p;
|
||||
|
||||
if (offset % FDT_TAGSIZE)
|
||||
return -1;
|
||||
|
||||
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
|
||||
if (! tagp)
|
||||
return FDT_END; /* premature end */
|
||||
tag = fdt32_to_cpu(*tagp);
|
||||
offset += FDT_TAGSIZE;
|
||||
|
||||
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;
|
||||
break;
|
||||
case FDT_PROP:
|
||||
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
|
||||
if (! lenp)
|
||||
return FDT_END;
|
||||
/* skip name offset, length and value */
|
||||
offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextoffset)
|
||||
*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_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)--;
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
default:
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
}
|
||||
} while (tag != FDT_BEGIN_NODE);
|
||||
|
||||
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;
|
||||
}
|
60
libfdt/fdt.h
Normal file
60
libfdt/fdt.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef _FDT_H
|
||||
#define _FDT_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct fdt_header {
|
||||
uint32_t magic; /* magic word FDT_MAGIC */
|
||||
uint32_t totalsize; /* total size of DT block */
|
||||
uint32_t off_dt_struct; /* offset to structure */
|
||||
uint32_t off_dt_strings; /* offset to strings */
|
||||
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
uint32_t version; /* format version */
|
||||
uint32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
uint32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
uint32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
struct fdt_reserve_entry {
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct fdt_node_header {
|
||||
uint32_t tag;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct fdt_property {
|
||||
uint32_t tag;
|
||||
uint32_t len;
|
||||
uint32_t nameoff;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY */
|
||||
|
||||
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
|
||||
#define FDT_TAGSIZE sizeof(uint32_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(uint32_t))
|
||||
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
|
||||
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
|
||||
#define FDT_V16_SIZE FDT_V3_SIZE
|
||||
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
|
||||
|
||||
#endif /* _FDT_H */
|
466
libfdt/fdt_ro.c
Normal file
466
libfdt/fdt_ro.c
Normal file
@ -0,0 +1,466 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
offset = fdt_next_node(fdt, offset, &depth)) {
|
||||
if (depth < 0)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
else if ((depth == 1)
|
||||
&& _fdt_nodename_eq(fdt, offset, name, namelen))
|
||||
return offset;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (*path != '/')
|
||||
return -FDT_ERR_BADPATH;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property(const void *fdt,
|
||||
int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
uint32_t tag;
|
||||
const struct fdt_property *prop;
|
||||
int namestroff;
|
||||
int offset, nextoffset;
|
||||
int err;
|
||||
|
||||
if (((err = fdt_check_header(fdt)) != 0)
|
||||
|| ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
|
||||
goto fail;
|
||||
|
||||
nextoffset = err;
|
||||
do {
|
||||
offset = nextoffset;
|
||||
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
switch (tag) {
|
||||
case FDT_END:
|
||||
err = -FDT_ERR_TRUNCATED;
|
||||
goto fail;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
case FDT_END_NODE:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
err = -FDT_ERR_BADSTRUCTURE;
|
||||
prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
|
||||
if (! prop)
|
||||
goto fail;
|
||||
namestroff = fdt32_to_cpu(prop->nameoff);
|
||||
if (strcmp(fdt_string(fdt, namestroff), name) == 0) {
|
||||
/* Found it! */
|
||||
int len = fdt32_to_cpu(prop->len);
|
||||
prop = fdt_offset_ptr(fdt, offset,
|
||||
sizeof(*prop)+len);
|
||||
if (! prop)
|
||||
goto fail;
|
||||
|
||||
if (lenp)
|
||||
*lenp = len;
|
||||
|
||||
return prop;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -FDT_ERR_BADSTRUCTURE;
|
||||
goto fail;
|
||||
}
|
||||
} while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
|
||||
|
||||
err = -FDT_ERR_NOTFOUND;
|
||||
fail:
|
||||
if (lenp)
|
||||
*lenp = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void *fdt_getprop(const void *fdt, int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property(fdt, nodeoffset, name, lenp);
|
||||
if (! prop)
|
||||
return NULL;
|
||||
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
|
||||
{
|
||||
const uint32_t *php;
|
||||
int len;
|
||||
|
||||
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
|
||||
if (!php || (len != sizeof(*php)))
|
||||
return 0;
|
||||
|
||||
return fdt32_to_cpu(*php);
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (pdepth < depth)
|
||||
continue; /* overflowed buffer */
|
||||
|
||||
while (pdepth > depth) {
|
||||
do {
|
||||
p--;
|
||||
} while (buf[p-1] != '/');
|
||||
pdepth--;
|
||||
}
|
||||
|
||||
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 p;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if ((phandle == 0) || (phandle == -1))
|
||||
return -FDT_ERR_BADPHANDLE;
|
||||
phandle = cpu_to_fdt32(phandle);
|
||||
return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
|
||||
&phandle, sizeof(phandle));
|
||||
}
|
||||
|
||||
int _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 (_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() */
|
||||
}
|
463
libfdt/fdt_rw.c
Normal file
463
libfdt/fdt_rw.c
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* 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_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;
|
||||
uint32_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 = (uint32_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 (!_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;
|
||||
}
|
96
libfdt/fdt_strerror.c
Normal file
96
libfdt/fdt_strerror.c
Normal file
@ -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>";
|
||||
}
|
257
libfdt/fdt_sw.c
Normal file
257
libfdt/fdt_sw.c
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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, int 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, len);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint32_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;
|
||||
uint32_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, sizeof(*prop));
|
||||
int nameoff;
|
||||
|
||||
if (! prop)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
nameoff = fdt32_to_cpu(prop->nameoff);
|
||||
nameoff += fdt_size_dt_strings(fdt);
|
||||
prop->nameoff = cpu_to_fdt32(nameoff);
|
||||
}
|
||||
offset = nextoffset;
|
||||
}
|
||||
|
||||
/* Finally, adjust the header */
|
||||
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
|
||||
fdt_set_magic(fdt, FDT_MAGIC);
|
||||
return 0;
|
||||
}
|
145
libfdt/fdt_wip.c
Normal file
145
libfdt/fdt_wip.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
uint32_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 nodeoffset)
|
||||
{
|
||||
int level = 0;
|
||||
uint32_t tag;
|
||||
int offset, nextoffset;
|
||||
|
||||
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
|
||||
if (tag != FDT_BEGIN_NODE)
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
switch (tag) {
|
||||
case FDT_END:
|
||||
return offset;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
level++;
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
level--;
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
}
|
||||
} while (level >= 0);
|
||||
|
||||
return nextoffset;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
1076
libfdt/libfdt.h
Normal file
1076
libfdt/libfdt.h
Normal file
File diff suppressed because it is too large
Load Diff
23
libfdt/libfdt_env.h
Normal file
23
libfdt/libfdt_env.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef _LIBFDT_ENV_H
|
||||
#define _LIBFDT_ENV_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define _B(n) ((unsigned long long)((uint8_t *)&x)[n])
|
||||
static inline uint32_t fdt32_to_cpu(uint32_t x)
|
||||
{
|
||||
return (_B(0) << 24) | (_B(1) << 16) | (_B(2) << 8) | _B(3);
|
||||
}
|
||||
#define cpu_to_fdt32(x) fdt32_to_cpu(x)
|
||||
|
||||
static inline uint64_t fdt64_to_cpu(uint64_t x)
|
||||
{
|
||||
return (_B(0) << 56) | (_B(1) << 48) | (_B(2) << 40) | (_B(3) << 32)
|
||||
| (_B(4) << 24) | (_B(5) << 16) | (_B(6) << 8) | _B(7);
|
||||
}
|
||||
#define cpu_to_fdt64(x) fdt64_to_cpu(x)
|
||||
#undef _B
|
||||
|
||||
#endif /* _LIBFDT_ENV_H */
|
95
libfdt/libfdt_internal.h
Normal file
95
libfdt/libfdt_internal.h
Normal file
@ -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; \
|
||||
}
|
||||
|
||||
uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset);
|
||||
int _fdt_check_node_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 */
|
308
livetree.c
Normal file
308
livetree.c
Normal file
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* (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
|
||||
*/
|
||||
|
||||
struct property *build_property(char *name, struct data val, char *label)
|
||||
{
|
||||
struct property *new = xmalloc(sizeof(*new));
|
||||
|
||||
new->name = name;
|
||||
new->val = val;
|
||||
|
||||
new->next = NULL;
|
||||
|
||||
new->label = label;
|
||||
|
||||
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 *name_node(struct node *node, char *name, char * label)
|
||||
{
|
||||
assert(node->name == NULL);
|
||||
|
||||
node->name = name;
|
||||
|
||||
node->label = label;
|
||||
|
||||
return 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 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;
|
||||
}
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size,
|
||||
char *label)
|
||||
{
|
||||
struct reserve_info *new = xmalloc(sizeof(*new));
|
||||
|
||||
new->re.address = address;
|
||||
new->re.size = size;
|
||||
|
||||
new->next = NULL;
|
||||
|
||||
new->label = label;
|
||||
|
||||
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 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))
|
||||
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;
|
||||
|
||||
assert(label && (strlen(label) > 0));
|
||||
|
||||
if (tree->label && streq(tree->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)
|
||||
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;
|
||||
|
||||
assert(! get_property(node, "linux,phandle"));
|
||||
|
||||
while (get_node_by_phandle(root, phandle))
|
||||
phandle++;
|
||||
|
||||
node->phandle = phandle;
|
||||
add_property(node,
|
||||
build_property("linux,phandle",
|
||||
data_append_cell(empty_data, phandle),
|
||||
NULL));
|
||||
|
||||
return node->phandle;
|
||||
}
|
22
scripts/setlocalversion
Executable file
22
scripts/setlocalversion
Executable file
@ -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
|
116
srcpos.c
Normal file
116
srcpos.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
/*
|
||||
* Like yylineno, this is the current open file pos.
|
||||
*/
|
||||
|
||||
struct dtc_file *srcpos_file;
|
||||
|
||||
static int dtc_open_one(struct dtc_file *file,
|
||||
const char *search,
|
||||
const char *fname)
|
||||
{
|
||||
char *fullname;
|
||||
|
||||
if (search) {
|
||||
fullname = xmalloc(strlen(search) + strlen(fname) + 2);
|
||||
|
||||
strcpy(fullname, search);
|
||||
strcat(fullname, "/");
|
||||
strcat(fullname, fname);
|
||||
} else {
|
||||
fullname = strdup(fname);
|
||||
}
|
||||
|
||||
file->file = fopen(fullname, "r");
|
||||
if (!file->file) {
|
||||
free(fullname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->name = fullname;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
struct dtc_file *dtc_open_file(const char *fname,
|
||||
const struct search_path *search)
|
||||
{
|
||||
static const struct search_path default_search = { NULL, NULL, NULL };
|
||||
|
||||
struct dtc_file *file;
|
||||
const char *slash;
|
||||
|
||||
file = xmalloc(sizeof(struct dtc_file));
|
||||
|
||||
slash = strrchr(fname, '/');
|
||||
if (slash) {
|
||||
char *dir = xmalloc(slash - fname + 1);
|
||||
|
||||
memcpy(dir, fname, slash - fname);
|
||||
dir[slash - fname] = 0;
|
||||
file->dir = dir;
|
||||
} else {
|
||||
file->dir = NULL;
|
||||
}
|
||||
|
||||
if (streq(fname, "-")) {
|
||||
file->name = "stdin";
|
||||
file->file = stdin;
|
||||
return file;
|
||||
}
|
||||
|
||||
if (fname[0] == '/') {
|
||||
file->file = fopen(fname, "r");
|
||||
if (!file->file)
|
||||
goto fail;
|
||||
|
||||
file->name = strdup(fname);
|
||||
return file;
|
||||
}
|
||||
|
||||
if (!search)
|
||||
search = &default_search;
|
||||
|
||||
while (search) {
|
||||
if (dtc_open_one(file, search->dir, fname))
|
||||
return file;
|
||||
|
||||
if (errno != ENOENT)
|
||||
goto fail;
|
||||
|
||||
search = search->next;
|
||||
}
|
||||
|
||||
fail:
|
||||
die("Couldn't open \"%s\": %s\n", fname, strerror(errno));
|
||||
}
|
||||
|
||||
void dtc_close_file(struct dtc_file *file)
|
||||
{
|
||||
if (fclose(file->file))
|
||||
die("Error closing \"%s\": %s\n", file->name, strerror(errno));
|
||||
|
||||
free(file->dir);
|
||||
free(file);
|
||||
}
|
85
srcpos.h
Normal file
85
srcpos.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Augment the standard YYLTYPE with a filenum index into an
|
||||
* array of all opened filenames.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct dtc_file {
|
||||
char *dir;
|
||||
const char *name;
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
#if ! defined(YYLTYPE) && ! defined(YYLTYPE_IS_DECLARED)
|
||||
typedef struct YYLTYPE {
|
||||
int first_line;
|
||||
int first_column;
|
||||
int last_line;
|
||||
int last_column;
|
||||
struct dtc_file *file;
|
||||
} YYLTYPE;
|
||||
|
||||
#define YYLTYPE_IS_DECLARED 1
|
||||
#define YYLTYPE_IS_TRIVIAL 1
|
||||
#endif
|
||||
|
||||
/* Cater to old parser templates. */
|
||||
#ifndef YYID
|
||||
#define YYID(n) (n)
|
||||
#endif
|
||||
|
||||
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||
do \
|
||||
if (YYID (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 (YYID (0))
|
||||
|
||||
|
||||
|
||||
extern void yyerror(char const *);
|
||||
extern void yyerrorf(char const *, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
extern struct dtc_file *srcpos_file;
|
||||
|
||||
struct search_path {
|
||||
const char *dir; /* NULL for current directory */
|
||||
struct search_path *prev, *next;
|
||||
};
|
||||
|
||||
extern struct dtc_file *dtc_open_file(const char *fname,
|
||||
const struct search_path *search);
|
||||
extern void dtc_close_file(struct dtc_file *file);
|
65
tests/Makefile.tests
Normal file
65
tests/Makefile.tests
Normal file
@ -0,0 +1,65 @@
|
||||
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 \
|
||||
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 \
|
||||
string_escapes references path-references boot-cpuid incbin \
|
||||
dtbs_equal_ordered \
|
||||
add_subnode_with_nops
|
||||
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
LIBTREE_TESTS_L = truncated_property
|
||||
LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
TESTS = $(LIB_TESTS) $(LIBTREE_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 = $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
BIN += $(TESTS) $(TESTS_PREFIX)dumptrees
|
||||
|
||||
.PHONY: tests
|
||||
tests: $(TESTS) $(TESTS_TREES)
|
||||
|
||||
$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o $(LIBFDT_lib)
|
||||
|
||||
$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o $(LIBFDT_lib)
|
||||
|
||||
$(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o
|
||||
|
||||
$(TESTS_TREES): $(TESTS_PREFIX)dumptrees
|
||||
@$(VECHO) DUMPTREES
|
||||
cd $(TESTS_PREFIX); ./dumptrees >/dev/null
|
||||
|
||||
tests_clean:
|
||||
@$(VECHO) CLEAN "(tests)"
|
||||
rm -f $(STD_CLEANFILES:%=$(TESTS_PREFIX)%)
|
||||
rm -f $(TESTS_CLEANFILES)
|
||||
|
||||
check: tests dtc convert-dtsv0
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh
|
||||
|
||||
checkm: tests dtc convert-dtsv0
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh -m 2>&1 | tee vglog.$$$$
|
||||
|
||||
checkv: tests dtc convert-dtsv0
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh -v
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include $(TESTS_DEPFILES)
|
||||
endif
|
||||
|
85
tests/add_subnode_with_nops.c
Normal file
85
tests/add_subnode_with_nops.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
11
tests/bad-empty-ranges.dts
Normal file
11
tests/bad-empty-ranges.dts
Normal file
@ -0,0 +1,11 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
node {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
};
|
||||
};
|
7
tests/bad-name-property.dts
Normal file
7
tests/bad-name-property.dts
Normal file
@ -0,0 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node@0 {
|
||||
name = "badthing";
|
||||
};
|
||||
};
|
7
tests/bad-ncells.dts
Normal file
7
tests/bad-ncells.dts
Normal file
@ -0,0 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = "badthing";
|
||||
#size-cells = "badthing";
|
||||
#interrupt-cells = "badthing";
|
||||
};
|
12
tests/bad-reg-ranges.dts
Normal file
12
tests/bad-reg-ranges.dts
Normal file
@ -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>;
|
||||
};
|
||||
};
|
7
tests/bad-string-props.dts
Normal file
7
tests/bad-string-props.dts
Normal file
@ -0,0 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
device_type = <0xdeadbeef>;
|
||||
model = <0xdeadbeef>;
|
||||
status = <0xdeadbeef>;
|
||||
};
|
175
tests/base01.asm
Normal file
175
tests/base01.asm
Normal file
@ -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:
|
1
tests/base01.cmd
Normal file
1
tests/base01.cmd
Normal file
@ -0,0 +1 @@
|
||||
dtc -f -b 0 -V 16 -I dts -O asm
|
31
tests/base01.dts
Normal file
31
tests/base01.dts
Normal file
@ -0,0 +1,31 @@
|
||||
/ {
|
||||
model = "SomeModel";
|
||||
compatible = "Nothing";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
memory@0 {
|
||||
device_type = "memory";
|
||||
reg = <00000000 00000000 00000000 20000000>;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
d10 = <d# 10>; // hex: 0xa
|
||||
d23 = <d# 23>; // hex: 0x17
|
||||
b101 = <b# 101>; // hex: 0x5
|
||||
o17 = <o# 17>; // hex: 0xf
|
||||
hd00d = <h# d00d>; // hex: 0xd00d
|
||||
|
||||
// hex: 0x4d2 0x163e 0x2334 0xd80
|
||||
stuff = <d# 1234 d# 5678 d# 9012 d# 3456>;
|
||||
|
||||
|
||||
bad-d-1 = <d# abc123>; // Hrm. 0
|
||||
bad-d-2 = <d# 123456789012345>;
|
||||
bad-o-1 = <o# 891>;
|
||||
bad-o-2 = <o# 123456123456>;
|
||||
};
|
||||
|
||||
};
|
6
tests/base01.stderr
Normal file
6
tests/base01.stderr
Normal file
@ -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
|
48
tests/boot-cpuid.c
Normal file
48
tests/boot-cpuid.c
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
16
tests/comments-cmp.dts
Normal file
16
tests/comments-cmp.dts
Normal file
@ -0,0 +1,16 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
prop1;
|
||||
prop2;
|
||||
prop3;
|
||||
prop4;
|
||||
prop5;
|
||||
prop6;
|
||||
prop7;
|
||||
prop8;
|
||||
prop9;
|
||||
prop10;
|
||||
child {
|
||||
};
|
||||
};
|
39
tests/comments.dts
Normal file
39
tests/comments.dts
Normal file
@ -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 */
|
7
tests/default-addr-size.dts
Normal file
7
tests/default-addr-size.dts
Normal file
@ -0,0 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node {
|
||||
reg = <0 0 0>;
|
||||
};
|
||||
};
|
124
tests/del_node.c
Normal file
124
tests/del_node.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
91
tests/del_property.c
Normal file
91
tests/del_property.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
149
tests/dtbs_equal_ordered.c
Normal file
149
tests/dtbs_equal_ordered.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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))
|
||||
FAIL("Trees have different number of reserve entries");
|
||||
for (i = 0; i < fdt_num_mem_rsv(fdt1); i++) {
|
||||
err = fdt_get_mem_rsv(fdt1, i, &addr1, &size1);
|
||||
if (err)
|
||||
FAIL("fdt_get_mem_rsv(fdt1, %d, ...): %s", i,
|
||||
fdt_strerror(err));
|
||||
err = fdt_get_mem_rsv(fdt2, i, &addr2, &size2);
|
||||
if (err)
|
||||
FAIL("fdt_get_mem_rsv(fdt2, %d, ...): %s", i,
|
||||
fdt_strerror(err));
|
||||
if ((addr1 != addr2) || (size1 != size2))
|
||||
FAIL("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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
FAIL("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))
|
||||
FAIL("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))
|
||||
FAIL("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)
|
||||
FAIL("Property length mismatch %u != %u "
|
||||
"at (%d, %d)", len1, len2, offset1, offset2);
|
||||
|
||||
if (memcmp(prop1->data, prop2->data, len1) != 0)
|
||||
FAIL("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)
|
||||
CONFIG("Usage: %s <dtb file> <dtb file>", argv[0]);
|
||||
fdt1 = load_blob(argv[1]);
|
||||
fdt2 = load_blob(argv[2]);
|
||||
|
||||
compare_mem_rsv(fdt1, fdt2);
|
||||
compare_structure(fdt1, fdt2);
|
||||
|
||||
cpuid1 = fdt_boot_cpuid_phys(fdt1);
|
||||
cpuid2 = fdt_boot_cpuid_phys(fdt2);
|
||||
if (cpuid1 != cpuid2)
|
||||
FAIL("boot_cpuid_phys mismatch 0x%x != 0x%x",
|
||||
cpuid1, cpuid2);
|
||||
|
||||
PASS();
|
||||
}
|
33
tests/dtc-checkfails.sh
Executable file
33
tests/dtc-checkfails.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#! /bin/sh
|
||||
|
||||
. ./tests.sh
|
||||
|
||||
for x; do
|
||||
shift
|
||||
if [ "$x" = "--" ]; then
|
||||
break;
|
||||
fi
|
||||
CHECKS="$CHECKS $x"
|
||||
done
|
||||
|
||||
LOG="tmp.log.$$"
|
||||
|
||||
rm -f $TMPFILE $LOG
|
||||
|
||||
verbose_run_log "$LOG" $VALGRIND "$DTC" -o /dev/null "$@"
|
||||
ret="$?"
|
||||
|
||||
if [ "$ret" -gt 127 ]; then
|
||||
signame=$(kill -l $[ret - 128])
|
||||
FAIL "Killed by SIG$signame"
|
||||
fi
|
||||
|
||||
for c in $CHECKS; do
|
||||
if ! grep -E "^(ERROR)|(Warning) \($c\):" $LOG > /dev/null; then
|
||||
FAIL "Failed to trigger check \"$c\""
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f $LOG
|
||||
|
||||
PASS
|
14
tests/dtc-fatal.sh
Executable file
14
tests/dtc-fatal.sh
Executable file
@ -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
|
71
tests/dumptrees.c
Normal file
71
tests/dumptrees.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.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);
|
||||
}
|
8
tests/dup-nodename.dts
Normal file
8
tests/dup-nodename.dts
Normal file
@ -0,0 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node {
|
||||
};
|
||||
node {
|
||||
};
|
||||
};
|
10
tests/dup-phandle.dts
Normal file
10
tests/dup-phandle.dts
Normal file
@ -0,0 +1,10 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node1 {
|
||||
linux,phandle = <1>;
|
||||
};
|
||||
node2 {
|
||||
linux,phandle = <1>;
|
||||
};
|
||||
};
|
6
tests/dup-propname.dts
Normal file
6
tests/dup-propname.dts
Normal file
@ -0,0 +1,6 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
prop;
|
||||
prop;
|
||||
};
|
2
tests/empty.dts
Normal file
2
tests/empty.dts
Normal file
@ -0,0 +1,2 @@
|
||||
/ {
|
||||
};
|
5
tests/escapes.dts
Normal file
5
tests/escapes.dts
Normal file
@ -0,0 +1,5 @@
|
||||
/ {
|
||||
compatible = "test_string_escapes";
|
||||
escape-str = "nastystring: \a\b\t\n\v\f\r\\\"";
|
||||
escape-str-2 = "\xde\xad\xbe\xef";
|
||||
};
|
42
tests/find_property.c
Normal file
42
tests/find_property.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
49
tests/get_mem_rsv.c
Normal file
49
tests/get_mem_rsv.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
83
tests/get_name.c
Normal file
83
tests/get_name.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
89
tests/get_path.c
Normal file
89
tests/get_path.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
#define POISON ('\xff')
|
||||
|
||||
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);
|
||||
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 != pathlen)
|
||||
FAIL("fdt_get_path([%d bytes]) reports length %d "
|
||||
"instead of %d", buflen, len, pathlen);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
58
tests/get_phandle.c
Normal file
58
tests/get_phandle.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
43
tests/getprop.c
Normal file
43
tests/getprop.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
1
tests/incbin.bin
Normal file
1
tests/incbin.bin
Normal file
@ -0,0 +1 @@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
|
76
tests/incbin.c
Normal file
76
tests/incbin.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
#define CHUNKSIZE 1024
|
||||
|
||||
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();
|
||||
}
|
6
tests/incbin.dts
Normal file
6
tests/incbin.dts
Normal file
@ -0,0 +1,6 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
incbin = /incbin/("incbin.bin");
|
||||
incbin-partial = /incbin/("incbin.bin", 13, 17);
|
||||
};
|
1
tests/include0.dts
Normal file
1
tests/include0.dts
Normal file
@ -0,0 +1 @@
|
||||
/include/ "include1.dts"
|
23
tests/include1.dts
Normal file
23
tests/include1.dts
Normal file
@ -0,0 +1,23 @@
|
||||
/dts-v1/;
|
||||
|
||||
/include/ "include2.dts"
|
||||
/memreserve/ /include/ "include3.dts";
|
||||
|
||||
/ {
|
||||
/include/ "include4.dts"
|
||||
/include/ "include5.dts" = <0xdeadbeef>;
|
||||
prop-str = /include/ "include6.dts";
|
||||
|
||||
/include/ "include7.dts"
|
||||
|
||||
subnode@2 {
|
||||
linux,phandle = <0x2000>;
|
||||
prop-int = <123456789>;
|
||||
|
||||
/include/ "include8.dts"
|
||||
linux,phandle = <0x2001>;
|
||||
compatible = "subsubnode2", "subsubnode";
|
||||
prop-int = <0726746425>;
|
||||
};
|
||||
};
|
||||
};
|
1
tests/include2.dts
Normal file
1
tests/include2.dts
Normal file
@ -0,0 +1 @@
|
||||
/memreserve/ 0xdeadbeef00000000 0x100000;
|
1
tests/include3.dts
Normal file
1
tests/include3.dts
Normal file
@ -0,0 +1 @@
|
||||
123456789 010000
|
1
tests/include4.dts
Normal file
1
tests/include4.dts
Normal file
@ -0,0 +1 @@
|
||||
compatible = "test_tree1";
|
1
tests/include5.dts
Normal file
1
tests/include5.dts
Normal file
@ -0,0 +1 @@
|
||||
prop-int
|
1
tests/include6.dts
Normal file
1
tests/include6.dts
Normal file
@ -0,0 +1 @@
|
||||
"hello world"
|
9
tests/include7.dts
Normal file
9
tests/include7.dts
Normal file
@ -0,0 +1,9 @@
|
||||
subnode@1 {
|
||||
compatible = "subnode1";
|
||||
prop-int = [deadbeef];
|
||||
|
||||
subsubnode {
|
||||
compatible = "subsubnode1", "subsubnode";
|
||||
prop-int = <0xdeadbeef>;
|
||||
};
|
||||
};
|
1
tests/include8.dts
Normal file
1
tests/include8.dts
Normal file
@ -0,0 +1 @@
|
||||
subsubnode@0 {
|
61
tests/label01.dts
Normal file
61
tests/label01.dts
Normal file
@ -0,0 +1,61 @@
|
||||
/memreserve/ 1000000000000000 0000000002000000;
|
||||
memrsv2: /memreserve/ 2000000000000000-20ffffffffffffff;
|
||||
/memreserve/ 0-13;
|
||||
|
||||
/ {
|
||||
model = "MyBoardName";
|
||||
compatible = "MyBoardName", "MyBoardFamilyName";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
cpus {
|
||||
linux,phandle = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
PowerPC,970@0 {
|
||||
name = "PowerPC,970";
|
||||
device_type = "cpu";
|
||||
reg = <0>;
|
||||
clock-frequency = <5f5e1000>;
|
||||
timebase-frequency = <1FCA055>;
|
||||
linux,boot-cpu;
|
||||
i-cache-size = <10000>;
|
||||
d-cache-size = <8000>;
|
||||
};
|
||||
|
||||
PowerPC,970@1 {
|
||||
name = "PowerPC,970";
|
||||
device_type = "cpu";
|
||||
reg = <1>;
|
||||
clock-frequency = <5f5e1000>;
|
||||
timebase-frequency = <1FCA055>;
|
||||
i-cache-size = <10000>;
|
||||
d-cache-size = <8000>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
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 0 cell_mid: ffffffff cell_end: >;
|
||||
mixed = "abc", pre: [1234] post: , gap: < aligned: a b c>;
|
||||
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 = <00000000 00000000 00000000 20000000>;
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "root=/dev/sda2";
|
||||
linux,platform = <00000600>;
|
||||
};
|
||||
|
||||
};
|
||||
|
164
tests/mangle-layout.c
Normal file
164
tests/mangle-layout.c
Normal file
@ -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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
struct bufstate {
|
||||
char *buf;
|
||||
int size;
|
||||
};
|
||||
|
||||
void expand_buf(struct bufstate *buf, int newsize)
|
||||
{
|
||||
buf->buf = realloc(buf->buf, newsize);
|
||||
if (!buf->buf)
|
||||
CONFIG("Allocation failure");
|
||||
buf->size = newsize;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void add_block(struct bufstate *buf, int version, char block, const void *fdt)
|
||||
{
|
||||
int align, size;
|
||||
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);
|
||||
}
|
||||
|
||||
offset = ALIGN(buf->size, align);
|
||||
|
||||
expand_buf(buf, offset+size);
|
||||
|
||||
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();
|
||||
}
|
7
tests/mangle-layout.supp
Normal file
7
tests/mangle-layout.supp
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
uninitialized alignment gaps can be dumped to output
|
||||
Memcheck:Param
|
||||
write(buf)
|
||||
obj:/lib/ld-*.so
|
||||
fun:main
|
||||
}
|
7
tests/minusone-phandle.dts
Normal file
7
tests/minusone-phandle.dts
Normal file
@ -0,0 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node {
|
||||
linux,phandle = <0xffffffff>;
|
||||
};
|
||||
};
|
76
tests/move_and_save.c
Normal file
76
tests/move_and_save.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
62
tests/node_check_compatible.c
Normal file
62
tests/node_check_compatible.c
Normal file
@ -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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
86
tests/node_offset_by_compatible.c
Normal file
86
tests/node_offset_by_compatible.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
64
tests/node_offset_by_phandle.c
Normal file
64
tests/node_offset_by_phandle.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
110
tests/node_offset_by_prop_value.c
Normal file
110
tests/node_offset_by_prop_value.c
Normal file
@ -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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
8
tests/nonexist-label-ref.dts
Normal file
8
tests/nonexist-label-ref.dts
Normal file
@ -0,0 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
ref = <&label>;
|
||||
badref = <&nosuchlabel>;
|
||||
label: node {
|
||||
};
|
||||
};
|
8
tests/nonexist-node-ref.dts
Normal file
8
tests/nonexist-node-ref.dts
Normal file
@ -0,0 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
ref = < &{/node} >;
|
||||
badref = < &{/nosuchnode} >;
|
||||
label: node {
|
||||
};
|
||||
};
|
105
tests/nop_node.c
Normal file
105
tests/nop_node.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
71
tests/nop_property.c
Normal file
71
tests/nop_property.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 <fdt.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();
|
||||
}
|
106
tests/nopulate.c
Normal file
106
tests/nopulate.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
73
tests/notfound.c
Normal file
73
tests/notfound.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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[])
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
void *fdt;
|
||||
int offset;
|
||||
int subnode1_offset;
|
||||
const void *val;
|
||||
int lenerr;
|
||||
|
||||
test_init(argc, argv);
|
||||
fdt = load_blob_arg(argc, argv);
|
||||
|
||||
prop = fdt_get_property(fdt, 0, "nonexistant-property", &lenerr);
|
||||
check_error("fdt_get_property(\"nonexistant-property\")", lenerr);
|
||||
|
||||
val = 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));
|
||||
|
||||
val = 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();
|
||||
}
|
13
tests/obsolete-chosen-interrupt-controller.dts
Normal file
13
tests/obsolete-chosen-interrupt-controller.dts
Normal file
@ -0,0 +1,13 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
PIC: pic@0 {
|
||||
reg = <0x0 0x10>;
|
||||
interrupt-controller;
|
||||
};
|
||||
chosen {
|
||||
interrupt-controller = <&PIC>;
|
||||
};
|
||||
};
|
70
tests/open_pack.c
Normal file
70
tests/open_pack.c
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 <fdt.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);
|
||||
|
||||
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();
|
||||
}
|
7
tests/open_pack.supp
Normal file
7
tests/open_pack.supp
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
opened blob dumps uninitialized data
|
||||
Memcheck:Param
|
||||
write(buf)
|
||||
obj:/lib/ld-*.so
|
||||
fun:main
|
||||
}
|
91
tests/parent_offset.c
Normal file
91
tests/parent_offset.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
83
tests/path-references.c
Normal file
83
tests/path-references.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
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();
|
||||
}
|
14
tests/path-references.dts
Normal file
14
tests/path-references.dts
Normal file
@ -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;
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user