diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..d511905c16 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..078ac10bb0 --- /dev/null +++ b/Makefile @@ -0,0 +1,1354 @@ +VERSION = 2 +PATCHLEVEL = 6 +SUBLEVEL = 17 +EXTRAVERSION = .13 +NAME=Crazed Snow-Weasel + +# *DOCUMENTATION* +# To see a list of typical targets execute "make help" +# More info can be located in ./README +# Comments in this file are targeted only to the developer, do not +# expect to learn how to build the kernel reading this file. + +# Do not print "Entering directory ..." +MAKEFLAGS += --no-print-directory + +# We are using a recursive build, so we need to do a little thinking +# to get the ordering right. +# +# Most importantly: sub-Makefiles should only ever modify files in +# their own directory. If in some directory we have a dependency on +# a file in another dir (which doesn't happen often, but it's often +# unavoidable when linking the built-in.o targets which finally +# turn into vmlinux), we will call a sub make in that other dir, and +# after that we are sure that everything which is in that other dir +# is now up to date. +# +# The only cases where we need to modify files which have global +# effects are thus separated out and done before the recursive +# descending is started. They are now explicitly listed as the +# prepare rule. + +# To put more focus on warnings, be less verbose as default +# Use 'make V=1' to see the full commands + +ifdef V + ifeq ("$(origin V)", "command line") + KBUILD_VERBOSE = $(V) + endif +endif +ifndef KBUILD_VERBOSE + KBUILD_VERBOSE = 0 +endif + +# Call sparse as part of compilation of C files +# Use 'make C=1' to enable sparse checking + +ifdef C + ifeq ("$(origin C)", "command line") + KBUILD_CHECKSRC = $(C) + endif +endif +ifndef KBUILD_CHECKSRC + KBUILD_CHECKSRC = 0 +endif + +# Use make M=dir to specify directory of external module to build +# Old syntax make ... SUBDIRS=$PWD is still supported +# Setting the environment variable KBUILD_EXTMOD take precedence +ifdef SUBDIRS + KBUILD_EXTMOD ?= $(SUBDIRS) +endif +ifdef M + ifeq ("$(origin M)", "command line") + KBUILD_EXTMOD := $(M) + endif +endif + + +# kbuild supports saving output files in a separate directory. +# To locate output files in a separate directory two syntaxes are supported. +# In both cases the working directory must be the root of the kernel src. +# 1) O= +# Use "make O=dir/to/store/output/files/" +# +# 2) Set KBUILD_OUTPUT +# Set the environment variable KBUILD_OUTPUT to point to the directory +# where the output files shall be placed. +# export KBUILD_OUTPUT=dir/to/store/output/files/ +# make +# +# The O= assignment takes precedence over the KBUILD_OUTPUT environment +# variable. + + +# KBUILD_SRC is set on invocation of make in OBJ directory +# KBUILD_SRC is not intended to be used by the regular user (for now) +ifeq ($(KBUILD_SRC),) + +# OK, Make called in directory where kernel src resides +# Do we want to locate output files in a separate directory? +ifdef O + ifeq ("$(origin O)", "command line") + KBUILD_OUTPUT := $(O) + endif +endif + +# That's our default target when none is given on the command line +PHONY := _all +_all: + +ifneq ($(KBUILD_OUTPUT),) +# Invoke a second make in the output directory, passing relevant variables +# check that the output directory actually exists +saved-output := $(KBUILD_OUTPUT) +KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) +$(if $(KBUILD_OUTPUT),, \ + $(error output directory "$(saved-output)" does not exist)) + +PHONY += $(MAKECMDGOALS) + +$(filter-out _all,$(MAKECMDGOALS)) _all: + $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ + KBUILD_SRC=$(CURDIR) \ + KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@ + +# Leave processing to above invocation of make +skip-makefile := 1 +endif # ifneq ($(KBUILD_OUTPUT),) +endif # ifeq ($(KBUILD_SRC),) + +# We process the rest of the Makefile if this is the final invocation of make +ifeq ($(skip-makefile),) + +# If building an external module we do not care about the all: rule +# but instead _all depend on modules +PHONY += all +ifeq ($(KBUILD_EXTMOD),) +_all: all +else +_all: modules +endif + +srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR)) +TOPDIR := $(srctree) +# FIXME - TOPDIR is obsolete, use srctree/objtree +objtree := $(CURDIR) +src := $(srctree) +obj := $(objtree) + +VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) + +export srctree objtree VPATH TOPDIR + + +# SUBARCH tells the usermode build what the underlying arch is. That is set +# first, and if a usermode build is happening, the "ARCH=um" on the command +# line overrides the setting of ARCH below. If a native build is happening, +# then ARCH is assigned, getting whatever value it gets normally, and +# SUBARCH is subsequently ignored. + +SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/s390x/s390/ -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) + +# Cross compiling and selecting different set of gcc/bin-utils +# --------------------------------------------------------------------------- +# +# When performing cross compilation for other architectures ARCH shall be set +# to the target architecture. (See arch/* for the possibilities). +# ARCH can be set during invocation of make: +# make ARCH=ia64 +# Another way is to have ARCH set in the environment. +# The default ARCH is the host where make is executed. + +# CROSS_COMPILE specify the prefix used for all executables used +# during compilation. Only gcc and related bin-utils executables +# are prefixed with $(CROSS_COMPILE). +# CROSS_COMPILE can be set on the command line +# make CROSS_COMPILE=ia64-linux- +# Alternatively CROSS_COMPILE can be set in the environment. +# Default value for CROSS_COMPILE is not to prefix executables +# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + +ARCH ?= $(SUBARCH) +CROSS_COMPILE ?= + +# Architecture as present in compile.h +UTS_MACHINE := $(ARCH) + +# SHELL used by kbuild +CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ + else if [ -x /bin/bash ]; then echo /bin/bash; \ + else echo sh; fi ; fi) + +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +HOSTCXXFLAGS = -O2 + +# Decide whether to build built-in, modular, or both. +# Normally, just do built-in. + +KBUILD_MODULES := +KBUILD_BUILTIN := 1 + +# If we have only "make modules", don't compile built-in objects. +# When we're building modules with modversions, we need to consider +# the built-in objects during the descend as well, in order to +# make sure the checksums are uptodate before we record them. + +ifeq ($(MAKECMDGOALS),modules) + KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) +endif + +# If we have "make modules", compile modules +# in addition to whatever we do anyway. +# Just "make" or "make all" shall build modules as well + +ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) + KBUILD_MODULES := 1 +endif + +ifeq ($(MAKECMDGOALS),) + KBUILD_MODULES := 1 +endif + +export KBUILD_MODULES KBUILD_BUILTIN +export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD + +# Beautify output +# --------------------------------------------------------------------------- +# +# Normally, we echo the whole command before executing it. By making +# that echo $($(quiet)$(cmd)), we now have the possibility to set +# $(quiet) to choose other forms of output instead, e.g. +# +# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@ +# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< +# +# If $(quiet) is empty, the whole command will be printed. +# If it is set to "quiet_", only the short version will be printed. +# If it is set to "silent_", nothing wil be printed at all, since +# the variable $(silent_cmd_cc_o_c) doesn't exist. +# +# A simple variant is to prefix commands with $(Q) - that's useful +# for commands that shall be hidden in non-verbose mode. +# +# $(Q)ln $@ :< +# +# If KBUILD_VERBOSE equals 0 then the above command will be hidden. +# If KBUILD_VERBOSE equals 1 then the above command is displayed. + +ifeq ($(KBUILD_VERBOSE),1) + quiet = + Q = +else + quiet=quiet_ + Q = @ +endif + +# If the user is running make -s (silent mode), suppress echoing of +# commands + +ifneq ($(findstring s,$(MAKEFLAGS)),) + quiet=silent_ +endif + +export quiet Q KBUILD_VERBOSE + + +# Look for make include files relative to root of kernel src +MAKEFLAGS += --include-dir=$(srctree) + +# We need some generic definitions +include $(srctree)/scripts/Kbuild.include + +# For maximum performance (+ possibly random breakage, uncomment +# the following) + +#MAKEFLAGS += -rR + +# Make variables (CC, etc...) + +AS = $(CROSS_COMPILE)as +LD = $(CROSS_COMPILE)ld +CC = $(CROSS_COMPILE)gcc +CPP = $(CC) -E +AR = $(CROSS_COMPILE)ar +NM = $(CROSS_COMPILE)nm +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +AWK = awk +GENKSYMS = scripts/genksyms/genksyms +DEPMOD = /sbin/depmod +KALLSYMS = scripts/kallsyms +PERL = perl +CHECK = sparse + +CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF) +MODFLAGS = -DMODULE +CFLAGS_MODULE = $(MODFLAGS) +AFLAGS_MODULE = $(MODFLAGS) +LDFLAGS_MODULE = -r +CFLAGS_KERNEL = +AFLAGS_KERNEL = + + +# Use LINUXINCLUDE when you must reference the include/ directory. +# Needed to be compatible with the O= option +LINUXINCLUDE := -Iinclude \ + $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \ + -include include/linux/autoconf.h + +CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE) + +CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ + -fno-strict-aliasing -fno-common +AFLAGS := -D__ASSEMBLY__ + +# Read KERNELRELEASE from .kernelrelease (if it exists) +KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null) +KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) + +export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ + ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ + CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ + HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS + +export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS +export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE +export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE + +# When compiling out-of-tree modules, put MODVERDIR in the module +# tree rather than in the kernel tree. The kernel tree might +# even be read-only. +export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions + +# Files to ignore in find ... statements + +RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o +export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git + +# =========================================================================== +# Rules shared between *config targets and build targets + +# Basic helpers built in scripts/ +PHONY += scripts_basic +scripts_basic: + $(Q)$(MAKE) $(build)=scripts/basic + +# To avoid any implicit rule to kick in, define an empty command. +scripts/basic/%: scripts_basic ; + +PHONY += outputmakefile +# outputmakefile generates a Makefile in the output directory, if using a +# separate output directory. This allows convenient use of make in the +# output directory. +outputmakefile: +ifneq ($(KBUILD_SRC),) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ + $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) +endif + +# To make sure we do not include .config for any of the *config targets +# catch them early, and hand them over to scripts/kconfig/Makefile +# It is allowed to specify more targets when calling make, including +# mixing *config targets and build targets. +# For example 'make oldconfig all'. +# Detect when mixed targets is specified, and make a second invocation +# of make so .config is not included in this case either (for *config). + +no-dot-config-targets := clean mrproper distclean \ + cscope TAGS tags help %docs check% + +config-targets := 0 +mixed-targets := 0 +dot-config := 1 + +ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) + ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) + dot-config := 0 + endif +endif + +ifeq ($(KBUILD_EXTMOD),) + ifneq ($(filter config %config,$(MAKECMDGOALS)),) + config-targets := 1 + ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) + mixed-targets := 1 + endif + endif +endif + +ifeq ($(mixed-targets),1) +# =========================================================================== +# We're called with mixed targets (*config and build targets). +# Handle them one by one. + +%:: FORCE + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@ + +else +ifeq ($(config-targets),1) +# =========================================================================== +# *config targets only - make sure prerequisites are updated, and descend +# in scripts/kconfig to make the *config target + +# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed. +# KBUILD_DEFCONFIG may point out an alternative default configuration +# used for 'make defconfig' +include $(srctree)/arch/$(ARCH)/Makefile +export KBUILD_DEFCONFIG + +config %config: scripts_basic outputmakefile FORCE + $(Q)mkdir -p include/linux + $(Q)$(MAKE) $(build)=scripts/kconfig $@ + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease + +else +# =========================================================================== +# Build targets only - this includes vmlinux, arch specific targets, clean +# targets and others. In general all targets except *config targets. + +ifeq ($(KBUILD_EXTMOD),) +# Additional helpers built in scripts/ +# Carefully list dependencies so we do not try to build scripts twice +# in parrallel +PHONY += scripts +scripts: scripts_basic include/config/MARKER + $(Q)$(MAKE) $(build)=$(@) + +scripts_basic: include/linux/autoconf.h + +# Objects we will link into vmlinux / subdirs we need to visit +init-y := init/ +drivers-y := drivers/ sound/ +net-y := net/ +libs-y := lib/ +core-y := usr/ +endif # KBUILD_EXTMOD + +ifeq ($(dot-config),1) +# In this section, we need .config + +# Read in dependencies to all Kconfig* files, make sure to run +# oldconfig if changes are detected. +-include .kconfig.d + +include .config + +# If .config needs to be updated, it will be done via the dependency +# that autoconf has on .config. +# To avoid any implicit rule to kick in, define an empty command +.config .kconfig.d: ; + +# If .config is newer than include/linux/autoconf.h, someone tinkered +# with it and forgot to run make oldconfig. +# If kconfig.d is missing then we are probarly in a cleaned tree so +# we execute the config step to be sure to catch updated Kconfig files +include/linux/autoconf.h: .kconfig.d .config + $(Q)mkdir -p include/linux + $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig +else +# Dummy target needed, because used as prerequisite +include/linux/autoconf.h: ; +endif + +# The all: target is the default when no target is given on the +# command line. +# This allow a user to issue only 'make' to build a kernel including modules +# Defaults vmlinux but it is usually overriden in the arch makefile +all: vmlinux + +ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE +CFLAGS += -Os +else +CFLAGS += -O2 +endif + +ifdef CONFIG_FRAME_POINTER +CFLAGS += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,) +else +CFLAGS += -fomit-frame-pointer +endif + +ifdef CONFIG_UNWIND_INFO +CFLAGS += -fasynchronous-unwind-tables +endif + +ifdef CONFIG_DEBUG_INFO +CFLAGS += -g +endif + +include $(srctree)/arch/$(ARCH)/Makefile + +# arch Makefile may override CC so keep this after arch Makefile is included +NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) +CHECKFLAGS += $(NOSTDINC_FLAGS) + +# warn about C99 declaration after statement +CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) + +# disable pointer signedness warnings in gcc 4.0 +CFLAGS += $(call cc-option,-Wno-pointer-sign,) + +# Default kernel image to build when no specific target is given. +# KBUILD_IMAGE may be overruled on the commandline or +# set in the environment +# Also any assignments in arch/$(ARCH)/Makefile take precedence over +# this default value +export KBUILD_IMAGE ?= vmlinux + +# +# INSTALL_PATH specifies where to place the updated kernel and system map +# images. Default is /boot, but you can set it to other values +export INSTALL_PATH ?= /boot + +# +# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory +# relocations required by build roots. This is not defined in the +# makefile but the arguement can be passed to make if needed. +# + +MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) +export MODLIB + + +ifeq ($(KBUILD_EXTMOD),) +core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ + +vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ + $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ + $(net-y) $(net-m) $(libs-y) $(libs-m))) + +vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \ + $(init-n) $(init-) \ + $(core-n) $(core-) $(drivers-n) $(drivers-) \ + $(net-n) $(net-) $(libs-n) $(libs-)))) + +init-y := $(patsubst %/, %/built-in.o, $(init-y)) +core-y := $(patsubst %/, %/built-in.o, $(core-y)) +drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) +net-y := $(patsubst %/, %/built-in.o, $(net-y)) +libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) +libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) +libs-y := $(libs-y1) $(libs-y2) + +# Build vmlinux +# --------------------------------------------------------------------------- +# vmlinux is build from the objects selected by $(vmlinux-init) and +# $(vmlinux-main). Most are built-in.o files from top-level directories +# in the kernel tree, others are specified in arch/$(ARCH)Makefile. +# Ordering when linking is important, and $(vmlinux-init) must be first. +# +# vmlinux +# ^ +# | +# +-< $(vmlinux-init) +# | +--< init/version.o + more +# | +# +--< $(vmlinux-main) +# | +--< driver/built-in.o mm/built-in.o + more +# | +# +-< kallsyms.o (see description in CONFIG_KALLSYMS section) +# +# vmlinux version (uname -v) cannot be updated during normal +# descending-into-subdirs phase since we do not yet know if we need to +# update vmlinux. +# Therefore this step is delayed until just before final link of vmlinux - +# except in the kallsyms case where it is done just before adding the +# symbols to the kernel. +# +# System.map is generated to document addresses of all kernel symbols + +vmlinux-init := $(head-y) $(init-y) +vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y) +vmlinux-all := $(vmlinux-init) $(vmlinux-main) +vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds + +# Rule to link vmlinux - also used during CONFIG_KALLSYMS +# May be overridden by arch/$(ARCH)/Makefile +quiet_cmd_vmlinux__ ?= LD $@ + cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ \ + -T $(vmlinux-lds) $(vmlinux-init) \ + --start-group $(vmlinux-main) --end-group \ + $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) FORCE ,$^) + +# Generate new vmlinux version +quiet_cmd_vmlinux_version = GEN .version + cmd_vmlinux_version = set -e; \ + if [ ! -r .version ]; then \ + rm -f .version; \ + echo 1 >.version; \ + else \ + mv .version .old_version; \ + expr 0$$(cat .old_version) + 1 >.version; \ + fi; \ + $(MAKE) $(build)=init + +# Generate System.map +quiet_cmd_sysmap = SYSMAP + cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap + +# Link of vmlinux +# If CONFIG_KALLSYMS is set .version is already updated +# Generate System.map and verify that the content is consistent +# Use + in front of the vmlinux_version rule to silent warning with make -j2 +# First command is ':' to allow us to use + in front of the rule +define rule_vmlinux__ + : + $(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version)) + + $(call cmd,vmlinux__) + $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd + + $(Q)$(if $($(quiet)cmd_sysmap), \ + echo ' $($(quiet)cmd_sysmap) System.map' &&) \ + $(cmd_sysmap) $@ System.map; \ + if [ $$? -ne 0 ]; then \ + rm -f $@; \ + /bin/false; \ + fi; + $(verify_kallsyms) +endef + + +ifdef CONFIG_KALLSYMS +# Generate section listing all symbols and add it into vmlinux $(kallsyms.o) +# It's a three stage process: +# o .tmp_vmlinux1 has all symbols and sections, but __kallsyms is +# empty +# Running kallsyms on that gives us .tmp_kallsyms1.o with +# the right size - vmlinux version (uname -v) is updated during this step +# o .tmp_vmlinux2 now has a __kallsyms section of the right size, +# but due to the added section, some addresses have shifted. +# From here, we generate a correct .tmp_kallsyms2.o +# o The correct .tmp_kallsyms2.o is linked into the final vmlinux. +# o Verify that the System.map from vmlinux matches the map from +# .tmp_vmlinux2, just in case we did not generate kallsyms correctly. +# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using +# .tmp_vmlinux3 and .tmp_kallsyms3.o. This is only meant as a +# temporary bypass to allow the kernel to be built while the +# maintainers work out what went wrong with kallsyms. + +ifdef CONFIG_KALLSYMS_EXTRA_PASS +last_kallsyms := 3 +else +last_kallsyms := 2 +endif + +kallsyms.o := .tmp_kallsyms$(last_kallsyms).o + +define verify_kallsyms + $(Q)$(if $($(quiet)cmd_sysmap), \ + echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \ + $(cmd_sysmap) .tmp_vmlinux$(last_kallsyms) .tmp_System.map + $(Q)cmp -s System.map .tmp_System.map || \ + (echo Inconsistent kallsyms data; \ + echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \ + rm .tmp_kallsyms* ; /bin/false ) +endef + +# Update vmlinux version before link +# Use + in front of this rule to silent warning about make -j1 +# First command is ':' to allow us to use + in front of this rule +cmd_ksym_ld = $(cmd_vmlinux__) +define rule_ksym_ld + : + +$(call cmd,vmlinux_version) + $(call cmd,vmlinux__) + $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd +endef + +# Generate .S file with all kernel symbols +quiet_cmd_kallsyms = KSYM $@ + cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \ + $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@ + +.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE + $(call if_changed_dep,as_o_S) + +.tmp_kallsyms%.S: .tmp_vmlinux% $(KALLSYMS) + $(call cmd,kallsyms) + +# .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version +.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE + $(call if_changed_rule,ksym_ld) + +.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE + $(call if_changed,vmlinux__) + +.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE + $(call if_changed,vmlinux__) + +# Needs to visit scripts/ before $(KALLSYMS) can be used. +$(KALLSYMS): scripts ; + +# Generate some data for debugging strange kallsyms problems +debug_kallsyms: .tmp_map$(last_kallsyms) + +.tmp_map%: .tmp_vmlinux% FORCE + ($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@ + +.tmp_map3: .tmp_map2 + +.tmp_map2: .tmp_map1 + +endif # ifdef CONFIG_KALLSYMS + +# vmlinux image - including updated kernel symbols +vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE + $(call if_changed_rule,vmlinux__) + $(Q)rm -f .old_version + +# The actual objects are generated when descending, +# make sure no implicit rule kicks in +$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ; + +# Handle descending into subdirectories listed in $(vmlinux-dirs) +# Preset locale variables to speed up the build process. Limit locale +# tweaks to this spot to avoid wrong language settings when running +# make menuconfig etc. +# Error messages still appears in the original language + +PHONY += $(vmlinux-dirs) +$(vmlinux-dirs): prepare scripts + $(Q)$(MAKE) $(build)=$@ + +# Build the kernel release string +# The KERNELRELEASE is stored in a file named .kernelrelease +# to be used when executing for example make install or make modules_install +# +# Take the contents of any files called localversion* and the config +# variable CONFIG_LOCALVERSION and append them to KERNELRELEASE. +# LOCALVERSION from the command line override all of this + +nullstring := +space := $(nullstring) # end of line + +___localver = $(objtree)/localversion* $(srctree)/localversion* +__localver = $(sort $(wildcard $(___localver))) +# skip backup files (containing '~') +_localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f))) + +localver = $(subst $(space),, \ + $(shell cat /dev/null $(_localver)) \ + $(patsubst "%",%,$(CONFIG_LOCALVERSION))) + +# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called +# and if the SCM is know a tag from the SCM is appended. +# The appended tag is determinded by the SCM used. +# +# Currently, only git is supported. +# Other SCMs can edit scripts/setlocalversion and add the appropriate +# checks as needed. +ifdef CONFIG_LOCALVERSION_AUTO + _localver-auto = $(shell $(CONFIG_SHELL) \ + $(srctree)/scripts/setlocalversion $(srctree)) + localver-auto = $(LOCALVERSION)$(_localver-auto) +endif + +localver-full = $(localver)$(localver-auto) + +# Store (new) KERNELRELASE string in .kernelrelease +kernelrelease = $(KERNELVERSION)$(localver-full) +.kernelrelease: FORCE + $(Q)rm -f $@ + $(Q)echo $(kernelrelease) > $@ + + +# Things we need to do before we recursively start building the kernel +# or the modules are listed in "prepare". +# A multi level approach is used. prepareN is processed before prepareN-1. +# archprepare is used in arch Makefiles and when processed asm symlink, +# version.h and scripts_basic is processed / created. + +# Listed in dependency order +PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 + +# prepare-all is deprecated, use prepare as valid replacement +PHONY += prepare-all + +# prepare3 is used to check if we are building in a separate output directory, +# and if so do: +# 1) Check that make has not been executed in the kernel src $(srctree) +# 2) Create the include2 directory, used for the second asm symlink +prepare3: .kernelrelease +ifneq ($(KBUILD_SRC),) + @echo ' Using $(srctree) as source for kernel' + $(Q)if [ -f $(srctree)/.config ]; then \ + echo " $(srctree) is not clean, please run 'make mrproper'";\ + echo " in the '$(srctree)' directory.";\ + /bin/false; \ + fi; + $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi; + $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm +endif + +# prepare2 creates a makefile if using a separate output directory +prepare2: prepare3 outputmakefile + +prepare1: prepare2 include/linux/version.h include/asm \ + include/config/MARKER +ifneq ($(KBUILD_MODULES),) + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* +endif + +archprepare: prepare1 scripts_basic + +prepare0: archprepare FORCE + $(Q)$(MAKE) $(build)=. + +# All the preparing.. +prepare prepare-all: prepare0 + +# Leave this as default for preprocessing vmlinux.lds.S, which is now +# done in arch/$(ARCH)/kernel/Makefile + +export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH) + +# FIXME: The asm symlink changes when $(ARCH) changes. That's +# hard to detect, but I suppose "make mrproper" is a good idea +# before switching between archs anyway. + +include/asm: + @echo ' SYMLINK $@ -> include/asm-$(ARCH)' + $(Q)if [ ! -d include ]; then mkdir -p include; fi; + @ln -fsn asm-$(ARCH) $@ + +# Split autoconf.h into include/linux/config/* + +include/config/MARKER: scripts/basic/split-include include/linux/autoconf.h + @echo ' SPLIT include/linux/autoconf.h -> include/config/*' + @scripts/basic/split-include include/linux/autoconf.h include/config + @touch $@ + +# Generate some files +# --------------------------------------------------------------------------- + +# KERNELRELEASE can change from a few different places, meaning version.h +# needs to be updated, so this check is forced on all builds + +uts_len := 64 + +define filechk_version.h + if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \ + echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ + exit 1; \ + fi; \ + (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ + echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \ + echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \ + ) +endef + +include/linux/version.h: $(srctree)/Makefile .config .kernelrelease FORCE + $(call filechk,version.h) + +# --------------------------------------------------------------------------- + +PHONY += depend dep +depend dep: + @echo '*** Warning: make $@ is unnecessary now.' + +# --------------------------------------------------------------------------- +# Modules + +ifdef CONFIG_MODULES + +# By default, build modules as well + +all: modules + +# Build modules + +PHONY += modules +modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + + +# Target to prepare building external modules +PHONY += modules_prepare +modules_prepare: prepare scripts + +# Target to install modules +PHONY += modules_install +modules_install: _modinst_ _modinst_post + +PHONY += _modinst_ +_modinst_: + @if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \ + echo "Warning: you may need to install module-init-tools"; \ + echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ + sleep 1; \ + fi + @rm -rf $(MODLIB)/kernel + @rm -f $(MODLIB)/source + @mkdir -p $(MODLIB)/kernel + @ln -s $(srctree) $(MODLIB)/source + @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ + rm -f $(MODLIB)/build ; \ + ln -s $(objtree) $(MODLIB)/build ; \ + fi + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# If System.map exists, run depmod. This deliberately does not have a +# dependency on System.map since that would run the dependency tree on +# vmlinux. This depmod is only for convenience to give the initial +# boot a modules.dep even before / is mounted read-write. However the +# boot script depmod is the master version. +ifeq "$(strip $(INSTALL_MOD_PATH))" "" +depmod_opts := +else +depmod_opts := -b $(INSTALL_MOD_PATH) -r +endif +PHONY += _modinst_post +_modinst_post: _modinst_ + if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi + +else # CONFIG_MODULES + +# Modules not configured +# --------------------------------------------------------------------------- + +modules modules_install: FORCE + @echo + @echo "The present kernel configuration has modules disabled." + @echo "Type 'make config' and enable loadable module support." + @echo "Then build a kernel with module support enabled." + @echo + @exit 1 + +endif # CONFIG_MODULES + +### +# Cleaning is done on three levels. +# make clean Delete most generated files +# Leave enough to build external modules +# make mrproper Delete the current configuration, and all generated files +# make distclean Remove editor backup files, patch leftover files and the like + +# Directories & files removed with 'make clean' +CLEAN_DIRS += $(MODVERDIR) +CLEAN_FILES += vmlinux System.map \ + .tmp_kallsyms* .tmp_version .tmp_vmlinux* .tmp_System.map + +# Directories & files removed with 'make mrproper' +MRPROPER_DIRS += include/config include2 +MRPROPER_FILES += .config .config.old include/asm .version .old_version \ + include/linux/autoconf.h include/linux/version.h \ + .kernelrelease Module.symvers tags TAGS cscope* + +# clean - Delete most, but leave enough to build external modules +# +clean: rm-dirs := $(CLEAN_DIRS) +clean: rm-files := $(CLEAN_FILES) +clean-dirs := $(addprefix _clean_,$(srctree) $(vmlinux-alldirs)) + +PHONY += $(clean-dirs) clean archclean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: archclean $(clean-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find . $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +# mrproper - Delete all generated files, including .config +# +mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) +mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) +mrproper-dirs := $(addprefix _mrproper_,Documentation/DocBook scripts) + +PHONY += $(mrproper-dirs) mrproper archmrproper +$(mrproper-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@) + +mrproper: clean archmrproper $(mrproper-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + +# distclean +# +PHONY += distclean + +distclean: mrproper + @find $(srctree) $(RCS_FIND_IGNORE) \ + \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ + -o -name '.*.rej' -o -size 0 \ + -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ + -type f -print | xargs rm -f + + +# Packaging of the kernel to various formats +# --------------------------------------------------------------------------- +# rpm target kept for backward compatibility +package-dir := $(srctree)/scripts/package + +%pkg: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ +rpm: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ + + +# Brief documentation of the typical targets used +# --------------------------------------------------------------------------- + +boards := $(wildcard $(srctree)/arch/$(ARCH)/configs/*_defconfig) +boards := $(notdir $(boards)) + +help: + @echo 'Cleaning targets:' + @echo ' clean - remove most generated files but keep the config' + @echo ' mrproper - remove all generated files + config + various backup files' + @echo '' + @echo 'Configuration targets:' + @$(MAKE) -f $(srctree)/scripts/kconfig/Makefile help + @echo '' + @echo 'Other generic targets:' + @echo ' all - Build all targets marked with [*]' + @echo '* vmlinux - Build the bare kernel' + @echo '* modules - Build all modules' + @echo ' modules_install - Install all modules to INSTALL_MOD_PATH (default: /)' + @echo ' dir/ - Build all files in dir and below' + @echo ' dir/file.[ois] - Build specified target only' + @echo ' dir/file.ko - Build module including final link' + @echo ' rpm - Build a kernel as an RPM package' + @echo ' tags/TAGS - Generate tags file for editors' + @echo ' cscope - Generate cscope index' + @echo ' kernelrelease - Output the release version string' + @echo ' kernelversion - Output the version stored in Makefile' + @echo '' + @echo 'Static analysers' + @echo ' checkstack - Generate a list of stack hogs' + @echo ' namespacecheck - Name space analysis on compiled kernel' + @echo '' + @echo 'Kernel packaging:' + @$(MAKE) $(build)=$(package-dir) help + @echo '' + @echo 'Documentation targets:' + @$(MAKE) -f $(srctree)/Documentation/DocBook/Makefile dochelp + @echo '' + @echo 'Architecture specific targets ($(ARCH)):' + @$(if $(archhelp),$(archhelp),\ + echo ' No architecture specific help defined for $(ARCH)') + @echo '' + @$(if $(boards), \ + $(foreach b, $(boards), \ + printf " %-24s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \ + echo '') + + @echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build' + @echo ' make O=dir [targets] Locate all output files in "dir", including .config' + @echo ' make C=1 [targets] Check all c source with $$CHECK (sparse)' + @echo ' make C=2 [targets] Force check of all c source with $$CHECK (sparse)' + @echo '' + @echo 'Execute "make" or "make all" to build all targets marked with [*] ' + @echo 'For further info see the ./README file' + + +# Documentation targets +# --------------------------------------------------------------------------- +%docs: scripts_basic FORCE + $(Q)$(MAKE) $(build)=Documentation/DocBook $@ + +else # KBUILD_EXTMOD + +### +# External module support. +# When building external modules the kernel used as basis is considered +# read-only, and no consistency checks are made and the make +# system is not used on the basis kernel. If updates are required +# in the basis kernel ordinary make commands (without M=...) must +# be used. +# +# The following are the only valid targets when building external +# modules. +# make M=dir clean Delete all automatically generated files +# make M=dir modules Make all modules in specified dir +# make M=dir Same as 'make M=dir modules' +# make M=dir modules_install +# Install the modules build in the module directory +# Assumes install directory is already created + +# We are always building modules +KBUILD_MODULES := 1 +PHONY += crmodverdir +crmodverdir: + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* + +PHONY += $(objtree)/Module.symvers +$(objtree)/Module.symvers: + @test -e $(objtree)/Module.symvers || ( \ + echo; \ + echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \ + echo " is missing; modules will have no dependencies and modversions."; \ + echo ) + +module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD)) +PHONY += $(module-dirs) modules +$(module-dirs): crmodverdir $(objtree)/Module.symvers + $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@) + +modules: $(module-dirs) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +PHONY += modules_install +modules_install: _emodinst_ _emodinst_post + +install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) +PHONY += _emodinst_ +_emodinst_: + $(Q)mkdir -p $(MODLIB)/$(install-dir) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# Run depmod only is we have System.map and depmod is executable +quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) + cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \ + $(DEPMOD) -ae -F System.map \ + $(if $(strip $(INSTALL_MOD_PATH)), \ + -b $(INSTALL_MOD_PATH) -r) \ + $(KERNELRELEASE); \ + fi + +PHONY += _emodinst_post +_emodinst_post: _emodinst_ + $(call cmd,depmod) + +clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD)) + +PHONY += $(clean-dirs) clean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: rm-dirs := $(MODVERDIR) +clean: $(clean-dirs) + $(call cmd,rmdirs) + @find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +help: + @echo ' Building external modules.' + @echo ' Syntax: make -C path/to/kernel/src M=$$PWD target' + @echo '' + @echo ' modules - default target, build the module(s)' + @echo ' modules_install - install the module' + @echo ' clean - remove generated files in module directory only' + @echo '' + +# Dummies... +PHONY += prepare scripts +prepare: ; +scripts: ; +endif # KBUILD_EXTMOD + +# Generate tags for editors +# --------------------------------------------------------------------------- + +#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set +#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file. +#Adding $(srctree) adds about 20M on i386 to the size of the output file! + +ifeq ($(src),$(obj)) +__srctree = +else +__srctree = $(srctree)/ +endif + +ifeq ($(ALLSOURCE_ARCHS),) +ifeq ($(ARCH),um) +ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH) +else +ALLINCLUDE_ARCHS := $(ARCH) +endif +else +#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour. +ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS) +endif + +ALLSOURCE_ARCHS := $(ARCH) + +define all-sources + ( find $(__srctree) $(RCS_FIND_IGNORE) \ + \( -name include -o -name arch \) -prune -o \ + -name '*.[chS]' -print; \ + for ARCH in $(ALLSOURCE_ARCHS) ; do \ + find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + done ; \ + find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + find $(__srctree)include $(RCS_FIND_IGNORE) \ + \( -name config -o -name 'asm-*' \) -prune \ + -o -name '*.[chS]' -print; \ + for ARCH in $(ALLINCLUDE_ARCHS) ; do \ + find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + done ; \ + find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print ) +endef + +quiet_cmd_cscope-file = FILELST cscope.files + cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files + +quiet_cmd_cscope = MAKE cscope.out + cmd_cscope = cscope -b + +cscope: FORCE + $(call cmd,cscope-file) + $(call cmd,cscope) + +quiet_cmd_TAGS = MAKE $@ +define cmd_TAGS + rm -f $@; \ + ETAGSF=`etags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs etags $$ETAGSF -a +endef + +TAGS: FORCE + $(call cmd,TAGS) + + +quiet_cmd_tags = MAKE $@ +define cmd_tags + rm -f $@; \ + CTAGSF=`ctags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs ctags $$CTAGSF -a +endef + +tags: FORCE + $(call cmd,tags) + + +# Scripts to check various things for consistency +# --------------------------------------------------------------------------- + +includecheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkincludes.pl + +versioncheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkversion.pl + +namespacecheck: + $(PERL) $(srctree)/scripts/namespace.pl + +endif #ifeq ($(config-targets),1) +endif #ifeq ($(mixed-targets),1) + +PHONY += checkstack +checkstack: + $(OBJDUMP) -d vmlinux $$(find . -name '*.ko') | \ + $(PERL) $(src)/scripts/checkstack.pl $(ARCH) + +kernelrelease: + $(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \ + $(error kernelrelease not valid - run 'make *config' to update it)) +kernelversion: + @echo $(KERNELVERSION) + +# Single targets +# --------------------------------------------------------------------------- +# Single targets are compatible with: +# - build whith mixed source and output +# - build with separate output dir 'make O=...' +# - external modules +# +# target-dir => where to store outputfile +# build-dir => directory in kernel source tree to use + +ifeq ($(KBUILD_EXTMOD),) + build-dir = $(patsubst %/,%,$(dir $@)) + target-dir = $(dir $@) +else + zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@))) + build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) + target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@)) +endif + +%.s: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.i: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.lst: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.s: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) + +# Modules +/ %/: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) +%.ko: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) $(@:.ko=.o) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +# FIXME Should go into a make.lib or something +# =========================================================================== + +quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) + cmd_rmdirs = rm -rf $(rm-dirs) + +quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) + cmd_rmfiles = rm -f $(rm-files) + + +a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ + $(NOSTDINC_FLAGS) $(CPPFLAGS) \ + $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) + +quiet_cmd_as_o_S = AS $@ +cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< + +# read all saved command lines + +targets := $(wildcard $(sort $(targets))) +cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + $(cmd_files): ; # Do not try to update included dependency files + include $(cmd_files) +endif + +# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir +# Usage: +# $(Q)$(MAKE) $(clean)=dir +clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj + +endif # skip-makefile + +PHONY += FORCE +FORCE: + + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. +.PHONY: $(PHONY) diff --git a/util/dtc/.gitignore b/util/dtc/.gitignore new file mode 100644 index 0000000000..85ccca234f --- /dev/null +++ b/util/dtc/.gitignore @@ -0,0 +1,6 @@ +*.o +*.d +*.tab.[ch] +lex.yy.c +dtc +ftdump diff --git a/util/dtc/COPYING b/util/dtc/COPYING new file mode 100644 index 0000000000..d60c31a97a --- /dev/null +++ b/util/dtc/COPYING @@ -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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/util/dtc/Documentation/booting-without-of.txt b/util/dtc/Documentation/booting-without-of.txt new file mode 100644 index 0000000000..60d5b7acc7 --- /dev/null +++ b/util/dtc/Documentation/booting-without-of.txt @@ -0,0 +1,1420 @@ + Booting the Linux/ppc kernel without Open Firmware + -------------------------------------------------- + + +(c) 2005 Benjamin Herrenschmidt , + IBM Corp. +(c) 2005 Becky Bruce , + Freescale Semiconductor, FSL SOC and 32-bit additions + + May 18, 2005: Rev 0.1 - Initial draft, no chapter III yet. + + May 19, 2005: Rev 0.2 - Add chapter III and bits & pieces here or + clarifies the fact that a lot of things are + optional, the kernel only requires a very + small device tree, though it is encouraged + to provide an as complete one as possible. + + May 24, 2005: Rev 0.3 - Precise that DT block has to be in RAM + - Misc fixes + - Define version 3 and new format version 16 + for the DT block (version 16 needs kernel + patches, will be fwd separately). + String block now has a size, and full path + is replaced by unit name for more + compactness. + linux,phandle is made optional, only nodes + that are referenced by other nodes need it. + "name" property is now automatically + deduced from the unit name + + June 1, 2005: Rev 0.4 - Correct confusion between OF_DT_END and + OF_DT_END_NODE in structure definition. + - Change version 16 format to always align + property data to 4 bytes. Since tokens are + already aligned, that means no specific + required alignement between property size + and property data. The old style variable + alignment would make it impossible to do + "simple" insertion of properties using + memove (thanks Milton for + noticing). Updated kernel patch as well + - Correct a few more alignement constraints + - Add a chapter about the device-tree + compiler and the textural representation of + the tree that can be "compiled" by dtc. + + + November 21, 2005: Rev 0.5 + - Additions/generalizations for 32-bit + - Changed to reflect the new arch/powerpc + structure + - Added chapter VI + + + ToDo: + - Add some definitions of interrupt tree (simple/complex) + - Add some definitions for pci host bridges + - Add some common address format examples + - Add definitions for standard properties and "compatible" + names for cells that are not already defined by the existing + OF spec. + - Compare FSL SOC use of PCI to standard and make sure no new + node definition required. + - Add more information about node definitions for SOC devices + that currently have no standard, like the FSL CPM. + + +I - Introduction +================ + +During the recent development of the Linux/ppc64 kernel, and more +specifically, the addition of new platform types outside of the old +IBM pSeries/iSeries pair, it was decided to enforce some strict rules +regarding the kernel entry and bootloader <-> kernel interfaces, in +order to avoid the degeneration that had become the ppc32 kernel entry +point and the way a new platform should be added to the kernel. The +legacy iSeries platform breaks those rules as it predates this scheme, +but no new board support will be accepted in the main tree that +doesn't follows them properly. In addition, since the advent of the +arch/powerpc merged architecture for ppc32 and ppc64, new 32-bit +platforms and 32-bit platforms which move into arch/powerpc will be +required to use these rules as well. + +The main requirement that will be defined in more detail below is +the presence of a device-tree whose format is defined after Open +Firmware specification. However, in order to make life easier +to embedded board vendors, the kernel doesn't require the device-tree +to represent every device in the system and only requires some nodes +and properties to be present. This will be described in detail in +section III, but, for example, the kernel does not require you to +create a node for every PCI device in the system. It is a requirement +to have a node for PCI host bridges in order to provide interrupt +routing informations and memory/IO ranges, among others. It is also +recommended to define nodes for on chip devices and other busses that +don't specifically fit in an existing OF specification. This creates a +great flexibility in the way the kernel can then probe those and match +drivers to device, without having to hard code all sorts of tables. It +also makes it more flexible for board vendors to do minor hardware +upgrades without significantly impacting the kernel code or cluttering +it with special cases. + + +1) Entry point for arch/powerpc +------------------------------- + + There is one and one single entry point to the kernel, at the start + of the kernel image. That entry point supports two calling + conventions: + + a) Boot from Open Firmware. If your firmware is compatible + with Open Firmware (IEEE 1275) or provides an OF compatible + client interface API (support for "interpret" callback of + forth words isn't required), you can enter the kernel with: + + r5 : OF callback pointer as defined by IEEE 1275 + bindings to powerpc. Only the 32 bit client interface + is currently supported + + r3, r4 : address & length of an initrd if any or 0 + + The MMU is either on or off; the kernel will run the + trampoline located in arch/powerpc/kernel/prom_init.c to + extract the device-tree and other information from open + firmware and build a flattened device-tree as described + in b). prom_init() will then re-enter the kernel using + the second method. This trampoline code runs in the + context of the firmware, which is supposed to handle all + exceptions during that time. + + b) Direct entry with a flattened device-tree block. This entry + point is called by a) after the OF trampoline and can also be + called directly by a bootloader that does not support the Open + Firmware client interface. It is also used by "kexec" to + implement "hot" booting of a new kernel from a previous + running one. This method is what I will describe in more + details in this document, as method a) is simply standard Open + Firmware, and thus should be implemented according to the + various standard documents defining it and its binding to the + PowerPC platform. The entry point definition then becomes: + + r3 : physical pointer to the device-tree block + (defined in chapter II) in RAM + + r4 : physical pointer to the kernel itself. This is + used by the assembly code to properly disable the MMU + in case you are entering the kernel with MMU enabled + and a non-1:1 mapping. + + r5 : NULL (as to differenciate with method a) + + Note about SMP entry: Either your firmware puts your other + CPUs in some sleep loop or spin loop in ROM where you can get + them out via a soft reset or some other means, in which case + you don't need to care, or you'll have to enter the kernel + with all CPUs. The way to do that with method b) will be + described in a later revision of this document. + + +2) Board support +---------------- + +64-bit kernels: + + Board supports (platforms) are not exclusive config options. An + arbitrary set of board supports can be built in a single kernel + image. The kernel will "know" what set of functions to use for a + given platform based on the content of the device-tree. Thus, you + should: + + a) add your platform support as a _boolean_ option in + arch/powerpc/Kconfig, following the example of PPC_PSERIES, + PPC_PMAC and PPC_MAPLE. The later is probably a good + example of a board support to start from. + + b) create your main platform file as + "arch/powerpc/platforms/myplatform/myboard_setup.c" and add it + to the Makefile under the condition of your CONFIG_ + option. This file will define a structure of type "ppc_md" + containing the various callbacks that the generic code will + use to get to your platform specific code + + c) Add a reference to your "ppc_md" structure in the + "machines" table in arch/powerpc/kernel/setup_64.c if you are + a 64-bit platform. + + d) request and get assigned a platform number (see PLATFORM_* + constants in include/asm-powerpc/processor.h + +32-bit embedded kernels: + + Currently, board support is essentially an exclusive config option. + The kernel is configured for a single platform. Part of the reason + for this is to keep kernels on embedded systems small and efficient; + part of this is due to the fact the code is already that way. In the + future, a kernel may support multiple platforms, but only if the + platforms feature the same core architectire. A single kernel build + cannot support both configurations with Book E and configurations + with classic Powerpc architectures. + + 32-bit embedded platforms that are moved into arch/powerpc using a + flattened device tree should adopt the merged tree practice of + setting ppc_md up dynamically, even though the kernel is currently + built with support for only a single platform at a time. This allows + unification of the setup code, and will make it easier to go to a + multiple-platform-support model in the future. + +NOTE: I believe the above will be true once Ben's done with the merge +of the boot sequences.... someone speak up if this is wrong! + + To add a 32-bit embedded platform support, follow the instructions + for 64-bit platforms above, with the exception that the Kconfig + option should be set up such that the kernel builds exclusively for + the platform selected. The processor type for the platform should + enable another config option to select the specific board + supported. + +NOTE: If ben doesn't merge the setup files, may need to change this to +point to setup_32.c + + + I will describe later the boot process and various callbacks that + your platform should implement. + + +II - The DT block format +======================== + + +This chapter defines the actual format of the flattened device-tree +passed to the kernel. The actual content of it and kernel requirements +are described later. You can find example of code manipulating that +format in various places, including arch/powerpc/kernel/prom_init.c +which will generate a flattened device-tree from the Open Firmware +representation, or the fs2dt utility which is part of the kexec tools +which will generate one from a filesystem representation. It is +expected that a bootloader like uboot provides a bit more support, +that will be discussed later as well. + +Note: The 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 + roughtly 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 */ +}; + + 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. You should always generate a structure of the highest + version defined at the time of your implementation. Currently + that is version 16, unless you explicitely 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 0x10 if you generate a tree of version 0x10 + 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) + + + 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 0x10 makes it +optional as it can generate it from the unit name defined below. + +There is also a "unit name" that is used to differenciate nodes with +the same name at the same level, it is usually made of the node +name's, 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 +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> + | |- linux,boot-cpu + | |- 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,platform = <00000600> + |- 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/linux,boot-cpu 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 of 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 summmarised as a start token, a full path, +a list of properties, a list of child node and an end token. Every +child node is a full node structure itself as defined above. + +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 - Required content of the device tree +========================================= + +WARNING: All "linux,*" properties defined in this document apply only +to a flattened device-tree. If your platform uses a real +implementation of Open Firmware or an implementation compatible with +the Open Firmware client interface, those properties will be created +by the trampoline code in the kernel's prom_init() file. For example, +that's where you'll have to add code to detect your board model and +set the platform number. However, when using the flatenned device-tree +entry point, there is no prom_init() pass, and thus you have to +provide those properties yourself. + + +1) Note about cells and address representation +---------------------------------------------- + +The general rule is documented in the various Open Firmware +documentations. If you chose to describe a bus with the device-tree +and there exist an OF bus binding, then you should follow the +specification. However, the kernel does not require every single +device or bus to be described by the device tree. + +In general, the format of an address for a device is defined by the +parent bus type, based on the #address-cells and #size-cells +property. In the absence of such a property, the parent's parent +values are used, etc... The kernel requires the root node to have +those properties defining addresses format for devices directly mapped +on the processor bus. + +Those 2 properties define 'cells' for representing an address and a +size. A "cell" is a 32 bit number. For example, if both contain 2 +like the example tree given above, then an address and a size are both +composed of 2 cells, and each is a 64 bit number (cells are +concatenated and expected to be in big endian format). Another example +is the way Apple firmware defines them, with 2 cells for an address +and one cell for a size. Most 32-bit implementations should define +#address-cells and #size-cells to 1, which represents a 32-bit value. +Some 32-bit processors allow for physical addresses greater than 32 +bits; these processors should define #address-cells as 2. + +"reg" properties are always a tuple of the type "address size" where +the number of cells of address and size is specified by the bus +#address-cells and #size-cells. When a bus supports various address +spaces and other flags relative to a given address allocation (like +prefetchable, etc...) those flags are usually added to the top level +bits of the physical address. For example, a PCI physical address is +made of 3 cells, the bottom two containing the actual address itself +while the top cell contains address space indication, flags, and pci +bus & device numbers. + +For busses that support dynamic allocation, it's the accepted practice +to then not provide the address in "reg" (keep it 0) though while +providing a flag indicating the address is dynamically allocated, and +then, to provide a separate "assigned-addresses" property that +contains the fully allocated addresses. See the PCI OF bindings for +details. + +In general, a simple bus with no address space bits and no dynamic +allocation is preferred if it reflects your hardware, as the existing +kernel address parsing functions will work out of the box. If you +define a bus type with a more complex address format, including things +like address space bits, you'll have to add a bus translator to the +prom_parse.c file of the recent kernels for your bus type. + +The "reg" property only defines addresses and sizes (if #size-cells +is +non-0) within a given bus. In order to translate addresses upward +(that is into parent bus addresses, and possibly into cpu physical +addresses), all busses must contain a "ranges" property. If the +"ranges" property is missing at a given level, it's assumed that +translation isn't possible. The format of the "ranges" proprety for a +bus is a list of: + + bus address, parent bus address, size + +"bus address" is in the format of the bus this bus node is defining, +that is, for a PCI bridge, it would be a PCI address. Thus, (bus +address, size) defines a range of addresses for child devices. "parent +bus address" is in the format of the parent bus of this bus. For +example, for a PCI host controller, that would be a CPU address. For a +PCI<->ISA bridge, that would be a PCI address. It defines the base +address in the parent bus where the beginning of that range is mapped. + +For a new 64 bit powerpc board, I recommend either the 2/2 format or +Apple's 2/1 format which is slightly more compact since sizes usually +fit in a single 32 bit word. New 32 bit powerpc boards should use a +1/1 format, unless the processor supports physical addresses greater +than 32-bits, in which case a 2/1 format is recommended. + + +2) Note about "compatible" properties +------------------------------------- + +These properties are optional, but recommended in devices and the root +node. The format of a "compatible" property is a list of concatenated +zero terminated strings. They allow a device to express its +compatibility with a family of similar devices, in some cases, +allowing a single driver to match against several devices regardless +of their actual names. + +3) Note about "name" properties +------------------------------- + +While earlier users of Open Firmware like OldWorld macintoshes tended +to use the actual device name for the "name" property, it's nowadays +considered a good practice to use a name that is closer to the device +class (often equal to device_type). For example, nowadays, ethernet +controllers are named "ethernet", an additional "model" property +defining precisely the chip type/model, and "compatible" property +defining the family in case a single driver can driver more than one +of these chips. However, the kernel doesn't generally put any +restriction on the "name" property; it is simply considered good +practice to follow the standard and its evolutions as closely as +possible. + +Note also that the new format version 16 makes the "name" property +optional. If it's absent for a node, then the node's unit name is then +used to reconstruct the name. That is, the part of the unit name +before the "@" sign is used (or the entire unit name if no "@" sign +is present). + +4) Note about node and property names and character set +------------------------------------------------------- + +While open firmware provides more flexibe usage of 8859-1, this +specification enforces more strict rules. Nodes and properties should +be comprised only of ASCII characters 'a' to 'z', '0' to +'9', ',', '.', '_', '+', '#', '?', and '-'. Node names additionally +allow uppercase characters 'A' to 'Z' (property names should be +lowercase. The fact that vendors like Apple don't respect this rule is +irrelevant here). Additionally, node and property names should always +begin with a character in the range 'a' to 'z' (or 'A' to 'Z' for node +names). + +The maximum number of characters for both nodes and property names +is 31. In the case of node names, this is only the leftmost part of +a unit name (the pure "name" property), it doesn't include the unit +address which can extend beyond that limit. + + +5) Required nodes and properties +-------------------------------- + These are all that are currently required. However, it is strongly + recommended that you expose PCI host bridges as documented in the + PCI binding to open firmware, and your interrupt tree as documented + in OF interrupt tree specification. + + a) The root node + + The root node requires some properties to be present: + + - model : this is your board name/model + - #address-cells : address representation for "root" devices + - #size-cells: the size representation for "root" devices + + Additionally, some recommended properties are: + + - compatible : the board "family" generally finds its way here, + for example, if you have 2 board models with a similar layout, + that typically get driven by the same platform code in the + kernel, you would use a different "model" property but put a + value in "compatible". The kernel doesn't directly use that + value (see /chosen/linux,platform for how the kernel choses a + platform type) but it is generally useful. + + The root node is also generally where you add additional properties + specific to your board like the serial number if any, that sort of + thing. it is recommended that if you add any "custom" property whose + name may clash with standard defined ones, you prefix them with your + vendor name and a comma. + + b) The /cpus node + + This node is the parent of all individual CPU nodes. It doesn't + have any specific requirements, though it's generally good practice + to have at least: + + #address-cells = <00000001> + #size-cells = <00000000> + + This defines that the "address" for a CPU is a single cell, and has + no meaningful size. This is not necessary but the kernel will assume + that format when reading the "reg" properties of a CPU node, see + below + + c) The /cpus/* nodes + + So under /cpus, you are supposed to create a node for every CPU on + the machine. There is no specific restriction on the name of the + CPU, though It's common practice to call it PowerPC,. For + example, Apple uses PowerPC,G5 while IBM uses PowerPC,970FX. + + Required properties: + + - device_type : has to be "cpu" + - reg : This is the physical cpu number, it's a single 32 bit cell + and is also used as-is as the unit number for constructing the + unit name in the full path. For example, with 2 CPUs, you would + have the full path: + /cpus/PowerPC,970FX@0 + /cpus/PowerPC,970FX@1 + (unit addresses do not require leading zeroes) + - d-cache-line-size : one cell, L1 data cache line size in bytes + - i-cache-line-size : one cell, L1 instruction cache line size in + bytes + - d-cache-size : one cell, size of L1 data cache in bytes + - i-cache-size : one cell, size of L1 instruction cache in bytes + - linux, boot-cpu : Should be defined if this cpu is the boot cpu. + + Recommended properties: + + - timebase-frequency : a cell indicating the frequency of the + timebase in Hz. This is not directly used by the generic code, + but you are welcome to copy/paste the pSeries code for setting + the kernel timebase/decrementer calibration based on this + value. + - clock-frequency : a cell indicating the CPU core clock frequency + in Hz. A new property will be defined for 64 bit values, but if + your frequency is < 4Ghz, one cell is enough. Here as well as + for the above, the common code doesn't use that property, but + you are welcome to re-use the pSeries or Maple one. A future + kernel version might provide a common function for this. + + You are welcome to add any property you find relevant to your board, + like some information about the mechanism used to soft-reset the + CPUs. For example, Apple puts the GPIO number for CPU soft reset + lines in there as a "soft-reset" property since they start secondary + CPUs by soft-resetting them. + + + d) the /memory node(s) + + To define the physical memory layout of your board, you should + create one or more memory node(s). You can either create a single + node with all memory ranges in its reg property, or you can create + several nodes, as you wish. The unit address (@ part) used for the + full path is the address of the first range of memory defined by a + given node. If you use a single memory node, this will typically be + @0. + + Required properties: + + - device_type : has to be "memory" + - reg : This property contains all the physical memory ranges of + your board. It's a list of addresses/sizes concatenated + together, with the number of cells of each defined by the + #address-cells and #size-cells of the root node. For example, + with both of these properties beeing 2 like in the example given + earlier, a 970 based machine with 6Gb of RAM could typically + have a "reg" property here that looks like: + + 00000000 00000000 00000000 80000000 + 00000001 00000000 00000001 00000000 + + That is a range starting at 0 of 0x80000000 bytes and a range + starting at 0x100000000 and of 0x100000000 bytes. You can see + that there is no memory covering the IO hole between 2Gb and + 4Gb. Some vendors prefer splitting those ranges into smaller + segments, but the kernel doesn't care. + + e) The /chosen node + + This node is a bit "special". Normally, that's where open firmware + puts some variable environment information, like the arguments, or + phandle pointers to nodes like the main interrupt controller, or the + default input/output devices. + + This specification makes a few of these mandatory, but also defines + some linux-specific properties that would be normally constructed by + the prom_init() trampoline when booting with an OF client interface, + but that you have to provide yourself when using the flattened format. + + Required properties: + + - linux,platform : This is your platform number as assigned by the + architecture maintainers + + Recommended properties: + + - bootargs : This zero-terminated string is passed as the kernel + command line + - linux,stdout-path : This is the full path to your standard + console device if any. Typically, if you have serial devices on + your board, you may want to put the full path to the one set as + the default console in the firmware here, for the kernel to pick + it up as it's own default console. If you look at the funciton + set_preferred_console() in arch/ppc64/kernel/setup.c, you'll see + that the kernel tries to find out the default console and has + knowledge of various types like 8250 serial ports. You may want + to extend this function to add your own. + - interrupt-controller : This is one cell containing a phandle + value that matches the "linux,phandle" property of your main + interrupt controller node. May be used for interrupt routing. + + + Note that u-boot creates and fills in the chosen node for platforms + that use it. + + f) the /soc node + + This node is used to represent a system-on-a-chip (SOC) and must be + present if the processor is a SOC. The top-level soc node contains + information that is global to all devices on the SOC. The node name + should contain a unit address for the SOC, which is the base address + of the memory-mapped register set for the SOC. The name of an soc + node should start with "soc", and the remainder of the name should + represent the part number for the soc. For example, the MPC8540's + soc node would be called "soc8540". + + Required properties: + + - device_type : Should be "soc" + - ranges : Should be defined as specified in 1) to describe the + translation of SOC addresses for memory mapped SOC registers. + + Recommended properties: + + - reg : This property defines the address and size of the + memory-mapped registers that are used for the SOC node itself. + It does not include the child device registers - these will be + defined inside each child node. The address specified in the + "reg" property should match the unit address of the SOC node. + - #address-cells : Address representation for "soc" devices. The + format of this field may vary depending on whether or not the + device registers are memory mapped. For memory mapped + registers, this field represents the number of cells needed to + represent the address of the registers. For SOCs that do not + use MMIO, a special address format should be defined that + contains enough cells to represent the required information. + See 1) above for more details on defining #address-cells. + - #size-cells : Size representation for "soc" devices + - #interrupt-cells : Defines the width of cells used to represent + interrupts. Typically this value is <2>, which includes a + 32-bit number that represents the interrupt number, and a + 32-bit number that represents the interrupt sense and level. + This field is only needed if the SOC contains an interrupt + controller. + + The SOC node may contain child nodes for each SOC device that the + platform uses. Nodes should not be created for devices which exist + on the SOC but are not used by a particular platform. See chapter VI + for more information on how to specify devices that are part of an +SOC. + + Example SOC node for the MPC8540: + + soc8540@e0000000 { + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + device_type = "soc"; + ranges = <00000000 e0000000 00100000> + reg = ; + } + + + +IV - "dtc", the device tree compiler +==================================== + + +dtc source code can be found at + + +WARNING: This version is still in early development stage; the +resulting device-tree "blobs" have not yet been validated with the +kernel. The current generated bloc lacks a useful reserve map (it will +be fixed to generate an empty one, it's up to the bootloader to fill +it up) among others. The error handling needs work, bugs are lurking, +etc... + +dtc basically takes a device-tree in a given format and outputs a +device-tree in another format. The currently supported formats are: + + Input formats: + ------------- + + - "dtb": "blob" format, that is a flattened device-tree block + with + header all in a binary blob. + - "dts": "source" format. This is a text file containing a + "source" for a device-tree. The format is defined later in this + chapter. + - "fs" format. This is a representation equivalent to the + output of /proc/device-tree, that is nodes are directories and + properties are files + + Output formats: + --------------- + + - "dtb": "blob" format + - "dts": "source" format + - "asm": assembly language file. This is 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 use + + +The syntax of the dtc tool is + + dtc [-I ] [-O ] + [-o output-filename] [-V output_version] input_filename + + +The "output_version" defines what versio of the "blob" format will be +generated. Supported versions are 1,2,3 and 16. The default is +currently version 3 but that may change in the future to version 16. + +Additionally, dtc performs various sanity checks on the tree, like the +uniqueness of linux,phandle properties, validity of strings, etc... + +The format of the .dts "source" file is "C" like, supports C and C++ +style commments. + +/ { +} + +The above is the "device-tree" definition. It's the only statement +supported currently at the toplevel. + +/ { + property1 = "string_value"; /* define a property containing a 0 + * terminated string + */ + + property2 = <1234abcd>; /* define a property containing a + * numerical 32 bits value (hexadecimal) + */ + + property3 = <12345678 12345678 deadbeef>; + /* define a property containing 3 + * numerical 32 bits values (cells) in + * hexadecimal + */ + property4 = [0a 0b 0c 0d de ea ad be ef]; + /* define a property whose content is + * an arbitrary array of bytes + */ + + childnode@addresss { /* define a child node named "childnode" + * whose unit name is "childnode at + * address" + */ + + childprop = "hello\n"; /* define a property "childprop" of + * childnode (in this case, a string) + */ + }; +}; + +Nodes can contain other nodes etc... thus defining the hierarchical +structure of the tree. + +Strings support common escape sequences from C: "\n", "\t", "\r", +"\(octal value)", "\x(hex value)". + +It is also suggested that you pipe your source file through cpp (gcc +preprocessor) so you can use #include's, #define for constants, etc... + +Finally, various options are planned but not yet implemented, like +automatic generation of phandles, labels (exported to the asm file so +you can point to a property content and change it easily from whatever +you link the device-tree with), label or path instead of numeric value +in some cells to "point" to a node (replaced by a phandle at compile +time), export of reserve map address to the asm file, ability to +specify reserve map content at compile time, etc... + +We may provide a .h include file with common definitions of that +proves useful for some properties (like building PCI properties or +interrupt maps) though it may be better to add a notion of struct +definitions to the compiler... + + +V - Recommendations for a bootloader +==================================== + + +Here are some various ideas/recommendations that have been proposed +while all this has been defined and implemented. + + - The bootloader may want to be able to use the device-tree itself + and may want to manipulate it (to add/edit some properties, + like physical memory size or kernel arguments). At this point, 2 + choices can be made. Either the bootloader works directly on the + flattened format, or the bootloader has its own internal tree + representation with pointers (similar to the kernel one) and + re-flattens the tree when booting the kernel. The former is a bit + more difficult to edit/modify, the later requires probably a bit + more code to handle the tree structure. Note that the structure + format has been designed so it's relatively easy to "insert" + properties or nodes or delete them by just memmoving things + around. It contains no internal offsets or pointers for this + purpose. + + - An example of code for iterating nodes & retreiving properties + directly from the flattened tree format can be found in the kernel + file arch/ppc64/kernel/prom.c, look at scan_flat_dt() function, + it's usage in early_init_devtree(), and the corresponding various + early_init_dt_scan_*() callbacks. That code can be re-used in a + GPL bootloader, and as the author of that code, I would be happy + do discuss possible free licencing to any vendor who wishes to + integrate all or part of this code into a non-GPL bootloader. + + + +VI - System-on-a-chip devices and nodes +======================================= + +Many companies are now starting to develop system-on-a-chip +processors, where the processor core (cpu) and many peripheral devices +exist on a single piece of silicon. For these SOCs, an SOC node +should be used that defines child nodes for the devices that make +up the SOC. While platforms are not required to use this model in +order to boot the kernel, it is highly encouraged that all SOC +implementations define as complete a flat-device-tree as possible to +describe the devices on the SOC. This will allow for the +genericization of much of the kernel code. + + +1) Defining child nodes of an SOC +--------------------------------- + +Each device that is part of an SOC may have its own node entry inside +the SOC node. For each device that is included in the SOC, the unit +address property represents the address offset for this device's +memory-mapped registers in the parent's address space. The parent's +address space is defined by the "ranges" property in the top-level soc +node. The "reg" property for each node that exists directly under the +SOC node should contain the address mapping from the child address space +to the parent SOC address space and the size of the device's +memory-mapped register file. + +For many devices that may exist inside an SOC, there are predefined +specifications for the format of the device tree node. All SOC child +nodes should follow these specifications, except where noted in this +document. + +See appendix A for an example partial SOC node definition for the +MPC8540. + + +2) Specifying interrupt information for SOC devices +--------------------------------------------------- + +Each device that is part of an SOC and which generates interrupts +should have the following properties: + + - interrupt-parent : contains the phandle of the interrupt + controller which handles interrupts for this device + - interrupts : a list of tuples representing the interrupt + number and the interrupt sense and level for each interupt + for this device. + +This information is used by the kernel to build the interrupt table +for the interrupt controllers in the system. + +Sense and level information should be encoded as follows: + + Devices connected to openPIC-compatible controllers should encode + sense and polarity as follows: + + 0 = high to low edge sensitive type enabled + 1 = active low level sensitive type enabled + 2 = low to high edge sensitive type enabled + 3 = active high level sensitive type enabled + + ISA PIC interrupt controllers should adhere to the ISA PIC + encodings listed below: + + 0 = active low level sensitive type enabled + 1 = active high level sensitive type enabled + 2 = high to low edge sensitive type enabled + 3 = low to high edge sensitive type enabled + + + +3) Representing devices without a current OF specification +---------------------------------------------------------- + +Currently, there are many devices on SOCs that do not have a standard +representation pre-defined as part of the open firmware +specifications, mainly because the boards that contain these SOCs are +not currently booted using open firmware. This section contains +descriptions for the SOC devices for which new nodes have been +defined; this list will expand as more and more SOC-containing +platforms are moved over to use the flattened-device-tree model. + + a) MDIO IO device + + The MDIO is a bus to which the PHY devices are connected. For each + device that exists on this bus, a child node should be created. See + the definition of the PHY node below for an example of how to define + a PHY. + + Required properties: + - reg : Offset and length of the register set for the device + - device_type : Should be "mdio" + - compatible : Should define the compatible device type for the + mdio. Currently, this is most likely to be "gianfar" + + Example: + + mdio@24520 { + reg = <24520 20>; + + ethernet-phy@0 { + ...... + }; + }; + + + b) Gianfar-compatible ethernet nodes + + Required properties: + + - device_type : Should be "network" + - model : Model of the device. Can be "TSEC", "eTSEC", or "FEC" + - compatible : Should be "gianfar" + - reg : Offset and length of the register set for the device + - address : List of bytes representing the ethernet address of + this controller + - interrupts : where a is the interrupt number and b is a + field that represents an encoding of the sense and level + information for the interrupt. This should be encoded based on + the information in section 2) depending on the type of interrupt + controller you have. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + - phy-handle : The phandle for the PHY connected to this ethernet + controller. + + Example: + + ethernet@24000 { + #size-cells = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <24000 1000>; + address = [ 00 E0 0C 00 73 00 ]; + interrupts = ; + interrupt-parent = <40000>; + phy-handle = <2452000> + }; + + + + c) PHY nodes + + Required properties: + + - device_type : Should be "ethernet-phy" + - interrupts : where a is the interrupt number and b is a + field that represents an encoding of the sense and level + information for the interrupt. This should be encoded based on + the information in section 2) depending on the type of interrupt + controller you have. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + - reg : The ID number for the phy, usually a small integer + - linux,phandle : phandle for this node; likely referenced by an + ethernet controller node. + + + Example: + + ethernet-phy@0 { + linux,phandle = <2452000> + interrupt-parent = <40000>; + interrupts = <35 1>; + reg = <0>; + device_type = "ethernet-phy"; + }; + + + d) Interrupt controllers + + Some SOC devices contain interrupt controllers that are different + from the standard Open PIC specification. The SOC device nodes for + these types of controllers should be specified just like a standard + OpenPIC controller. Sense and level information should be encoded + as specified in section 2) of this chapter for each device that + specifies an interrupt. + + Example : + + pic@40000 { + linux,phandle = <40000>; + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + reg = <40000 40000>; + built-in; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + big-endian; + }; + + + e) I2C + + Required properties : + + - device_type : Should be "i2c" + - reg : Offset and length of the register set for the device + + Recommended properties : + + - compatible : Should be "fsl-i2c" for parts compatible with + Freescale I2C specifications. + - interrupts : where a is the interrupt number and b is a + field that represents an encoding of the sense and level + information for the interrupt. This should be encoded based on + the information in section 2) depending on the type of interrupt + controller you have. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + - dfsrr : boolean; if defined, indicates that this I2C device has + a digital filter sampling rate register + - fsl5200-clocking : boolean; if defined, indicated that this device + uses the FSL 5200 clocking mechanism. + + Example : + + i2c@3000 { + interrupt-parent = <40000>; + interrupts = <1b 3>; + reg = <3000 18>; + device_type = "i2c"; + compatible = "fsl-i2c"; + dfsrr; + }; + + + More devices will be defined as this spec matures. + + +Appendix A - Sample SOC node for MPC8540 +======================================== + +Note that the #address-cells and #size-cells for the SoC node +in this example have been explicitly listed; these are likely +not necessary as they are usually the same as the root node. + + soc8540@e0000000 { + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + device_type = "soc"; + ranges = <00000000 e0000000 00100000> + reg = ; + + mdio@24520 { + reg = <24520 20>; + device_type = "mdio"; + compatible = "gianfar"; + + ethernet-phy@0 { + linux,phandle = <2452000> + interrupt-parent = <40000>; + interrupts = <35 1>; + reg = <0>; + device_type = "ethernet-phy"; + }; + + ethernet-phy@1 { + linux,phandle = <2452001> + interrupt-parent = <40000>; + interrupts = <35 1>; + reg = <1>; + device_type = "ethernet-phy"; + }; + + ethernet-phy@3 { + linux,phandle = <2452002> + interrupt-parent = <40000>; + interrupts = <35 1>; + reg = <3>; + device_type = "ethernet-phy"; + }; + + }; + + ethernet@24000 { + #size-cells = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <24000 1000>; + address = [ 00 E0 0C 00 73 00 ]; + interrupts = ; + interrupt-parent = <40000>; + phy-handle = <2452000>; + }; + + ethernet@25000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <25000 1000>; + address = [ 00 E0 0C 00 73 01 ]; + interrupts = <13 3 14 3 18 3>; + interrupt-parent = <40000>; + phy-handle = <2452001>; + }; + + ethernet@26000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "network"; + model = "FEC"; + compatible = "gianfar"; + reg = <26000 1000>; + address = [ 00 E0 0C 00 73 02 ]; + interrupts = <19 3>; + interrupt-parent = <40000>; + phy-handle = <2452002>; + }; + + serial@4500 { + device_type = "serial"; + compatible = "ns16550"; + reg = <4500 100>; + clock-frequency = <0>; + interrupts = <1a 3>; + interrupt-parent = <40000>; + }; + + pic@40000 { + linux,phandle = <40000>; + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + reg = <40000 40000>; + built-in; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + big-endian; + }; + + i2c@3000 { + interrupt-parent = <40000>; + interrupts = <1b 3>; + reg = <3000 18>; + device_type = "i2c"; + compatible = "fsl-i2c"; + dfsrr; + }; + + }; diff --git a/util/dtc/Documentation/dtc-paper.bib b/util/dtc/Documentation/dtc-paper.bib new file mode 100644 index 0000000000..d01e2ff9e9 --- /dev/null +++ b/util/dtc/Documentation/dtc-paper.bib @@ -0,0 +1,43 @@ +@STRING{pub-IEEE = "IEEE Computer Society"} +@STRING{pub-IEEE:adr = "345 E. 47th St, New York, NY 10017, USA"} + +@BOOK{IEEE1275, + key = "IEEE1275", + title = "{IEEE} {S}tandard for {B}oot ({I}nitialization {C}onfiguration) {F}irmware: {C}ore {R}equirements and {P}ractices", + publisher = pub-IEEE, + address = pub-IEEE:adr, + series = "IEEE Std 1275-1994", + year = 1994, +} + +@BOOK{IEEE1275-pci, + key = "IEEE1275-pci", + title = "{PCI} {B}us {B}inding to: {IEEE} {S}td 1275-1994 {S}tandard for {B}oot ({I}nitialization {C}onfiguration) {F}irmware", + publisher = pub-IEEE, + address = pub-IEEE:adr, + note = "Revision 2.1", + year = 1998, +} + +@MISC{noof1, + author = "Benjamin Herrenschmidt", + title = "Booting the {L}inux/ppc kernel without {O}pen {F}irmware", + month = may, + year = 2005, + note = "v0.1, \url{http://ozlabs.org/pipermail/linuxppc64-dev/2005-May/004073.html}", +} + +@MISC{noof5, + author = "Benjamin Herrenschmidt", + title = "Booting the {L}inux/ppc kernel without {O}pen {F}irmware", + month = nov, + year = 2005, + note = "v0.5, \url{http://ozlabs.org/pipermail/linuxppc64-dev/2005-December/006994.html}", +} + +@MISC{dtcgit, + author = "David Gibson et al.", + title = "\dtc{}", + howpublished = "git tree", + note = "\url{http://ozlabs.org/~dgibson/dtc/dtc.git}", +} diff --git a/util/dtc/Documentation/dtc-paper.tex b/util/dtc/Documentation/dtc-paper.tex new file mode 100644 index 0000000000..91c4a49578 --- /dev/null +++ b/util/dtc/Documentation/dtc-paper.tex @@ -0,0 +1,597 @@ +\documentclass[a4paper,twocolumn]{article} + +\usepackage{abstract} +\usepackage{xspace} +\usepackage{amssymb} +\usepackage{latexsym} +\usepackage{tabularx} +\usepackage[T1]{fontenc} +\usepackage{calc} +\usepackage{listings} +\usepackage{color} +\usepackage{url} + +\title{Device trees everywhere} + +\author{David Gibson \texttt{<{dwg}{@}{au1.ibm.com}>}\\ + Benjamin Herrenschmidt \texttt{<{benh}{@}{kernel.crashing.org}>}\\ + \emph{OzLabs, IBM Linux Technology Center}} + +\newcommand{\R}{\textsuperscript{\textregistered}\xspace} +\newcommand{\tm}{\textsuperscript{\texttrademark}\xspace} +\newcommand{\tge}{$\geqslant$} +%\newcommand{\ditto}{\textquotedbl\xspace} + +\newcommand{\fixme}[1]{$\bigstar$\emph{\textbf{\large #1}}$\bigstar$\xspace} + +\newcommand{\ppc}{\mbox{PowerPC}\xspace} +\newcommand{\of}{Open Firmware\xspace} +\newcommand{\benh}{Ben Herrenschmidt\xspace} +\newcommand{\kexec}{\texttt{kexec()}\xspace} +\newcommand{\dtbeginnode}{\texttt{OF\_DT\_BEGIN\_NODE\xspace}} +\newcommand{\dtendnode}{\texttt{OF\_DT\_END\_NODE\xspace}} +\newcommand{\dtprop}{\texttt{OF\_DT\_PROP\xspace}} +\newcommand{\dtend}{\texttt{OF\_DT\_END\xspace}} +\newcommand{\dtc}{\texttt{dtc}\xspace} +\newcommand{\phandle}{\texttt{linux,phandle}\xspace} +\begin{document} + +\maketitle + +\begin{abstract} + We present a method for booting a \ppc{}\R Linux\R kernel on an + embedded machine. To do this, we supply the kernel with a compact + flattened-tree representation of the system's hardware based on the + device tree supplied by Open Firmware on IBM\R servers and Apple\R + Power Macintosh\R machines. + + The ``blob'' representing the device tree can be created using \dtc + --- the Device Tree Compiler --- that turns a simple text + representation of the tree into the compact representation used by + the kernel. The compiler can produce either a binary ``blob'' or an + assembler file ready to be built into a firmware or bootwrapper + image. + + This flattened-tree approach is now the only supported method of + booting a \texttt{ppc64} kernel without Open Firmware, and we plan + to make it the only supported method for all \texttt{powerpc} + kernels in the future. +\end{abstract} + +\section{Introduction} + +\subsection{OF and the device tree} + +Historically, ``everyday'' \ppc machines have booted with the help of +\of (OF), a firmware environment defined by IEEE1275 \cite{IEEE1275}. +Among other boot-time services, OF maintains a device tree that +describes all of the system's hardware devices and how they're +connected. During boot, before taking control of memory management, +the Linux kernel uses OF calls to scan the device tree and transfer it +to an internal representation that is used at run time to look up +various device information. + +The device tree consists of nodes representing devices or +buses\footnote{Well, mostly. There are a few special exceptions.}. +Each node contains \emph{properties}, name--value pairs that give +information about the device. The values are arbitrary byte strings, +and for some properties, they contain tables or other structured +information. + +\subsection{The bad old days} + +Embedded systems, by contrast, usually have a minimal firmware that +might supply a few vital system parameters (size of RAM and the like), +but nothing as detailed or complete as the OF device tree. This has +meant that the various 32-bit \ppc embedded ports have required a +variety of hacks spread across the kernel to deal with the lack of +device tree. These vary from specialised boot wrappers to parse +parameters (which are at least reasonably localised) to +CONFIG-dependent hacks in drivers to override normal probe logic with +hardcoded addresses for a particular board. As well as being ugly of +itself, such CONFIG-dependent hacks make it hard to build a single +kernel image that supports multiple embedded machines. + +Until relatively recently, the only 64-bit \ppc machines without OF +were legacy (pre-POWER5\R) iSeries\R machines. iSeries machines often +only have virtual IO devices, which makes it quite simple to work +around the lack of a device tree. Even so, the lack means the iSeries +boot sequence must be quite different from the pSeries or Macintosh, +which is not ideal. + +The device tree also presents a problem for implementing \kexec. When +the kernel boots, it takes over full control of the system from OF, +even re-using OF's memory. So, when \kexec comes to boot another +kernel, OF is no longer around for the second kernel to query. + +\section{The Flattened Tree} + +In May 2005 \benh implemented a new approach to handling the device +tree that addresses all these problems. When booting on OF systems, +the first thing the kernel runs is a small piece of code in +\texttt{prom\_init.c}, which executes in the context of OF. This code +walks the device tree using OF calls, and transcribes it into a +compact, flattened format. The resulting device tree ``blob'' is then +passed to the kernel proper, which eventually unflattens the tree into +its runtime form. This blob is the only data communicated between the +\texttt{prom\_init.c} bootstrap and the rest of the kernel. + +When OF isn't available, either because the machine doesn't have it at +all or because \kexec has been used, the kernel instead starts +directly from the entry point taking a flattened device tree. The +device tree blob must be passed in from outside, rather than generated +by part of the kernel from OF. For \kexec, the userland +\texttt{kexec} tools build the blob from the runtime device tree +before invoking the new kernel. For embedded systems the blob can +come either from the embedded bootloader, or from a specialised +version of the \texttt{zImage} wrapper for the system in question. + +\subsection{Properties of the flattened tree} + +The flattened tree format should be easy to handle, both for the +kernel that parses it and the bootloader that generates it. In +particular, the following properties are desirable: + +\begin{itemize} +\item \emph{relocatable}: the bootloader or kernel should be able to + move the blob around as a whole, without needing to parse or adjust + its internals. In practice that means we must not use pointers + within the blob. +\item \emph{insert and delete}: sometimes the bootloader might want to + make tweaks to the flattened tree, such as deleting or inserting a + node (or whole subtree). It should be possible to do this without + having to effectively regenerate the whole flattened tree. In + practice this means limiting the use of internal offsets in the blob + that need recalculation if a section is inserted or removed with + \texttt{memmove()}. +\item \emph{compact}: embedded systems are frequently short of + resources, particularly RAM and flash memory space. Thus, the tree + representation should be kept as small as conveniently possible. +\end{itemize} + +\subsection{Format of the device tree blob} +\label{sec:format} + +\begin{figure}[htb!] + \centering + \footnotesize + \begin{tabular}{r|c|l} + \multicolumn{1}{r}{\textbf{Offset}}& \multicolumn{1}{c}{\textbf{Contents}} \\\cline{2-2} + \texttt{0x00} & \texttt{0xd00dfeed} & magic number \\\cline{2-2} + \texttt{0x04} & \emph{totalsize} \\\cline{2-2} + \texttt{0x08} & \emph{off\_struct} & \\\cline{2-2} + \texttt{0x0C} & \emph{off\_strs} & \\\cline{2-2} + \texttt{0x10} & \emph{off\_rsvmap} & \\\cline{2-2} + \texttt{0x14} & \emph{version} \\\cline{2-2} + \texttt{0x18} & \emph{last\_comp\_ver} & \\\cline{2-2} + \texttt{0x1C} & \emph{boot\_cpu\_id} & \tge v2 only\\\cline{2-2} + \texttt{0x20} & \emph{size\_strs} & \tge v3 only\\\cline{2-2} + \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2} + \emph{off\_rsvmap} & \emph{address0} & memory reserve \\ + + \texttt{0x04} & ...& table \\\cline{2-2} + + \texttt{0x08} & \emph{len0} & \\ + + \texttt{0x0C} & ...& \\\cline{2-2} + \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2} + & \texttt{0x00000000}- & end marker\\ + & \texttt{00000000} & \\\cline{2-2} + & \texttt{0x00000000}- & \\ + & \texttt{00000000} & \\\cline{2-2} + \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2} + \emph{off\_strs} & \texttt{'n' 'a' 'm' 'e'} & strings block \\ + + \texttt{0x04} & \texttt{~0~ 'm' 'o' 'd'} & \\ + + \texttt{0x08} & \texttt{'e' 'l' ~0~ \makebox[\widthof{~~~}]{\textrm{...}}} & \\ + \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2} + \multicolumn{1}{r}{+ \emph{size\_strs}} \\ + \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2} + \emph{off\_struct} & \dtbeginnode & structure block \\\cline{2-2} + + \texttt{0x04} & \texttt{'/' ~0~ ~0~ ~0~} & root node\\\cline{2-2} + + \texttt{0x08} & \dtprop & \\\cline{2-2} + + \texttt{0x0C} & \texttt{0x00000005} & ``\texttt{model}''\\\cline{2-2} + + \texttt{0x10} & \texttt{0x00000008} & \\\cline{2-2} + + \texttt{0x14} & \texttt{'M' 'y' 'B' 'o'} & \\ + + \texttt{0x18} & \texttt{'a' 'r' 'd' ~0~} & \\\cline{2-2} + \vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2} + & \texttt{\dtendnode} \\\cline{2-2} + & \texttt{\dtend} \\\cline{2-2} + \multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2} + \multicolumn{1}{r}{\emph{totalsize}} \\ + \end{tabular} + \caption{Device tree blob layout} + \label{fig:blob-layout} +\end{figure} + +The format for the blob we devised, was first described on the +\texttt{linuxppc64-dev} mailing list in \cite{noof1}. The format has +since evolved through various revisions, and the current version is +included as part of the \dtc (see \S\ref{sec:dtc}) git tree, +\cite{dtcgit}. + +Figure \ref{fig:blob-layout} shows the layout of the blob of data +containing the device tree. It has three sections of variable size: +the \emph{memory reserve table}, the \emph{structure block} and the +\emph{strings block}. A small header gives the blob's size and +version and the locations of the three sections, plus a handful of +vital parameters used during early boot. + +The memory reserve map section gives a list of regions of memory that +the kernel must not use\footnote{Usually such ranges contain some data +structure initialised by the firmware that must be preserved by the +kernel.}. The list is represented as a simple array of (address, +size) pairs of 64 bit values, terminated by a zero size entry. The +strings block is similarly simple, consisting of a number of +null-terminated strings appended together, which are referenced from +the structure block as described below. + +The structure block contains the device tree proper. Each node is +introduced with a 32-bit \dtbeginnode tag, followed by the node's name +as a null-terminated string, padded to a 32-bit boundary. Then +follows all of the properties of the node, each introduced with a +\dtprop tag, then all of the node's subnodes, each introduced with +their own \dtbeginnode tag. The node ends with an \dtendnode tag, and +after the \dtendnode for the root node is an \dtend tag, indicating +the end of the whole tree\footnote{This is redundant, but included for +ease of parsing.}. The structure block starts with the \dtbeginnode +introducing the description of the root node (named \texttt{/}). + +Each property, after the \dtprop, has a 32-bit value giving an offset +from the beginning of the strings block at which the property name is +stored. Because it's common for many nodes to have properties with +the same name, this approach can substantially reduce the total size +of the blob. The name offset is followed by the length of the +property value (as a 32-bit value) and then the data itself padded to +a 32-bit boundary. + +\subsection{Contents of the tree} +\label{sec:treecontents} + +Having seen how to represent the device tree structure as a flattened +blob, what actually goes into the tree? The short answer is ``the +same as an OF tree''. On OF systems, the flattened tree is +transcribed directly from the OF device tree, so for simplicity we +also use OF conventions for the tree on other systems. + +In many cases a flat tree can be simpler than a typical OF provided +device tree. The flattened tree need only provide those nodes and +properties that the kernel actually requires; the flattened tree +generally need not include devices that the kernel can probe itself. +For example, an OF device tree would normally include nodes for each +PCI device on the system. A flattened tree need only include nodes +for the PCI host bridges; the kernel will scan the buses thus +described to find the subsidiary devices. The device tree can include +nodes for devices where the kernel needs extra information, though: +for example, for ISA devices on a subsidiary PCI/ISA bridge, or for +devices with unusual interrupt routing. + +Where they exist, we follow the IEEE1275 bindings that specify how to +describe various buses in the device tree (for example, +\cite{IEEE1275-pci} describe how to represent PCI devices). The +standard has not been updated for a long time, however, and lacks +bindings for many modern buses and devices. In particular, embedded +specific devices such as the various System-on-Chip buses are not +covered. We intend to create new bindings for such buses, in keeping +with the general conventions of IEEE1275 (a simple such binding for a +System-on-Chip bus was included in \cite{noof5} a revision of +\cite{noof1}). + +One complication arises for representing ``phandles'' in the flattened +tree. In OF, each node in the tree has an associated phandle, a +32-bit integer that uniquely identifies the node\footnote{In practice +usually implemented as a pointer or offset within OF memory.}. This +handle is used by the various OF calls to query and traverse the tree. +Sometimes phandles are also used within the tree to refer to other +nodes in the tree. For example, devices that produce interrupts +generally have an \texttt{interrupt-parent} property giving the +phandle of the interrupt controller that handles interrupts from this +device. Parsing these and other interrupt related properties allows +the kernel to build a complete representation of the system's +interrupt tree, which can be quite different from the tree of bus +connections. + +In the flattened tree, a node's phandle is represented by a special +\phandle property. When the kernel generates a flattened tree from +OF, it adds a \phandle property to each node, containing the phandle +retrieved from OF. When the tree is generated without OF, however, +only nodes that are actually referred to by phandle need to have this +property. + +Another complication arises because nodes in an OF tree have two +names. First they have the ``unit name'', which is how the node is +referred to in an OF path. The unit name generally consists of a +device type followed by an \texttt{@} followed by a \emph{unit +address}. For example \texttt{/memory@0} is the full path of a memory +node at address 0, \texttt{/ht@0,f2000000/pci@1} is the path of a PCI +bus node, which is under a HyperTransport\tm bus node. The form of +the unit address is bus dependent, but is generally derived from the +node's \texttt{reg} property. In addition, nodes have a property, +\texttt{name}, whose value is usually equal to the first path of the +unit name. For example, the nodes in the previous example would have +\texttt{name} properties equal to \texttt{memory} and \texttt{pci}, +respectively. To save space in the blob, the current version of the +flattened tree format only requires the unit names to be present. +When the kernel unflattens the tree, it automatically generates a +\texttt{name} property from the node's path name. + +\section{The Device Tree Compiler} +\label{sec:dtc} + +\begin{figure}[htb!] + \centering + \begin{lstlisting}[frame=single,basicstyle=\footnotesize\ttfamily, + tabsize=3,numbers=left,xleftmargin=2em] +/memreserve/ 0x20000000-0x21FFFFFF; + +/ { + model = "MyBoard"; + compatible = "MyBoardFamily"; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + PowerPC,970@0 { + device_type = "cpu"; + reg = <0>; + clock-frequency = <5f5e1000>; + timebase-frequency = <1FCA055>; + linux,boot-cpu; + i-cache-size = <10000>; + d-cache-size = <8000>; + }; + }; + + memory@0 { + device_type = "memory"; + memreg: reg = <00000000 00000000 + 00000000 20000000>; + }; + + mpic@0x3fffdd08400 { + /* Interrupt controller */ + /* ... */ + }; + + pci@40000000000000 { + /* PCI host bridge */ + /* ... */ + }; + + chosen { + bootargs = "root=/dev/sda2"; + linux,platform = <00000600>; + interrupt-controller = + < &/mpic@0x3fffdd08400 >; + }; +}; +\end{lstlisting} + \caption{Example \dtc source} + \label{fig:dts} +\end{figure} + +As we've seen, the flattened device tree format provides a convenient +way of communicating device tree information to the kernel. It's +simple for the kernel to parse, and simple for bootloaders to +manipulate. On OF systems, it's easy to generate the flattened tree +by walking the OF maintained tree. However, for embedded systems, the +flattened tree must be generated from scratch. + +Embedded bootloaders are generally built for a particular board. So, +it's usually possible to build the device tree blob at compile time +and include it in the bootloader image. For minor revisions of the +board, the bootloader can contain code to make the necessary tweaks to +the tree before passing it to the booted kernel. + +The device trees for embedded boards are usually quite simple, and +it's possible to hand construct the necessary blob by hand, but doing +so is tedious. The ``device tree compiler'', \dtc{}\footnote{\dtc can +be obtained from \cite{dtcgit}.}, is designed to make creating device +tree blobs easier by converting a text representation of the tree +into the necessary blob. + +\subsection{Input and output formats} + +As well as the normal mode of compiling a device tree blob from text +source, \dtc can convert a device tree between a number of +representations. It can take its input in one of three different +formats: +\begin{itemize} +\item source, the normal case. The device tree is described in a text + form, described in \S\ref{sec:dts}. +\item blob (\texttt{dtb}), the flattened tree format described in + \S\ref{sec:format}. This mode is useful for checking a pre-existing + device tree blob. +\item filesystem (\texttt{fs}), input is a directory tree in the + layout of \texttt{/proc/device-tree} (roughly, a directory for each + node in the device tree, a file for each property). This is useful + for building a blob for the device tree in use by the currently + running kernel. +\end{itemize} + +In addition, \dtc can output the tree in one of three different +formats: +\begin{itemize} +\item blob (\texttt{dtb}), as in \S\ref{sec:format}. The most + straightforward use of \dtc is to compile from ``source'' to + ``blob'' format. +\item source (\texttt{dts}), as in \S\ref{sec:dts}. If used with blob + input, this allows \dtc to act as a ``decompiler''. +\item assembler source (\texttt{asm}). \dtc can produce an assembler + file, which will assemble into a \texttt{.o} file containing the + device tree blob, with symbols giving the beginning of the blob and + its various subsections. This can then be linked directly into a + bootloader or firmware image. +\end{itemize} + +For maximum applicability, \dtc can both read and write any of the +existing revisions of the blob format. When reading, \dtc takes the +version from the blob header, and when writing it takes a command line +option specifying the desired version. It automatically makes any +necessary adjustments to the tree that are necessary for the specified +version. For example, formats before 0x10 require each node to have +an explicit \texttt{name} property. When \dtc creates such a blob, it +will automatically generate \texttt{name} properties from the unit +names. + +\subsection{Source format} +\label{sec:dts} + +The ``source'' format for \dtc is a text description of the device +tree in a vaguely C-like form. Figure \ref{fig:dts} shows an +example. The file starts with \texttt{/memreserve/} directives, which +gives address ranges to add to the output blob's memory reserve table, +then the device tree proper is described. + +Nodes of the tree are introduced with the node name, followed by a +\texttt{\{} ... \texttt{\};} block containing the node's properties +and subnodes. Properties are given as just {\emph{name} \texttt{=} + \emph{value}\texttt{;}}. The property values can be given in any +of three forms: +\begin{itemize} +\item \emph{string} (for example, \texttt{"MyBoard"}). The property + value is the given string, including terminating NULL. C-style + escapes (\verb+\t+, \verb+\n+, \verb+\0+ and so forth) are allowed. +\item \emph{cells} (for example, \texttt{<0 8000 f0000000>}). The + property value is made up of a list of 32-bit ``cells'', each given + as a hex value. +\item \emph{bytestring} (for example, \texttt{[1234abcdef]}). The + property value is given as a hex bytestring. +\end{itemize} + +Cell properties can also contain \emph{references}. Instead of a hex +number, the source can give an ampersand (\texttt{\&}) followed by the +full path to some node in the tree. For example, in Figure +\ref{fig:dts}, the \texttt{/chosen} node has an +\texttt{interrupt-controller} property referring to the interrupt +controller described by the node \texttt{/mpic@0x3fffdd08400}. In the +output tree, the value of the referenced node's phandle is included in +the property. If that node doesn't have an explicit phandle property, +\dtc will automatically create a unique phandle for it. This approach +makes it easy to create interrupt trees without having to explicitly +assign and remember phandles for the various interrupt controller +nodes. + +The \dtc source can also include ``labels'', which are placed on a +particular node or property. For example, Figure \ref{fig:dts} has a +label ``\texttt{memreg}'' on the \texttt{reg} property of the node +\texttt{/memory@0}. When using assembler output, corresponding labels +in the output are generated, which will assemble into symbols +addressing the part of the blob with the node or property in question. +This is useful for the common case where an embedded board has an +essentially fixed device tree with a few variable properties, such as +the size of memory. The bootloader for such a board can have a device +tree linked in, including a symbol referring to the right place in the +blob to update the parameter with the correct value determined at +runtime. + +\subsection{Tree checking} + +Between reading in the device tree and writing it out in the new +format, \dtc performs a number of checks on the tree: +\begin{itemize} +\item \emph{syntactic structure}: \dtc checks that node and property + names contain only allowed characters and meet length restrictions. + It checks that a node does not have multiple properties or subnodes + with the same name. +\item \emph{semantic structure}: In some cases, \dtc checks that + properties whose contents are defined by convention have appropriate + values. For example, it checks that \texttt{reg} properties have a + length that makes sense given the address forms specified by the + \texttt{\#address-cells} and \texttt{\#size-cells} properties. It + checks that properties such as \texttt{interrupt-parent} contain a + valid phandle. +\item \emph{Linux requirements}: \dtc checks that the device tree + contains those nodes and properties that are required by the Linux + kernel to boot correctly. +\end{itemize} + +These checks are useful to catch simple problems with the device tree, +rather than having to debug the results on an embedded kernel. With +the blob input mode, it can also be used for diagnosing problems with +an existing blob. + +\section{Future Work} + +\subsection{Board ports} + +The flattened device tree has always been the only supported way to +boot a \texttt{ppc64} kernel on an embedded system. With the merge of +\texttt{ppc32} and \texttt{ppc64} code it has also become the only +supported way to boot any merged \texttt{powerpc} kernel, 32-bit or +64-bit. In fact, the old \texttt{ppc} architecture exists mainly just +to support the old ppc32 embedded ports that have not been migrated +to the flattened device tree approach. We plan to remove the +\texttt{ppc} architecture eventually, which will mean porting all the +various embedded boards to use the flattened device tree. + +\subsection{\dtc features} + +While it is already quite usable, there are a number of extra features +that \dtc could include to make creating device trees more convenient: +\begin{itemize} +\item \emph{better tree checking}: Although \dtc already performs a + number of checks on the device tree, they are rather haphazard. In + many cases \dtc will give up after detecting a minor error early and + won't pick up more interesting errors later on. There is a + \texttt{-f} parameter that forces \dtc to generate an output tree + even if there are errors. At present, this needs to be used more + often than one might hope, because \dtc is bad at deciding which + errors should really be fatal, and which rate mere warnings. +\item \emph{binary include}: Occasionally, it is useful for the device + tree to incorporate as a property a block of binary data for some + board-specific purpose. For example, many of Apple's device trees + incorporate bytecode drivers for certain platform devices. \dtc's + source format ought to allow this by letting a property's value be + read directly from a binary file. +\item \emph{macros}: it might be useful for \dtc to implement some + sort of macros so that a tree containing a number of similar devices + (for example, multiple identical ethernet controllers or PCI buses) + can be written more quickly. At present, this can be accomplished + in part by running the source file through CPP before compiling with + \dtc. It's not clear whether ``native'' support for macros would be + more useful. +\end{itemize} + +\bibliographystyle{amsplain} +\bibliography{dtc-paper} + +\section*{About the authors} + +David Gibson has been a member of the IBM Linux Technology Center, +working from Canberra, Australia, since 2001. Recently he has worked +on Linux hugepage support and performance counter support for ppc64, +as well as the device tree compiler. In the past, he has worked on +bringup for various ppc and ppc64 embedded systems, the orinoco +wireless driver, ramfs, and a userspace checkpointing system +(\texttt{esky}). + +Benjamin Herrenschmidt was a MacOS developer for about 10 years, but +ultimately saw the light and installed Linux on his Apple PowerPC +machine. After writing a bootloader, BootX, for it in 1998, he +started contributing to the PowerPC Linux port in various areas, +mostly around the support for Apple machines. He became official +PowerMac maintainer in 2001. In 2003, he joined the IBM Linux +Technology Center in Canberra, Australia, where he ported the 64 bit +PowerPC kernel to Apple G5 machines and the Maple embedded board, +among others things. He's a member of the ppc64 development ``team'' +and one of his current goals is to make the integration of embedded +platforms smoother and more maintainable than in the 32-bit PowerPC +kernel. + +\section*{Legal Statement} + +This work represents the view of the author and does not necessarily +represent the view of IBM. + +IBM, \ppc, \ppc Architecture, POWER5, pSeries and iSeries are +trademarks or registered trademarks of International Business Machines +Corporation in the United States and/or other countries. + +Apple and Power Macintosh are a registered trademarks of Apple +Computer Inc. in the United States, other countries, or both. + +Linux is a registered trademark of Linus Torvalds. + +Other company, product, and service names may be trademarks or service +marks of others. + +\end{document} diff --git a/util/dtc/Makefile b/util/dtc/Makefile new file mode 100644 index 0000000000..8439f560bd --- /dev/null +++ b/util/dtc/Makefile @@ -0,0 +1,42 @@ +TARGETS = dtc ftdump +CFLAGS = -Wall -g + +BISON = bison + +DTC_OBJS = dtc.o livetree.o flattree.o data.o treesource.o fstree.o \ + dtc-parser.tab.o lex.yy.o + +OBJS = $(DTC_OBJS) libdt.o ftdump.o + +DEPFILES = $(DTC_OBJS:.o=.d) + +all: $(TARGETS) + +dtc: $(DTC_OBJS) + $(LINK.c) -o $@ $^ + +ftdump: ftdump.o + $(LINK.c) -o $@ $^ + +dtc-parser.tab.c dtc-parser.tab.h dtc-parser.output: dtc-parser.y + $(BISON) -d $< + +lex.yy.c: dtc-lexer.l + $(LEX) $< + +lex.yy.o: lex.yy.c dtc-parser.tab.h + +check: all + cd tests && $(MAKE) check + +clean: + rm -f *~ *.o a.out core $(TARGETS) + rm -f *.tab.[ch] lex.yy.c + rm -f *.i *.output vgcore.* + rm -f *.d + cd tests && $(MAKE) clean + +%.d: %.c + $(CC) -MM -MG -MT "$*.o $@" $< > $@ + +-include $(DEPFILES) diff --git a/util/dtc/TODO b/util/dtc/TODO new file mode 100644 index 0000000000..52d480e8ad --- /dev/null +++ b/util/dtc/TODO @@ -0,0 +1,14 @@ +- Bugfixes: + * Proper handling of boot cpu information +- Error handling / reporting + * Better categorization of errors into severity levels + * Don't stop checking for later errors because of earlier ones + whenever possible +- Generate mem reserve map + * linux,reserve-map property + * generating reserve entry for device tree itself + * generating reserve entries from tce, rtas etc. properties + + +- Testsuite +- Actually number releases, all that kind of jazz diff --git a/util/dtc/comment-test.dts b/util/dtc/comment-test.dts new file mode 100644 index 0000000000..01743478fe --- /dev/null +++ b/util/dtc/comment-test.dts @@ -0,0 +1,38 @@ +/* regexps for lexing comments are.. tricky. Check if we've actually + * got it right */ + +/ { + // line comment + prop1; + /* comment */ + prop2; + /* multiline + + notaprop1; + + comment */ + prop3; + /**/ + prop4; + /***/ + prop5; + /****/ + prop6; + /* another + * multiline + * comment */ + prop7; + /* yet + * another + * multline + * comment + */ + prop8; + /** try this */ + prop9; + /* and this **/ + prop10; + child /* finally */ { + }; +}; +/* final comment */ diff --git a/util/dtc/data.c b/util/dtc/data.c new file mode 100644 index 0000000000..d3b55f9ffe --- /dev/null +++ b/util/dtc/data.c @@ -0,0 +1,276 @@ +/* + * (C) Copyright David Gibson , 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 fixup_free(struct fixup *f) +{ + free(f->ref); + free(f); +} + +void data_free(struct data d) +{ + struct fixup *f; + + f = d.refs; + while (f) { + struct fixup *nf; + + nf = f->next; + fixup_free(f); + f = nf; + } + + assert(!d.val || d.asize); + + if (d.val) + free(d.val); +} + +struct data data_grow_for(struct data d, int xlen) +{ + struct data nd; + int newsize; + + /* we must start with an allocated datum */ + assert(!d.val || d.asize); + + if (xlen == 0) + return d; + + newsize = xlen; + + while ((d.len + xlen) > newsize) + newsize *= 2; + + nd.asize = newsize; + nd.val = xrealloc(d.val, newsize); + nd.len = d.len; + nd.refs = d.refs; + + assert(nd.asize >= (d.len + xlen)); + + return nd; +} + +struct data data_copy_mem(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(char *s, int *i) +{ + char x[4]; + char *endx; + long val; + + x[3] = '\0'; + x[0] = s[(*i)]; + if (x[0]) { + x[1] = s[(*i)+1]; + if (x[1]) + x[2] = s[(*i)+2]; + } + + val = strtol(x, &endx, 8); + if ((endx - x) == 0) + fprintf(stderr, "Empty \\nnn escape\n"); + + (*i) += endx - x; + return val; +} + +static char get_hex_char(char *s, int *i) +{ + char x[3]; + char *endx; + long val; + + x[2] = '\0'; + x[0] = s[(*i)]; + if (x[0]) + x[1] = s[(*i)+1]; + + val = strtol(x, &endx, 16); + if ((endx - x) == 0) + fprintf(stderr, "Empty \\x escape\n"); + + (*i) += endx - x; + return val; +} + +struct data data_copy_escape_string(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 't': + q[d.len++] = '\t'; + break; + case 'n': + q[d.len++] = '\n'; + 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 len) +{ + struct data d; + + d = data_grow_for(empty_data, len); + + d.len = len; + fread(d.val, len, 1, f); + + return d; +} + +struct data data_append_data(struct data d, 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_append_cell(struct data d, cell_t word) +{ + cell_t beword = cpu_to_be32(word); + + return data_append_data(d, &beword, sizeof(beword)); +} + +struct data data_append_re(struct data d, struct reserve_entry *re) +{ + struct reserve_entry bere; + + bere.address = cpu_to_be64(re->address); + bere.size = cpu_to_be64(re->size); + + return data_append_data(d, &bere, sizeof(bere)); +} + +struct data data_append_addr(struct data d, u64 addr) +{ + u64 beaddr = cpu_to_be64(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_fixup(struct data d, char *ref) +{ + struct fixup *f; + struct data nd; + + f = xmalloc(sizeof(*f)); + f->offset = d.len; + f->ref = ref; + f->next = d.refs; + + nd = d; + nd.refs = f; + + return nd; +} + +int data_is_one_string(struct data d) +{ + int i; + int len = d.len; + + if (len == 0) + return 0; + + for (i = 0; i < len-1; i++) + if (d.val[i] == '\0') + return 0; + + if (d.val[len-1] != '\0') + return 0; + + return 1; +} diff --git a/util/dtc/dtc-lexer.l b/util/dtc/dtc-lexer.l new file mode 100644 index 0000000000..c0822282cd --- /dev/null +++ b/util/dtc/dtc-lexer.l @@ -0,0 +1,180 @@ +/* + * (C) Copyright David Gibson , 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 CELLDATA +%x BYTESTRING +%x MEMRESERVE + +PROPCHAR [a-zA-Z0-9,._+*#?-] +UNITCHAR [0-9a-f,] +WS [ \t\n] + +REFCHAR ({PROPCHAR}|{UNITCHAR}|[/@]) + +%{ +#include "dtc.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 + + + +%} + +%% + +\"[^"]*\" { + 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; + } + +"/memreserve/" { + yylloc.first_line = yylineno; + DPRINT("Keyword: /memreserve/\n"); + BEGIN(MEMRESERVE); + return DT_MEMRESERVE; + } + +[0-9a-fA-F]+ { + yylloc.first_line = yylineno; + if (yyleng > 2*sizeof(yylval.addr)) { + fprintf(stderr, "Address value %s too large\n", + yytext); + } + yylval.addr = (u64) strtoull(yytext, NULL, 16); + DPRINT("Addr: %llx\n", + (unsigned long long)yylval.addr); + return DT_ADDR; + } + +";" { + yylloc.first_line = yylineno; + DPRINT("/MEMRESERVE\n"); + BEGIN(INITIAL); + return ';'; + } + +[0-9a-fA-F]+ { + yylloc.first_line = yylineno; + if (yyleng > 2*sizeof(yylval.cval)) { + fprintf(stderr, + "Cell value %s too long\n", yytext); + } + yylval.cval = strtoul(yytext, NULL, 16); + DPRINT("Cell: %x\n", yylval.cval); + return DT_CELL; + } + +">" { + yylloc.first_line = yylineno; + DPRINT("/CELLDATA\n"); + BEGIN(INITIAL); + return '>'; + } + +\&{REFCHAR}* { + yylloc.first_line = yylineno; + DPRINT("Ref: %s\n", yytext+1); + yylval.str = strdup(yytext+1); + return DT_REF; + } + +[0-9a-fA-F]{2} { + yylloc.first_line = yylineno; + yylval.byte = strtol(yytext, NULL, 16); + DPRINT("Byte: %02x\n", (int)yylval.byte); + return DT_BYTE; + } + +"]" { + yylloc.first_line = yylineno; + DPRINT("/BYTESTRING\n"); + BEGIN(INITIAL); + return ']'; + } + +{PROPCHAR}+ { + yylloc.first_line = yylineno; + DPRINT("PropName: %s\n", yytext); + yylval.str = strdup(yytext); + return DT_PROPNAME; + } + +{PROPCHAR}+(@{UNITCHAR}+)? { + yylloc.first_line = yylineno; + DPRINT("NodeName: %s\n", yytext); + yylval.str = strdup(yytext); + return DT_NODENAME; + } + + +[a-zA-Z_][a-zA-Z0-9_]*: { + yylloc.first_line = yylineno; + DPRINT("Label: %s\n", yytext); + yylval.str = strdup(yytext); + yylval.str[yyleng-1] = '\0'; + return DT_LABEL; + } + +<*>{WS}+ /* eat whitespace */ + +<*>"/*"([^*]|\*+[^*/])*\*+"/" { + yylloc.first_line = yylineno; + DPRINT("Comment: %s\n", yytext); + /* eat comments */ + } + +<*>"//".*\n /* eat line comments */ + +<*>. { + yylloc.first_line = yylineno; + switch (yytext[0]) { + case '<': + DPRINT("CELLDATA\n"); + BEGIN(CELLDATA); + break; + case '[': + DPRINT("BYTESTRING\n"); + BEGIN(BYTESTRING); + break; + default: + + DPRINT("Char: %c (\\x%02x)\n", yytext[0], + (unsigned)yytext[0]); + break; + } + + return yytext[0]; + } + +%% diff --git a/util/dtc/dtc-parser.y b/util/dtc/dtc-parser.y new file mode 100644 index 0000000000..16d22770c8 --- /dev/null +++ b/util/dtc/dtc-parser.y @@ -0,0 +1,162 @@ +/* + * (C) Copyright David Gibson , 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 + */ + +%glr-parser +%locations + +%{ +#include "dtc.h" + +int yylex (void); +void yyerror (char const *); + +extern struct boot_info *the_boot_info; + +%} + +%union { + cell_t cval; + u8 byte; + char *str; + struct data data; + struct property *prop; + struct property *proplist; + struct node *node; + struct node *nodelist; + int datalen; + int hexlen; + u64 addr; + struct reserve_info *re; +} + +%token DT_MEMRESERVE +%token DT_ADDR +%token DT_PROPNAME +%token DT_NODENAME +%token DT_CELL +%token DT_BYTE +%token DT_STRING +%token DT_UNIT +%token DT_LABEL +%token DT_REF + +%type propdata +%type memreserve +%type memreserves +%type celllist +%type bytestring +%type propdef +%type proplist + +%type devicetree +%type nodedef +%type subnode +%type subnodes +%type label +%type nodename + +%% + +sourcefile: memreserves devicetree { + the_boot_info = build_boot_info($1, $2); + } + ; + +memreserves: memreserve memreserves { + $$ = chain_reserve_entry($1, $2); + } + | /* empty */ { + $$ = NULL; + } + ; + +memreserve: DT_MEMRESERVE DT_ADDR DT_ADDR ';' { + $$ = build_reserve_entry($2, $3, NULL); + } + | DT_MEMRESERVE DT_ADDR '-' DT_ADDR ';' { + $$ = build_reserve_entry($2, $4 - $2 + 1, NULL); + } + ; + +devicetree: '/' nodedef { + $$ = name_node($2, "", NULL); + } + ; + +nodedef: '{' proplist subnodes '}' ';' { + $$ = build_node($2, $3); + } + ; + +proplist: propdef proplist { + $$ = chain_property($1, $2); + } + | /* empty */ { + $$ = NULL; + } + ; + +propdef: label DT_PROPNAME '=' propdata ';' { + $$ = build_property($2, $4, $1); + } + | label DT_PROPNAME ';' { + $$ = build_property($2, empty_data, $1); + } + ; + +propdata: DT_STRING { $$ = $1; } + | '<' celllist '>' { $$ = $2; } + | '[' bytestring ']' { $$ = $2; } + ; + +celllist: celllist DT_CELL { $$ = data_append_cell($1, $2); } + | celllist DT_REF { + $$ = data_append_cell(data_add_fixup($1, $2), -1); + } + | /* empty */ { $$ = empty_data; } + ; + +bytestring: bytestring DT_BYTE { $$ = data_append_byte($1, $2); } + | /* empty */ { $$ = empty_data; } + ; + +subnodes: subnode subnodes { + $$ = chain_node($1, $2); + } + | /* empty */ { $$ = NULL; } + ; + +subnode: label nodename nodedef { $$ = name_node($3, $2, $1); } + ; + +nodename: DT_NODENAME { $$ = $1; } + | DT_PROPNAME { $$ = $1; } + ; + +label: DT_LABEL { $$ = $1; } + | /* empty */ { $$ = NULL; } + ; + +%% + +void yyerror (char const *s) +{ + fprintf (stderr, "%s at line %d\n", s, yylloc.first_line); +} diff --git a/util/dtc/dtc.c b/util/dtc/dtc.c new file mode 100644 index 0000000000..d11282a3c9 --- /dev/null +++ b/util/dtc/dtc.c @@ -0,0 +1,209 @@ +/* + * (C) Copyright David Gibson , 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" + +char *join_path(char *path, 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; +} + +void fill_fullpaths(struct node *tree, char *prefix) +{ + struct node *child; + 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 FILE *dtc_open_file(char *fname) +{ + FILE *f; + + if (streq(fname, "-")) + f = stdin; + else + f = fopen(fname, "r"); + + if (! f) + die("Couldn't open \"%s\": %s\n", fname, strerror(errno)); + + return f; +} + +static void usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\tdtc [options] \n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, "\t-I \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 \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\t\tlinuxbios (or just lb) - linuxbios source\n"); + fprintf(stderr, "\t-V \n"); + fprintf(stderr, "\t\tBlob version to produce, defaults to 3 (relevant for dtb\n\t\tand asm output only)\n"); + fprintf(stderr, "\t-R \n"); + fprintf(stderr, "\t\tMake space for reserve map entries (relevant for \n\t\tdtb and asm output only)\n"); + fprintf(stderr, "\t-b \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"); + exit(2); +} + +int main(int argc, char *argv[]) +{ + struct boot_info *bi; + char *inform = "dts"; + char *outform = "dts"; + char *outname = "-"; + int force = 0; + char *arg; + int opt; + FILE *inf = NULL; + FILE *outf = NULL; + int outversion = 3; + int reservenum = 1; + int boot_cpuid_phys = 0xfeedbeef; + + while ((opt = getopt(argc, argv, "I:O:o:V:R:fb:")) != 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 'f': + force = 1; + break; + case 'b': + boot_cpuid_phys = strtol(optarg, NULL, 0); + break; + default: + usage(); + } + } + + if (argc > (optind+1)) + usage(); + else if (argc < (optind+1)) + arg = "-"; + else + arg = argv[optind]; + + fprintf(stderr, "DTC: %s->%s on file \"%s\"\n", + inform, outform, arg); + + if (streq(inform, "dts")) { + inf = dtc_open_file(arg); + bi = dt_from_source(inf); + } else if (streq(inform, "fs")) { + bi = dt_from_fs(arg); + } else if(streq(inform, "dtb")) { + inf = dtc_open_file(arg); + bi = dt_from_blob(inf); + } else { + die("Unknown input format \"%s\"\n", inform); + } + + if (inf && (inf != stdin)) + fclose(inf); + + if (! bi || ! bi->dt) + die("Couldn't read input tree\n"); + + if (! check_device_tree(bi->dt, outversion, boot_cpuid_phys)) { + fprintf(stderr, "Input tree has errors\n"); + if (! force) + exit(1); + } + + 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, boot_cpuid_phys); + } else if (streq(outform, "asm")) { + dt_to_asm(outf, bi, outversion, boot_cpuid_phys); + } else if (streq(outform, "C")) { + dt_to_C(outf, bi, outversion, boot_cpuid_phys); + } else if (streq(outform, "linuxbios") || streq(outform, "lb")) { + dt_to_linuxbios(outf, bi, outversion, boot_cpuid_phys); + } else if (streq(outform, "null")) { + /* do nothing */ + } else { + die("Unknown output format \"%s\"\n", outform); + } + + exit(0); +} diff --git a/util/dtc/dtc.h b/util/dtc/dtc.h new file mode 100644 index 0000000000..7366504eef --- /dev/null +++ b/util/dtc/dtc.h @@ -0,0 +1,233 @@ +#ifndef _DTC_H +#define _DTC_H + +/* + * (C) Copyright David Gibson , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flat_dt.h" + +static inline void 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 uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef u32 cell_t; + +#define cpu_to_be16(x) htons(x) +#define be16_to_cpu(x) ntohs(x) + +#define cpu_to_be32(x) htonl(x) +#define be32_to_cpu(x) ntohl(x) + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be64(x) (x) +#define be64_to_cpu(x) (x) +#else +#define cpu_to_be64(x) bswap_64(x) +#define be64_to_cpu(x) bswap_64(x) +#endif + +#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 */ +struct fixup { + int offset; + char *ref; + struct fixup *next; +}; + +struct data { + int len; + char *val; + int asize; + struct fixup *refs; +}; + +#define empty_data \ + ((struct data){.len = 0, .val = NULL, .asize = 0, .refs = NULL}) + +void fixup_free(struct fixup *f); +void data_free(struct data d); + +struct data data_grow_for(struct data d, int xlen); + +struct data data_copy_mem(char *mem, int len); +struct data data_copy_escape_string(char *s, int len); +struct data data_copy_file(FILE *f, size_t len); + +struct data data_append_data(struct data d, void *p, int len); +struct data data_append_cell(struct data d, cell_t word); +struct data data_append_re(struct data d, struct reserve_entry *re); +struct data data_append_addr(struct data d, u64 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_fixup(struct data d, 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 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); + +int check_device_tree(struct node *dt, int outversion, int boot_cpuid_phys); + +/* Boot info (tree plus memreserve information */ + +struct reserve_info { + struct reserve_entry re; + + struct reserve_info *next; + + char *label; +}; + +struct reserve_info *build_reserve_entry(u64 start, u64 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 */ +}; + +struct boot_info *build_boot_info(struct reserve_info *reservelist, + struct node *tree); + +/* Flattened trees */ + +void dt_to_blob(FILE *f, struct boot_info *bi, int version, + int boot_cpuid_phys); +void dt_to_asm(FILE *f, struct boot_info *bi, int version, + int boot_cpuid_phys); +void dt_to_C(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys); +void dt_to_linuxbios(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys); + +struct boot_info *dt_from_blob(FILE *f); + +/* Tree source */ + +void dt_to_source(FILE *f, struct boot_info *bi); +struct boot_info *dt_from_source(FILE *f); + +/* FS trees */ + +struct boot_info *dt_from_fs(char *dirname); + +/* misc */ + +char *join_path(char *path, char *name); +void fill_fullpaths(struct node *tree, char *prefix); + +#endif /* _DTC_H */ diff --git a/util/dtc/dtsqemu b/util/dtc/dtsqemu new file mode 100644 index 0000000000..f14ada765b --- /dev/null +++ b/util/dtc/dtsqemu @@ -0,0 +1,50 @@ +/{ + model = "qemu"; + #address-cells = <1>; + #size-cells = <1>; + compatible = "emulation-i386,qemu"; + cpus { + #address-cells = <1>; + #size-cells = <0>; + + emulation,qemu-i386@0{ + name = "emulation,qemu-i386"; + device_type = "cpu"; + clock-frequency = <5f5e1000>; + timebase-frequency = <1FCA055>; + linux,boot-cpu; + reg = <0>; + i-cache-size = <2000>; + d-cache-size = <2000>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <00000000 20000000>; + }; + + /* the I/O stuff */ + northbridge,intel,440bx{ + associated-cpu = <&/cpus/emulation,qemu-i386@0>; + southbridge,intel,piix4{ + superio,nsc,sucks{ + uart@0{ + enabled=<1>; + }; + }; + }; + }; + + chosen { + bootargs = "root=/dev/sda2"; + linux,platform = <00000600>; + linux,stdout-path="/dev/ttyS0"; + }; + options { + normal="normal"; + fallback="fallback"; + }; + + +}; diff --git a/util/dtc/flat_dt.h b/util/dtc/flat_dt.h new file mode 100644 index 0000000000..5b1dcb66e2 --- /dev/null +++ b/util/dtc/flat_dt.h @@ -0,0 +1,45 @@ +#ifndef _FLAT_DT_H_ +#define _FLAT_DT_H_ + + +#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_NOP 0x4 /* nop */ +#define OF_DT_END 0x9 + +struct boot_param_header { + uint32_t magic; /* magic word OF_DT_HEADER */ + 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 */ +}; + +#define BPH_V1_SIZE (7*sizeof(uint32_t)) +#define BPH_V2_SIZE (BPH_V1_SIZE + sizeof(uint32_t)) +#define BPH_V3_SIZE (BPH_V2_SIZE + sizeof(uint32_t)) + +struct reserve_entry { + uint64_t address; + uint64_t size; +}; + +struct flat_dt_property { + uint32_t nameoff; + uint32_t len; + char data[0]; +}; + +#endif /* _FLAT_DT_H_ */ diff --git a/util/dtc/flattree.c b/util/dtc/flattree.c new file mode 100644 index 0000000000..66f6e7134d --- /dev/null +++ b/util/dtc/flattree.c @@ -0,0 +1,1389 @@ +/* + * (C) Copyright David Gibson , 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 "flat_dt.h" + +#define FTF_FULLPATH 0x1 +#define FTF_VARALIGN 0x2 +#define FTF_NAMEPROPS 0x4 +#define FTF_BOOTCPUID 0x8 +#define FTF_STRTABSIZE 0x10 + +static struct version_info { + int version; + int last_comp_version; + int hdr_size; + int flags; +} version_table[] = { + {1, 1, BPH_V1_SIZE, + FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS}, + {2, 1, BPH_V2_SIZE, + FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID}, + {3, 1, BPH_V3_SIZE, + FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE}, + {0x10, 0x10, BPH_V3_SIZE, + FTF_BOOTCPUID|FTF_STRTABSIZE}, +}; + +struct emitter { + void (*cell)(void *, cell_t); + void (*string)(void *, char *, int); + void (*align)(void *, int); + void (*data)(void *, struct property *); + void (*beginnode)(void *, char *); + void (*endnode)(void *, char *); + void (*property)(void *, char *); + void(*special)(FILE *f, struct node *); +}; + +char * +clean(char *name, int instantiate){ + char *cleanname = strdup(name), *cp; + + for(cp = cleanname; *cp; cp++) + switch(*cp) { + case '#': + *cp = 'H'; + break; + case '@': + /* if we are declaring it, this is an instance. If not, this is a struct type */ + if (instantiate) + *cp = '_'; + else + *cp = 0; + break; + case '-': + case ',': + *cp = '_'; + break; + } + + return cleanname; +} + + + +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 property *p) +{ + struct data d = p->val; + struct data *dtbuf = e; + + *dtbuf = data_append_data(*dtbuf, d.val, d.len); +} + +static void bin_emit_beginnode(void *e, char *label) +{ + bin_emit_cell(e, OF_DT_BEGIN_NODE); +} + +static void bin_emit_endnode(void *e, char *label) +{ + bin_emit_cell(e, OF_DT_END_NODE); +} + +static void bin_emit_property(void *e, char *label) +{ + bin_emit_cell(e, OF_DT_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, char *prefix, 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 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; + + 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 property *p) +{ + struct data d = p->val; + FILE *f = e; + int off = 0; + + while ((d.len - off) >= sizeof(u32)) { + fprintf(f, "\t.long\t0x%x\n", + be32_to_cpu(*((u32 *)(d.val+off)))); + off += sizeof(u32); + } + + if ((d.len - off) >= sizeof(u16)) { + fprintf(f, "\t.short\t0x%hx\n", + be16_to_cpu(*((u16 *)(d.val+off)))); + off += sizeof(u16); + } + + if ((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, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "\t.globl\t%s\n", label); + fprintf(f, "%s:\n", label); + } + fprintf(f, "\t.long\tOF_DT_BEGIN_NODE\n"); +} + +static void asm_emit_endnode(void *e, char *label) +{ + FILE *f = e; + + fprintf(f, "\t.long\tOF_DT_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, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "\t.globl\t%s\n", label); + fprintf(f, "%s:\n", label); + } + fprintf(f, "\t.long\tOF_DT_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, +}; + +int unique = 0; +static void C_emit_cell(void *e, cell_t val) +{ + FILE *f = e; + + fprintf(f, "\tu32\tc%d = 0x%x;\n", unique++, val); +} + +static void C_emit_string(void *e, char *str, int len) +{ + FILE *f = e; + char c; + + if (len != 0) { + /* XXX: ewww */ + c = str[len]; + str[len] = '\0'; + } + + fprintf(f, "\tunsigned char *c%d = \t\"%s\";\n", unique++, str); + + if (len != 0) { + str[len] = c; + } +} + +static void C_emit_align(void *e, int a) +{ + FILE *f = e; + + fprintf(f, "\tALIGN(x)\t%d\n", a); +} + +static void C_emit_data(void *e, struct property *p) +{ + struct data d = p->val; + FILE *f = e; + int off = 0; + + while ((d.len - off) >= sizeof(u32)) { + fprintf(f, "\tu32 d%d = \t0x%x;\n",unique++, + be32_to_cpu(*((u32 *)(d.val+off)))); + off += sizeof(u32); + } + + if ((d.len - off) >= sizeof(u16)) { + fprintf(f, "\tu16 d%d = \t0x%hx;\n", unique++, + be16_to_cpu(*((u16 *)(d.val+off)))); + off += sizeof(u16); + } + + if ((d.len - off) >= 1) { + fprintf(f, "\tu8 d%d = \t0x%hhx;\n", unique++, d.val[off]); + off += 1; + } + + assert(off == d.len); +} + +static void C_emit_beginnode(void *e, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "struct\t%s {\n", label); + } + fprintf(f, "\tu32\tn%d = OF_DT_BEGIN_NODE;\n", unique++); +} + +static void C_emit_endnode(void *e, char *label) +{ + FILE *f = e; + + fprintf(f, "\tu32\ten%d = OF_DT_END_NODE;\n", unique++); + if (label) { + fprintf(f, "\t\t%s;\n", label); + } +} + +static void C_emit_struct_start(void *e, char *prefix, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "struct\t%s {\n", label); + } +} + +static void C_emit_struct_end(void *e, char *prefix, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "}; /*%s*/\n", label); + } +} + + + +static void C_emit_property(void *e, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "\tstruct p%d\t%s{\n", unique++, label); + } + fprintf(f, "\tu32 p%d = \tOF_DT_PROP;\n", unique++); +} + +static struct emitter C_emitter = { + .cell = C_emit_cell, + .string = C_emit_string, + .align = C_emit_align, + .data = C_emit_data, + .beginnode = C_emit_beginnode, + .endnode = C_emit_endnode, + .property = C_emit_property, +}; + + +/* linuxbios static.c */ + +static void linuxbios_emit_cell(void *e, cell_t val) +{ + FILE *f = e; + + fprintf(f, "\tu32\tc%d = 0x%x;\n", unique++, val); +} + +static void linuxbios_emit_string(void *e, char *str, int len) +{ + FILE *f = e; + char c; + + if (len != 0) { + /* XXX: ewww */ + c = str[len]; + str[len] = '\0'; + } + + fprintf(f, "\tunsigned char *c%d = \t\"%s\";\n", unique++, str); + + if (len != 0) { + str[len] = c; + } +} + +static void linuxbios_emit_align(void *e, int a) +{ + FILE *f = e; + + fprintf(f, "\tALIGN(x)\t%d\n", a); +} + +static void linuxbios_emit_data(void *e, struct property *p) +{ + struct data d = p->val; + FILE *f = e; + int i; + char *cleanname; + + /* nothing to do? */ + if (d.len == 0) + return; + + cleanname = clean(p->name, 1); + fprintf(f, "\tu8 %s = {", cleanname); + free(cleanname); + + for(i = 0; i < d.len; i++) + fprintf(f, "%02x,", d.val[i]); + + fprintf(f, "};\n"); +} + +static void linuxbios_emit_beginnode(void *e, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "struct\t%s {\n", label); + } +} + +static void linuxbios_emit_endnode(void *e, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "}; /*%s*/\n", label); + } +} + +static void linuxbios_emit_struct_start(void *e, char *prefix, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "struct\t%s {\n", label); + } +} + +static void linuxbios_emit_struct_end(void *e, char *prefix, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "}; /*%s*/\n", label); + } +} + + + +static void linuxbios_emit_property(void *e, char *label) +{ + FILE *f = e; + + if (label) { + fprintf(f, "\tstruct p%d\t%s{\n", unique++, label); + } + fprintf(f, "\tu32 p%d = \tOF_DT_PROP;\n", unique++); +} + +static void linuxbios_emit_special(void *e, struct node *tree) +{ + FILE *f = e; + + fprintf(f, "struct device dev_%s = {\n", tree->label); + if (tree->next_sibling) + fprintf(f, "\t.sibling = dev_%s\n", tree->next_sibling->label); + /* now do we do next? */ + if (tree->children) + fprintf(f, "\t.children = dev_%s\n", tree->children->label); + + fprintf(f, "}\n"); +} + +static struct emitter linuxbios_emitter = { + .cell = linuxbios_emit_cell, + .string = linuxbios_emit_string, + .align = linuxbios_emit_align, + .data = linuxbios_emit_data, + .beginnode = linuxbios_emit_beginnode, + .endnode = linuxbios_emit_endnode, + .property = linuxbios_emit_property, + .special = linuxbios_emit_special, +}; + + +static int stringtable_insert(struct data *d, 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_emit_structdecls(struct node *tree, struct emitter *emit, + void *etarget, struct data *strbuf, + struct version_info *vi) +{ + char *treename; + + struct property *prop; + struct node *child; + int seen_name_prop = 0; + FILE *f = etarget; + + treename = clean(tree->name, 0); + emit->beginnode(etarget, treename); + + +#if 0 + if (vi->flags & FTF_FULLPATH) + emit->string(etarget, tree->fullpath, 0); + else + emit->string(etarget, tree->name, 0); +#endif + + for_each_property(tree, prop) { + char *cleanname; + if (streq(prop->name, "name")) + seen_name_prop = 1; + cleanname = clean(prop->name, 0); + fprintf(f, "\tu32 %s;\n", cleanname); + free(cleanname); + + } +#if 0 + if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { + fprintf(f, "\tu32 %s\n", prop->name); + } +#endif + emit->endnode(etarget, treename); + free(treename); + + for_each_child(tree, child) { + flatten_tree_emit_structdecls(child, emit, etarget, strbuf, vi); + } + + +} + +int structunique = 0; + +static void flatten_tree_emit_structinits(struct node *tree, struct emitter *emit, + void *etarget, struct data *strbuf, + struct version_info *vi) +{ + char *treename; + + struct property *prop; + struct node *child; + int seen_name_prop = 0; + FILE *f = etarget; + + treename = clean(tree->name, 0); + fprintf(f, "struct %s %s = {\n", treename, tree->label); + free(treename); + + +#if 0 + if (vi->flags & FTF_FULLPATH) + emit->string(etarget, tree->fullpath, 0); + else + emit->string(etarget, tree->name, 0); +#endif + + for_each_property(tree, prop) { + if (streq(prop->name, "name")) + seen_name_prop = 1; + emit->data(etarget, prop); + } +#if 0 + if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { + fprintf(f, "\tu32 %s\n", prop->name); + } +#endif + + emit->endnode(etarget, tree->label); + + /* now emit the device for this node, with sibling and child pointers etc. */ + emit->special(f, tree); + + for_each_child(tree, child) { + flatten_tree_emit_structinits(child, emit, etarget, strbuf, vi); + } + + +} + + +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); + 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; + + for (re = reservelist; re; re = re->next) { + d = data_append_re(d, &re->re); + } + + return d; +} +static void make_bph(struct boot_param_header *bph, + struct version_info *vi, + int reservesize, int dtsize, int strsize, + int boot_cpuid_phys) +{ + int reserve_off; + + reservesize += sizeof(struct reserve_entry); + + memset(bph, 0xff, sizeof(*bph)); + + bph->magic = cpu_to_be32(OF_DT_HEADER); + bph->version = cpu_to_be32(vi->version); + bph->last_comp_version = cpu_to_be32(vi->last_comp_version); + + /* Reserve map should be doubleword aligned */ + reserve_off = ALIGN(vi->hdr_size, 8); + + bph->off_mem_rsvmap = cpu_to_be32(reserve_off); + bph->off_dt_struct = cpu_to_be32(reserve_off + reservesize); + bph->off_dt_strings = cpu_to_be32(reserve_off + reservesize + + dtsize); + bph->totalsize = cpu_to_be32(reserve_off + reservesize + + dtsize + strsize); + + if (vi->flags & FTF_BOOTCPUID) + bph->boot_cpuid_phys = cpu_to_be32(boot_cpuid_phys); + if (vi->flags & FTF_STRTABSIZE) + bph->size_dt_strings = cpu_to_be32(strsize); +} + +void dt_to_blob(FILE *f, struct boot_info *bi, int version, + int boot_cpuid_phys) +{ + struct version_info *vi = NULL; + int i; + struct data dtbuf = empty_data; + struct data strbuf = empty_data; + struct data reservebuf; + struct boot_param_header bph; + struct reserve_entry termre = {.address = 0, .size = 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); + + dtbuf = empty_data; + strbuf = empty_data; + + flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi); + bin_emit_cell(&dtbuf, OF_DT_END); + + reservebuf = flatten_reserve_list(bi->reservelist, vi); + + /* Make header */ + make_bph(&bph, vi, reservebuf.len, dtbuf.len, strbuf.len, + boot_cpuid_phys); + + fwrite(&bph, vi->hdr_size, 1, f); + + /* Align the reserve map to an 8 byte boundary */ + for (i = vi->hdr_size; i < be32_to_cpu(bph.off_mem_rsvmap); i++) + fputc(0, f); + + /* + * Reserve map entries. + * Each entry is an (address, size) pair of u64 values. + * Always supply a zero-sized temination entry. + */ + fwrite(reservebuf.val, reservebuf.len, 1, f); + fwrite(&termre, sizeof(termre), 1, f); + + fwrite(dtbuf.val, dtbuf.len, 1, f); + fwrite(strbuf.val, strbuf.len, 1, f); + + if (ferror(f)) + die("Error writing device tree blob: %s\n", strerror(errno)); + + data_free(dtbuf); + data_free(strbuf); +} + +static void dump_stringtable_asm(FILE *f, struct data strbuf) +{ + 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, int boot_cpuid_phys) +{ + struct version_info *vi = NULL; + int i; + struct data strbuf = empty_data; + struct reserve_info *re; + 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 OF_DT_HEADER 0x%x\n", OF_DT_HEADER); + fprintf(f, "#define OF_DT_BEGIN_NODE 0x%x\n", OF_DT_BEGIN_NODE); + fprintf(f, "#define OF_DT_END_NODE 0x%x\n", OF_DT_END_NODE); + fprintf(f, "#define OF_DT_PROP 0x%x\n", OF_DT_PROP); + fprintf(f, "#define OF_DT_END 0x%x\n", OF_DT_END); + fprintf(f, "\n"); + + emit_label(f, symprefix, "blob_start"); + emit_label(f, symprefix, "header"); + fprintf(f, "\t.long\tOF_DT_HEADER /* magic */\n"); + fprintf(f, "\t.long\t_%s_blob_end - _%s_blob_start /* totalsize */\n", + symprefix, symprefix); + fprintf(f, "\t.long\t_%s_struct_start - _%s_blob_start /* off_dt_struct */\n", + symprefix, symprefix); + fprintf(f, "\t.long\t_%s_strings_start - _%s_blob_start /* off_dt_strings */\n", + symprefix, symprefix); + fprintf(f, "\t.long\t_%s_reserve_map - _%s_blob_start /* off_dt_strings */\n", + symprefix, symprefix); + fprintf(f, "\t.long\t%d /* version */\n", vi->version); + fprintf(f, "\t.long\t%d /* last_comp_version */\n", + vi->last_comp_version); + + if (vi->flags & FTF_BOOTCPUID) + fprintf(f, "\t.long\t%i\t/*boot_cpuid_phys*/\n", + 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); + + /* + * 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) { + fprintf(f, "\t.long\t0x%08x\n\t.long\t0x%08x\n", + (unsigned int)(re->re.address >> 32), + (unsigned int)(re->re.address & 0xffffffff)); + fprintf(f, "\t.long\t0x%08x\n\t.long\t0x%08x\n", + (unsigned int)(re->re.size >> 32), + (unsigned int)(re->re.size & 0xffffffff)); + } + + 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\tOF_DT_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"); + + data_free(strbuf); +} + +static void dump_stringtable_C(FILE *f, struct data strbuf) +{ + char *p; + int len; + + p = strbuf.val; + + fprintf(f, "\tchar *stringtable[] = {\n"); + while (p < (strbuf.val + strbuf.len)) { + len = strlen(p); + fprintf(f, "\t\"%s\"\n", p); + p += len+1; + } + fprintf(f, "\t};\n"); + +} + + +void dt_to_C(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys) +{ + struct version_info *vi = NULL; + int i; + struct data strbuf = empty_data; + struct reserve_info *re; + 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 OF_DT_HEADER 0x%x\n", OF_DT_HEADER); + fprintf(f, "#define OF_DT_BEGIN_NODE 0x%x\n", OF_DT_BEGIN_NODE); + fprintf(f, "#define OF_DT_END_NODE 0x%x\n", OF_DT_END_NODE); + fprintf(f, "#define OF_DT_PROP 0x%x\n", OF_DT_PROP); + fprintf(f, "#define OF_DT_END 0x%x\n", OF_DT_END); + fprintf(f, "\n"); + + C_emit_struct_start(f, symprefix, "blob"); + fprintf(f, "\tu32\tmagic = OF_DT_HEADER;\n"); + fprintf(f, "\tu32\ttotalsize = sizeof(struct blob);\n"); + fprintf(f, "\tu32\toffdt = offsetof(struct blob, dt);\n"); + fprintf(f, "\tu32\toffstrings = offsetof(struct blob, strings);\n"); + fprintf(f, "\tu32\toffreserve = offsetof(struct blob, reserve);\n"); + fprintf(f, "\tu32\tversion = %d;\n", vi->version); + fprintf(f, "\tu32\tlast_comp_version = %d;\n", + vi->last_comp_version); + + if (vi->flags & FTF_BOOTCPUID) + fprintf(f, "\tu32\tboot_cpuid_phys = 0x%x;\n", + boot_cpuid_phys); + + if (vi->flags & FTF_STRTABSIZE) + fprintf(f, "\tu32\tsize_%s_strings_end = sizeof(dt.strings);\n", + 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. + */ + C_emit_align(f, 8); + + fprintf(f, "\t/* 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. + */ + fprintf(f, "\tu64 reservemap[] = {\n"); + for (re = bi->reservelist; re; re = re->next) { + fprintf(f, "\tu64\t0x%qx\n", re->re.address); + fprintf(f, "\tu64\t0x%qx\n", re->re.size); + } + + + fprintf(f, "\t0, 0\n"); + fprintf(f, "\t};\n"); + + C_emit_struct_start(f, symprefix, "dt_blob"); + flatten_tree(bi->dt, &C_emitter, f, &strbuf, vi); + fprintf(f, "\tu32\tend = OF_DT_END;\n"); + C_emit_struct_end(f, symprefix, "dt_blob"); + + dump_stringtable_C(f, strbuf); + + C_emit_struct_end(f, symprefix, "blob"); + + data_free(strbuf); +} + +/* the label is not really used. So go ahead and make clean names for all labels */ + +void +labeltree(struct node *tree) +{ + struct node *child; + + tree->label = clean(tree->name, 1); + + if (tree->next_sibling) + labeltree(tree->next_sibling); + + for_each_child(tree, child) { + labeltree(child); + } + +} + + +void dt_to_linuxbios(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys) +{ + struct version_info *vi = NULL; + int i; + struct data strbuf = empty_data; + char *symprefix = "dt"; + + labeltree(bi->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); + + /* the root is special -- the parser gives it no name. We fix that here. + * fix in parser? + */ + bi->dt->name = bi->dt->label = "root"; + /* steps: emit all structs. Then emit the initializers, with the pointers to other structs etc. */ + + flatten_tree_emit_structdecls(bi->dt, &linuxbios_emitter, f, &strbuf, vi); + flatten_tree_emit_structinits(bi->dt, &linuxbios_emitter, f, &strbuf, vi); + 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 u32 flat_read_word(struct inbuf *inb) +{ + u32 val; + + assert(((inb->ptr - inb->base) % sizeof(val)) == 0); + + flat_read_chunk(inb, &val, sizeof(val)); + + return be32_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; + 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(u32)); + + 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(u32)); + + return d; +} + +static char *flat_read_stringtable(struct inbuf *inb, int offset) +{ + 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) +{ + u32 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; + char *p; + struct 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 = be64_to_cpu(re.address); + re.size = be64_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(char *ppath, char *cpath) +{ + char *lslash; + int plen; + + lslash = strrchr(cpath, '/'); + if (! lslash) + return NULL; + + plen = lslash - cpath; + + if (streq(cpath, "/") && streq(ppath, "")) + return ""; + + if ((plen == 0) && streq(ppath, "/")) + return strdup(lslash+1); + + if (! strneq(ppath, cpath, plen)) + return NULL; + + return strdup(lslash+1); +} + +static const char PROPCHAR[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,._+*#?-"; +static const char UNITCHAR[] = "0123456789abcdef,"; + +static int check_node_name(char *name) +{ + char *atpos; + int basenamelen; + + atpos = strrchr(name, '@'); + + if (atpos) + basenamelen = atpos - name; + else + basenamelen = strlen(name); + + if (strspn(name, PROPCHAR) < basenamelen) + return -1; + + if (atpos + && ((basenamelen + 1 + strspn(atpos+1, UNITCHAR)) < strlen(name))) + return -1; + + return basenamelen; +} + +static struct node *unflatten_tree(struct inbuf *dtbuf, + struct inbuf *strbuf, + char *parent_path, int flags) +{ + struct node *node; + u32 val; + + node = build_node(NULL, NULL); + + if (flags & FTF_FULLPATH) { + node->fullpath = flat_read_string(dtbuf); + node->name = nodename_from_path(parent_path, node->fullpath); + + if (! node->name) + die("Path \"%s\" is not valid as a child of \"%s\"\n", + node->fullpath, parent_path); + } else { + node->name = flat_read_string(dtbuf); + node->fullpath = join_path(parent_path, node->name); + } + + node->basenamelen = check_node_name(node->name); + if (node->basenamelen < 0) { + fprintf(stderr, "Warning \"%s\" has incorrect format\n", node->name); + } + + do { + struct property *prop; + struct node *child; + + val = flat_read_word(dtbuf); + switch (val) { + case OF_DT_PROP: + prop = flat_read_property(dtbuf, strbuf, flags); + add_property(node, prop); + break; + + case OF_DT_BEGIN_NODE: + child = unflatten_tree(dtbuf,strbuf, node->fullpath, + flags); + add_child(node, child); + break; + + case OF_DT_END_NODE: + break; + + case OF_DT_END: + die("Premature OF_DT_END in device tree blob\n"); + break; + + default: + die("Invalid opcode word %08x in device tree blob\n", + val); + } + } while (val != OF_DT_END_NODE); + + return node; +} + + +struct boot_info *dt_from_blob(FILE *f) +{ + u32 magic, totalsize, version, size_str; + u32 off_dt, off_str, off_mem_rsvmap; + int rc; + char *blob; + struct boot_param_header *bph; + char *p; + struct inbuf dtbuf, strbuf; + struct inbuf memresvbuf; + int sizeleft; + struct reserve_info *reservelist; + struct node *tree; + u32 val; + int flags = 0; + + rc = fread(&magic, sizeof(magic), 1, f); + if (ferror(f)) + die("Error reading DT blob magic number: %s\n", + strerror(errno)); + if (rc < 1) { + if (feof(f)) + die("EOF reading DT blob magic number\n"); + else + die("Mysterious short read reading magic number\n"); + } + + magic = be32_to_cpu(magic); + if (magic != OF_DT_HEADER) + die("Blob has incorrect magic number\n"); + + rc = fread(&totalsize, sizeof(totalsize), 1, f); + if (ferror(f)) + die("Error reading DT blob size: %s\n", strerror(errno)); + if (rc < 1) { + if (feof(f)) + die("EOF reading DT blob size\n"); + else + die("Mysterious short read reading blob size\n"); + } + + totalsize = be32_to_cpu(totalsize); + if (totalsize < BPH_V1_SIZE) + die("DT blob size (%d) is too small\n", totalsize); + + blob = xmalloc(totalsize); + + bph = (struct boot_param_header *)blob; + bph->magic = cpu_to_be32(magic); + bph->totalsize = cpu_to_be32(totalsize); + + sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); + p = blob + sizeof(magic) + sizeof(totalsize); + + while (sizeleft) { + if (feof(f)) + die("EOF before reading %d bytes of DT blob\n", + totalsize); + + rc = fread(p, 1, sizeleft, f); + if (ferror(f)) + die("Error reading DT blob: %s\n", + strerror(errno)); + + sizeleft -= rc; + p += rc; + } + + off_dt = be32_to_cpu(bph->off_dt_struct); + off_str = be32_to_cpu(bph->off_dt_strings); + off_mem_rsvmap = be32_to_cpu(bph->off_mem_rsvmap); + version = be32_to_cpu(bph->version); + + fprintf(stderr, "\tmagic:\t\t\t0x%x\n", magic); + fprintf(stderr, "\ttotalsize:\t\t%d\n", totalsize); + fprintf(stderr, "\toff_dt_struct:\t\t0x%x\n", off_dt); + fprintf(stderr, "\toff_dt_strings:\t\t0x%x\n", off_str); + fprintf(stderr, "\toff_mem_rsvmap:\t\t0x%x\n", off_mem_rsvmap); + fprintf(stderr, "\tversion:\t\t0x%x\n", version ); + fprintf(stderr, "\tlast_comp_version:\t0x%x\n", + be32_to_cpu(bph->last_comp_version)); + + 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 >= 2) + fprintf(stderr, "\tboot_cpuid_phys:\t0x%x\n", + be32_to_cpu(bph->boot_cpuid_phys)); + + if (version >= 3) { + size_str = be32_to_cpu(bph->size_dt_strings); + fprintf(stderr, "\tsize_dt_strings:\t%d\n", size_str); + if (off_str+size_str > totalsize) + die("String table extends past total size\n"); + } + + if (version < 0x10) { + flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; + } + + inbuf_init(&memresvbuf, + blob + off_mem_rsvmap, blob + totalsize); + inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); + inbuf_init(&strbuf, blob + off_str, blob + totalsize); + + if (version >= 3) + strbuf.limit = strbuf.base + size_str; + + reservelist = flat_read_mem_reserve(&memresvbuf); + + val = flat_read_word(&dtbuf); + + if (val != OF_DT_BEGIN_NODE) + die("Device tree blob doesn't begin with OF_DT_BEGIN_NODE (begins with 0x%08x)\n", val); + + tree = unflatten_tree(&dtbuf, &strbuf, "", flags); + + val = flat_read_word(&dtbuf); + if (val != OF_DT_END) + die("Device tree blob doesn't end with OF_DT_END\n"); + + free(blob); + + return build_boot_info(reservelist, tree); +} diff --git a/util/dtc/fstree.c b/util/dtc/fstree.c new file mode 100644 index 0000000000..a5bf9d3114 --- /dev/null +++ b/util/dtc/fstree.c @@ -0,0 +1,94 @@ +/* + * (C) Copyright David Gibson , 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 +#include + +static struct node *read_fstree(char *dirname) +{ + DIR *d; + struct dirent *de; + struct stat st; + struct node *tree; + + d = opendir(dirname); + if (! d) + die("opendir(): %s\n", 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(char *dirname) +{ + struct node *tree; + + tree = read_fstree(dirname); + tree = name_node(tree, "", NULL); + + fill_fullpaths(tree, ""); + + return build_boot_info(NULL, tree); +} + diff --git a/util/dtc/ftdump.c b/util/dtc/ftdump.c new file mode 100644 index 0000000000..4d4354b68c --- /dev/null +++ b/util/dtc/ftdump.c @@ -0,0 +1,188 @@ +/* + * ftdump.c - Contributed by Pantelis Antoniou + */ + +#include +#include +#include +#include +#include +#include + +#include "flat_dt.h" + +#define cpu_to_be16(x) htons(x) +#define be16_to_cpu(x) ntohs(x) + +#define cpu_to_be32(x) htonl(x) +#define be32_to_cpu(x) ntohl(x) + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be64(x) (x) +#define be64_to_cpu(x) (x) +#else +#define cpu_to_be64(x) bswap_64(x) +#define be64_to_cpu(x) bswap_64(x) +#endif + +#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\"", (char *)data); + } else if ((len % 4) == 0) { + printf(" = <"); + for (i = 0; i < len; i += 4) + printf("%08x%s", *((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 boot_param_header *bph = blob; + struct reserve_entry *p_rsvmap = + (struct reserve_entry *)(blob + + be32_to_cpu(bph->off_mem_rsvmap)); + char *p_struct = blob + be32_to_cpu(bph->off_dt_struct); + char *p_strings = blob + be32_to_cpu(bph->off_dt_strings); + uint32_t version = be32_to_cpu(bph->version); + uint32_t tag; + char *p; + char *s, *t; + int depth, sz, shift; + int i; + uint64_t addr, size; + + depth = 0; + shift = 4; + + printf("// Version 0x%x tree\n", version); + for (i = 0; ; i++) { + addr = be64_to_cpu(p_rsvmap[i].address); + size = be64_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 = be32_to_cpu(GET_CELL(p))) != OF_DT_END) { + + /* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */ + + if (tag == OF_DT_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 == OF_DT_END_NODE) { + depth--; + + printf("%*s};\n", depth * shift, ""); + continue; + } + + if (tag == OF_DT_NOP) { + printf("%*s// [NOP]\n", depth * shift, ""); + continue; + } + + if (tag != OF_DT_PROP) { + fprintf(stderr, "%*s ** Unknown tag 0x%08x\n", depth * shift, "", tag); + break; + } + sz = GET_CELL(p); + s = p_strings + be32_to_cpu(GET_CELL(p)); + if (version < 0x10 && 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; +} diff --git a/util/dtc/libdt.c b/util/dtc/libdt.c new file mode 100644 index 0000000000..d187ca95f2 --- /dev/null +++ b/util/dtc/libdt.c @@ -0,0 +1,230 @@ +/* + * (C) Copyright David Gibson , 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 +#include + +#include "flat_dt.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 char *skip_name(char *p) +{ + while (*p != '\0') + p++; + + return PALIGN(p, sizeof(uint32_t)); +} + +static char *skip_prop(void *blob, char *p) +{ + struct boot_param_header *bph = blob; + uint32_t len, nameoff; + + len = GET_CELL(p); + nameoff = GET_CELL(p); + if ((bph->version < 0x10) && (len >= sizeof(uint64_t))) + p = PALIGN(p, sizeof(uint64_t)); + return PALIGN(p + len, sizeof(uint32_t)); +} + +static char *get_unit(char *dtpath) +{ + char *p; + + if (dtpath[0] != '/') + return dtpath; + + p = dtpath + strlen(dtpath); + while (*p != '/') + p--; + + return p+1; +} + +static int first_seg_len(char *dtpath) +{ + int len = 0; + + while ((dtpath[len] != '/') && (dtpath[len] != '\0')) + len++; + + return len; +} + +char *flat_dt_get_string(void *blob, uint32_t offset) +{ + struct boot_param_header *bph = blob; + + return (char *)blob + bph->off_dt_strings + offset; +} + +void *flat_dt_get_subnode(void *blob, void *node, char *uname, int unamelen) +{ + struct boot_param_header *bph = blob; + char *p = node; + uint32_t tag; + int depth = 0; + char *nuname; + + if (! unamelen) + unamelen = strlen(uname); + + do { + tag = GET_CELL(p); + + switch (tag) { + case OF_DT_PROP: + p = skip_prop(blob, p); + break; + + case OF_DT_BEGIN_NODE: + if (depth == 0) { + nuname = p; + + if (bph->version < 0x10) + nuname = get_unit(nuname); + + p = skip_name(p); + + if (strncmp(nuname, uname, unamelen) == 0) + return p; + } + depth++; + break; + + case OF_DT_END_NODE: + depth--; + break; + + case OF_DT_END: + /* looks like a malformed tree */ + return NULL; + break; + + default: + /* FIXME: throw some sort of error */ + return NULL; + } + } while (depth >= 0); + + return NULL; +} + +void *flat_dt_get_node(void *blob, char *path) +{ + struct boot_param_header *bph = blob; + char *node; + int seglen; + + node = blob + bph->off_dt_struct; + node += sizeof(uint32_t); /* skip initial OF_DT_BEGIN_NODE */ + node = skip_name(node); /* skip root node name */ + + while (node && (*path)) { + if (path[0] == '/') + path++; + + seglen = first_seg_len(path); + + node = flat_dt_get_subnode(blob, node, path, seglen); + + path += seglen; + } + + return node; +} + +void flat_dt_traverse(void *blob, + int (*fn)(void *blob, void *node, void *priv), + void *private) +{ + struct boot_param_header *bph = blob; + char *p; + uint32_t tag; + int depth = 0; + char *uname; + + p = (char *)blob + bph->off_dt_struct; + + tag = GET_CELL(p); + while (tag != OF_DT_END) { + switch (tag) { + case OF_DT_BEGIN_NODE: + uname = p; + + if (bph->version < 0x10) + uname = get_unit(uname); + + p = skip_name(p); + + (*fn)(blob, p, private); + depth++; + break; + + case OF_DT_END_NODE: + depth--; + break; + + case OF_DT_PROP: + p = skip_prop(blob, p); + break; + + default: + /* FIXME: badly formed tree */ + return; + } + } +} + +void *flat_dt_get_prop(void *blob, void *node, char *name, uint32_t *len) +{ + struct boot_param_header *bph = blob; + char *p = node; + + do { + uint32_t tag = GET_CELL(p); + uint32_t sz, noff; + const char *nstr; + + if (tag != OF_DT_PROP) + return NULL; + + sz = GET_CELL(p); + noff = GET_CELL(p); + + /* Old versions have variable alignment of the + * property value */ + if ((bph->version < 0x10) && (sz >= 8)) + p = PALIGN(p, 8); + + nstr = flat_dt_get_string(blob, noff); + + if (strcmp(name, nstr) == 0) { + if (len) + *len = sz; + return (void *)p; + } + + p = PALIGN(p + sz, sizeof(uint32_t)); + } while(1); +} diff --git a/util/dtc/livetree.c b/util/dtc/livetree.c new file mode 100644 index 0000000000..f64de0174d --- /dev/null +++ b/util/dtc/livetree.c @@ -0,0 +1,742 @@ +/* + * (C) Copyright David Gibson , 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 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 = 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; + + p = &parent->children; + while (*p) + p = &((*p)->next_sibling); + + *p = child; +} + +struct reserve_info *build_reserve_entry(u64 address, u64 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; +} + +/* + * Tree accessor functions + */ + +static char *get_unitname(struct node *node) +{ + if (node->name[node->basenamelen] == '\0') + return ""; + else + return node->name + node->basenamelen + 1; +} + +static struct property *get_property(struct node *node, char *propname) +{ + struct property *prop; + + for_each_property(node, prop) + if (streq(prop->name, propname)) + return prop; + + return NULL; +} + +static cell_t propval_cell(struct property *prop) +{ + assert(prop->val.len == sizeof(cell_t)); + return be32_to_cpu(*((cell_t *)prop->val.val)); +} + +static struct node *get_subnode(struct node *node, char *nodename) +{ + struct node *child; + + for_each_child(node, child) + if (streq(child->name, nodename)) + return child; + + return NULL; +} + +static struct node *get_node_by_path(struct node *tree, char *path) +{ + 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; +} + +static 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; +} +/* + * Tree checking functions + */ + +#define ERRMSG(...) fprintf(stderr, "ERROR: " __VA_ARGS__) +#define WARNMSG(...) fprintf(stderr, "Warning: " __VA_ARGS__) + +static int must_be_one_cell(struct property *prop, struct node *node) +{ + if (prop->val.len != sizeof(cell_t)) { + ERRMSG("\"%s\" property in %s has the wrong length (should be 1 cell)\n", + prop->name, node->fullpath); + return 0; + } + + return 1; +} + +static int must_be_cells(struct property *prop, struct node *node) +{ + if ((prop->val.len % sizeof(cell_t)) != 0) { + ERRMSG("\"%s\" property in %s is not a multiple of cell size\n", + prop->name, node->fullpath); + return 0; + } + + return 1; +} + +static int must_be_string(struct property *prop, struct node *node) +{ + if (! data_is_one_string(prop->val)) { + ERRMSG("\"%s\" property in %s is not a string\n", + prop->name, node->fullpath); + return 0; + } + + return 1; +} + +static int name_prop_check(struct property *prop, struct node *node) +{ + if ((prop->val.len != node->basenamelen+1) + || !strneq(prop->val.val, node->name, node->basenamelen)) { + ERRMSG("name property \"%s\" does not match node basename in %s\n", + prop->val.val, + node->fullpath); + return 0; + } + + return 1; +} + +static struct { + char *propname; + int (*check_fn)(struct property *prop, struct node *node); +} prop_checker_table[] = { + {"name", must_be_string}, + {"name", name_prop_check}, + {"linux,phandle", must_be_one_cell}, + {"#address-cells", must_be_one_cell}, + {"#size-cells", must_be_one_cell}, + {"reg", must_be_cells}, + {"model", must_be_string}, + {"device_type", must_be_string}, +}; + +#define DO_ERR(...) do {ERRMSG(__VA_ARGS__); ok = 0; } while (0) + +static int check_properties(struct node *node) +{ + struct property *prop, *prop2; + int ok = 1; + + for_each_property(node, prop) { + int i; + + /* check for duplicates */ + /* FIXME: do this more efficiently */ + for (prop2 = prop->next; prop2; prop2 = prop2->next) { + if (streq(prop->name, prop2->name)) { + DO_ERR("Duplicate propertyname %s in node %s\n", + prop->name, node->fullpath); + } + } + + + /* check name length */ + if (strlen(prop->name) > MAX_PROPNAME_LEN) + WARNMSG("Property name %s is too long in %s\n", + prop->name, node->fullpath); + + /* check this property */ + for (i = 0; i < ARRAY_SIZE(prop_checker_table); i++) { + if (streq(prop->name, prop_checker_table[i].propname)) + if (! prop_checker_table[i].check_fn(prop, node)) { + ok = 0; + break; + } + } + } + + return ok; +} + +static int check_node_name(struct node *node) +{ + int ok = 1; + int len = strlen(node->name); + + if (len == 0 && node->parent) + DO_ERR("Empty, non-root nodename at %s\n", node->fullpath); + + if (len > MAX_NODENAME_LEN) + DO_ERR("Overlength nodename at %s\n", node->fullpath); + + + return ok; +} + +static int check_structure(struct node *tree) +{ + struct node *child, *child2; + int ok = 1; + + if (! check_node_name(tree)) + ok = 0; + + if (! check_properties(tree)) + ok = 0; + + for_each_child(tree, child) { + /* Check for duplicates */ + + for (child2 = child->next_sibling; + child2; + child2 = child2->next_sibling) { + if (streq(child->name, child2->name)) + DO_ERR("Duplicate node name %s\n", + child->fullpath); + } + if (! check_structure(child)) + ok = 0; + } + + return ok; +} + +#define CHECK_HAVE(node, propname) \ + do { \ + if (! (prop = get_property((node), (propname)))) \ + DO_ERR("Missing \"%s\" property in %s\n", (propname), \ + (node)->fullpath); \ + } while (0); + +#define CHECK_HAVE_WARN(node, propname) \ + do { \ + if (! (prop = get_property((node), (propname)))) \ + WARNMSG("%s has no \"%s\" property\n", \ + (node)->fullpath, (propname)); \ + } while (0) + +#define CHECK_HAVE_STRING(node, propname) \ + do { \ + CHECK_HAVE((node), (propname)); \ + if (prop && !data_is_one_string(prop->val)) \ + DO_ERR("\"%s\" property in %s is not a string\n", \ + (propname), (node)->fullpath); \ + } while (0) + +#define CHECK_HAVE_STREQ(node, propname, value) \ + do { \ + CHECK_HAVE_STRING((node), (propname)); \ + if (prop && !streq(prop->val.val, (value))) \ + DO_ERR("%s has wrong %s, %s (should be %s\n", \ + (node)->fullpath, (propname), \ + prop->val.val, (value)); \ + } while (0) + +#define CHECK_HAVE_ONECELL(node, propname) \ + do { \ + CHECK_HAVE((node), (propname)); \ + if (prop && (prop->val.len != sizeof(cell_t))) \ + DO_ERR("\"%s\" property in %s has wrong size %d (should be 1 cell)\n", (propname), (node)->fullpath, prop->val.len); \ + } while (0) + +#define CHECK_HAVE_WARN_ONECELL(node, propname) \ + do { \ + CHECK_HAVE_WARN((node), (propname)); \ + if (prop && (prop->val.len != sizeof(cell_t))) \ + DO_ERR("\"%s\" property in %s has wrong size %d (should be 1 cell)\n", (propname), (node)->fullpath, prop->val.len); \ + } while (0) + +#define CHECK_HAVE_WARN_PHANDLE(xnode, propname, root) \ + do { \ + struct node *ref; \ + CHECK_HAVE_WARN_ONECELL((xnode), (propname)); \ + if (prop) {\ + ref = get_node_by_phandle((root), propval_cell(prop)); \ + if (! ref) \ + DO_ERR("\"%s\" property in %s refers to non-existant phandle %x\n", (propname), (xnode)->fullpath, propval_cell(prop)); \ + } \ + } while (0) + +#define CHECK_HAVE_WARN_STRING(node, propname) \ + do { \ + CHECK_HAVE_WARN((node), (propname)); \ + if (prop && !data_is_one_string(prop->val)) \ + DO_ERR("\"%s\" property in %s is not a string\n", \ + (propname), (node)->fullpath); \ + } while (0) + +static int check_root(struct node *root) +{ + struct property *prop; + int ok = 1; + + CHECK_HAVE_STRING(root, "model"); + + CHECK_HAVE(root, "#address-cells"); + CHECK_HAVE(root, "#size-cells"); + + CHECK_HAVE_WARN(root, "compatible"); + + return ok; +} + +static int check_cpus(struct node *root, int outversion, int boot_cpuid_phys) +{ + struct node *cpus, *cpu; + struct property *prop; + struct node *bootcpu = NULL; + int ok = 1; + + cpus = get_subnode(root, "cpus"); + if (! cpus) { + ERRMSG("Missing /cpus node\n"); + return 0; + } + + CHECK_HAVE_WARN(cpus, "#address-cells"); + CHECK_HAVE_WARN(cpus, "#size-cells"); + + for_each_child(cpus, cpu) { + CHECK_HAVE_STREQ(cpu, "device_type", "cpu"); + + if (cpu->addr_cells != 1) + DO_ERR("%s has bad #address-cells value %d (should be 1)\n", + cpu->fullpath, cpu->addr_cells); + if (cpu->size_cells != 0) + DO_ERR("%s has bad #size-cells value %d (should be 0)\n", + cpu->fullpath, cpu->size_cells); + + CHECK_HAVE_ONECELL(cpu, "reg"); + if (prop) { + cell_t unitnum; + char *eptr; + + unitnum = strtol(get_unitname(cpu), &eptr, 16); + if (*eptr) + WARNMSG("%s has bad format unit name %s (should be CPU number\n", + cpu->fullpath, get_unitname(cpu)); + else if (unitnum != propval_cell(prop)) + WARNMSG("%s unit name \"%s\" does not match \"reg\" property <%x>\n", + cpu->fullpath, get_unitname(cpu), + propval_cell(prop)); + } + +/* CHECK_HAVE_ONECELL(cpu, "d-cache-line-size"); */ +/* CHECK_HAVE_ONECELL(cpu, "i-cache-line-size"); */ + CHECK_HAVE_ONECELL(cpu, "d-cache-size"); + CHECK_HAVE_ONECELL(cpu, "i-cache-size"); + + CHECK_HAVE_WARN_ONECELL(cpu, "clock-frequency"); + CHECK_HAVE_WARN_ONECELL(cpu, "timebase-frequency"); + + prop = get_property(cpu, "linux,boot-cpu"); + if (prop) { + if (prop->val.len) + WARNMSG("\"linux,boot-cpu\" property in %s is non-empty\n", + cpu->fullpath); + if (bootcpu) + DO_ERR("Multiple boot cpus (%s and %s)\n", + bootcpu->fullpath, cpu->fullpath); + else + bootcpu = cpu; + } + } + + if (outversion < 2) { + if (! bootcpu) + WARNMSG("No cpu has \"linux,boot-cpu\" property\n"); + } else { + if (bootcpu) + WARNMSG("\"linux,boot-cpu\" property is deprecated in blob version 2 or higher\n"); + if (boot_cpuid_phys == 0xfeedbeef) + WARNMSG("physical boot CPU not set. Use -b option to set\n"); + } + + return ok; +} + +static int check_memory(struct node *root) +{ + struct node *mem; + struct property *prop; + int nnodes = 0; + int ok = 1; + + for_each_child(root, mem) { + if (! strneq(mem->name, "memory", mem->basenamelen)) + continue; + + nnodes++; + + CHECK_HAVE_STREQ(mem, "device_type", "memory"); + CHECK_HAVE(mem, "reg"); + } + + if (nnodes == 0) { + ERRMSG("No memory nodes\n"); + return 0; + } + + return ok; +} + +static int check_chosen(struct node *root) +{ + struct node *chosen; + struct property *prop; + int ok = 1; + + chosen = get_subnode(root, "chosen"); + if (! chosen) { + ERRMSG("Missing /chosen node\n"); + return 0; + } + + CHECK_HAVE_ONECELL(chosen, "linux,platform"); + + CHECK_HAVE_WARN_STRING(chosen, "bootargs"); + CHECK_HAVE_WARN_STRING(chosen, "linux,stdout-path"); + CHECK_HAVE_WARN_PHANDLE(chosen, "interrupt-controller", root); + + return ok; +} + +static int check_addr_size_reg(struct node *node, + int p_addr_cells, int p_size_cells) +{ + int addr_cells = p_addr_cells; + int size_cells = p_size_cells; + struct property *prop; + struct node *child; + int ok = 1; + + node->addr_cells = addr_cells; + node->size_cells = size_cells; + + prop = get_property(node, "reg"); + if (prop) { + int len = prop->val.len / 4; + + if ((len % (addr_cells+size_cells)) != 0) + DO_ERR("\"reg\" property in %s has invalid length (%d) for given #address-cells (%d) and #size-cells (%d)\n", + node->fullpath, prop->val.len, + addr_cells, size_cells); + } + + prop = get_property(node, "#address-cells"); + if (prop) + addr_cells = propval_cell(prop); + + prop = get_property(node, "#size-cells"); + if (prop) + size_cells = propval_cell(prop); + + for_each_child(node, child) { + ok = ok && check_addr_size_reg(child, addr_cells, size_cells); + } + + return ok; +} + +static int check_phandles(struct node *root, struct node *node) +{ + struct property *prop; + struct node *child, *other; + cell_t phandle; + int ok = 1; + + prop = get_property(node, "linux,phandle"); + if (prop) { + phandle = propval_cell(prop); + if ((phandle == 0) || (phandle == -1)) { + DO_ERR("%s has invalid linux,phandle %x\n", + node->fullpath, phandle); + } else { + other = get_node_by_phandle(root, phandle); + if (other) + DO_ERR("%s has duplicated phandle %x (seen before at %s)\n", + node->fullpath, phandle, other->fullpath); + + node->phandle = phandle; + } + } + + for_each_child(node, child) + ok = ok && check_phandles(root, child); + + return 1; +} + +static cell_t get_node_phandle(struct node *root, struct node *node) +{ + static cell_t phandle = 1; /* FIXME: ick, static local */ + + fprintf(stderr, "get_node_phandle(%s) phandle=%x\n", + node->fullpath, node->phandle); + + 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; +} + +static void apply_fixup(struct node *root, struct property *prop, + struct fixup *f) +{ + struct node *refnode; + cell_t phandle; + + refnode = get_node_by_path(root, f->ref); + if (! refnode) + die("Reference to non-existent node \"%s\"\n", f->ref); + + phandle = get_node_phandle(root, refnode); + + assert(f->offset + sizeof(cell_t) <= prop->val.len); + + *((cell_t *)(prop->val.val + f->offset)) = cpu_to_be32(phandle); +} + +static void fixup_phandles(struct node *root, struct node *node) +{ + struct property *prop; + struct node *child; + + for_each_property(node, prop) { + struct fixup *f = prop->val.refs; + + while (f) { + apply_fixup(root, prop, f); + prop->val.refs = f->next; + fixup_free(f); + f = prop->val.refs; + } + } + + for_each_child(node, child) + fixup_phandles(root, child); +} + +int check_device_tree(struct node *dt, int outversion, int boot_cpuid_phys) +{ + int ok = 1; + + if (! check_structure(dt)) + return 0; + + ok = ok && check_addr_size_reg(dt, -1, -1); + ok = ok && check_phandles(dt, dt); + + fixup_phandles(dt, dt); + + if (! ok) + return 0; + + ok = ok && check_root(dt); + ok = ok && check_cpus(dt, outversion, boot_cpuid_phys); + ok = ok && check_memory(dt); + ok = ok && check_chosen(dt); + if (! ok) + return 0; + + return 1; +} + +struct boot_info *build_boot_info(struct reserve_info *reservelist, + struct node *tree) +{ + struct boot_info *bi; + + bi = xmalloc(sizeof(*bi)); + bi->reservelist = reservelist; + bi->dt = tree; + + return bi; +} diff --git a/util/dtc/newstatic.c b/util/dtc/newstatic.c new file mode 100644 index 0000000000..2707e5d1a3 --- /dev/null +++ b/util/dtc/newstatic.c @@ -0,0 +1,116 @@ +struct root { + u32 model; + u32 Haddress_cells; + u32 Hsize_cells; + u32 compatible; +}; /*root*/ +struct cpus { + u32 Haddress_cells; + u32 Hsize_cells; +}; /*cpus*/ +struct emulation_qemu_i386 { + u32 name; + u32 device_type; + u32 clock_frequency; + u32 timebase_frequency; + u32 linux_boot_cpu; + u32 reg; + u32 i_cache_size; + u32 d_cache_size; + u32 linux_phandle; +}; /*emulation_qemu_i386*/ +struct memory { + u32 device_type; + u32 reg; +}; /*memory*/ +struct northbridge_intel_440bx { + u32 associated_cpu; +}; /*northbridge_intel_440bx*/ +struct southbridge_intel_piix4 { +}; /*southbridge_intel_piix4*/ +struct superio_nsc_sucks { +}; /*superio_nsc_sucks*/ +struct uart { + u32 enabled; +}; /*uart*/ +struct chosen { + u32 bootargs; + u32 linux_platform; + u32 linux_stdout_path; +}; /*chosen*/ +struct options { + u32 normal; + u32 fallback; +}; /*options*/ +struct root root = { + u8 model = {71,65,6d,75,00,}; + u8 Haddress_cells = {00,00,00,01,}; + u8 Hsize_cells = {00,00,00,01,}; + u8 compatible = {65,6d,75,6c,61,74,69,6f,6e,2d,69,33,38,36,2c,71,65,6d,75,00,}; +}; /*root*/ +struct device dev_root = { + .children = dev_cpus +} +struct cpus cpus = { + u8 Haddress_cells = {00,00,00,01,}; + u8 Hsize_cells = {00,00,00,00,}; +}; /*cpus*/ +struct device dev_cpus = { + .sibling = dev_memory_0 + .children = dev_emulation_qemu_i386_0 +} +struct emulation_qemu_i386 emulation_qemu_i386_0 = { + u8 name = {65,6d,75,6c,61,74,69,6f,6e,2c,71,65,6d,75,2d,69,33,38,36,00,}; + u8 device_type = {63,70,75,00,}; + u8 clock_frequency = {5f,5e,10,00,}; + u8 timebase_frequency = {01,fffffffc,ffffffa0,55,}; + u8 reg = {00,00,00,00,}; + u8 i_cache_size = {00,00,20,00,}; + u8 d_cache_size = {00,00,20,00,}; + u8 linux_phandle = {00,00,00,01,}; +}; /*emulation_qemu_i386_0*/ +struct device dev_emulation_qemu_i386_0 = { +} +struct memory memory_0 = { + u8 device_type = {6d,65,6d,6f,72,79,00,}; + u8 reg = {00,00,00,00,20,00,00,00,}; +}; /*memory_0*/ +struct device dev_memory_0 = { + .sibling = dev_northbridge_intel_440bx +} +struct northbridge_intel_440bx northbridge_intel_440bx = { + u8 associated_cpu = {00,00,00,01,}; +}; /*northbridge_intel_440bx*/ +struct device dev_northbridge_intel_440bx = { + .sibling = dev_chosen + .children = dev_southbridge_intel_piix4 +} +struct southbridge_intel_piix4 southbridge_intel_piix4 = { +}; /*southbridge_intel_piix4*/ +struct device dev_southbridge_intel_piix4 = { + .children = dev_superio_nsc_sucks +} +struct superio_nsc_sucks superio_nsc_sucks = { +}; /*superio_nsc_sucks*/ +struct device dev_superio_nsc_sucks = { + .children = dev_uart_0 +} +struct uart uart_0 = { + u8 enabled = {00,00,00,01,}; +}; /*uart_0*/ +struct device dev_uart_0 = { +} +struct chosen chosen = { + u8 bootargs = {72,6f,6f,74,3d,2f,64,65,76,2f,73,64,61,32,00,}; + u8 linux_platform = {00,00,06,00,}; + u8 linux_stdout_path = {2f,64,65,76,2f,74,74,79,53,30,00,}; +}; /*chosen*/ +struct device dev_chosen = { + .sibling = dev_options +} +struct options options = { + u8 normal = {6e,6f,72,6d,61,6c,00,}; + u8 fallback = {66,61,6c,6c,62,61,63,6b,00,}; +}; /*options*/ +struct device dev_options = { +} diff --git a/util/dtc/test.dts b/util/dtc/test.dts new file mode 100644 index 0000000000..742d874951 --- /dev/null +++ b/util/dtc/test.dts @@ -0,0 +1,54 @@ +/memreserve/ 1000000000000000 0000000002000000; +/memreserve/ 2000000000000000-20ffffffffffffff; +/memreserve/ 0-13; + +/ { + model = "MyBoardName"; + compatible = "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>; + }; + + }; + + randomnode { + string = "\xff\0stuffstuff\t\t\t\n\n\n"; + blob = [0a 0b 0c 0d de ea ad be ef]; + ref = < &/memory@0 >; + }; + + memory@0 { + device_type = "memory"; + memreg: reg = <00000000 00000000 00000000 20000000>; + }; + + chosen { + bootargs = "root=/dev/sda2"; + linux,platform = <00000600>; + }; + +}; diff --git a/util/dtc/tests/Makefile b/util/dtc/tests/Makefile new file mode 100644 index 0000000000..912f832e32 --- /dev/null +++ b/util/dtc/tests/Makefile @@ -0,0 +1,12 @@ +DTC = ../dtc +VG_DTC = valgrind --tool=memcheck ../dtc + +check: all + ./run_all_tests.sh + +all: + +clean: + rm -f *~ + + diff --git a/util/dtc/tests/empty.dts b/util/dtc/tests/empty.dts new file mode 100644 index 0000000000..336d7a2506 --- /dev/null +++ b/util/dtc/tests/empty.dts @@ -0,0 +1,2 @@ +/ { +}; diff --git a/util/dtc/tests/run_all_tests.sh b/util/dtc/tests/run_all_tests.sh new file mode 100755 index 0000000000..a8b7c3e935 --- /dev/null +++ b/util/dtc/tests/run_all_tests.sh @@ -0,0 +1,2 @@ +#! /bin/sh + diff --git a/util/dtc/treesource.c b/util/dtc/treesource.c new file mode 100644 index 0000000000..e9bbaa51bb --- /dev/null +++ b/util/dtc/treesource.c @@ -0,0 +1,159 @@ +/* + * (C) Copyright David Gibson , 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" + +extern FILE *yyin; +extern int yyparse(void); +extern void yyerror(char const *); + +struct boot_info *the_boot_info; + +struct boot_info *dt_from_source(FILE *f) +{ + the_boot_info = NULL; + + yyin = f; + if (yyparse() != 0) + return NULL; + + fill_fullpaths(the_boot_info->dt, ""); + + return the_boot_info; +} + +static void write_prefix(FILE *f, int level) +{ + int i; + + for (i = 0; i < level; i++) + fputc('\t', f); +} + +enum proptype { + PROP_EMPTY, + PROP_STRING, + PROP_CELLS, + PROP_BYTES, +}; + +static enum proptype guess_type(struct property *prop) +{ + int len = prop->val.len; + char *p = prop->val.val; + int nnoprint = 0; + int i; + + if (len == 0) + return PROP_EMPTY; + + for (i = 0; i < len; i++) { + if (! isprint(p[i])) + nnoprint++; + } + + if ((nnoprint == 1) && (p[len-1] == '\0')) + return PROP_STRING; + else if ((len % sizeof(cell_t)) == 0) + return PROP_CELLS; + else + return PROP_BYTES; + +} + + +static void write_tree_source_node(FILE *f, struct node *tree, int level) +{ + struct property *prop; + struct node *child; + + write_prefix(f, level); + if (tree->name && (*tree->name)) + fprintf(f, "%s {\n", tree->name); + else + fprintf(f, "/ {\n"); + + for_each_property(tree, prop) { + cell_t *cp; + char *bp; + void *propend; + enum proptype type; + + write_prefix(f, level); + fprintf(f, "\t%s", prop->name); + type = guess_type(prop); + propend = prop->val.val + prop->val.len; + + switch (type) { + case PROP_EMPTY: + fprintf(f, ";\n"); + break; + + case PROP_STRING: + fprintf(f, " = \"%s\";\n", (char *)prop->val.val); + break; + + case PROP_CELLS: + fprintf(f, " = <"); + cp = (cell_t *)prop->val.val; + for (;;) { + fprintf(f, "%x", be32_to_cpu(*cp++)); + if ((void *)cp >= propend) + break; + fprintf(f, " "); + } + fprintf(f, ">;\n"); + break; + + case PROP_BYTES: + fprintf(f, " = ["); + bp = prop->val.val; + for (;;) { + fprintf(f, "%02hhx", *bp++); + if ((void *)bp >= propend) + break; + fprintf(f, " "); + } + fprintf(f, "];\n"); + break; + } + } + for_each_child(tree, child) { + fprintf(f, "\n"); + write_tree_source_node(f, child, level+1); + } + write_prefix(f, level); + fprintf(f, "};\n"); +} + + +void dt_to_source(FILE *f, struct boot_info *bi) +{ + struct reserve_info *re; + + for (re = bi->reservelist; re; re = re->next) { + fprintf(f, "/memreserve/\t%016llx-%016llx;\n", + (unsigned long long)re->re.address, + (unsigned long long)(re->re.address + re->re.size - 1)); + } + + write_tree_source_node(f, bi->dt, 0); +} +